Compare commits

...

46 Commits

Author SHA1 Message Date
Marc Mueller
b63ea35959 Update requests to 2.33.1 (#167014) 2026-04-01 00:32:00 +02:00
J. Nick Koston
bb345dfd09 Bump aiohttp to 3.13.5 (#167015) 2026-03-31 12:25:09 -10:00
smarthome-10
c05c2b7f70 Rename component to integration in Start.ca (#166989) 2026-03-31 23:30:29 +02:00
Leon Grave
3d07ec8696 Add freshr reconfiguration flow (#166907) 2026-03-31 23:27:33 +02:00
Marc Mueller
3b396814ae Update mypy to 1.20.0 (#167000) 2026-03-31 23:27:18 +02:00
smarthome-10
b2047c1aca Rename component to integration in SNMP (#166994) 2026-03-31 23:26:11 +02:00
smarthome-10
2b0cff2c93 Rename component to integration in DNS IP (#166993) 2026-03-31 23:24:21 +02:00
smarthome-10
fa7af34678 Rename component to integration in EBox (#166996) 2026-03-31 23:24:19 +02:00
smarthome-10
7563ea6217 Rename component to integration in Bbox (#166998) 2026-03-31 23:24:17 +02:00
smarthome-10
08726af215 Rename component to integration in EBox (#166996) 2026-03-31 23:22:51 +02:00
smarthome-10
4fa1d6b0a1 Rename component to integration in Actiontec (#167004) 2026-03-31 23:22:25 +02:00
smarthome-10
3c86f1eee8 Rename component to integration in Fido (#166997) 2026-03-31 23:22:05 +02:00
smarthome-10
3a63f9fbb1 Rename component to integration in Tomato (#167002) 2026-03-31 23:20:52 +02:00
smarthome-10
7b5408d20c Rename component to integration in Denon Network Receivers (#167006) 2026-03-31 23:19:14 +02:00
potelux
058e8ba455 Add reload service to shell_command (#166557)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-03-31 23:16:54 +02:00
smarthome-10
bba3c0e6bb Rename component to integration in Denon AVR (#167008) 2026-03-31 23:13:44 +02:00
smarthome-10
a266976c33 Rename component to integration in Edimax (#167011) 2026-03-31 23:12:48 +02:00
smarthome-10
f29c051c73 Rename component to integration in BlinkStick (#167009) 2026-03-31 23:11:02 +02:00
smarthome-10
8842b4840e Rename component to integration in Glances (#167012) 2026-03-31 23:09:00 +02:00
potelux
586d2ceff6 Add reload service to shell_command (#166557)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-03-31 23:07:26 +02:00
epenet
69a2284a00 Migrate nightscout to use runtime_data (#166927)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 22:48:29 +02:00
Manu
19761a25da Improve strings in HTML5 integration (#166985) 2026-03-31 22:45:32 +02:00
dontinelli
e4328fe34d Bump solarlog_cli to 0.7.1 (#166990) 2026-03-31 22:26:03 +02:00
Jackson_57
e91b49e7cd Bump led-ble to 1.1.8 (#166999) 2026-03-31 22:21:39 +02:00
Brett Adams
7d145cd3b8 Add command compatibility scaffold for Tessie migration (#166458) 2026-03-31 21:52:09 +02:00
Denis Shulyaka
962d5386c7 Add diagnostics to Anthropic integration (#166739) 2026-03-31 21:35:09 +02:00
Joost Lekkerkerker
3ba985f771 Pull out Dropbox integration (#166986) 2026-03-31 20:40:04 +02:00
Ariel Ebersberger
ef6718c242 Add skeleton with repair issue to bmw integration (#166983)
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-03-31 20:31:45 +02:00
Denis Shulyaka
02bcae00cf Document supported features for Anthropic integration (#166818) 2026-03-31 21:27:12 +03:00
Norbert Rittel
d6cd1dffa4 Fix grammar of input_shutdown_failure error in victron_ble (#166972) 2026-03-31 20:00:37 +02:00
Joost Lekkerkerker
fc32f0dbd3 Make sure we can fetch player stats in Chess.com (#166980) 2026-03-31 19:59:28 +02:00
Manu
cda1974e40 Add html5.dismiss_message action to HTML5 integration (#166909) 2026-03-31 19:02:58 +02:00
epenet
5425e82fb4 Migrate nuheat to use runtime_data (#166937)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 17:55:47 +01:00
Claw Explorer
84f36b0d4d Migrate tilt_ble to use runtime_data (#166663) 2026-03-31 18:33:48 +02:00
Bram Kragten
0807525e1b Update frontend to 20260325.4 (#166970) 2026-03-31 18:26:51 +02:00
Erik Montnemery
73a86b8606 Remove redundant field descriptions from triggers and conditions (#166955) 2026-03-31 17:46:07 +02:00
Erik Montnemery
b8652e70e5 Remove calendar and todo from unconditionally loaded integrations (#166951)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2026-03-31 17:39:42 +02:00
Marc Mueller
a3f3b0bed4 Fix lingering tasks in update_coordinator test (#166968) 2026-03-31 16:21:26 +02:00
prpr19xx
daaa68ce22 London Underground integration: Add Tram and IFS Cloud Cable Car status (#166712) 2026-03-31 16:01:21 +02:00
Branden Cash
9ada10e0cf Bump srpenergy to 1.3.8 (#166926) 2026-03-31 15:48:48 +02:00
Andrew Jackson
35287c381b Bump aiomealie to 1.2.3 (#166942) 2026-03-31 15:42:14 +02:00
bkobus-bbx
2ff84b633c Add myself to blebox codeowners (#166966) 2026-03-31 15:38:39 +02:00
Andrew Jackson
c09d91765f Bump aiomealie to 1.2.3 (#166942) 2026-03-31 15:36:23 +02:00
Manu
ac6ddf32c8 Fix StopIteration error in ista EcoTrend coordinator (#166929) 2026-03-31 15:35:17 +02:00
Paul Bottein
f15d9e5956 Fix Shutdown grammar in Synology DSM strings (#166946) 2026-03-31 15:32:07 +02:00
Paul Bottein
f95601a2e7 Fix "Shutdown" grammar in Roborock strings (#166948)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:26:21 +02:00
164 changed files with 1418 additions and 2411 deletions

View File

@@ -174,7 +174,6 @@ homeassistant.components.dnsip.*
homeassistant.components.doorbird.*
homeassistant.components.dormakaba_dkey.*
homeassistant.components.downloader.*
homeassistant.components.dropbox.*
homeassistant.components.droplet.*
homeassistant.components.dsmr.*
homeassistant.components.duckdns.*

6
CODEOWNERS generated
View File

@@ -222,8 +222,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/binary_sensor/ @home-assistant/core
/tests/components/binary_sensor/ @home-assistant/core
/homeassistant/components/bizkaibus/ @UgaitzEtxebarria
/homeassistant/components/blebox/ @bbx-a @swistakm
/tests/components/blebox/ @bbx-a @swistakm
/homeassistant/components/blebox/ @bbx-a @swistakm @bkobus-bbx
/tests/components/blebox/ @bbx-a @swistakm @bkobus-bbx
/homeassistant/components/blink/ @fronzbot
/tests/components/blink/ @fronzbot
/homeassistant/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23
@@ -401,8 +401,6 @@ build.json @home-assistant/supervisor
/tests/components/dremel_3d_printer/ @tkdrob
/homeassistant/components/drop_connect/ @ChandlerSystems @pfrazer
/tests/components/drop_connect/ @ChandlerSystems @pfrazer
/homeassistant/components/dropbox/ @bdr99
/tests/components/dropbox/ @bdr99
/homeassistant/components/droplet/ @sarahseidman
/tests/components/droplet/ @sarahseidman
/homeassistant/components/dsmr/ @Robbie1221

View File

@@ -238,7 +238,9 @@ DEFAULT_INTEGRATIONS = {
"timer",
#
# Base platforms:
*BASE_PLATFORMS,
# Note: Calendar and todo are not included to prevent them from registering
# their frontend panels when there are no calendar or todo integrations.
*(BASE_PLATFORMS - {"calendar", "todo"}),
#
# Integrations providing triggers and conditions for base platforms:
"air_quality",

View File

@@ -1 +1 @@
"""The actiontec component."""
"""The Actiontec integration."""

View File

@@ -1,25 +1,18 @@
{
"common": {
"condition_behavior_description": "How the value should match on the targeted entities.",
"condition_behavior_name": "Behavior",
"condition_threshold_description": "What to test for and threshold values.",
"condition_threshold_name": "Threshold configuration",
"trigger_behavior_description": "The behavior of the targeted entities to trigger on.",
"trigger_behavior_name": "Behavior",
"trigger_threshold_changed_description": "Which changes to trigger on and threshold values.",
"trigger_threshold_crossed_description": "Which threshold crossing to trigger on and threshold values.",
"trigger_threshold_name": "Threshold configuration"
"condition_behavior_name": "Condition passes if",
"condition_threshold_name": "Threshold type",
"trigger_behavior_name": "Trigger when",
"trigger_threshold_name": "Threshold type"
},
"conditions": {
"is_co2_value": {
"description": "Tests the carbon dioxide level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -29,7 +22,6 @@
"description": "Tests if one or more carbon monoxide sensors are cleared.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
}
},
@@ -39,7 +31,6 @@
"description": "Tests if one or more carbon monoxide sensors are detecting carbon monoxide.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
}
},
@@ -49,11 +40,9 @@
"description": "Tests the carbon monoxide level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -63,7 +52,6 @@
"description": "Tests if one or more gas sensors are cleared.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
}
},
@@ -73,7 +61,6 @@
"description": "Tests if one or more gas sensors are detecting gas.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
}
},
@@ -83,11 +70,9 @@
"description": "Tests the nitrous oxide level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -97,11 +82,9 @@
"description": "Tests the nitrogen dioxide level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -111,11 +94,9 @@
"description": "Tests the nitrogen monoxide level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -125,11 +106,9 @@
"description": "Tests the ozone level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -139,11 +118,9 @@
"description": "Tests the PM10 level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -153,11 +130,9 @@
"description": "Tests the PM1 level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -167,11 +142,9 @@
"description": "Tests the PM2.5 level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -181,11 +154,9 @@
"description": "Tests the PM4 level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -195,7 +166,6 @@
"description": "Tests if one or more smoke sensors are cleared.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
}
},
@@ -205,7 +175,6 @@
"description": "Tests if one or more smoke sensors are detecting smoke.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
}
},
@@ -215,11 +184,9 @@
"description": "Tests the sulphur dioxide level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -229,11 +196,9 @@
"description": "Tests the volatile organic compounds ratio of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -243,11 +208,9 @@
"description": "Tests the volatile organic compounds level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::condition_behavior_description%]",
"name": "[%key:component::air_quality::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::condition_threshold_description%]",
"name": "[%key:component::air_quality::common::condition_threshold_name%]"
}
},
@@ -275,7 +238,6 @@
"description": "Triggers after one or more carbon dioxide levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -285,11 +247,9 @@
"description": "Triggers after one or more carbon dioxide levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -299,7 +259,6 @@
"description": "Triggers after one or more carbon monoxide levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -309,7 +268,6 @@
"description": "Triggers after one or more carbon monoxide sensors stop detecting carbon monoxide.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
}
},
@@ -319,11 +277,9 @@
"description": "Triggers after one or more carbon monoxide levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -333,7 +289,6 @@
"description": "Triggers after one or more carbon monoxide sensors start detecting carbon monoxide.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
}
},
@@ -343,7 +298,6 @@
"description": "Triggers after one or more gas sensors stop detecting gas.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
}
},
@@ -353,7 +307,6 @@
"description": "Triggers after one or more gas sensors start detecting gas.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
}
},
@@ -363,7 +316,6 @@
"description": "Triggers after one or more nitrous oxide levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -373,11 +325,9 @@
"description": "Triggers after one or more nitrous oxide levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -387,7 +337,6 @@
"description": "Triggers after one or more nitrogen dioxide levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -397,11 +346,9 @@
"description": "Triggers after one or more nitrogen dioxide levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -411,7 +358,6 @@
"description": "Triggers after one or more nitrogen monoxide levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -421,11 +367,9 @@
"description": "Triggers after one or more nitrogen monoxide levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -435,7 +379,6 @@
"description": "Triggers after one or more ozone levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -445,11 +388,9 @@
"description": "Triggers after one or more ozone levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -459,7 +400,6 @@
"description": "Triggers after one or more PM10 levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -469,11 +409,9 @@
"description": "Triggers after one or more PM10 levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -483,7 +421,6 @@
"description": "Triggers after one or more PM1 levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -493,11 +430,9 @@
"description": "Triggers after one or more PM1 levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -507,7 +442,6 @@
"description": "Triggers after one or more PM2.5 levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -517,11 +451,9 @@
"description": "Triggers after one or more PM2.5 levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -531,7 +463,6 @@
"description": "Triggers after one or more PM4 levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -541,11 +472,9 @@
"description": "Triggers after one or more PM4 levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -555,7 +484,6 @@
"description": "Triggers after one or more smoke sensors stop detecting smoke.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
}
},
@@ -565,7 +493,6 @@
"description": "Triggers after one or more smoke sensors start detecting smoke.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
}
},
@@ -575,7 +502,6 @@
"description": "Triggers after one or more sulphur dioxide levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -585,11 +511,9 @@
"description": "Triggers after one or more sulphur dioxide levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -599,7 +523,6 @@
"description": "Triggers after one or more volatile organic compound levels change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -609,11 +532,9 @@
"description": "Triggers after one or more volatile organic compounds levels cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -623,7 +544,6 @@
"description": "Triggers after one or more volatile organic compound ratios change.",
"fields": {
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_changed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},
@@ -633,11 +553,9 @@
"description": "Triggers after one or more volatile organic compounds ratios cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::air_quality::common::trigger_behavior_description%]",
"name": "[%key:component::air_quality::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::air_quality::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::air_quality::common::trigger_threshold_name%]"
}
},

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted alarms.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted alarms to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_armed": {
"description": "Tests if one or more alarms are armed.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::condition_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more alarms are armed in away mode.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::condition_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
}
},
@@ -30,7 +26,6 @@
"description": "Tests if one or more alarms are armed in home mode.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::condition_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
}
},
@@ -40,7 +35,6 @@
"description": "Tests if one or more alarms are armed in night mode.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::condition_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
}
},
@@ -50,7 +44,6 @@
"description": "Tests if one or more alarms are armed in vacation mode.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::condition_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
}
},
@@ -60,7 +53,6 @@
"description": "Tests if one or more alarms are disarmed.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::condition_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
}
},
@@ -70,7 +62,6 @@
"description": "Tests if one or more alarms are triggered.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::condition_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::condition_behavior_name%]"
}
},
@@ -242,7 +233,6 @@
"description": "Triggers after one or more alarms become armed, regardless of the mode.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
}
},
@@ -252,7 +242,6 @@
"description": "Triggers after one or more alarms become armed in away mode.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
}
},
@@ -262,7 +251,6 @@
"description": "Triggers after one or more alarms become armed in home mode.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
}
},
@@ -272,7 +260,6 @@
"description": "Triggers after one or more alarms become armed in night mode.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
}
},
@@ -282,7 +269,6 @@
"description": "Triggers after one or more alarms become armed in vacation mode.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
}
},
@@ -292,7 +278,6 @@
"description": "Triggers after one or more alarms become disarmed.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
}
},
@@ -302,7 +287,6 @@
"description": "Triggers after one or more alarms become triggered.",
"fields": {
"behavior": {
"description": "[%key:component::alarm_control_panel::common::trigger_behavior_description%]",
"name": "[%key:component::alarm_control_panel::common::trigger_behavior_name%]"
}
},

View File

@@ -3,10 +3,10 @@
from __future__ import annotations
import logging
from typing import Any
from typing import Any, cast
from adext import AdExt
from alarmdecoder.devices import SerialDevice, SocketDevice
from alarmdecoder.devices import Device, SerialDevice, SocketDevice
from alarmdecoder.util import NoDeviceError
import voluptuous as vol
@@ -102,16 +102,21 @@ class AlarmDecoderFlowHandler(ConfigFlow, domain=DOMAIN):
self._async_current_entries(), user_input, self.protocol
):
return self.async_abort(reason="already_configured")
connection = {}
connection: dict[str, Any] = {}
baud = None
device: Device
if self.protocol == PROTOCOL_SOCKET:
host = connection[CONF_HOST] = user_input[CONF_HOST]
port = connection[CONF_PORT] = user_input[CONF_PORT]
title = f"{host}:{port}"
host = connection[CONF_HOST] = cast(str, user_input[CONF_HOST])
port = connection[CONF_PORT] = cast(int, user_input[CONF_PORT])
title: str = f"{host}:{port}"
device = SocketDevice(interface=(host, port))
if self.protocol == PROTOCOL_SERIAL:
path = connection[CONF_DEVICE_PATH] = user_input[CONF_DEVICE_PATH]
baud = connection[CONF_DEVICE_BAUD] = user_input[CONF_DEVICE_BAUD]
path = connection[CONF_DEVICE_PATH] = cast(
str, user_input[CONF_DEVICE_PATH]
)
baud = connection[CONF_DEVICE_BAUD] = cast(
int, user_input[CONF_DEVICE_BAUD]
)
title = path
device = SerialDevice(interface=path)
@@ -132,6 +137,7 @@ class AlarmDecoderFlowHandler(ConfigFlow, domain=DOMAIN):
_LOGGER.exception("Unexpected exception during AlarmDecoder setup")
errors["base"] = "unknown"
schema: vol.Schema
if self.protocol == PROTOCOL_SOCKET:
schema = vol.Schema(
{

View File

@@ -0,0 +1,64 @@
"""Diagnostics support for Anthropic."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from anthropic import __title__, __version__
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_API_KEY
from homeassistant.helpers import entity_registry as er
from .const import (
CONF_PROMPT,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_COUNTRY,
CONF_WEB_SEARCH_REGION,
CONF_WEB_SEARCH_TIMEZONE,
)
if TYPE_CHECKING:
from homeassistant.core import HomeAssistant
from . import AnthropicConfigEntry
TO_REDACT = {
CONF_API_KEY,
CONF_PROMPT,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_REGION,
CONF_WEB_SEARCH_COUNTRY,
CONF_WEB_SEARCH_TIMEZONE,
}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: AnthropicConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
return {
"client": f"{__title__}=={__version__}",
"title": entry.title,
"entry_id": entry.entry_id,
"entry_version": f"{entry.version}.{entry.minor_version}",
"state": entry.state.value,
"data": async_redact_data(entry.data, TO_REDACT),
"options": async_redact_data(entry.options, TO_REDACT),
"subentries": {
subentry.subentry_id: {
"title": subentry.title,
"subentry_type": subentry.subentry_type,
"data": async_redact_data(subentry.data, TO_REDACT),
}
for subentry in entry.subentries.values()
},
"entities": {
entity_entry.entity_id: entity_entry.extended_dict
for entity_entry in er.async_entries_for_config_entry(
er.async_get(hass), entry.entry_id
)
},
}

View File

@@ -46,7 +46,7 @@ rules:
test-coverage: done
# Gold
devices: done
diagnostics: todo
diagnostics: done
discovery-update-info:
status: exempt
comment: |
@@ -61,10 +61,7 @@ rules:
No data updates.
docs-examples: done
docs-known-limitations: done
docs-supported-devices:
status: todo
comment: |
To write something about what models we support.
docs-supported-devices: done
docs-supported-functions: done
docs-troubleshooting: done
docs-use-cases: done

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted Assist satellites.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted Assist satellites to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_idle": {
"description": "Tests if one or more Assist satellites are idle.",
"fields": {
"behavior": {
"description": "[%key:component::assist_satellite::common::condition_behavior_description%]",
"name": "[%key:component::assist_satellite::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more Assist satellites are listening.",
"fields": {
"behavior": {
"description": "[%key:component::assist_satellite::common::condition_behavior_description%]",
"name": "[%key:component::assist_satellite::common::condition_behavior_name%]"
}
},
@@ -30,7 +26,6 @@
"description": "Tests if one or more Assist satellites are processing.",
"fields": {
"behavior": {
"description": "[%key:component::assist_satellite::common::condition_behavior_description%]",
"name": "[%key:component::assist_satellite::common::condition_behavior_name%]"
}
},
@@ -40,7 +35,6 @@
"description": "Tests if one or more Assist satellites are responding.",
"fields": {
"behavior": {
"description": "[%key:component::assist_satellite::common::condition_behavior_description%]",
"name": "[%key:component::assist_satellite::common::condition_behavior_name%]"
}
},
@@ -165,7 +159,6 @@
"description": "Triggers after one or more voice assistant satellites become idle after having processed a command.",
"fields": {
"behavior": {
"description": "[%key:component::assist_satellite::common::trigger_behavior_description%]",
"name": "[%key:component::assist_satellite::common::trigger_behavior_name%]"
}
},
@@ -175,7 +168,6 @@
"description": "Triggers after one or more voice assistant satellites start listening for a command from someone.",
"fields": {
"behavior": {
"description": "[%key:component::assist_satellite::common::trigger_behavior_description%]",
"name": "[%key:component::assist_satellite::common::trigger_behavior_name%]"
}
},
@@ -185,7 +177,6 @@
"description": "Triggers after one or more voice assistant satellites start processing a command after having heard it.",
"fields": {
"behavior": {
"description": "[%key:component::assist_satellite::common::trigger_behavior_description%]",
"name": "[%key:component::assist_satellite::common::trigger_behavior_name%]"
}
},
@@ -195,7 +186,6 @@
"description": "Triggers after one or more voice assistant satellites start responding to a command after having processed it, or start announcing something.",
"fields": {
"behavior": {
"description": "[%key:component::assist_satellite::common::trigger_behavior_description%]",
"name": "[%key:component::assist_satellite::common::trigger_behavior_name%]"
}
},

View File

@@ -1,21 +1,15 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted batteries.",
"condition_behavior_name": "Behavior",
"condition_threshold_description": "What to test for and threshold values.",
"condition_threshold_name": "Threshold configuration",
"trigger_behavior_description": "The behavior of the targeted batteries to trigger on.",
"trigger_behavior_name": "Behavior",
"trigger_threshold_changed_description": "Which changes to trigger on and threshold values.",
"trigger_threshold_crossed_description": "Which threshold crossing to trigger on and threshold values.",
"trigger_threshold_name": "Threshold configuration"
"condition_behavior_name": "Condition passes if",
"condition_threshold_name": "Threshold type",
"trigger_behavior_name": "Trigger when",
"trigger_threshold_name": "Threshold type"
},
"conditions": {
"is_charging": {
"description": "Tests if one or more batteries are charging.",
"fields": {
"behavior": {
"description": "[%key:component::battery::common::condition_behavior_description%]",
"name": "[%key:component::battery::common::condition_behavior_name%]"
}
},
@@ -25,11 +19,9 @@
"description": "Tests the battery level of one or more batteries.",
"fields": {
"behavior": {
"description": "[%key:component::battery::common::condition_behavior_description%]",
"name": "[%key:component::battery::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::battery::common::condition_threshold_description%]",
"name": "[%key:component::battery::common::condition_threshold_name%]"
}
},
@@ -39,7 +31,6 @@
"description": "Tests if one or more batteries are low.",
"fields": {
"behavior": {
"description": "[%key:component::battery::common::condition_behavior_description%]",
"name": "[%key:component::battery::common::condition_behavior_name%]"
}
},
@@ -49,7 +40,6 @@
"description": "Tests if one or more batteries are not charging.",
"fields": {
"behavior": {
"description": "[%key:component::battery::common::condition_behavior_description%]",
"name": "[%key:component::battery::common::condition_behavior_name%]"
}
},
@@ -59,7 +49,6 @@
"description": "Tests if one or more batteries are not low.",
"fields": {
"behavior": {
"description": "[%key:component::battery::common::condition_behavior_description%]",
"name": "[%key:component::battery::common::condition_behavior_name%]"
}
},
@@ -87,7 +76,6 @@
"description": "Triggers after the battery level of one or more batteries changes.",
"fields": {
"threshold": {
"description": "[%key:component::battery::common::trigger_threshold_changed_description%]",
"name": "[%key:component::battery::common::trigger_threshold_name%]"
}
},
@@ -97,11 +85,9 @@
"description": "Triggers after the battery level of one or more batteries crosses a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::battery::common::trigger_behavior_description%]",
"name": "[%key:component::battery::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::battery::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::battery::common::trigger_threshold_name%]"
}
},
@@ -111,7 +97,6 @@
"description": "Triggers after one or more batteries become low.",
"fields": {
"behavior": {
"description": "[%key:component::battery::common::trigger_behavior_description%]",
"name": "[%key:component::battery::common::trigger_behavior_name%]"
}
},
@@ -121,7 +106,6 @@
"description": "Triggers after one or more batteries are no longer low.",
"fields": {
"behavior": {
"description": "[%key:component::battery::common::trigger_behavior_description%]",
"name": "[%key:component::battery::common::trigger_behavior_name%]"
}
},
@@ -131,7 +115,6 @@
"description": "Triggers after one or more batteries start charging.",
"fields": {
"behavior": {
"description": "[%key:component::battery::common::trigger_behavior_description%]",
"name": "[%key:component::battery::common::trigger_behavior_name%]"
}
},
@@ -141,7 +124,6 @@
"description": "Triggers after one or more batteries stop charging.",
"fields": {
"behavior": {
"description": "[%key:component::battery::common::trigger_behavior_description%]",
"name": "[%key:component::battery::common::trigger_behavior_name%]"
}
},

View File

@@ -1 +1 @@
"""The bbox component."""
"""The Bbox integration."""

View File

@@ -1,7 +1,7 @@
{
"domain": "blebox",
"name": "BleBox devices",
"codeowners": ["@bbx-a", "@swistakm"],
"codeowners": ["@bbx-a", "@swistakm", "@bkobus-bbx"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/blebox",
"integration_type": "device",

View File

@@ -1 +1 @@
"""The blinksticklight component."""
"""The BlinkStick integration."""

View File

@@ -1,4 +1,4 @@
"""Support for Blinkstick lights."""
"""Support for BlinkStick lights."""
# mypy: ignore-errors
from __future__ import annotations
@@ -40,7 +40,7 @@ def setup_platform(
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up Blinkstick device specified by serial number."""
"""Set up BlinkStick device specified by serial number."""
name = config[CONF_NAME]
serial = config[CONF_SERIAL]

View File

@@ -0,0 +1,41 @@
"""The BMW Connected Drive integration."""
from __future__ import annotations
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir
DOMAIN = "bmw_connected_drive"
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up BMW Connected Drive from a config entry."""
ir.async_create_issue(
hass,
DOMAIN,
DOMAIN,
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
translation_key="integration_removed",
translation_placeholders={
"entries": "/config/integrations/integration/bmw_connected_drive",
"custom_component_url": "https://github.com/kvanbiesen/bmw-cardata-ha",
},
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return True
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Remove a config entry."""
if not hass.config_entries.async_loaded_entries(DOMAIN):
ir.async_delete_issue(hass, DOMAIN, DOMAIN)
# Remove any remaining disabled or ignored entries
for _entry in hass.config_entries.async_entries(DOMAIN):
hass.async_create_task(hass.config_entries.async_remove(_entry.entry_id))

View File

@@ -0,0 +1,9 @@
"""The BMW Connected Drive integration config flow."""
from homeassistant.config_entries import ConfigFlow
from . import DOMAIN
class BMWConnectedDriveConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for BMW Connected Drive."""

View File

@@ -0,0 +1,10 @@
{
"domain": "bmw_connected_drive",
"name": "BMW Connected Drive",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
"integration_type": "system",
"iot_class": "cloud_polling",
"quality_scale": "legacy",
"requirements": []
}

View File

@@ -0,0 +1,8 @@
{
"issues": {
"integration_removed": {
"description": "The BMW Connected Drive integration has been removed from Home Assistant.\n\nIn September 2025, BMW blocked third-party access to their servers by adding additional security measures. For EU-registered cars, a community-developed [custom component]({custom_component_url}) using BMW's CarData API is available as an alternative.\n\nTo resolve this issue, please remove the (now defunct) integration entries from your Home Assistant setup. [Click here to see your existing BMW Connected Drive integration entries]({entries}).",
"title": "The BMW Connected Drive integration has been removed"
}
}
}

View File

@@ -1,14 +1,12 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted calendars.",
"condition_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if"
},
"conditions": {
"is_event_active": {
"description": "Tests if one or more calendars have an active event.",
"fields": {
"behavior": {
"description": "[%key:component::calendar::common::condition_behavior_description%]",
"name": "[%key:component::calendar::common::condition_behavior_name%]"
}
},

View File

@@ -30,6 +30,7 @@ class ChessConfigFlow(ConfigFlow, domain=DOMAIN):
client = ChessComClient(session=session)
try:
user = await client.get_player(user_input[CONF_USERNAME])
await client.get_player_stats(user_input[CONF_USERNAME])
except NotFoundError:
errors["base"] = "player_not_found"
except Exception:

View File

@@ -1,21 +1,15 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted climate-control devices.",
"condition_behavior_name": "Behavior",
"condition_threshold_description": "What to test for and threshold values.",
"condition_threshold_name": "Threshold configuration",
"trigger_behavior_description": "The behavior of the targeted climates to trigger on.",
"trigger_behavior_name": "Behavior",
"trigger_threshold_changed_description": "Which changes to trigger on and threshold values.",
"trigger_threshold_crossed_description": "Which threshold crossing to trigger on and threshold values.",
"trigger_threshold_name": "Threshold configuration"
"condition_behavior_name": "Condition passes if",
"condition_threshold_name": "Threshold type",
"trigger_behavior_name": "Trigger when",
"trigger_threshold_name": "Threshold type"
},
"conditions": {
"is_cooling": {
"description": "Tests if one or more climate-control devices are cooling.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::condition_behavior_description%]",
"name": "[%key:component::climate::common::condition_behavior_name%]"
}
},
@@ -25,7 +19,6 @@
"description": "Tests if one or more climate-control devices are drying.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::condition_behavior_description%]",
"name": "[%key:component::climate::common::condition_behavior_name%]"
}
},
@@ -35,7 +28,6 @@
"description": "Tests if one or more climate-control devices are heating.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::condition_behavior_description%]",
"name": "[%key:component::climate::common::condition_behavior_name%]"
}
},
@@ -45,7 +37,6 @@
"description": "Tests if one or more climate-control devices are set to a specific HVAC mode.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::condition_behavior_description%]",
"name": "[%key:component::climate::common::condition_behavior_name%]"
},
"hvac_mode": {
@@ -59,7 +50,6 @@
"description": "Tests if one or more climate-control devices are off.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::condition_behavior_description%]",
"name": "[%key:component::climate::common::condition_behavior_name%]"
}
},
@@ -69,7 +59,6 @@
"description": "Tests if one or more climate-control devices are on.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::condition_behavior_description%]",
"name": "[%key:component::climate::common::condition_behavior_name%]"
}
},
@@ -79,11 +68,9 @@
"description": "Tests the humidity setpoint of one or more climate-control devices.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::condition_behavior_description%]",
"name": "[%key:component::climate::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::climate::common::condition_threshold_description%]",
"name": "[%key:component::climate::common::condition_threshold_name%]"
}
},
@@ -93,11 +80,9 @@
"description": "Tests the temperature setpoint of one or more climate-control devices.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::condition_behavior_description%]",
"name": "[%key:component::climate::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::climate::common::condition_threshold_description%]",
"name": "[%key:component::climate::common::condition_threshold_name%]"
}
},
@@ -398,7 +383,6 @@
"description": "Triggers after the mode of one or more climate-control devices changes.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::trigger_behavior_description%]",
"name": "[%key:component::climate::common::trigger_behavior_name%]"
},
"hvac_mode": {
@@ -412,7 +396,6 @@
"description": "Triggers after one or more climate-control devices start cooling.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::trigger_behavior_description%]",
"name": "[%key:component::climate::common::trigger_behavior_name%]"
}
},
@@ -422,7 +405,6 @@
"description": "Triggers after one or more climate-control devices start drying.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::trigger_behavior_description%]",
"name": "[%key:component::climate::common::trigger_behavior_name%]"
}
},
@@ -432,7 +414,6 @@
"description": "Triggers after one or more climate-control devices start heating.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::trigger_behavior_description%]",
"name": "[%key:component::climate::common::trigger_behavior_name%]"
}
},
@@ -442,7 +423,6 @@
"description": "Triggers after the humidity setpoint of one or more climate-control devices changes.",
"fields": {
"threshold": {
"description": "[%key:component::climate::common::trigger_threshold_changed_description%]",
"name": "[%key:component::climate::common::trigger_threshold_name%]"
}
},
@@ -452,11 +432,9 @@
"description": "Triggers after the humidity setpoint of one or more climate-control devices crosses a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::trigger_behavior_description%]",
"name": "[%key:component::climate::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::climate::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::climate::common::trigger_threshold_name%]"
}
},
@@ -466,7 +444,6 @@
"description": "Triggers after the temperature setpoint of one or more climate-control devices changes.",
"fields": {
"threshold": {
"description": "[%key:component::climate::common::trigger_threshold_changed_description%]",
"name": "[%key:component::climate::common::trigger_threshold_name%]"
}
},
@@ -476,11 +453,9 @@
"description": "Triggers after the temperature setpoint of one or more climate-control devices crosses a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::trigger_behavior_description%]",
"name": "[%key:component::climate::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::climate::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::climate::common::trigger_threshold_name%]"
}
},
@@ -490,7 +465,6 @@
"description": "Triggers after one or more climate-control devices turn off.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::trigger_behavior_description%]",
"name": "[%key:component::climate::common::trigger_behavior_name%]"
}
},
@@ -500,7 +474,6 @@
"description": "Triggers after one or more climate-control devices turn on, regardless of the mode.",
"fields": {
"behavior": {
"description": "[%key:component::climate::common::trigger_behavior_description%]",
"name": "[%key:component::climate::common::trigger_behavior_name%]"
}
},

View File

@@ -210,7 +210,7 @@ def websocket_update_entity(
)
return
changes = {}
changes: dict[str, Any] = {}
for key in (
"area_id",

View File

@@ -1,19 +1,16 @@
{
"common": {
"trigger_behavior_description": "The behavior of the targeted counters to trigger on.",
"trigger_behavior_name": "Behavior"
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_value": {
"description": "Tests the value of one or more counters.",
"fields": {
"behavior": {
"description": "How the state should match on the targeted counters.",
"name": "Behavior"
"name": "Condition passes if"
},
"threshold": {
"description": "What to test for and threshold values.",
"name": "Threshold"
"name": "Threshold type"
}
},
"name": "Counter value"
@@ -98,7 +95,6 @@
"description": "Triggers after one or more counters reach their maximum value.",
"fields": {
"behavior": {
"description": "[%key:component::counter::common::trigger_behavior_description%]",
"name": "[%key:component::counter::common::trigger_behavior_name%]"
}
},
@@ -108,7 +104,6 @@
"description": "Triggers after one or more counters reach their minimum value.",
"fields": {
"behavior": {
"description": "[%key:component::counter::common::trigger_behavior_description%]",
"name": "[%key:component::counter::common::trigger_behavior_name%]"
}
},
@@ -118,7 +113,6 @@
"description": "Triggers after one or more counters are reset.",
"fields": {
"behavior": {
"description": "[%key:component::counter::common::trigger_behavior_description%]",
"name": "[%key:component::counter::common::trigger_behavior_name%]"
}
},

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted covers.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted covers to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"awning_is_closed": {
"description": "Tests if one or more awnings are closed.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::condition_behavior_description%]",
"name": "[%key:component::cover::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more awnings are open.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::condition_behavior_description%]",
"name": "[%key:component::cover::common::condition_behavior_name%]"
}
},
@@ -30,7 +26,6 @@
"description": "Tests if one or more blinds are closed.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::condition_behavior_description%]",
"name": "[%key:component::cover::common::condition_behavior_name%]"
}
},
@@ -40,7 +35,6 @@
"description": "Tests if one or more blinds are open.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::condition_behavior_description%]",
"name": "[%key:component::cover::common::condition_behavior_name%]"
}
},
@@ -50,7 +44,6 @@
"description": "Tests if one or more curtains are closed.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::condition_behavior_description%]",
"name": "[%key:component::cover::common::condition_behavior_name%]"
}
},
@@ -60,7 +53,6 @@
"description": "Tests if one or more curtains are open.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::condition_behavior_description%]",
"name": "[%key:component::cover::common::condition_behavior_name%]"
}
},
@@ -70,7 +62,6 @@
"description": "Tests if one or more shades are closed.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::condition_behavior_description%]",
"name": "[%key:component::cover::common::condition_behavior_name%]"
}
},
@@ -80,7 +71,6 @@
"description": "Tests if one or more shades are open.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::condition_behavior_description%]",
"name": "[%key:component::cover::common::condition_behavior_name%]"
}
},
@@ -90,7 +80,6 @@
"description": "Tests if one or more shutters are closed.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::condition_behavior_description%]",
"name": "[%key:component::cover::common::condition_behavior_name%]"
}
},
@@ -100,7 +89,6 @@
"description": "Tests if one or more shutters are open.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::condition_behavior_description%]",
"name": "[%key:component::cover::common::condition_behavior_name%]"
}
},
@@ -265,7 +253,6 @@
"description": "Triggers after one or more awnings close.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::trigger_behavior_description%]",
"name": "[%key:component::cover::common::trigger_behavior_name%]"
}
},
@@ -275,7 +262,6 @@
"description": "Triggers after one or more awnings open.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::trigger_behavior_description%]",
"name": "[%key:component::cover::common::trigger_behavior_name%]"
}
},
@@ -285,7 +271,6 @@
"description": "Triggers after one or more blinds close.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::trigger_behavior_description%]",
"name": "[%key:component::cover::common::trigger_behavior_name%]"
}
},
@@ -295,7 +280,6 @@
"description": "Triggers after one or more blinds open.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::trigger_behavior_description%]",
"name": "[%key:component::cover::common::trigger_behavior_name%]"
}
},
@@ -305,7 +289,6 @@
"description": "Triggers after one or more curtains close.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::trigger_behavior_description%]",
"name": "[%key:component::cover::common::trigger_behavior_name%]"
}
},
@@ -315,7 +298,6 @@
"description": "Triggers after one or more curtains open.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::trigger_behavior_description%]",
"name": "[%key:component::cover::common::trigger_behavior_name%]"
}
},
@@ -325,7 +307,6 @@
"description": "Triggers after one or more shades close.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::trigger_behavior_description%]",
"name": "[%key:component::cover::common::trigger_behavior_name%]"
}
},
@@ -335,7 +316,6 @@
"description": "Triggers after one or more shades open.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::trigger_behavior_description%]",
"name": "[%key:component::cover::common::trigger_behavior_name%]"
}
},
@@ -345,7 +325,6 @@
"description": "Triggers after one or more shutters close.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::trigger_behavior_description%]",
"name": "[%key:component::cover::common::trigger_behavior_name%]"
}
},
@@ -355,7 +334,6 @@
"description": "Triggers after one or more shutters open.",
"fields": {
"behavior": {
"description": "[%key:component::cover::common::trigger_behavior_description%]",
"name": "[%key:component::cover::common::trigger_behavior_name%]"
}
},

View File

@@ -1 +1 @@
"""The denon component."""
"""The Denon Network Receivers integration."""

View File

@@ -1,4 +1,4 @@
"""The denonavr component."""
"""The Denon AVR Network Receivers integration."""
import logging

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted device trackers.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted device trackers to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_home": {
"description": "Tests if one or more device trackers are home.",
"fields": {
"behavior": {
"description": "[%key:component::device_tracker::common::condition_behavior_description%]",
"name": "[%key:component::device_tracker::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more device trackers are not home.",
"fields": {
"behavior": {
"description": "[%key:component::device_tracker::common::condition_behavior_description%]",
"name": "[%key:component::device_tracker::common::condition_behavior_name%]"
}
},
@@ -129,7 +125,6 @@
"description": "Triggers when one or more device trackers enter home.",
"fields": {
"behavior": {
"description": "[%key:component::device_tracker::common::trigger_behavior_description%]",
"name": "[%key:component::device_tracker::common::trigger_behavior_name%]"
}
},
@@ -139,7 +134,6 @@
"description": "Triggers when one or more device trackers leave home.",
"fields": {
"behavior": {
"description": "[%key:component::device_tracker::common::trigger_behavior_description%]",
"name": "[%key:component::device_tracker::common::trigger_behavior_name%]"
}
},

View File

@@ -2,6 +2,8 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from devolo_home_control_api.devices.zwave import Zwave
from devolo_home_control_api.homecontrol import HomeControl
@@ -188,6 +190,8 @@ class DevoloConsumptionEntity(DevoloMultiLevelDeviceEntity):
def sync_callback(self, message: tuple) -> None:
"""Update the consumption sensor state."""
if message[0] == self._attr_unique_id:
if TYPE_CHECKING:
assert self._attr_unique_id is not None
self._value = getattr(
self._device_instance.consumption_property[self._attr_unique_id],
self._sensor_type,

View File

@@ -1,4 +1,4 @@
"""The dnsip component."""
"""The DNS IP integration."""
from __future__ import annotations
@@ -17,7 +17,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload dnsip config entry."""
"""Unload DNS IP config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted doors.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted doors to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_closed": {
"description": "Tests if one or more doors are closed.",
"fields": {
"behavior": {
"description": "[%key:component::door::common::condition_behavior_description%]",
"name": "[%key:component::door::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more doors are open.",
"fields": {
"behavior": {
"description": "[%key:component::door::common::condition_behavior_description%]",
"name": "[%key:component::door::common::condition_behavior_name%]"
}
},
@@ -48,7 +44,6 @@
"description": "Triggers after one or more doors close.",
"fields": {
"behavior": {
"description": "[%key:component::door::common::trigger_behavior_description%]",
"name": "[%key:component::door::common::trigger_behavior_name%]"
}
},
@@ -58,7 +53,6 @@
"description": "Triggers after one or more doors open.",
"fields": {
"behavior": {
"description": "[%key:component::door::common::trigger_behavior_description%]",
"name": "[%key:component::door::common::trigger_behavior_name%]"
}
},

View File

@@ -1,64 +0,0 @@
"""The Dropbox integration."""
from __future__ import annotations
from python_dropbox_api import (
DropboxAPIClient,
DropboxAuthException,
DropboxUnknownException,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.config_entry_oauth2_flow import (
ImplementationUnavailableError,
OAuth2Session,
async_get_config_entry_implementation,
)
from .auth import DropboxConfigEntryAuth
from .const import DATA_BACKUP_AGENT_LISTENERS, DOMAIN
type DropboxConfigEntry = ConfigEntry[DropboxAPIClient]
async def async_setup_entry(hass: HomeAssistant, entry: DropboxConfigEntry) -> bool:
"""Set up Dropbox from a config entry."""
try:
oauth2_implementation = await async_get_config_entry_implementation(hass, entry)
except ImplementationUnavailableError as err:
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="oauth2_implementation_unavailable",
) from err
oauth2_session = OAuth2Session(hass, entry, oauth2_implementation)
auth = DropboxConfigEntryAuth(
aiohttp_client.async_get_clientsession(hass), oauth2_session
)
client = DropboxAPIClient(auth)
try:
await client.get_account_info()
except DropboxAuthException as err:
raise ConfigEntryAuthFailed from err
except (DropboxUnknownException, TimeoutError) as err:
raise ConfigEntryNotReady from err
entry.runtime_data = client
def async_notify_backup_listeners() -> None:
for listener in hass.data.get(DATA_BACKUP_AGENT_LISTENERS, []):
listener()
entry.async_on_unload(entry.async_on_state_change(async_notify_backup_listeners))
return True
async def async_unload_entry(hass: HomeAssistant, entry: DropboxConfigEntry) -> bool:
"""Unload a config entry."""
return True

View File

@@ -1,38 +0,0 @@
"""Application credentials platform for the Dropbox integration."""
from homeassistant.components.application_credentials import ClientCredential
from homeassistant.core import HomeAssistant
from homeassistant.helpers.config_entry_oauth2_flow import (
AbstractOAuth2Implementation,
LocalOAuth2ImplementationWithPkce,
)
from .const import OAUTH2_AUTHORIZE, OAUTH2_SCOPES, OAUTH2_TOKEN
async def async_get_auth_implementation(
hass: HomeAssistant, auth_domain: str, credential: ClientCredential
) -> AbstractOAuth2Implementation:
"""Return custom auth implementation."""
return DropboxOAuth2Implementation(
hass,
auth_domain,
credential.client_id,
OAUTH2_AUTHORIZE,
OAUTH2_TOKEN,
credential.client_secret,
)
class DropboxOAuth2Implementation(LocalOAuth2ImplementationWithPkce):
"""Custom Dropbox OAuth2 implementation to add the necessary authorize url parameters."""
@property
def extra_authorize_data(self) -> dict:
"""Extra data that needs to be appended to the authorize url."""
data: dict = {
"token_access_type": "offline",
"scope": " ".join(OAUTH2_SCOPES),
}
data.update(super().extra_authorize_data)
return data

View File

@@ -1,44 +0,0 @@
"""Authentication for Dropbox."""
from typing import cast
from aiohttp import ClientSession
from python_dropbox_api import Auth
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
class DropboxConfigEntryAuth(Auth):
"""Provide Dropbox authentication tied to an OAuth2 based config entry."""
def __init__(
self,
websession: ClientSession,
oauth_session: OAuth2Session,
) -> None:
"""Initialize DropboxConfigEntryAuth."""
super().__init__(websession)
self._oauth_session = oauth_session
async def async_get_access_token(self) -> str:
"""Return a valid access token."""
await self._oauth_session.async_ensure_token_valid()
return cast(str, self._oauth_session.token["access_token"])
class DropboxConfigFlowAuth(Auth):
"""Provide authentication tied to a fixed token for the config flow."""
def __init__(
self,
websession: ClientSession,
token: str,
) -> None:
"""Initialize DropboxConfigFlowAuth."""
super().__init__(websession)
self._token = token
async def async_get_access_token(self) -> str:
"""Return the fixed access token."""
return self._token

View File

@@ -1,230 +0,0 @@
"""Backup platform for the Dropbox integration."""
from collections.abc import AsyncIterator, Callable, Coroutine
from functools import wraps
import json
import logging
from typing import Any, Concatenate
from python_dropbox_api import (
DropboxAPIClient,
DropboxAuthException,
DropboxFileOrFolderNotFoundException,
DropboxUnknownException,
)
from homeassistant.components.backup import (
AgentBackup,
BackupAgent,
BackupAgentError,
BackupNotFound,
suggested_filename,
)
from homeassistant.core import HomeAssistant, callback
from . import DropboxConfigEntry
from .const import DATA_BACKUP_AGENT_LISTENERS, DOMAIN
_LOGGER = logging.getLogger(__name__)
def _suggested_filenames(backup: AgentBackup) -> tuple[str, str]:
"""Return the suggested filenames for the backup and metadata."""
base_name = suggested_filename(backup).rsplit(".", 1)[0]
return f"{base_name}.tar", f"{base_name}.metadata.json"
async def _async_string_iterator(content: str) -> AsyncIterator[bytes]:
"""Yield a string as a single bytes chunk."""
yield content.encode()
def handle_backup_errors[_R, **P](
func: Callable[Concatenate[DropboxBackupAgent, P], Coroutine[Any, Any, _R]],
) -> Callable[Concatenate[DropboxBackupAgent, P], Coroutine[Any, Any, _R]]:
"""Handle backup errors."""
@wraps(func)
async def wrapper(
self: DropboxBackupAgent, *args: P.args, **kwargs: P.kwargs
) -> _R:
try:
return await func(self, *args, **kwargs)
except DropboxFileOrFolderNotFoundException as err:
raise BackupNotFound(
f"Failed to {func.__name__.removeprefix('async_').replace('_', ' ')}"
) from err
except DropboxAuthException as err:
self._entry.async_start_reauth(self._hass)
raise BackupAgentError("Authentication error") from err
except DropboxUnknownException as err:
_LOGGER.error(
"Error during %s: %s",
func.__name__,
err,
)
_LOGGER.debug("Full error: %s", err, exc_info=True)
raise BackupAgentError(
f"Failed to {func.__name__.removeprefix('async_').replace('_', ' ')}"
) from err
return wrapper
async def async_get_backup_agents(
hass: HomeAssistant,
**kwargs: Any,
) -> list[BackupAgent]:
"""Return a list of backup agents."""
entries = hass.config_entries.async_loaded_entries(DOMAIN)
return [DropboxBackupAgent(hass, entry) for entry in entries]
@callback
def async_register_backup_agents_listener(
hass: HomeAssistant,
*,
listener: Callable[[], None],
**kwargs: Any,
) -> Callable[[], None]:
"""Register a listener to be called when agents are added or removed.
:return: A function to unregister the listener.
"""
hass.data.setdefault(DATA_BACKUP_AGENT_LISTENERS, []).append(listener)
@callback
def remove_listener() -> None:
"""Remove the listener."""
hass.data[DATA_BACKUP_AGENT_LISTENERS].remove(listener)
if not hass.data[DATA_BACKUP_AGENT_LISTENERS]:
del hass.data[DATA_BACKUP_AGENT_LISTENERS]
return remove_listener
class DropboxBackupAgent(BackupAgent):
"""Backup agent for the Dropbox integration."""
domain = DOMAIN
def __init__(self, hass: HomeAssistant, entry: DropboxConfigEntry) -> None:
"""Initialize the backup agent."""
super().__init__()
self._hass = hass
self._entry = entry
self.name = entry.title
assert entry.unique_id
self.unique_id = entry.unique_id
self._api: DropboxAPIClient = entry.runtime_data
async def _async_get_backups(self) -> list[tuple[AgentBackup, str]]:
"""Get backups and their corresponding file names."""
files = await self._api.list_folder("")
tar_files = {f.name for f in files if f.name.endswith(".tar")}
metadata_files = [f for f in files if f.name.endswith(".metadata.json")]
backups: list[tuple[AgentBackup, str]] = []
for metadata_file in metadata_files:
tar_name = metadata_file.name.removesuffix(".metadata.json") + ".tar"
if tar_name not in tar_files:
_LOGGER.warning(
"Found metadata file '%s' without matching backup file",
metadata_file.name,
)
continue
metadata_stream = self._api.download_file(f"/{metadata_file.name}")
raw = b"".join([chunk async for chunk in metadata_stream])
try:
data = json.loads(raw)
backup = AgentBackup.from_dict(data)
except (json.JSONDecodeError, ValueError, TypeError, KeyError) as err:
_LOGGER.warning(
"Skipping invalid metadata file '%s': %s",
metadata_file.name,
err,
)
continue
backups.append((backup, tar_name))
return backups
@handle_backup_errors
async def async_upload_backup(
self,
*,
open_stream: Callable[[], Coroutine[Any, Any, AsyncIterator[bytes]]],
backup: AgentBackup,
**kwargs: Any,
) -> None:
"""Upload a backup."""
backup_filename, metadata_filename = _suggested_filenames(backup)
backup_path = f"/{backup_filename}"
metadata_path = f"/{metadata_filename}"
file_stream = await open_stream()
await self._api.upload_file(backup_path, file_stream)
metadata_stream = _async_string_iterator(json.dumps(backup.as_dict()))
try:
await self._api.upload_file(metadata_path, metadata_stream)
except (
DropboxAuthException,
DropboxUnknownException,
):
await self._api.delete_file(backup_path)
raise
@handle_backup_errors
async def async_list_backups(self, **kwargs: Any) -> list[AgentBackup]:
"""List backups."""
return [backup for backup, _ in await self._async_get_backups()]
@handle_backup_errors
async def async_download_backup(
self,
backup_id: str,
**kwargs: Any,
) -> AsyncIterator[bytes]:
"""Download a backup file."""
backups = await self._async_get_backups()
for backup, filename in backups:
if backup.backup_id == backup_id:
return self._api.download_file(f"/{filename}")
raise BackupNotFound(f"Backup {backup_id} not found")
@handle_backup_errors
async def async_get_backup(
self,
backup_id: str,
**kwargs: Any,
) -> AgentBackup:
"""Return a backup."""
backups = await self._async_get_backups()
for backup, _ in backups:
if backup.backup_id == backup_id:
return backup
raise BackupNotFound(f"Backup {backup_id} not found")
@handle_backup_errors
async def async_delete_backup(
self,
backup_id: str,
**kwargs: Any,
) -> None:
"""Delete a backup file."""
backups = await self._async_get_backups()
for backup, tar_filename in backups:
if backup.backup_id == backup_id:
metadata_filename = tar_filename.removesuffix(".tar") + ".metadata.json"
await self._api.delete_file(f"/{tar_filename}")
await self._api.delete_file(f"/{metadata_filename}")
return
raise BackupNotFound(f"Backup {backup_id} not found")

View File

@@ -1,60 +0,0 @@
"""Config flow for Dropbox."""
from collections.abc import Mapping
import logging
from typing import Any
from python_dropbox_api import DropboxAPIClient
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlowResult
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler
from .auth import DropboxConfigFlowAuth
from .const import DOMAIN
class DropboxConfigFlow(AbstractOAuth2FlowHandler, domain=DOMAIN):
"""Config flow to handle Dropbox OAuth2 authentication."""
DOMAIN = DOMAIN
@property
def logger(self) -> logging.Logger:
"""Return logger."""
return logging.getLogger(__name__)
async def async_oauth_create_entry(self, data: dict[str, Any]) -> ConfigFlowResult:
"""Create an entry for the flow, or update existing entry."""
access_token = data[CONF_TOKEN][CONF_ACCESS_TOKEN]
auth = DropboxConfigFlowAuth(async_get_clientsession(self.hass), access_token)
client = DropboxAPIClient(auth)
account_info = await client.get_account_info()
await self.async_set_unique_id(account_info.account_id)
if self.source == SOURCE_REAUTH:
self._abort_if_unique_id_mismatch(reason="wrong_account")
return self.async_update_reload_and_abort(
self._get_reauth_entry(), data=data
)
self._abort_if_unique_id_configured()
return self.async_create_entry(title=account_info.email, data=data)
async def async_step_reauth(
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
"""Perform reauth upon an API authentication error."""
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Dialog that informs the user that reauth is required."""
if user_input is None:
return self.async_show_form(step_id="reauth_confirm")
return await self.async_step_user()

View File

@@ -1,19 +0,0 @@
"""Constants for the Dropbox integration."""
from collections.abc import Callable
from homeassistant.util.hass_dict import HassKey
DOMAIN = "dropbox"
OAUTH2_AUTHORIZE = "https://www.dropbox.com/oauth2/authorize"
OAUTH2_TOKEN = "https://api.dropboxapi.com/oauth2/token"
OAUTH2_SCOPES = [
"account_info.read",
"files.content.read",
"files.content.write",
]
DATA_BACKUP_AGENT_LISTENERS: HassKey[list[Callable[[], None]]] = HassKey(
f"{DOMAIN}.backup_agent_listeners"
)

View File

@@ -1,13 +0,0 @@
{
"domain": "dropbox",
"name": "Dropbox",
"after_dependencies": ["backup"],
"codeowners": ["@bdr99"],
"config_flow": true,
"dependencies": ["application_credentials"],
"documentation": "https://www.home-assistant.io/integrations/dropbox",
"integration_type": "service",
"iot_class": "cloud_polling",
"quality_scale": "bronze",
"requirements": ["python-dropbox-api==0.1.3"]
}

View File

@@ -1,112 +0,0 @@
rules:
# Bronze
action-setup:
status: exempt
comment: Integration does not register any actions.
appropriate-polling:
status: exempt
comment: Integration does not poll.
brands: done
common-modules:
status: exempt
comment: Integration does not have any entities or coordinators.
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: Integration does not register any actions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup:
status: exempt
comment: Integration does not have any entities.
entity-unique-id:
status: exempt
comment: Integration does not have any entities.
has-entity-name:
status: exempt
comment: Integration does not have any entities.
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry: done
# Silver
action-exceptions:
status: exempt
comment: Integration does not register any actions.
config-entry-unloading: done
docs-configuration-parameters:
status: exempt
comment: Integration does not have any configuration parameters.
docs-installation-parameters: done
entity-unavailable:
status: exempt
comment: Integration does not have any entities.
integration-owner: done
log-when-unavailable: todo
parallel-updates:
status: exempt
comment: Integration does not make any entity updates.
reauthentication-flow: done
test-coverage: done
# Gold
devices:
status: exempt
comment: Integration does not have any entities.
diagnostics:
status: exempt
comment: Integration does not have any data to diagnose.
discovery-update-info:
status: exempt
comment: Integration is a service.
discovery:
status: exempt
comment: Integration is a service.
docs-data-update:
status: exempt
comment: Integration does not update any data.
docs-examples:
status: exempt
comment: Integration only provides backup functionality.
docs-known-limitations: todo
docs-supported-devices:
status: exempt
comment: Integration does not support any devices.
docs-supported-functions: done
docs-troubleshooting: todo
docs-use-cases: done
dynamic-devices:
status: exempt
comment: Integration does not use any devices.
entity-category:
status: exempt
comment: Integration does not have any entities.
entity-device-class:
status: exempt
comment: Integration does not have any entities.
entity-disabled-by-default:
status: exempt
comment: Integration does not have any entities.
entity-translations:
status: exempt
comment: Integration does not have any entities.
exception-translations: todo
icon-translations:
status: exempt
comment: Integration does not have any entities.
reconfiguration-flow: todo
repair-issues:
status: exempt
comment: Integration does not have any repairs.
stale-devices:
status: exempt
comment: Integration does not have any devices.
# Platinum
async-dependency: done
inject-websession: done
strict-typing: done

View File

@@ -1,35 +0,0 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
"wrong_account": "Wrong account: Please authenticate with the correct account."
},
"create_entry": {
"default": "[%key:common::config_flow::create_entry::authenticated%]"
},
"step": {
"pick_implementation": {
"title": "[%key:common::config_flow::title::oauth2_pick_implementation%]"
},
"reauth_confirm": {
"description": "The Dropbox integration needs to re-authenticate your account.",
"title": "[%key:common::config_flow::title::reauth%]"
}
}
},
"exceptions": {
"oauth2_implementation_unavailable": {
"message": "[%key:common::exceptions::oauth2_implementation_unavailable::message%]"
}
}
}

View File

@@ -25,7 +25,7 @@ def _fix_device_registry_identifiers(
if old_identifier not in device_entry.identifiers: # type: ignore[comparison-overlap]
continue
new_identifiers = device_entry.identifiers.copy()
new_identifiers.discard(old_identifier) # type: ignore[arg-type]
new_identifiers.discard(old_identifier)
new_identifiers.add((DOMAIN, entry.data["station"]))
device_registry.async_update_device(
device_entry.id, new_identifiers=new_identifiers

View File

@@ -1 +1 @@
"""The ebox component."""
"""The EBox integration."""

View File

@@ -1 +1 @@
"""The edimax component."""
"""The Edimax integration."""

View File

@@ -273,7 +273,7 @@ class ElevenLabsTTSEntity(TextToSpeechEntity):
continue
# Build kwargs common to both modes
kwargs = base_stream_params | {
kwargs: dict[str, Any] = base_stream_params | {
"text": text,
}

View File

@@ -293,7 +293,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ElkM1ConfigEntry) -> boo
elk_temp_unit = elk.panel.temperature_units
if elk_temp_unit == "C":
temperature_unit = UnitOfTemperature.CELSIUS
temperature_unit = UnitOfTemperature.CELSIUS # type: ignore[unreachable]
else:
temperature_unit = UnitOfTemperature.FAHRENHEIT
config["temperature_unit"] = temperature_unit

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted fans.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted fans to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_off": {
"description": "Tests if one or more fans are off.",
"fields": {
"behavior": {
"description": "[%key:component::fan::common::condition_behavior_description%]",
"name": "[%key:component::fan::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more fans are on.",
"fields": {
"behavior": {
"description": "[%key:component::fan::common::condition_behavior_description%]",
"name": "[%key:component::fan::common::condition_behavior_name%]"
}
},
@@ -199,7 +195,6 @@
"description": "Triggers after one or more fans turn off.",
"fields": {
"behavior": {
"description": "[%key:component::fan::common::trigger_behavior_description%]",
"name": "[%key:component::fan::common::trigger_behavior_name%]"
}
},
@@ -209,7 +204,6 @@
"description": "Triggers after one or more fans turn on.",
"fields": {
"behavior": {
"description": "[%key:component::fan::common::trigger_behavior_description%]",
"name": "[%key:component::fan::common::trigger_behavior_name%]"
}
},

View File

@@ -1 +1 @@
"""The fido component."""
"""The Fido integration."""

View File

@@ -30,22 +30,31 @@ class FreshrFlowHandler(ConfigFlow, domain=DOMAIN):
VERSION = 1
MINOR_VERSION = 1
async def _validate_input(self, username: str, password: str) -> str | None:
"""Validate credentials, returning an error key or None on success."""
client = FreshrClient(session=async_get_clientsession(self.hass))
try:
await client.login(username, password)
except LoginError:
return "invalid_auth"
except ClientError:
return "cannot_connect"
except Exception: # noqa: BLE001
LOGGER.exception("Unexpected exception")
return "unknown"
return None
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
errors: dict[str, str] = {}
if user_input is not None:
client = FreshrClient(session=async_get_clientsession(self.hass))
try:
await client.login(user_input[CONF_USERNAME], user_input[CONF_PASSWORD])
except LoginError:
errors["base"] = "invalid_auth"
except ClientError:
errors["base"] = "cannot_connect"
except Exception: # noqa: BLE001
LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
error = await self._validate_input(
user_input[CONF_USERNAME], user_input[CONF_PASSWORD]
)
if error:
errors["base"] = error
else:
await self.async_set_unique_id(user_input[CONF_USERNAME].lower())
self._abort_if_unique_id_configured()
@@ -58,6 +67,34 @@ class FreshrFlowHandler(ConfigFlow, domain=DOMAIN):
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration."""
reconfigure_entry = self._get_reconfigure_entry()
errors: dict[str, str] = {}
if user_input is not None:
error = await self._validate_input(
reconfigure_entry.data[CONF_USERNAME], user_input[CONF_PASSWORD]
)
if error:
errors["base"] = error
else:
return self.async_update_reload_and_abort(
reconfigure_entry,
data_updates={CONF_PASSWORD: user_input[CONF_PASSWORD]},
)
return self.async_show_form(
step_id="reconfigure",
data_schema=vol.Schema({vol.Required(CONF_PASSWORD): str}),
description_placeholders={
CONF_USERNAME: reconfigure_entry.data[CONF_USERNAME]
},
errors=errors,
)
async def async_step_reauth(
self, _user_input: Mapping[str, Any]
) -> ConfigFlowResult:
@@ -72,18 +109,11 @@ class FreshrFlowHandler(ConfigFlow, domain=DOMAIN):
reauth_entry = self._get_reauth_entry()
if user_input is not None:
client = FreshrClient(session=async_get_clientsession(self.hass))
try:
await client.login(
reauth_entry.data[CONF_USERNAME], user_input[CONF_PASSWORD]
)
except LoginError:
errors["base"] = "invalid_auth"
except ClientError:
errors["base"] = "cannot_connect"
except Exception: # noqa: BLE001
LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
error = await self._validate_input(
reauth_entry.data[CONF_USERNAME], user_input[CONF_PASSWORD]
)
if error:
errors["base"] = error
else:
return self.async_update_reload_and_abort(
reauth_entry,

View File

@@ -62,7 +62,7 @@ rules:
entity-translations: done
exception-translations: done
icon-translations: done
reconfiguration-flow: todo
reconfiguration-flow: done
repair-issues:
status: exempt
comment: No actionable repair scenarios exist; authentication failures are handled via the reauthentication flow.

View File

@@ -2,7 +2,8 @@
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
@@ -19,6 +20,15 @@
},
"description": "Re-enter the password for your Fresh-r account `{username}`."
},
"reconfigure": {
"data": {
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"password": "[%key:component::freshr::config::step::user::data_description::password%]"
},
"description": "Update the password for your Fresh-r account `{username}`."
},
"user": {
"data": {
"password": "[%key:common::config_flow::data::password%]",

View File

@@ -21,5 +21,5 @@
"integration_type": "system",
"preview_features": { "winter_mode": {} },
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20260325.2"]
"requirements": ["home-assistant-frontend==20260325.4"]
}

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted garage doors.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted garage doors to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_closed": {
"description": "Tests if one or more garage doors are closed.",
"fields": {
"behavior": {
"description": "[%key:component::garage_door::common::condition_behavior_description%]",
"name": "[%key:component::garage_door::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more garage doors are open.",
"fields": {
"behavior": {
"description": "[%key:component::garage_door::common::condition_behavior_description%]",
"name": "[%key:component::garage_door::common::condition_behavior_name%]"
}
},
@@ -48,7 +44,6 @@
"description": "Triggers after one or more garage doors close.",
"fields": {
"behavior": {
"description": "[%key:component::garage_door::common::trigger_behavior_description%]",
"name": "[%key:component::garage_door::common::trigger_behavior_name%]"
}
},
@@ -58,7 +53,6 @@
"description": "Triggers after one or more garage doors open.",
"fields": {
"behavior": {
"description": "[%key:component::garage_door::common::trigger_behavior_description%]",
"name": "[%key:component::garage_door::common::trigger_behavior_name%]"
}
},

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted gates.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted gates to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_closed": {
"description": "Tests if one or more gates are closed.",
"fields": {
"behavior": {
"description": "[%key:component::gate::common::condition_behavior_description%]",
"name": "[%key:component::gate::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more gates are open.",
"fields": {
"behavior": {
"description": "[%key:component::gate::common::condition_behavior_description%]",
"name": "[%key:component::gate::common::condition_behavior_name%]"
}
},
@@ -48,7 +44,6 @@
"description": "Triggers after one or more gates close.",
"fields": {
"behavior": {
"description": "[%key:component::gate::common::trigger_behavior_description%]",
"name": "[%key:component::gate::common::trigger_behavior_name%]"
}
},
@@ -58,7 +53,6 @@
"description": "Triggers after one or more gates open.",
"fields": {
"behavior": {
"description": "[%key:component::gate::common::trigger_behavior_description%]",
"name": "[%key:component::gate::common::trigger_behavior_name%]"
}
},

View File

@@ -1,4 +1,4 @@
"""The Glances component."""
"""The Glances integration."""
import logging
from typing import Any

View File

@@ -1,4 +1,4 @@
"""Constants for Glances component."""
"""Constants for Glances integration."""
from datetime import timedelta
import sys

View File

@@ -1,4 +1,4 @@
"""Support gathering system information of hosts which are running glances."""
"""Support gathering system information of hosts which are running Glances."""
from __future__ import annotations

View File

@@ -979,7 +979,7 @@ class HomeKit:
for entry in dev_reg.devices.get_devices_for_config_entry_id(self._entry_id)
if (
identifier not in entry.identifiers # type: ignore[comparison-overlap]
or connection not in entry.connections
or connection not in entry.connections # type: ignore[unreachable]
)
]

View File

@@ -10,8 +10,11 @@
"dismiss": {
"service": "mdi:bell-off"
},
"dismiss_message": {
"service": "mdi:comment-remove"
},
"send_message": {
"service": "mdi:message-arrow-right"
"service": "mdi:comment-arrow-right"
}
}
}

View File

@@ -27,5 +27,28 @@ def deprecated_notify_action_call(
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_notify_action",
translation_placeholders={"action": action},
translation_placeholders={
"action": action,
"new_action_1": "notify.send_message",
"new_action_2": "html5.send_message",
},
)
@callback
def deprecated_dismiss_action_call(hass: HomeAssistant) -> None:
"""Deprecated action call."""
async_create_issue(
hass,
DOMAIN,
"deprecated_dismiss_action",
breaks_in_ha_version="2026.11.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_dismiss_action",
translation_placeholders={
"action": "html5.dismiss",
"new_action": "html5.dismiss_message",
},
)

View File

@@ -60,7 +60,7 @@ from .const import (
SERVICE_DISMISS,
)
from .entity import HTML5Entity, Registration
from .issue import deprecated_notify_action_call
from .issue import deprecated_dismiss_action_call, deprecated_notify_action_call
_LOGGER = logging.getLogger(__name__)
@@ -460,6 +460,9 @@ class HTML5NotificationService(BaseNotificationService):
This method must be run in the event loop.
"""
deprecated_dismiss_action_call(self.hass)
data: dict[str, Any] | None = kwargs.get(ATTR_DATA)
tag: str = data.get(ATTR_TAG, "") if data else ""
payload = {ATTR_TAG: tag, ATTR_DISMISS: True, ATTR_DATA: {}}
@@ -624,6 +627,11 @@ class HTML5NotifyEntity(HTML5Entity, NotifyEntity):
await self._webpush(**kwargs)
self._async_record_notification()
async def dismiss_notification(self, tag: str = "") -> None:
"""Dismiss a message via html5.dismiss_message action."""
await self._webpush(dismiss=True, tag=tag)
self._async_record_notification()
async def _webpush(
self,
message: str | None = None,

View File

@@ -32,6 +32,7 @@ from .const import (
)
SERVICE_SEND_MESSAGE = "send_message"
SERVICE_DISMISS_MESSAGE = "dismiss_message"
SERVICE_SEND_MESSAGE_SCHEMA = cv.make_entity_service_schema(
{
@@ -67,6 +68,10 @@ SERVICE_SEND_MESSAGE_SCHEMA = cv.make_entity_service_schema(
}
)
SERVICE_DISMISS_MESSAGE_SCHEMA = cv.make_entity_service_schema(
{vol.Optional(ATTR_TAG): cv.string}
)
@callback
def async_setup_services(hass: HomeAssistant) -> None:
@@ -80,3 +85,11 @@ def async_setup_services(hass: HomeAssistant) -> None:
schema=SERVICE_SEND_MESSAGE_SCHEMA,
func="send_push_notification",
)
service.async_register_platform_entity_service(
hass,
DOMAIN,
SERVICE_DISMISS_MESSAGE,
entity_domain=NOTIFY_DOMAIN,
schema=SERVICE_DISMISS_MESSAGE_SCHEMA,
func="dismiss_notification",
)

View File

@@ -44,7 +44,7 @@ send_message:
text:
type: url
example: /static/images/image.jpg
tag:
tag: &tag
required: false
selector:
text:
@@ -142,3 +142,10 @@ send_message:
selector:
object:
example: "{'customKey': 'customValue'}"
dismiss_message:
target:
entity:
domain: notify
integration: html5
fields:
tag: *tag

View File

@@ -32,7 +32,9 @@
"received": "Received"
}
},
"tag": { "name": "Tag" }
"tag": {
"name": "[%key:component::html5::services::send_message::fields::tag::name%]"
}
}
}
}
@@ -49,8 +51,12 @@
}
},
"issues": {
"deprecated_dismiss_action": {
"description": "The action `{action}` is deprecated and will be removed in a future release.\n\nPlease update your automations and scripts to use the notify entities with the `{new_action}` action instead.",
"title": "[%key:component::html5::issues::deprecated_notify_action::title%]"
},
"deprecated_notify_action": {
"description": "The action `{action}` is deprecated and will be removed in a future release.\n\nPlease update your automations and scripts to use the notify entities with the `notify.send_message` or `html5.send_message` actions instead.",
"description": "The action `{action}` is deprecated and will be removed in a future release.\n\nPlease update your automations and scripts to use the notify entities with the `{new_action_1}` or `{new_action_2}` actions instead.",
"title": "Detected use of deprecated action {action}"
}
},
@@ -101,6 +107,16 @@
},
"name": "Dismiss"
},
"dismiss_message": {
"description": "Dismisses one or more HTML5 notifications.",
"fields": {
"tag": {
"description": "The tag of the notifications to dismiss. If not specified, all notifications will be dismissed.",
"name": "[%key:component::html5::services::send_message::fields::tag::name%]"
}
},
"name": "Dismiss message"
},
"send_message": {
"description": "Sends a message via HTML5 Push Notifications",
"fields": {

View File

@@ -1,18 +1,14 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted humidifiers.",
"condition_behavior_name": "Behavior",
"condition_threshold_description": "What to test for and threshold values.",
"condition_threshold_name": "Threshold configuration",
"trigger_behavior_description": "The behavior of the targeted humidifiers to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"condition_threshold_name": "Threshold type",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_drying": {
"description": "Tests if one or more humidifiers are drying.",
"fields": {
"behavior": {
"description": "[%key:component::humidifier::common::condition_behavior_description%]",
"name": "[%key:component::humidifier::common::condition_behavior_name%]"
}
},
@@ -22,7 +18,6 @@
"description": "Tests if one or more humidifiers are humidifying.",
"fields": {
"behavior": {
"description": "[%key:component::humidifier::common::condition_behavior_description%]",
"name": "[%key:component::humidifier::common::condition_behavior_name%]"
}
},
@@ -32,7 +27,6 @@
"description": "Tests if one or more humidifiers are set to a specific mode.",
"fields": {
"behavior": {
"description": "[%key:component::humidifier::common::condition_behavior_description%]",
"name": "[%key:component::humidifier::common::condition_behavior_name%]"
},
"mode": {
@@ -46,7 +40,6 @@
"description": "Tests if one or more humidifiers are off.",
"fields": {
"behavior": {
"description": "[%key:component::humidifier::common::condition_behavior_description%]",
"name": "[%key:component::humidifier::common::condition_behavior_name%]"
}
},
@@ -56,7 +49,6 @@
"description": "Tests if one or more humidifiers are on.",
"fields": {
"behavior": {
"description": "[%key:component::humidifier::common::condition_behavior_description%]",
"name": "[%key:component::humidifier::common::condition_behavior_name%]"
}
},
@@ -66,11 +58,9 @@
"description": "Tests the target humidity of one or more humidifiers.",
"fields": {
"behavior": {
"description": "[%key:component::humidifier::common::condition_behavior_description%]",
"name": "[%key:component::humidifier::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::humidifier::common::condition_threshold_description%]",
"name": "[%key:component::humidifier::common::condition_threshold_name%]"
}
},
@@ -219,7 +209,6 @@
"description": "Triggers after the operation mode of one or more humidifiers changes.",
"fields": {
"behavior": {
"description": "[%key:component::humidifier::common::trigger_behavior_description%]",
"name": "[%key:component::humidifier::common::trigger_behavior_name%]"
},
"mode": {
@@ -233,7 +222,6 @@
"description": "Triggers after one or more humidifiers start drying.",
"fields": {
"behavior": {
"description": "[%key:component::humidifier::common::trigger_behavior_description%]",
"name": "[%key:component::humidifier::common::trigger_behavior_name%]"
}
},
@@ -243,7 +231,6 @@
"description": "Triggers after one or more humidifiers start humidifying.",
"fields": {
"behavior": {
"description": "[%key:component::humidifier::common::trigger_behavior_description%]",
"name": "[%key:component::humidifier::common::trigger_behavior_name%]"
}
},
@@ -253,7 +240,6 @@
"description": "Triggers after one or more humidifiers turn off.",
"fields": {
"behavior": {
"description": "[%key:component::humidifier::common::trigger_behavior_description%]",
"name": "[%key:component::humidifier::common::trigger_behavior_name%]"
}
},
@@ -263,7 +249,6 @@
"description": "Triggers after one or more humidifiers turn on.",
"fields": {
"behavior": {
"description": "[%key:component::humidifier::common::trigger_behavior_description%]",
"name": "[%key:component::humidifier::common::trigger_behavior_name%]"
}
},

View File

@@ -1,25 +1,18 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted entities.",
"condition_behavior_name": "Behavior",
"condition_threshold_description": "What to test for and threshold values.",
"condition_threshold_name": "Threshold configuration",
"trigger_behavior_description": "The behavior of the targeted entities to trigger on.",
"trigger_behavior_name": "Behavior",
"trigger_threshold_changed_description": "Which changes to trigger on and threshold values.",
"trigger_threshold_crossed_description": "Which threshold crossing to trigger on and threshold values.",
"trigger_threshold_name": "Threshold configuration"
"condition_behavior_name": "Condition passes if",
"condition_threshold_name": "Threshold type",
"trigger_behavior_name": "Trigger when",
"trigger_threshold_name": "Threshold type"
},
"conditions": {
"is_value": {
"description": "Tests if a relative humidity value is above a threshold, below a threshold, or in a range of values.",
"fields": {
"behavior": {
"description": "[%key:component::humidity::common::condition_behavior_description%]",
"name": "[%key:component::humidity::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::humidity::common::condition_threshold_description%]",
"name": "[%key:component::humidity::common::condition_threshold_name%]"
}
},
@@ -47,7 +40,6 @@
"description": "Triggers after one or more relative humidity values change.",
"fields": {
"threshold": {
"description": "[%key:component::humidity::common::trigger_threshold_changed_description%]",
"name": "[%key:component::humidity::common::trigger_threshold_name%]"
}
},
@@ -57,11 +49,9 @@
"description": "Triggers after one or more relative humidity values cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::humidity::common::trigger_behavior_description%]",
"name": "[%key:component::humidity::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::humidity::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::humidity::common::trigger_threshold_name%]"
}
},

View File

@@ -1,21 +1,15 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted entities.",
"condition_behavior_name": "Behavior",
"condition_threshold_description": "What to test for and threshold values.",
"condition_threshold_name": "Threshold configuration",
"trigger_behavior_description": "The behavior of the targeted entities to trigger on.",
"trigger_behavior_name": "Behavior",
"trigger_threshold_changed_description": "Which changes to trigger on and threshold values.",
"trigger_threshold_crossed_description": "Which threshold crossing to trigger on and threshold values.",
"trigger_threshold_name": "Threshold configuration"
"condition_behavior_name": "Condition passes if",
"condition_threshold_name": "Threshold type",
"trigger_behavior_name": "Trigger when",
"trigger_threshold_name": "Threshold type"
},
"conditions": {
"is_detected": {
"description": "Tests if light is currently detected.",
"fields": {
"behavior": {
"description": "[%key:component::illuminance::common::condition_behavior_description%]",
"name": "[%key:component::illuminance::common::condition_behavior_name%]"
}
},
@@ -25,7 +19,6 @@
"description": "Tests if light is currently not detected.",
"fields": {
"behavior": {
"description": "[%key:component::illuminance::common::condition_behavior_description%]",
"name": "[%key:component::illuminance::common::condition_behavior_name%]"
}
},
@@ -35,11 +28,9 @@
"description": "Tests the illuminance value.",
"fields": {
"behavior": {
"description": "[%key:component::illuminance::common::condition_behavior_description%]",
"name": "[%key:component::illuminance::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::illuminance::common::condition_threshold_description%]",
"name": "[%key:component::illuminance::common::condition_threshold_name%]"
}
},
@@ -67,7 +58,6 @@
"description": "Triggers after one or more illuminance values change.",
"fields": {
"threshold": {
"description": "[%key:component::illuminance::common::trigger_threshold_changed_description%]",
"name": "[%key:component::illuminance::common::trigger_threshold_name%]"
}
},
@@ -77,7 +67,6 @@
"description": "Triggers after one or more light sensors stop detecting light.",
"fields": {
"behavior": {
"description": "[%key:component::illuminance::common::trigger_behavior_description%]",
"name": "[%key:component::illuminance::common::trigger_behavior_name%]"
}
},
@@ -87,11 +76,9 @@
"description": "Triggers after one or more illuminance values cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::illuminance::common::trigger_behavior_description%]",
"name": "[%key:component::illuminance::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::illuminance::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::illuminance::common::trigger_threshold_name%]"
}
},
@@ -101,7 +88,6 @@
"description": "Triggers after one or more light sensors start detecting light.",
"fields": {
"behavior": {
"description": "[%key:component::illuminance::common::trigger_behavior_description%]",
"name": "[%key:component::illuminance::common::trigger_behavior_name%]"
}
},

View File

@@ -23,7 +23,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: IstaConfigEntry) -> bool
ista = PyEcotrendIsta(
entry.data[CONF_EMAIL],
entry.data[CONF_PASSWORD],
_LOGGER,
)
coordinator = IstaCoordinator(hass, entry, ista)

View File

@@ -51,7 +51,6 @@ class IstaConfigFlow(ConfigFlow, domain=DOMAIN):
ista = PyEcotrendIsta(
user_input[CONF_EMAIL],
user_input[CONF_PASSWORD],
_LOGGER,
)
try:
await self.hass.async_add_executor_job(ista.login)
@@ -102,7 +101,6 @@ class IstaConfigFlow(ConfigFlow, domain=DOMAIN):
ista = PyEcotrendIsta(
user_input[CONF_EMAIL],
user_input[CONF_PASSWORD],
_LOGGER,
)
def get_consumption_units() -> set[str]:

View File

@@ -94,10 +94,8 @@ class IstaCoordinator(DataUpdateCoordinator[dict[str, Any]]):
result = self.ista.get_consumption_unit_details()
return {
consumption_unit: next(
details
for details in result["consumptionUnits"]
if details["id"] == consumption_unit
)
consumption_unit: details
for consumption_unit in self.ista.get_uuids()
for details in result["consumptionUnits"]
if details["id"] == consumption_unit
}

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted lawn mowers.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted lawn mowers to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_docked": {
"description": "Tests if one or more lawn mowers are docked.",
"fields": {
"behavior": {
"description": "[%key:component::lawn_mower::common::condition_behavior_description%]",
"name": "[%key:component::lawn_mower::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more lawn mowers are encountering an error.",
"fields": {
"behavior": {
"description": "[%key:component::lawn_mower::common::condition_behavior_description%]",
"name": "[%key:component::lawn_mower::common::condition_behavior_name%]"
}
},
@@ -30,7 +26,6 @@
"description": "Tests if one or more lawn mowers are mowing.",
"fields": {
"behavior": {
"description": "[%key:component::lawn_mower::common::condition_behavior_description%]",
"name": "[%key:component::lawn_mower::common::condition_behavior_name%]"
}
},
@@ -40,7 +35,6 @@
"description": "Tests if one or more lawn mowers are paused.",
"fields": {
"behavior": {
"description": "[%key:component::lawn_mower::common::condition_behavior_description%]",
"name": "[%key:component::lawn_mower::common::condition_behavior_name%]"
}
},
@@ -50,7 +44,6 @@
"description": "Tests if one or more lawn mowers are returning to the dock.",
"fields": {
"behavior": {
"description": "[%key:component::lawn_mower::common::condition_behavior_description%]",
"name": "[%key:component::lawn_mower::common::condition_behavior_name%]"
}
},
@@ -104,7 +97,6 @@
"description": "Triggers after one or more lawn mowers have returned to dock.",
"fields": {
"behavior": {
"description": "[%key:component::lawn_mower::common::trigger_behavior_description%]",
"name": "[%key:component::lawn_mower::common::trigger_behavior_name%]"
}
},
@@ -114,7 +106,6 @@
"description": "Triggers after one or more lawn mowers encounter an error.",
"fields": {
"behavior": {
"description": "[%key:component::lawn_mower::common::trigger_behavior_description%]",
"name": "[%key:component::lawn_mower::common::trigger_behavior_name%]"
}
},
@@ -124,7 +115,6 @@
"description": "Triggers after one or more lawn mowers pause mowing.",
"fields": {
"behavior": {
"description": "[%key:component::lawn_mower::common::trigger_behavior_description%]",
"name": "[%key:component::lawn_mower::common::trigger_behavior_name%]"
}
},
@@ -134,7 +124,6 @@
"description": "Triggers after one or more lawn mowers start mowing.",
"fields": {
"behavior": {
"description": "[%key:component::lawn_mower::common::trigger_behavior_description%]",
"name": "[%key:component::lawn_mower::common::trigger_behavior_name%]"
}
},
@@ -144,7 +133,6 @@
"description": "Triggers after one or more lawn mowers start returning to dock.",
"fields": {
"behavior": {
"description": "[%key:component::lawn_mower::common::trigger_behavior_description%]",
"name": "[%key:component::lawn_mower::common::trigger_behavior_name%]"
}
},

View File

@@ -36,5 +36,5 @@
"documentation": "https://www.home-assistant.io/integrations/led_ble",
"integration_type": "device",
"iot_class": "local_polling",
"requirements": ["bluetooth-data-tools==1.28.4", "led-ble==1.1.7"]
"requirements": ["bluetooth-data-tools==1.28.4", "led-ble==1.1.8"]
}

View File

@@ -97,7 +97,8 @@ SENSOR_TYPES: dict[str, LidarrSensorEntityDescription[Any]] = {
state_class=SensorStateClass.TOTAL,
entity_registry_enabled_default=False,
attributes_fn=lambda data: {
album.title: album.artist.artistName for album in data.records
album.title: album.artist.artistName # type: ignore[misc]
for album in data.records
},
),
"albums": LidarrSensorEntityDescription[int](

View File

@@ -1,9 +1,7 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted lights.",
"condition_behavior_name": "Behavior",
"condition_threshold_description": "What to test for and threshold values.",
"condition_threshold_name": "Threshold configuration",
"condition_behavior_name": "Condition passes if",
"condition_threshold_name": "Threshold type",
"field_brightness_description": "Number indicating brightness, where 0 turns the light off, 1 is the minimum brightness, and 255 is the maximum brightness.",
"field_brightness_name": "Brightness value",
"field_brightness_pct_description": "Number indicating the percentage of full brightness, where 0 turns the light off, 1 is the minimum brightness, and 100 is the maximum brightness.",
@@ -37,22 +35,17 @@
"field_xy_color_description": "Color in XY-format. A list of two decimal numbers between 0 and 1.",
"field_xy_color_name": "XY-color",
"section_advanced_fields_name": "Advanced options",
"trigger_behavior_description": "The behavior of the targeted lights to trigger on.",
"trigger_behavior_name": "Behavior",
"trigger_threshold_changed_description": "Which changes to trigger on and threshold values.",
"trigger_threshold_crossed_description": "Which threshold crossing to trigger on and threshold values.",
"trigger_threshold_name": "Threshold configuration"
"trigger_behavior_name": "Trigger when",
"trigger_threshold_name": "Threshold type"
},
"conditions": {
"is_brightness": {
"description": "Tests the brightness of one or more lights.",
"fields": {
"behavior": {
"description": "[%key:component::light::common::condition_behavior_description%]",
"name": "[%key:component::light::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::light::common::condition_threshold_description%]",
"name": "[%key:component::light::common::condition_threshold_name%]"
}
},
@@ -62,7 +55,6 @@
"description": "Tests if one or more lights are off.",
"fields": {
"behavior": {
"description": "[%key:component::light::common::condition_behavior_description%]",
"name": "[%key:component::light::common::condition_behavior_name%]"
}
},
@@ -72,7 +64,6 @@
"description": "Tests if one or more lights are on.",
"fields": {
"behavior": {
"description": "[%key:component::light::common::condition_behavior_description%]",
"name": "[%key:component::light::common::condition_behavior_name%]"
}
},
@@ -513,7 +504,6 @@
"description": "Triggers after the brightness of one or more lights changes.",
"fields": {
"threshold": {
"description": "[%key:component::light::common::trigger_threshold_changed_description%]",
"name": "[%key:component::light::common::trigger_threshold_name%]"
}
},
@@ -523,11 +513,9 @@
"description": "Triggers after the brightness of one or more lights crosses a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::light::common::trigger_behavior_description%]",
"name": "[%key:component::light::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::light::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::light::common::trigger_threshold_name%]"
}
},
@@ -537,7 +525,6 @@
"description": "Triggers after one or more lights turn off.",
"fields": {
"behavior": {
"description": "[%key:component::light::common::trigger_behavior_description%]",
"name": "[%key:component::light::common::trigger_behavior_name%]"
}
},
@@ -547,7 +534,6 @@
"description": "Triggers after one or more lights turn on.",
"fields": {
"behavior": {
"description": "[%key:component::light::common::trigger_behavior_description%]",
"name": "[%key:component::light::common::trigger_behavior_name%]"
}
},

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted locks.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted locks to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_jammed": {
"description": "Tests if one or more locks are jammed.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::condition_behavior_description%]",
"name": "[%key:component::lock::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more locks are locked.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::condition_behavior_description%]",
"name": "[%key:component::lock::common::condition_behavior_name%]"
}
},
@@ -30,7 +26,6 @@
"description": "Tests if one or more locks are open.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::condition_behavior_description%]",
"name": "[%key:component::lock::common::condition_behavior_name%]"
}
},
@@ -40,7 +35,6 @@
"description": "Tests if one or more locks are unlocked.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::condition_behavior_description%]",
"name": "[%key:component::lock::common::condition_behavior_name%]"
}
},
@@ -151,7 +145,6 @@
"description": "Triggers after one or more locks jam.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::trigger_behavior_description%]",
"name": "[%key:component::lock::common::trigger_behavior_name%]"
}
},
@@ -161,7 +154,6 @@
"description": "Triggers after one or more locks lock.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::trigger_behavior_description%]",
"name": "[%key:component::lock::common::trigger_behavior_name%]"
}
},
@@ -171,7 +163,6 @@
"description": "Triggers after one or more locks open.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::trigger_behavior_description%]",
"name": "[%key:component::lock::common::trigger_behavior_name%]"
}
},
@@ -181,7 +172,6 @@
"description": "Triggers after one or more locks unlock.",
"fields": {
"behavior": {
"description": "[%key:component::lock::common::trigger_behavior_description%]",
"name": "[%key:component::lock::common::trigger_behavior_name%]"
}
},

View File

@@ -29,6 +29,8 @@ TUBE_LINES = [
"Suffragette",
"Weaver",
"Windrush",
"Tram",
"IFS Cloud Cable Car",
]
# Default lines to monitor if none selected

View File

@@ -7,6 +7,6 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["london_tube_status"],
"requirements": ["london-tube-status==0.5"],
"requirements": ["london-tube-status==0.7"],
"single_config_entry": true
}

View File

@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "local_polling",
"quality_scale": "platinum",
"requirements": ["aiomealie==1.2.2"]
"requirements": ["aiomealie==1.2.3"]
}

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted media players.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted media players to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_not_playing": {
"description": "Tests if one or more media players are not playing.",
"fields": {
"behavior": {
"description": "[%key:component::media_player::common::condition_behavior_description%]",
"name": "[%key:component::media_player::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more media players are off.",
"fields": {
"behavior": {
"description": "[%key:component::media_player::common::condition_behavior_description%]",
"name": "[%key:component::media_player::common::condition_behavior_name%]"
}
},
@@ -30,7 +26,6 @@
"description": "Tests if one or more media players are on.",
"fields": {
"behavior": {
"description": "[%key:component::media_player::common::condition_behavior_description%]",
"name": "[%key:component::media_player::common::condition_behavior_name%]"
}
},
@@ -40,7 +35,6 @@
"description": "Tests if one or more media players are paused.",
"fields": {
"behavior": {
"description": "[%key:component::media_player::common::condition_behavior_description%]",
"name": "[%key:component::media_player::common::condition_behavior_name%]"
}
},
@@ -50,7 +44,6 @@
"description": "Tests if one or more media players are playing.",
"fields": {
"behavior": {
"description": "[%key:component::media_player::common::condition_behavior_description%]",
"name": "[%key:component::media_player::common::condition_behavior_name%]"
}
},
@@ -444,7 +437,6 @@
"description": "Triggers after one or more media players stop playing media.",
"fields": {
"behavior": {
"description": "[%key:component::media_player::common::trigger_behavior_description%]",
"name": "[%key:component::media_player::common::trigger_behavior_name%]"
}
},

View File

@@ -1,21 +1,15 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted entities.",
"condition_behavior_name": "Behavior",
"condition_threshold_description": "What to test for and threshold values.",
"condition_threshold_name": "Threshold configuration",
"trigger_behavior_description": "The behavior of the targeted entities to trigger on.",
"trigger_behavior_name": "Behavior",
"trigger_threshold_changed_description": "Which changes to trigger on and threshold values.",
"trigger_threshold_crossed_description": "Which threshold crossing to trigger on and threshold values.",
"trigger_threshold_name": "Threshold configuration"
"condition_behavior_name": "Condition passes if",
"condition_threshold_name": "Threshold type",
"trigger_behavior_name": "Trigger when",
"trigger_threshold_name": "Threshold type"
},
"conditions": {
"is_detected": {
"description": "Tests if one or more moisture sensors are detecting moisture.",
"fields": {
"behavior": {
"description": "[%key:component::moisture::common::condition_behavior_description%]",
"name": "[%key:component::moisture::common::condition_behavior_name%]"
}
},
@@ -25,7 +19,6 @@
"description": "Tests if one or more moisture sensors are not detecting moisture.",
"fields": {
"behavior": {
"description": "[%key:component::moisture::common::condition_behavior_description%]",
"name": "[%key:component::moisture::common::condition_behavior_name%]"
}
},
@@ -35,11 +28,9 @@
"description": "Tests the moisture level of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::moisture::common::condition_behavior_description%]",
"name": "[%key:component::moisture::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::moisture::common::condition_threshold_description%]",
"name": "[%key:component::moisture::common::condition_threshold_name%]"
}
},
@@ -67,7 +58,6 @@
"description": "Triggers after one or more moisture content values change.",
"fields": {
"threshold": {
"description": "[%key:component::moisture::common::trigger_threshold_changed_description%]",
"name": "[%key:component::moisture::common::trigger_threshold_name%]"
}
},
@@ -77,7 +67,6 @@
"description": "Triggers after one or more moisture sensors stop detecting moisture.",
"fields": {
"behavior": {
"description": "[%key:component::moisture::common::trigger_behavior_description%]",
"name": "[%key:component::moisture::common::trigger_behavior_name%]"
}
},
@@ -87,11 +76,9 @@
"description": "Triggers after one or more moisture content values cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::moisture::common::trigger_behavior_description%]",
"name": "[%key:component::moisture::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::moisture::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::moisture::common::trigger_threshold_name%]"
}
},
@@ -101,7 +88,6 @@
"description": "Triggers after one or more moisture sensors start detecting moisture.",
"fields": {
"behavior": {
"description": "[%key:component::moisture::common::trigger_behavior_description%]",
"name": "[%key:component::moisture::common::trigger_behavior_name%]"
}
},

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted motion sensors.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted motion sensors to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_detected": {
"description": "Tests if one or more motion sensors are detecting motion.",
"fields": {
"behavior": {
"description": "[%key:component::motion::common::condition_behavior_description%]",
"name": "[%key:component::motion::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more motion sensors are not detecting motion.",
"fields": {
"behavior": {
"description": "[%key:component::motion::common::condition_behavior_description%]",
"name": "[%key:component::motion::common::condition_behavior_name%]"
}
},
@@ -48,7 +44,6 @@
"description": "Triggers after one or more motion sensors stop detecting motion.",
"fields": {
"behavior": {
"description": "[%key:component::motion::common::trigger_behavior_description%]",
"name": "[%key:component::motion::common::trigger_behavior_name%]"
}
},
@@ -58,7 +53,6 @@
"description": "Triggers after one or more motion sensors start detecting motion.",
"fields": {
"behavior": {
"description": "[%key:component::motion::common::trigger_behavior_description%]",
"name": "[%key:component::motion::common::trigger_behavior_name%]"
}
},

View File

@@ -16,8 +16,10 @@ from .const import DOMAIN
PLATFORMS = [Platform.SENSOR]
_API_TIMEOUT = SLOW_UPDATE_WARNING - 1
type NightscoutConfigEntry = ConfigEntry[NightscoutAPI]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: NightscoutConfigEntry) -> bool:
"""Set up Nightscout from a config entry."""
server_url = entry.data[CONF_URL]
api_key = entry.data.get(CONF_API_KEY)
@@ -28,8 +30,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except (ClientError, TimeoutError, OSError) as error:
raise ConfigEntryNotReady from error
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = api
entry.runtime_data = api
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
@@ -46,10 +47,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: NightscoutConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@@ -10,12 +10,12 @@ from aiohttp import ClientError
from py_nightscout import Api as NightscoutAPI
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_DATE, UnitOfBloodGlucoseConcentration
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import ATTR_DELTA, ATTR_DEVICE, ATTR_DIRECTION, DOMAIN
from . import NightscoutConfigEntry
from .const import ATTR_DELTA, ATTR_DEVICE, ATTR_DIRECTION
SCAN_INTERVAL = timedelta(minutes=1)
@@ -26,11 +26,11 @@ DEFAULT_NAME = "Blood Glucose"
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: NightscoutConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Glucose Sensor."""
api = hass.data[DOMAIN][entry.entry_id]
api = entry.runtime_data
async_add_entities([NightscoutSensor(api, "Blood Sugar", entry.unique_id)], True)

View File

@@ -6,13 +6,12 @@ import logging
import nuheat
import requests
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from .const import CONF_SERIAL_NUMBER, DOMAIN, PLATFORMS
from .coordinator import NuHeatCoordinator
from .const import CONF_SERIAL_NUMBER, PLATFORMS
from .coordinator import NuHeatConfigEntry, NuHeatCoordinator
_LOGGER = logging.getLogger(__name__)
@@ -23,7 +22,7 @@ def _get_thermostat(api: nuheat.NuHeat, serial_number: str) -> nuheat.NuHeatTher
return api.get_thermostat(serial_number)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: NuHeatConfigEntry) -> bool:
"""Set up NuHeat from a config entry."""
conf = entry.data
@@ -52,20 +51,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
_LOGGER.error("Failed to login to nuheat: %s", ex)
return False
coordinator = NuHeatCoordinator(hass, entry, thermostat)
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = (thermostat, coordinator)
entry.runtime_data = NuHeatCoordinator(hass, entry, thermostat)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: NuHeatConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@@ -18,7 +18,6 @@ from homeassistant.components.climate import (
HVACAction,
HVACMode,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import event as event_helper
@@ -27,7 +26,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, MANUFACTURER, NUHEAT_API_STATE_SHIFT_DELAY
from .coordinator import NuHeatCoordinator
from .coordinator import NuHeatConfigEntry, NuHeatCoordinator
_LOGGER = logging.getLogger(__name__)
@@ -55,14 +54,15 @@ SCHEDULE_MODE_TO_PRESET_MODE_MAP = {
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: NuHeatConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the NuHeat thermostat(s)."""
thermostat, coordinator = hass.data[DOMAIN][config_entry.entry_id]
coordinator = config_entry.runtime_data
temperature_unit = hass.config.units.temperature_unit
entity = NuHeatThermostat(coordinator, thermostat, temperature_unit)
entity = NuHeatThermostat(coordinator, coordinator.thermostat, temperature_unit)
# No longer need a service as set_hvac_mode to auto does this
# since climate 1.0 has been implemented

View File

@@ -16,15 +16,18 @@ _LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(minutes=5)
type NuHeatConfigEntry = ConfigEntry[NuHeatCoordinator]
class NuHeatCoordinator(DataUpdateCoordinator[None]):
"""Coordinator for NuHeat thermostat data."""
config_entry: ConfigEntry
config_entry: NuHeatConfigEntry
def __init__(
self,
hass: HomeAssistant,
entry: ConfigEntry,
entry: NuHeatConfigEntry,
thermostat: nuheat.NuHeatThermostat,
) -> None:
"""Initialize the coordinator."""

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted occupancy sensors.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted occupancy sensors to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_detected": {
"description": "Tests if one or more occupancy sensors are detecting occupancy.",
"fields": {
"behavior": {
"description": "[%key:component::occupancy::common::condition_behavior_description%]",
"name": "[%key:component::occupancy::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more occupancy sensors are not detecting occupancy.",
"fields": {
"behavior": {
"description": "[%key:component::occupancy::common::condition_behavior_description%]",
"name": "[%key:component::occupancy::common::condition_behavior_name%]"
}
},
@@ -48,7 +44,6 @@
"description": "Triggers after one or more occupancy sensors stop detecting occupancy.",
"fields": {
"behavior": {
"description": "[%key:component::occupancy::common::trigger_behavior_description%]",
"name": "[%key:component::occupancy::common::trigger_behavior_name%]"
}
},
@@ -58,7 +53,6 @@
"description": "Triggers after one or more occupancy sensors start detecting occupancy.",
"fields": {
"behavior": {
"description": "[%key:component::occupancy::common::trigger_behavior_description%]",
"name": "[%key:component::occupancy::common::trigger_behavior_name%]"
}
},

View File

@@ -122,7 +122,7 @@ class ChannelMutingCoordinator(DataUpdateCoordinator[ChannelMutingData]):
"""Send muting command for a channel."""
self._desired[channel] = param
message_data: ChannelMutingDesired = self.data | self._desired
message = command.ChannelMuting(**message_data) # type: ignore[misc]
message = command.ChannelMuting(**message_data)
await self.manager.write(message)
async def _update_callback(self, message: Status) -> None:

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted persons.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted persons to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_home": {
"description": "Tests if one or more persons are home.",
"fields": {
"behavior": {
"description": "[%key:component::person::common::condition_behavior_description%]",
"name": "[%key:component::person::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more persons are not home.",
"fields": {
"behavior": {
"description": "[%key:component::person::common::condition_behavior_description%]",
"name": "[%key:component::person::common::condition_behavior_name%]"
}
},
@@ -80,7 +76,6 @@
"description": "Triggers when one or more persons enter home.",
"fields": {
"behavior": {
"description": "[%key:component::person::common::trigger_behavior_description%]",
"name": "[%key:component::person::common::trigger_behavior_name%]"
}
},
@@ -90,7 +85,6 @@
"description": "Triggers when one or more persons leave home.",
"fields": {
"behavior": {
"description": "[%key:component::person::common::trigger_behavior_description%]",
"name": "[%key:component::person::common::trigger_behavior_name%]"
}
},

View File

@@ -1,25 +1,18 @@
{
"common": {
"condition_behavior_description": "How the power value should match on the targeted entities.",
"condition_behavior_name": "Behavior",
"condition_threshold_description": "What to test for and threshold values.",
"condition_threshold_name": "Threshold configuration",
"trigger_behavior_description": "The behavior of the targeted entities to trigger on.",
"trigger_behavior_name": "Behavior",
"trigger_threshold_changed_description": "Which changes to trigger on and threshold values.",
"trigger_threshold_crossed_description": "Which threshold crossing to trigger on and threshold values.",
"trigger_threshold_name": "Threshold configuration"
"condition_behavior_name": "Condition passes if",
"condition_threshold_name": "Threshold type",
"trigger_behavior_name": "Trigger when",
"trigger_threshold_name": "Threshold type"
},
"conditions": {
"is_value": {
"description": "Tests the power value of one or more entities.",
"fields": {
"behavior": {
"description": "[%key:component::power::common::condition_behavior_description%]",
"name": "[%key:component::power::common::condition_behavior_name%]"
},
"threshold": {
"description": "[%key:component::power::common::condition_threshold_description%]",
"name": "[%key:component::power::common::condition_threshold_name%]"
}
},
@@ -47,7 +40,6 @@
"description": "Triggers after one or more power values change.",
"fields": {
"threshold": {
"description": "[%key:component::power::common::trigger_threshold_changed_description%]",
"name": "[%key:component::power::common::trigger_threshold_name%]"
}
},
@@ -57,11 +49,9 @@
"description": "Triggers after one or more power values cross a threshold.",
"fields": {
"behavior": {
"description": "[%key:component::power::common::trigger_behavior_description%]",
"name": "[%key:component::power::common::trigger_behavior_name%]"
},
"threshold": {
"description": "[%key:component::power::common::trigger_threshold_crossed_description%]",
"name": "[%key:component::power::common::trigger_threshold_name%]"
}
},

View File

@@ -1,7 +1,6 @@
{
"common": {
"trigger_behavior_description": "The behavior of the targeted remotes to trigger on.",
"trigger_behavior_name": "Behavior"
"trigger_behavior_name": "Trigger when"
},
"device_automation": {
"action_type": {
@@ -132,7 +131,6 @@
"description": "Triggers when one or more remotes turn off.",
"fields": {
"behavior": {
"description": "[%key:component::remote::common::trigger_behavior_description%]",
"name": "[%key:component::remote::common::trigger_behavior_name%]"
}
},
@@ -142,7 +140,6 @@
"description": "Triggers when one or more remotes turn on.",
"fields": {
"behavior": {
"description": "[%key:component::remote::common::trigger_behavior_description%]",
"name": "[%key:component::remote::common::trigger_behavior_name%]"
}
},

View File

@@ -103,7 +103,7 @@
"name": "Reset side brush consumable"
},
"shutdown": {
"name": "Shutdown"
"name": "Shut down"
},
"start": {
"name": "Start"

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted schedules.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted schedules to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_off": {
"description": "Tests if one or more schedule blocks are currently not active.",
"fields": {
"behavior": {
"description": "[%key:component::schedule::common::condition_behavior_description%]",
"name": "[%key:component::schedule::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more schedule blocks are currently active.",
"fields": {
"behavior": {
"description": "[%key:component::schedule::common::condition_behavior_description%]",
"name": "[%key:component::schedule::common::condition_behavior_name%]"
}
},
@@ -79,7 +75,6 @@
"description": "Triggers when a schedule block ends.",
"fields": {
"behavior": {
"description": "[%key:component::schedule::common::trigger_behavior_description%]",
"name": "[%key:component::schedule::common::trigger_behavior_name%]"
}
},
@@ -89,7 +84,6 @@
"description": "Triggers when a schedule block starts.",
"fields": {
"behavior": {
"description": "[%key:component::schedule::common::trigger_behavior_description%]",
"name": "[%key:component::schedule::common::trigger_behavior_name%]"
}
},

View File

@@ -4,8 +4,7 @@
"description": "Tests if one or more dropdowns have a specific option selected.",
"fields": {
"behavior": {
"description": "Whether the condition should pass when any or all targeted entities match.",
"name": "Behavior"
"name": "Condition passes if"
},
"option": {
"description": "The options to check for.",

View File

@@ -3,12 +3,16 @@
from __future__ import annotations
import asyncio
from collections.abc import Callable, Coroutine
from contextlib import suppress
import logging
import shlex
from typing import Any
import voluptuous as vol
import homeassistant.config as conf_util
from homeassistant.const import SERVICE_RELOAD
from homeassistant.core import (
HomeAssistant,
ServiceCall,
@@ -16,7 +20,12 @@ from homeassistant.core import (
SupportsResponse,
)
from homeassistant.exceptions import HomeAssistantError, TemplateError
from homeassistant.helpers import config_validation as cv, template
from homeassistant.helpers import (
config_validation as cv,
issue_registry as ir,
service as service_helper,
template,
)
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.json import JsonObjectType
@@ -31,16 +40,14 @@ CONFIG_SCHEMA = vol.Schema(
)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the shell_command component."""
conf = config.get(DOMAIN, {})
cache: dict[str, tuple[str, str | None, template.Template | None]] = {}
def _make_handler(
cmd: str,
hass: HomeAssistant,
cache: dict[str, tuple[str, str | None, template.Template | None]],
) -> Callable[[ServiceCall], Coroutine[Any, Any, ServiceResponse]]:
"""Return a service handler that executes the given shell command."""
async def async_service_handler(service: ServiceCall) -> ServiceResponse:
"""Execute a shell command service."""
cmd = conf[service.service]
if cmd in cache:
prog, args, args_compiled = cache[cmd]
elif " " not in cmd:
@@ -66,7 +73,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
if rendered_args == args:
# No template used. default behavior
create_process = asyncio.create_subprocess_shell(
cmd,
stdin=None,
@@ -78,7 +84,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
# Template used. Break into list and use create_subprocess_exec
# (which uses shell=False) for security
shlexed_cmd = [prog, *shlex.split(rendered_args)]
create_process = asyncio.create_subprocess_exec(
*shlexed_cmd,
stdin=None,
@@ -153,11 +158,81 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return service_response
return None
for name in conf:
return async_service_handler
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the shell_command component."""
conf = config.get(DOMAIN, {})
cache: dict[str, tuple[str, str | None, template.Template | None]] = {}
for name, command in conf.items():
if name == SERVICE_RELOAD:
ir.async_create_issue(
hass,
DOMAIN,
f"reserved_{SERVICE_RELOAD}",
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
translation_key="reserved_reload_name",
translation_placeholders={"name": name},
)
_LOGGER.warning("Skipping shell_command entry '%s': name is reserved", name)
continue
hass.services.async_register(
DOMAIN,
name,
async_service_handler,
_make_handler(command, hass, cache),
supports_response=SupportsResponse.OPTIONAL,
)
async def reload_service_handler(service_call: ServiceCall) -> None:
"""Reload shell_command from YAML configuration."""
try:
raw_config = await conf_util.async_hass_config_yaml(hass)
except HomeAssistantError as err:
_LOGGER.error("Error loading configuration.yaml: %s", err)
return
try:
new_conf = CONFIG_SCHEMA(raw_config).get(DOMAIN, {})
except vol.Invalid as err:
_LOGGER.error("Invalid shell_command configuration: %s", err)
return
for svc in list(hass.services.async_services_for_domain(DOMAIN)):
if svc != SERVICE_RELOAD:
hass.services.async_remove(DOMAIN, svc)
cache.clear()
ir.async_delete_issue(hass, DOMAIN, f"reserved_{SERVICE_RELOAD}")
for name, command in new_conf.items():
if name == SERVICE_RELOAD:
ir.async_create_issue(
hass,
DOMAIN,
f"reserved_{SERVICE_RELOAD}",
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
translation_key="reserved_reload_name",
translation_placeholders={"name": name},
)
_LOGGER.warning(
"Skipping shell_command entry '%s': name is reserved", name
)
continue
hass.services.async_register(
DOMAIN,
name,
_make_handler(command, hass, cache),
supports_response=SupportsResponse.OPTIONAL,
)
service_helper.async_register_admin_service(
hass,
DOMAIN,
SERVICE_RELOAD,
reload_service_handler,
)
return True

View File

@@ -0,0 +1,7 @@
{
"services": {
"reload": {
"service": "mdi:reload"
}
}
}

View File

@@ -1 +1 @@
# Empty file, shell_command services are dynamically created
reload:

View File

@@ -6,5 +6,17 @@
"timeout": {
"message": "Timed out running command: `{command}`, after: {timeout} seconds"
}
},
"issues": {
"reserved_reload_name": {
"description": "The shell command name {name} is a reserved for the reload action and cannot be used for user-defined commands. Please rename or remove this entry from your configuration.",
"title": "Reserved shell command action name"
}
},
"services": {
"reload": {
"description": "Reloads shell command configuration.",
"name": "[%key:common::action::reload%]"
}
}
}

View File

@@ -1,16 +1,13 @@
{
"common": {
"condition_behavior_description": "How the state should match on the targeted sirens.",
"condition_behavior_name": "Behavior",
"trigger_behavior_description": "The behavior of the targeted sirens to trigger on.",
"trigger_behavior_name": "Behavior"
"condition_behavior_name": "Condition passes if",
"trigger_behavior_name": "Trigger when"
},
"conditions": {
"is_off": {
"description": "Tests if one or more sirens are off.",
"fields": {
"behavior": {
"description": "[%key:component::siren::common::condition_behavior_description%]",
"name": "[%key:component::siren::common::condition_behavior_name%]"
}
},
@@ -20,7 +17,6 @@
"description": "Tests if one or more sirens are on.",
"fields": {
"behavior": {
"description": "[%key:component::siren::common::condition_behavior_description%]",
"name": "[%key:component::siren::common::condition_behavior_name%]"
}
},
@@ -90,7 +86,6 @@
"description": "Triggers after one or more sirens turn off.",
"fields": {
"behavior": {
"description": "[%key:component::siren::common::trigger_behavior_description%]",
"name": "[%key:component::siren::common::trigger_behavior_name%]"
}
},
@@ -100,7 +95,6 @@
"description": "Triggers after one or more sirens turn on.",
"fields": {
"behavior": {
"description": "[%key:component::siren::common::trigger_behavior_description%]",
"name": "[%key:component::siren::common::trigger_behavior_name%]"
}
},

View File

@@ -1,4 +1,4 @@
"""The snmp component."""
"""The SNMP integration."""
from .util import async_get_snmp_engine

View File

@@ -8,5 +8,5 @@
"iot_class": "local_polling",
"loggers": ["solarlog_cli"],
"quality_scale": "platinum",
"requirements": ["solarlog_cli==0.7.0"]
"requirements": ["solarlog_cli==0.7.1"]
}

Some files were not shown because too many files have changed in this diff Show More