Compare commits

...

118 Commits

Author SHA1 Message Date
Joost Lekkerkerker
29d06cfcc9 Add integration_type service to github (#159032) 2025-12-14 19:29:44 +01:00
Joost Lekkerkerker
365d168ddd Add integration_type device to gardena_bluetooth (#159029) 2025-12-14 19:20:54 +01:00
Joost Lekkerkerker
234191336e Add integration_type service to fitbit (#159015) 2025-12-14 10:19:34 -08:00
Joost Lekkerkerker
ba57b72658 Add integration_type service to elvia (#159002) 2025-12-14 19:09:12 +01:00
Allen Porter
bb08b315b8 Add exception handling for rate limited or unauthorized MQTT requests (#158997) 2025-12-14 18:45:12 +01:00
Petro31
50621df244 Update unnecessary error logging of unknown and unavailable source states from mold indicator (#158979) 2025-12-14 16:52:38 +01:00
Jan Bouwhuis
2db7b5c99f Assume cover or valve is always "running" in google assistant when the state is assumed or the position is reported to allow it to be be stopped (#158919) 2025-12-14 10:24:40 -05:00
Michael
78af3acf35 Bump pyfritzhome to 0.6.18 (#158877) 2025-12-14 08:12:25 +01:00
Joost Lekkerkerker
b72f04d44e Add integration_type hub to electrasmart (#158942) 2025-12-14 07:17:50 +01:00
Joost Lekkerkerker
35f287e330 Add integration_type hub to ekeybionyx (#158941) 2025-12-14 07:16:40 +01:00
Joost Lekkerkerker
0a55f83b46 Add integration_type hub to econet (#158940) 2025-12-14 07:14:44 +01:00
Joost Lekkerkerker
5030d0ba90 Add integration_type device to ecoforest (#158939) 2025-12-14 07:13:44 +01:00
Joost Lekkerkerker
f582f06ee4 Add integration_type service to eafm (#158937) 2025-12-14 06:59:46 +01:00
Joost Lekkerkerker
662bada5d8 Add integration_type hub to duotecno (#158936) 2025-12-14 06:59:13 +01:00
Joost Lekkerkerker
3ca338dd25 Add integration_type device to dunehd (#158935) 2025-12-14 06:58:03 +01:00
Joost Lekkerkerker
9337a0e71b Add integration_type device to droplet (#158933) 2025-12-14 06:56:08 +01:00
Joost Lekkerkerker
ccbb00197d Add integration_type hub to drop_connect (#158932) 2025-12-14 06:55:22 +01:00
Joost Lekkerkerker
0f59c17e61 Add integration_type service to dexcom (#158928) 2025-12-14 06:42:34 +01:00
Joost Lekkerkerker
6253ade3e2 Add integration_type service to datadog (#158927) 2025-12-14 06:40:48 +01:00
Joost Lekkerkerker
e5890378a1 Add integration_type device to daikin (#158926) 2025-12-14 06:40:29 +01:00
Joost Lekkerkerker
b8ab0bcadf Add integration_type service to dnsip (#158930) 2025-12-13 16:25:51 -06:00
Joost Lekkerkerker
19cb827577 Add integration_type device to doorbird (#158931) 2025-12-13 16:23:37 -06:00
Joost Lekkerkerker
03676d7e5a Add integration_type hub to ecobee (#158938) 2025-12-13 16:23:15 -06:00
Magnus
13f3b49b96 Bump aioasuswrt 1.5.3 (#158882) 2025-12-13 22:43:21 +01:00
Allen Porter
90c8c56a06 Suppress roborock failures under some unavailability threshold (#158673)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 22:30:21 +01:00
Josef Zweck
afb9e18a7d Add brew by weight controls to lamarzocco (#158169) 2025-12-13 22:28:11 +01:00
Andrew Jackson
2c2934065f Bump aiomealie to 1.1.1 and statically define mealplan entry types (#158907) 2025-12-13 22:26:31 +01:00
mettolen
0bead67df9 Add device uptime to Airobot integration (#158516) 2025-12-13 22:20:52 +01:00
James Cole
2895849203 Update strings for Firefly III integration (#158911) 2025-12-13 22:20:30 +01:00
David Recordon
b2400708ac Add myself as a maintainer for Control4 (#158948) 2025-12-13 22:15:35 +01:00
Anthony Garera
0bed9c20b3 Bump python-overseerr to 0.8.0 (#158924) 2025-12-13 19:31:21 +01:00
Brett Adams
d3fb7a7b87 Bump tesla-fleet-api to 1.2.7 (#158904) 2025-12-13 15:02:19 +01:00
Bouwe Westerdijk
60dcca4143 Show Plugwise configuration-link on gateway only (#158094) 2025-12-13 11:38:23 +01:00
Paul Tarjan
01f498f239 Clarify previous state in total_increasing warning message (#158805) 2025-12-13 11:15:37 +01:00
Andre Lengwenus
15055b8e8e Fix race condition in LCN climate and cover entites (#158894) 2025-12-13 11:12:20 +01:00
Bouwe Westerdijk
6826619e12 Revert adding entity_category to Plugwise thermostat schedule select (#158901) 2025-12-13 11:08:17 +01:00
Joost Lekkerkerker
b50a8e04a8 Add integration_type hub to airtouch5 (#158834)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:47:27 +01:00
Joost Lekkerkerker
c6c67c5357 Add integration_type hub to blue_current (#158863) 2025-12-13 10:46:12 +01:00
Joost Lekkerkerker
c82803d1e2 Add integration_type hub to agent_dvr (#158829)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:45:09 +01:00
Joost Lekkerkerker
732b30f181 Add integration_type hub to airzone (#158835)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:44:05 +01:00
Joost Lekkerkerker
0e2e57a657 Add integration_type device to android_ip_webcam (#158838)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:42:39 +01:00
Joost Lekkerkerker
f00b0080a9 Add integration_type device to advantage_air (#158826)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:39:58 +01:00
Joost Lekkerkerker
ad970c1234 Add integration_type hub to cert_expiry (#158897) 2025-12-13 10:39:14 +01:00
Joost Lekkerkerker
02ec56bffa Add integration_type device to ccm15 (#158896)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:37:36 +01:00
Joost Lekkerkerker
8388c290bf Add integration_type hub to canary (#158895) 2025-12-13 09:41:01 +01:00
Joost Lekkerkerker
576ee99faf Add integration_type hub to control4 (#158900) 2025-12-13 09:36:36 +01:00
Joost Lekkerkerker
8a3534c345 Add integration_type service to coinbase (#158899) 2025-12-13 09:31:54 +01:00
Joost Lekkerkerker
e1e91c5568 Add integration_type service to cloudflare (#158898) 2025-12-13 09:31:25 +01:00
epenet
1e09bddb1d Cleanup deprecated alias in core (#158799) 2025-12-13 09:29:15 +01:00
Joost Lekkerkerker
90e4340595 Add integration_type hub to brunt (#158870)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-12-13 09:04:05 +01:00
Joost Lekkerkerker
120b17349c Add integration_type service to aussie_broadband (#158853) 2025-12-13 08:56:35 +01:00
Joost Lekkerkerker
8a26961304 Add integration_type service to aurora (#158852) 2025-12-13 08:56:01 +01:00
Joost Lekkerkerker
407b675080 Add integration_type device to atag (#158850) 2025-12-13 08:55:35 +01:00
Joost Lekkerkerker
274844271b Add integration_type hub to aseko_pool_live (#158849) 2025-12-13 08:54:46 +01:00
Joost Lekkerkerker
f11e4e7bda Add integration_type hub to aosmith (#158843) 2025-12-13 08:52:45 +01:00
Joost Lekkerkerker
96f8c39c6f Add integration_type device to anthemav (#158841) 2025-12-13 08:51:25 +01:00
Joost Lekkerkerker
77b79fef8d Add integration_type hub to anova (#158840) 2025-12-13 08:50:24 +01:00
mkmer
a0d2f285f3 blink: Remove mkmer as codeowner (#158884) 2025-12-13 08:45:13 +01:00
Joost Lekkerkerker
3aef05d1ec Add integration_type hub to airzone_cloud (#158836) 2025-12-13 08:43:57 +01:00
Joost Lekkerkerker
510e391ee4 Add integration_type device to airtouch4 (#158833) 2025-12-13 08:41:17 +01:00
Joost Lekkerkerker
54adfdd694 Add integration_type device to bluesound (#158865) 2025-12-13 08:38:48 +01:00
Joost Lekkerkerker
d45f920b4a Add integration_type service to amberelectric (#158837) 2025-12-13 08:37:16 +01:00
Joost Lekkerkerker
3080ef9a4a Add integration_type device to airthings_ble (#158832) 2025-12-13 08:36:06 +01:00
Joost Lekkerkerker
51cebb52f3 Add integration_type hub to airthings (#158831) 2025-12-13 08:34:28 +01:00
Joost Lekkerkerker
7b0d4c47b7 Add integration_type service to airnow (#158830) 2025-12-13 08:33:53 +01:00
Joost Lekkerkerker
a660ab3f97 Add integration_type service to aftership (#158828) 2025-12-13 08:32:31 +01:00
Joost Lekkerkerker
dd8fc16788 Add integration_type service to aemet (#158827) 2025-12-13 08:32:01 +01:00
Joost Lekkerkerker
2b0fab0468 Add integration_type service to brottsplatskartan (#158869) 2025-12-13 08:30:59 +01:00
Joost Lekkerkerker
3bb88ed433 Add integration_type hub to bosch_shc (#158868) 2025-12-13 08:30:04 +01:00
Joost Lekkerkerker
984385cd98 Add integration_type service to buienradar (#158871) 2025-12-13 08:27:55 +01:00
Joost Lekkerkerker
09de108676 Add integration_type service to caldav (#158872) 2025-12-13 08:26:40 +01:00
Joost Lekkerkerker
ebc7581718 Add integration_type hub to bmw_connected_drive (#158866) 2025-12-13 08:26:16 +01:00
Joost Lekkerkerker
e55162812d Add integration_type hub to blink (#158862) 2025-12-13 08:22:22 +01:00
Joost Lekkerkerker
aa6ccaa024 Add integration_type device to blebox (#158860) 2025-12-13 08:21:25 +01:00
Joost Lekkerkerker
e1b009a6de Add integration_type device to balboa (#158859) 2025-12-13 08:20:12 +01:00
Joost Lekkerkerker
91ddc525b0 Add integration_type service to azure_event_hub (#158857) 2025-12-13 08:18:56 +01:00
Joost Lekkerkerker
d7d7954ac2 Add integration_type service to azure_devops (#158856) 2025-12-13 08:18:35 +01:00
Joost Lekkerkerker
e87c260df7 Add integration_type service to azure_data_explorer (#158855) 2025-12-13 08:18:13 +01:00
Joost Lekkerkerker
5185c6cd68 Add integration_type hub to arve (#158848) 2025-12-13 08:17:37 +01:00
Joost Lekkerkerker
7599c918e2 Add integration_type hub to august (#158851) 2025-12-12 23:00:06 +01:00
Joost Lekkerkerker
fa7e22ec91 Add integration_type device to arcam_fmj (#158846) 2025-12-12 22:59:47 +01:00
Joost Lekkerkerker
606519e51b Add integration_type device to baf (#158858) 2025-12-12 22:59:28 +01:00
Joost Lekkerkerker
8e39e010f7 Add integration_type device to bluemaestro (#158864) 2025-12-12 22:59:13 +01:00
Joost Lekkerkerker
dc01cf49a0 Add integration_type hub to bond (#158867) 2025-12-12 22:58:57 +01:00
Joost Lekkerkerker
1f3ad382f1 Set Denon AVR integration type to device (#158815) 2025-12-12 20:33:56 +01:00
Joost Lekkerkerker
2595c7dcb2 Set Actron Air integration type to hub (#158816) 2025-12-12 20:33:25 +01:00
Kamil Breguła
d445b320de Accept URLs in WLED Host input (#157793)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
2025-12-12 18:55:03 +01:00
epenet
7b6df1a8a0 Cleanup deprecated typing helpers (#158806) 2025-12-12 17:04:41 +01:00
Manu
2a151dcd19 Add tests for discovery to Xbox integration (#158808) 2025-12-12 17:02:32 +01:00
epenet
adbab150af Move blue_current services to separate module (#158389) 2025-12-12 16:50:34 +01:00
Allen Porter
d20edf7928 Improve Roborock exception logging behavior for Zeo/Dyad devices (#158465)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-12 16:43:45 +01:00
Ludovic BOUÉ
7d6d37fe76 Fix Matter Door Lock Operating Mode select entity (#158468) 2025-12-12 16:30:48 +01:00
Ludovic BOUÉ
228e0453a7 Add Matter Thermostat remote sensing status (#157650) 2025-12-12 16:26:27 +01:00
Raphael Hehl
1da31c0530 Move icons to icons.json for unifiprotect (#158800)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2025-12-12 16:23:11 +01:00
Joost Lekkerkerker
41ad15e577 Bump pySmartThings to 3.5.1 (#158795) 2025-12-12 15:45:08 +01:00
Markus Jacobsen
421af881fe Add video source reporting to Bang & Olufsen (#158675) 2025-12-12 15:40:12 +01:00
Klaas Schoute
715a484f7e Add AutarcoSensorBase class for Autarco sensors (#158691)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-12 15:39:18 +01:00
Samuel Xiao
0a789f51b8 Switchbot Cloud: Fixed binary sensors didn't update automatically (#158434)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-12 15:39:08 +01:00
epenet
fa25d45123 Remove incorrect bring test (#158797) 2025-12-12 15:32:27 +01:00
johanzander
6d255b2521 Add state_class to Growatt power and energy sensors (#158705)
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-12 15:27:47 +01:00
peteS-UK
5ffb39f064 Trap for missing UUID in config_flow for Squeezebox (#158721)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-12 15:15:04 +01:00
Zoltán Farkasdi
d642109436 Netatmo NOCamera on/off fix (#158741) 2025-12-12 14:57:57 +01:00
Heindrich Paul
10f6d8d14f Add diagnostics support for Nederlandse Spoorwegen integration (#158722)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-12 14:36:35 +01:00
Thomas55555
a94678cb06 Bump google air quality api to 2.0.2 (#158742) 2025-12-12 14:35:07 +01:00
Manu
0d8d466003 Increase Xbox update interval to 15 seconds and refactor title data handling (#158780) 2025-12-12 14:04:25 +01:00
Marc Mueller
8ddf3e1734 Update pytest warnings filter (#158790) 2025-12-12 13:54:00 +01:00
cdutr
d88047a750 Migrate Blink component to use hardware_id instead of device_id (#158765) 2025-12-12 13:49:19 +01:00
Jordan Harvey
61c7ac81d6 Bump pynintendoparental to 2.1.1 (#158779) 2025-12-12 13:45:48 +01:00
Josef Zweck
bbe07bddb0 Bump pylamarzocco to 2.2.4 (#158774) 2025-12-12 13:45:14 +01:00
Denis Shulyaka
a3afc2beb1 Bump openai to 2.11.0 (#158785) 2025-12-12 13:40:43 +01:00
epenet
374cd93d3d Replace Tuya remap methods with helper class (#158718) 2025-12-12 13:37:29 +01:00
Maciej Bieniek
6e99411084 Add get_kvs_value and set_kvs_value actions for Shelly RPC devices (#157349) 2025-12-12 13:15:25 +01:00
dependabot[bot]
41d5415c86 Bump actions/cache from 4.3.0 to 5.0.0 (#158771) 2025-12-12 10:39:58 +01:00
Allen Porter
052d56f358 Bump ical to 12.1.1 (#158770) 2025-12-12 08:34:22 +01:00
Abílio Costa
0a676b5812 Remove alarm panel test from text tests (#158743) 2025-12-12 02:18:40 +01:00
Michael
1f4cf67daa Add turned off and turned on triggers to switch platform (#158688)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-11 22:02:22 +00:00
Maikel Punie
bb4ec229ce Add Velbus VLP file loading (#154883) 2025-12-11 22:53:01 +01:00
ndrwrbgs
ff62b460d5 Update advanced_options display text for MQTT (#158728) 2025-12-11 22:16:35 +01:00
211 changed files with 5492 additions and 1017 deletions

View File

@@ -263,7 +263,7 @@ jobs:
check-latest: true check-latest: true
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: &actions-cache actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 uses: &actions-cache actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with: with:
path: venv path: venv
key: &key-pre-commit-venv >- key: &key-pre-commit-venv >-
@@ -304,7 +304,7 @@ jobs:
- &cache-restore-pre-commit-venv - &cache-restore-pre-commit-venv
name: Restore base Python virtual environment name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: &actions-cache-restore actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 uses: &actions-cache-restore actions/cache/restore@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with: with:
path: venv path: venv
fail-on-cache-miss: true fail-on-cache-miss: true
@@ -511,7 +511,7 @@ jobs:
fi fi
- name: Save apt cache - name: Save apt cache
if: steps.cache-apt-check.outputs.cache-hit != 'true' if: steps.cache-apt-check.outputs.cache-hit != 'true'
uses: &actions-cache-save actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 uses: &actions-cache-save actions/cache/save@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with: with:
path: *path-apt-cache path: *path-apt-cache
key: *key-apt-cache key: *key-apt-cache

8
CODEOWNERS generated
View File

@@ -220,8 +220,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/bizkaibus/ @UgaitzEtxebarria /homeassistant/components/bizkaibus/ @UgaitzEtxebarria
/homeassistant/components/blebox/ @bbx-a @swistakm /homeassistant/components/blebox/ @bbx-a @swistakm
/tests/components/blebox/ @bbx-a @swistakm /tests/components/blebox/ @bbx-a @swistakm
/homeassistant/components/blink/ @fronzbot @mkmer /homeassistant/components/blink/ @fronzbot
/tests/components/blink/ @fronzbot @mkmer /tests/components/blink/ @fronzbot
/homeassistant/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23 /homeassistant/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23
/tests/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23 /tests/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23
/homeassistant/components/bluemaestro/ @bdraco /homeassistant/components/bluemaestro/ @bdraco
@@ -308,8 +308,8 @@ build.json @home-assistant/supervisor
/tests/components/config/ @home-assistant/core /tests/components/config/ @home-assistant/core
/homeassistant/components/configurator/ @home-assistant/core /homeassistant/components/configurator/ @home-assistant/core
/tests/components/configurator/ @home-assistant/core /tests/components/configurator/ @home-assistant/core
/homeassistant/components/control4/ @lawtancool /homeassistant/components/control4/ @lawtancool @davidrecordon
/tests/components/control4/ @lawtancool /tests/components/control4/ @lawtancool @davidrecordon
/homeassistant/components/conversation/ @home-assistant/core @synesthesiam @arturpragacz /homeassistant/components/conversation/ @home-assistant/core @synesthesiam @arturpragacz
/tests/components/conversation/ @home-assistant/core @synesthesiam @arturpragacz /tests/components/conversation/ @home-assistant/core @synesthesiam @arturpragacz
/homeassistant/components/cookidoo/ @miaucl /homeassistant/components/cookidoo/ @miaucl

View File

@@ -10,6 +10,7 @@
} }
], ],
"documentation": "https://www.home-assistant.io/integrations/actron_air", "documentation": "https://www.home-assistant.io/integrations/actron_air",
"integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"quality_scale": "bronze", "quality_scale": "bronze",
"requirements": ["actron-neo-api==0.1.87"] "requirements": ["actron-neo-api==0.1.87"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@Bre77"], "codeowners": ["@Bre77"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/advantage_air", "documentation": "https://www.home-assistant.io/integrations/advantage_air",
"integration_type": "hub",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["advantage_air"], "loggers": ["advantage_air"],
"requirements": ["advantage-air==0.4.4"] "requirements": ["advantage-air==0.4.4"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@Noltari"], "codeowners": ["@Noltari"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/aemet", "documentation": "https://www.home-assistant.io/integrations/aemet",
"integration_type": "service",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["aemet_opendata"], "loggers": ["aemet_opendata"],
"requirements": ["AEMET-OpenData==0.6.4"] "requirements": ["AEMET-OpenData==0.6.4"]

View File

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

View File

@@ -4,6 +4,7 @@
"codeowners": ["@ispysoftware"], "codeowners": ["@ispysoftware"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/agent_dvr", "documentation": "https://www.home-assistant.io/integrations/agent_dvr",
"integration_type": "hub",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["agent"], "loggers": ["agent"],
"requirements": ["agent-py==0.0.24"] "requirements": ["agent-py==0.0.24"]

View File

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

View File

@@ -4,6 +4,7 @@ from __future__ import annotations
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timedelta
from pyairobotrest.models import ThermostatStatus from pyairobotrest.models import ThermostatStatus
@@ -23,6 +24,8 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.util.dt import utcnow
from homeassistant.util.variance import ignore_variance
from . import AirobotConfigEntry from . import AirobotConfigEntry
from .entity import AirobotEntity from .entity import AirobotEntity
@@ -34,10 +37,15 @@ PARALLEL_UPDATES = 0
class AirobotSensorEntityDescription(SensorEntityDescription): class AirobotSensorEntityDescription(SensorEntityDescription):
"""Describes Airobot sensor entity.""" """Describes Airobot sensor entity."""
value_fn: Callable[[ThermostatStatus], StateType] value_fn: Callable[[ThermostatStatus], StateType | datetime]
supported_fn: Callable[[ThermostatStatus], bool] = lambda _: True supported_fn: Callable[[ThermostatStatus], bool] = lambda _: True
uptime_to_stable_datetime = ignore_variance(
lambda value: utcnow().replace(microsecond=0) - timedelta(seconds=value),
timedelta(minutes=2),
)
SENSOR_TYPES: tuple[AirobotSensorEntityDescription, ...] = ( SENSOR_TYPES: tuple[AirobotSensorEntityDescription, ...] = (
AirobotSensorEntityDescription( AirobotSensorEntityDescription(
key="air_temperature", key="air_temperature",
@@ -96,6 +104,14 @@ SENSOR_TYPES: tuple[AirobotSensorEntityDescription, ...] = (
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda status: status.errors, value_fn=lambda status: status.errors,
), ),
AirobotSensorEntityDescription(
key="device_uptime",
translation_key="device_uptime",
device_class=SensorDeviceClass.TIMESTAMP,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda status: uptime_to_stable_datetime(status.device_uptime),
entity_registry_enabled_default=False,
),
) )
@@ -129,6 +145,6 @@ class AirobotSensor(AirobotEntity, SensorEntity):
self._attr_unique_id = f"{coordinator.data.status.device_id}_{description.key}" self._attr_unique_id = f"{coordinator.data.status.device_id}_{description.key}"
@property @property
def native_value(self) -> StateType: def native_value(self) -> StateType | datetime:
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self.entity_description.value_fn(self.coordinator.data.status) return self.entity_description.value_fn(self.coordinator.data.status)

View File

@@ -17,6 +17,7 @@
} }
], ],
"documentation": "https://www.home-assistant.io/integrations/airthings", "documentation": "https://www.home-assistant.io/integrations/airthings",
"integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["airthings"], "loggers": ["airthings"],
"requirements": ["airthings-cloud==0.2.0"] "requirements": ["airthings-cloud==0.2.0"]

View File

@@ -27,6 +27,7 @@
"config_flow": true, "config_flow": true,
"dependencies": ["bluetooth_adapters"], "dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/airthings_ble", "documentation": "https://www.home-assistant.io/integrations/airthings_ble",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["airthings-ble==1.2.0"] "requirements": ["airthings-ble==1.2.0"]
} }

View File

@@ -4,6 +4,7 @@
"codeowners": ["@samsinnamon"], "codeowners": ["@samsinnamon"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airtouch4", "documentation": "https://www.home-assistant.io/integrations/airtouch4",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["airtouch4pyapi"], "loggers": ["airtouch4pyapi"],
"requirements": ["airtouch4pyapi==1.0.5"] "requirements": ["airtouch4pyapi==1.0.5"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@danzel"], "codeowners": ["@danzel"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airtouch5", "documentation": "https://www.home-assistant.io/integrations/airtouch5",
"integration_type": "hub",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["airtouch5py"], "loggers": ["airtouch5py"],
"requirements": ["airtouch5py==0.3.0"] "requirements": ["airtouch5py==0.3.0"]

View File

@@ -9,6 +9,7 @@
} }
], ],
"documentation": "https://www.home-assistant.io/integrations/airzone", "documentation": "https://www.home-assistant.io/integrations/airzone",
"integration_type": "hub",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["aioairzone"], "loggers": ["aioairzone"],
"requirements": ["aioairzone==1.0.4"] "requirements": ["aioairzone==1.0.4"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@Noltari"], "codeowners": ["@Noltari"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airzone_cloud", "documentation": "https://www.home-assistant.io/integrations/airzone_cloud",
"integration_type": "hub",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["aioairzone_cloud"], "loggers": ["aioairzone_cloud"],
"requirements": ["aioairzone-cloud==0.7.2"] "requirements": ["aioairzone-cloud==0.7.2"]

View File

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

View File

@@ -4,6 +4,7 @@
"codeowners": ["@engrbm87"], "codeowners": ["@engrbm87"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/android_ip_webcam", "documentation": "https://www.home-assistant.io/integrations/android_ip_webcam",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["pydroid-ipcam==3.0.0"] "requirements": ["pydroid-ipcam==3.0.0"]
} }

View File

@@ -4,6 +4,7 @@
"codeowners": ["@Lash-L"], "codeowners": ["@Lash-L"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/anova", "documentation": "https://www.home-assistant.io/integrations/anova",
"integration_type": "hub",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["anova_wifi"], "loggers": ["anova_wifi"],
"requirements": ["anova-wifi==0.17.0"] "requirements": ["anova-wifi==0.17.0"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@hyralex"], "codeowners": ["@hyralex"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/anthemav", "documentation": "https://www.home-assistant.io/integrations/anthemav",
"integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["anthemav"], "loggers": ["anthemav"],
"requirements": ["anthemav==1.4.1"] "requirements": ["anthemav==1.4.1"]

View File

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

View File

@@ -4,6 +4,7 @@
"codeowners": ["@elupus"], "codeowners": ["@elupus"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/arcam_fmj", "documentation": "https://www.home-assistant.io/integrations/arcam_fmj",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["arcam"], "loggers": ["arcam"],
"requirements": ["arcam-fmj==1.8.2"], "requirements": ["arcam-fmj==1.8.2"],

View File

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

View File

@@ -4,6 +4,7 @@
"codeowners": ["@milanmeu"], "codeowners": ["@milanmeu"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/aseko_pool_live", "documentation": "https://www.home-assistant.io/integrations/aseko_pool_live",
"integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["aioaseko"], "loggers": ["aioaseko"],
"requirements": ["aioaseko==1.0.0"] "requirements": ["aioaseko==1.0.0"]

View File

@@ -7,5 +7,5 @@
"integration_type": "hub", "integration_type": "hub",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["aioasuswrt", "asusrouter", "asyncssh"], "loggers": ["aioasuswrt", "asusrouter", "asyncssh"],
"requirements": ["aioasuswrt==1.5.2", "asusrouter==1.21.3"] "requirements": ["aioasuswrt==1.5.3", "asusrouter==1.21.3"]
} }

View File

@@ -4,6 +4,7 @@
"codeowners": ["@MatsNL"], "codeowners": ["@MatsNL"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/atag", "documentation": "https://www.home-assistant.io/integrations/atag",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["pyatag"], "loggers": ["pyatag"],
"requirements": ["pyatag==0.3.5.3"] "requirements": ["pyatag==0.3.5.3"]

View File

@@ -27,6 +27,7 @@
} }
], ],
"documentation": "https://www.home-assistant.io/integrations/august", "documentation": "https://www.home-assistant.io/integrations/august",
"integration_type": "hub",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"], "loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==9.2.0", "yalexs-ble==3.2.2"] "requirements": ["yalexs==9.2.0", "yalexs-ble==3.2.2"]

View File

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

View File

@@ -4,6 +4,7 @@
"codeowners": ["@nickw444", "@Bre77"], "codeowners": ["@nickw444", "@Bre77"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/aussie_broadband", "documentation": "https://www.home-assistant.io/integrations/aussie_broadband",
"integration_type": "service",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["aussiebb"], "loggers": ["aussiebb"],
"requirements": ["pyaussiebb==0.1.5"] "requirements": ["pyaussiebb==0.1.5"]

View File

@@ -6,10 +6,7 @@ rules:
This integration does not provide additional actions. This integration does not provide additional actions.
appropriate-polling: done appropriate-polling: done
brands: done brands: done
common-modules: common-modules: done
status: todo
comment: |
The entity.py file is not used in this integration.
config-flow-test-coverage: done config-flow-test-coverage: done
config-flow: done config-flow: done
dependency-transparency: done dependency-transparency: done

View File

@@ -204,13 +204,25 @@ async def async_setup_entry(
async_add_entities(entities) async_add_entities(entities)
class AutarcoBatterySensorEntity( class AutarcoSensorBase(CoordinatorEntity[AutarcoDataUpdateCoordinator], SensorEntity):
CoordinatorEntity[AutarcoDataUpdateCoordinator], SensorEntity """Base class for Autarco sensors."""
):
_attr_has_entity_name = True
def __init__(
self,
coordinator: AutarcoDataUpdateCoordinator,
description: SensorEntityDescription,
) -> None:
"""Initialize Autarco sensor base."""
super().__init__(coordinator)
self.entity_description = description
class AutarcoBatterySensorEntity(AutarcoSensorBase):
"""Defines an Autarco battery sensor.""" """Defines an Autarco battery sensor."""
entity_description: AutarcoBatterySensorEntityDescription entity_description: AutarcoBatterySensorEntityDescription
_attr_has_entity_name = True
def __init__( def __init__(
self, self,
@@ -218,10 +230,8 @@ class AutarcoBatterySensorEntity(
coordinator: AutarcoDataUpdateCoordinator, coordinator: AutarcoDataUpdateCoordinator,
description: AutarcoBatterySensorEntityDescription, description: AutarcoBatterySensorEntityDescription,
) -> None: ) -> None:
"""Initialize Autarco sensor.""" """Initialize Autarco battery sensor."""
super().__init__(coordinator) super().__init__(coordinator, description)
self.entity_description = description
self._attr_unique_id = ( self._attr_unique_id = (
f"{coordinator.account_site.site_id}_battery_{description.key}" f"{coordinator.account_site.site_id}_battery_{description.key}"
) )
@@ -239,13 +249,10 @@ class AutarcoBatterySensorEntity(
return self.entity_description.value_fn(self.coordinator.data.battery) return self.entity_description.value_fn(self.coordinator.data.battery)
class AutarcoSolarSensorEntity( class AutarcoSolarSensorEntity(AutarcoSensorBase):
CoordinatorEntity[AutarcoDataUpdateCoordinator], SensorEntity
):
"""Defines an Autarco solar sensor.""" """Defines an Autarco solar sensor."""
entity_description: AutarcoSolarSensorEntityDescription entity_description: AutarcoSolarSensorEntityDescription
_attr_has_entity_name = True
def __init__( def __init__(
self, self,
@@ -253,10 +260,8 @@ class AutarcoSolarSensorEntity(
coordinator: AutarcoDataUpdateCoordinator, coordinator: AutarcoDataUpdateCoordinator,
description: AutarcoSolarSensorEntityDescription, description: AutarcoSolarSensorEntityDescription,
) -> None: ) -> None:
"""Initialize Autarco sensor.""" """Initialize Autarco solar sensor."""
super().__init__(coordinator) super().__init__(coordinator, description)
self.entity_description = description
self._attr_unique_id = ( self._attr_unique_id = (
f"{coordinator.account_site.site_id}_solar_{description.key}" f"{coordinator.account_site.site_id}_solar_{description.key}"
) )
@@ -273,13 +278,10 @@ class AutarcoSolarSensorEntity(
return self.entity_description.value_fn(self.coordinator.data.solar) return self.entity_description.value_fn(self.coordinator.data.solar)
class AutarcoInverterSensorEntity( class AutarcoInverterSensorEntity(AutarcoSensorBase):
CoordinatorEntity[AutarcoDataUpdateCoordinator], SensorEntity
):
"""Defines an Autarco inverter sensor.""" """Defines an Autarco inverter sensor."""
entity_description: AutarcoInverterSensorEntityDescription entity_description: AutarcoInverterSensorEntityDescription
_attr_has_entity_name = True
def __init__( def __init__(
self, self,
@@ -288,10 +290,8 @@ class AutarcoInverterSensorEntity(
description: AutarcoInverterSensorEntityDescription, description: AutarcoInverterSensorEntityDescription,
serial_number: str, serial_number: str,
) -> None: ) -> None:
"""Initialize Autarco sensor.""" """Initialize Autarco inverter sensor."""
super().__init__(coordinator) super().__init__(coordinator, description)
self.entity_description = description
self._serial_number = serial_number self._serial_number = serial_number
self._attr_unique_id = f"{serial_number}_{description.key}" self._attr_unique_id = f"{serial_number}_{description.key}"
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(

View File

@@ -131,6 +131,7 @@ _EXPERIMENTAL_TRIGGER_PLATFORMS = {
"lawn_mower", "lawn_mower",
"light", "light",
"media_player", "media_player",
"switch",
"text", "text",
"vacuum", "vacuum",
} }

View File

@@ -4,6 +4,7 @@
"codeowners": ["@kaareseras"], "codeowners": ["@kaareseras"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/azure_data_explorer", "documentation": "https://www.home-assistant.io/integrations/azure_data_explorer",
"integration_type": "service",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["azure"], "loggers": ["azure"],
"requirements": ["azure-kusto-ingest==4.5.1", "azure-kusto-data[aio]==4.5.1"] "requirements": ["azure-kusto-ingest==4.5.1", "azure-kusto-data[aio]==4.5.1"]

View File

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

View File

@@ -4,6 +4,7 @@
"codeowners": ["@eavanvalkenburg"], "codeowners": ["@eavanvalkenburg"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/azure_event_hub", "documentation": "https://www.home-assistant.io/integrations/azure_event_hub",
"integration_type": "service",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["azure"], "loggers": ["azure"],
"requirements": ["azure-eventhub==5.11.1"], "requirements": ["azure-eventhub==5.11.1"],

View File

@@ -4,6 +4,7 @@
"codeowners": ["@bdraco", "@jfroy"], "codeowners": ["@bdraco", "@jfroy"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/baf", "documentation": "https://www.home-assistant.io/integrations/baf",
"integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"requirements": ["aiobafi6==0.9.0"], "requirements": ["aiobafi6==0.9.0"],
"zeroconf": [ "zeroconf": [

View File

@@ -12,6 +12,7 @@
} }
], ],
"documentation": "https://www.home-assistant.io/integrations/balboa", "documentation": "https://www.home-assistant.io/integrations/balboa",
"integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["pybalboa"], "loggers": ["pybalboa"],
"requirements": ["pybalboa==1.1.3"] "requirements": ["pybalboa==1.1.3"]

View File

@@ -22,6 +22,7 @@ class BeoSource:
NET_RADIO: Final[Source] = Source(name="B&O Radio", id="netRadio") NET_RADIO: Final[Source] = Source(name="B&O Radio", id="netRadio")
SPDIF: Final[Source] = Source(name="Optical", id="spdif") SPDIF: Final[Source] = Source(name="Optical", id="spdif")
TIDAL: Final[Source] = Source(name="Tidal", id="tidal") TIDAL: Final[Source] = Source(name="Tidal", id="tidal")
TV: Final[Source] = Source(name="TV", id="tv")
UNKNOWN: Final[Source] = Source(name="Unknown Source", id="unknown") UNKNOWN: Final[Source] = Source(name="Unknown Source", id="unknown")
URI_STREAMER: Final[Source] = Source(name="Audio Streamer", id="uriStreamer") URI_STREAMER: Final[Source] = Source(name="Audio Streamer", id="uriStreamer")
@@ -55,12 +56,13 @@ BEO_REPEAT_TO_HA: dict[str, RepeatMode] = {
class BeoMediaType(StrEnum): class BeoMediaType(StrEnum):
"""Bang & Olufsen specific media types.""" """Bang & Olufsen specific media types."""
FAVOURITE = "favourite"
DEEZER = "deezer" DEEZER = "deezer"
FAVOURITE = "favourite"
OVERLAY_TTS = "overlay_tts"
RADIO = "radio" RADIO = "radio"
TIDAL = "tidal" TIDAL = "tidal"
TTS = "provider" TTS = "provider"
OVERLAY_TTS = "overlay_tts" TV = "tv"
class BeoModel(StrEnum): class BeoModel(StrEnum):

View File

@@ -218,6 +218,7 @@ class BeoMediaPlayer(BeoEntity, MediaPlayerEntity):
self._sources: dict[str, str] = {} self._sources: dict[str, str] = {}
self._state: str = MediaPlayerState.IDLE self._state: str = MediaPlayerState.IDLE
self._video_sources: dict[str, str] = {} self._video_sources: dict[str, str] = {}
self._video_source_id_map: dict[str, str] = {}
self._sound_modes: dict[str, int] = {} self._sound_modes: dict[str, int] = {}
# Beolink compatible sources # Beolink compatible sources
@@ -355,6 +356,9 @@ class BeoMediaPlayer(BeoEntity, MediaPlayerEntity):
and menu_item.label != "TV" and menu_item.label != "TV"
): ):
self._video_sources[key] = menu_item.label self._video_sources[key] = menu_item.label
self._video_source_id_map[
menu_item.content.content_uri.removeprefix("tv://")
] = menu_item.label
# Combine the source dicts # Combine the source dicts
self._sources = self._audio_sources | self._video_sources self._sources = self._audio_sources | self._video_sources
@@ -627,10 +631,11 @@ class BeoMediaPlayer(BeoEntity, MediaPlayerEntity):
def media_content_type(self) -> MediaType | str | None: def media_content_type(self) -> MediaType | str | None:
"""Return the current media type.""" """Return the current media type."""
content_type = { content_type = {
BeoSource.URI_STREAMER.id: MediaType.URL,
BeoSource.DEEZER.id: BeoMediaType.DEEZER, BeoSource.DEEZER.id: BeoMediaType.DEEZER,
BeoSource.TIDAL.id: BeoMediaType.TIDAL,
BeoSource.NET_RADIO.id: BeoMediaType.RADIO, BeoSource.NET_RADIO.id: BeoMediaType.RADIO,
BeoSource.TIDAL.id: BeoMediaType.TIDAL,
BeoSource.TV.id: BeoMediaType.TV,
BeoSource.URI_STREAMER.id: MediaType.URL,
} }
# Hard to determine content type. # Hard to determine content type.
if self._source_change.id in content_type: if self._source_change.id in content_type:
@@ -690,7 +695,11 @@ class BeoMediaPlayer(BeoEntity, MediaPlayerEntity):
@property @property
def source(self) -> str | None: def source(self) -> str | None:
"""Return the current audio source.""" """Return the current audio/video source."""
# Associate TV content ID with a video source
if self.media_content_id in self._video_source_id_map:
return self._video_source_id_map[self.media_content_id]
return self._source_change.name return self._source_change.name
@property @property

View File

@@ -4,6 +4,7 @@
"codeowners": ["@bbx-a", "@swistakm"], "codeowners": ["@bbx-a", "@swistakm"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/blebox", "documentation": "https://www.home-assistant.io/integrations/blebox",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["blebox_uniapi"], "loggers": ["blebox_uniapi"],
"requirements": ["blebox-uniapi==2.5.0"], "requirements": ["blebox-uniapi==2.5.0"],

View File

@@ -64,6 +64,12 @@ async def async_migrate_entry(hass: HomeAssistant, entry: BlinkConfigEntry) -> b
if entry.version == 2: if entry.version == 2:
await _reauth_flow_wrapper(hass, entry, data) await _reauth_flow_wrapper(hass, entry, data)
return False return False
if entry.version == 3:
# Migrate device_id to hardware_id for blinkpy 0.25.x OAuth2 compatibility
if "device_id" in data:
data["hardware_id"] = data.pop("device_id")
hass.config_entries.async_update_entry(entry, data=data, version=4)
return True
return True return True

View File

@@ -21,7 +21,7 @@ from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DEVICE_ID, DOMAIN from .const import DOMAIN, HARDWARE_ID
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -43,7 +43,7 @@ async def _send_blink_2fa_pin(blink: Blink, pin: str | None) -> bool:
class BlinkConfigFlow(ConfigFlow, domain=DOMAIN): class BlinkConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a Blink config flow.""" """Handle a Blink config flow."""
VERSION = 3 VERSION = 4
def __init__(self) -> None: def __init__(self) -> None:
"""Initialize the blink flow.""" """Initialize the blink flow."""
@@ -53,7 +53,7 @@ class BlinkConfigFlow(ConfigFlow, domain=DOMAIN):
async def _handle_user_input(self, user_input: dict[str, Any]): async def _handle_user_input(self, user_input: dict[str, Any]):
"""Handle user input.""" """Handle user input."""
self.auth = Auth( self.auth = Auth(
{**user_input, "device_id": DEVICE_ID}, {**user_input, "hardware_id": HARDWARE_ID},
no_prompt=True, no_prompt=True,
session=async_get_clientsession(self.hass), session=async_get_clientsession(self.hass),
) )

View File

@@ -3,7 +3,7 @@
from homeassistant.const import Platform from homeassistant.const import Platform
DOMAIN = "blink" DOMAIN = "blink"
DEVICE_ID = "Home Assistant" HARDWARE_ID = "Home Assistant"
CONF_MIGRATE = "migrate" CONF_MIGRATE = "migrate"
CONF_CAMERA = "camera" CONF_CAMERA = "camera"

View File

@@ -1,7 +1,7 @@
{ {
"domain": "blink", "domain": "blink",
"name": "Blink", "name": "Blink",
"codeowners": ["@fronzbot", "@mkmer"], "codeowners": ["@fronzbot"],
"config_flow": true, "config_flow": true,
"dhcp": [ "dhcp": [
{ {
@@ -18,6 +18,7 @@
} }
], ],
"documentation": "https://www.home-assistant.io/integrations/blink", "documentation": "https://www.home-assistant.io/integrations/blink",
"integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["blinkpy"], "loggers": ["blinkpy"],
"requirements": ["blinkpy==0.25.1"] "requirements": ["blinkpy==0.25.1"]

View File

@@ -13,32 +13,25 @@ from bluecurrent_api.exceptions import (
RequestLimitReached, RequestLimitReached,
WebsocketError, WebsocketError,
) )
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_TOKEN, CONF_DEVICE_ID, Platform from homeassistant.const import CONF_API_TOKEN, Platform
from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ( from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
ConfigEntryAuthFailed, from homeassistant.helpers import config_validation as cv
ConfigEntryNotReady,
ServiceValidationError,
)
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from .const import ( from .const import (
BCU_APP,
CHARGEPOINT_SETTINGS, CHARGEPOINT_SETTINGS,
CHARGEPOINT_STATUS, CHARGEPOINT_STATUS,
CHARGING_CARD_ID,
DOMAIN, DOMAIN,
EVSE_ID, EVSE_ID,
LOGGER, LOGGER,
PLUG_AND_CHARGE, PLUG_AND_CHARGE,
SERVICE_START_CHARGE_SESSION,
VALUE, VALUE,
) )
from .services import async_setup_services
type BlueCurrentConfigEntry = ConfigEntry[Connector] type BlueCurrentConfigEntry = ConfigEntry[Connector]
@@ -54,13 +47,12 @@ VALUE_TYPES = [CHARGEPOINT_STATUS, CHARGEPOINT_SETTINGS]
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN) CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
SERVICE_START_CHARGE_SESSION_SCHEMA = vol.Schema(
{ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
vol.Required(CONF_DEVICE_ID): cv.string, """Set up Blue Current."""
# When no charging card is provided, use no charging card (BCU_APP = no charging card).
vol.Optional(CHARGING_CARD_ID, default=BCU_APP): cv.string, async_setup_services(hass)
} return True
)
async def async_setup_entry( async def async_setup_entry(
@@ -88,66 +80,6 @@ async def async_setup_entry(
return True return True
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up Blue Current."""
async def start_charge_session(service_call: ServiceCall) -> None:
"""Start a charge session with the provided device and charge card ID."""
# When no charge card is provided, use the default charge card set in the config flow.
charging_card_id = service_call.data[CHARGING_CARD_ID]
device_id = service_call.data[CONF_DEVICE_ID]
# Get the device based on the given device ID.
device = dr.async_get(hass).devices.get(device_id)
if device is None:
raise ServiceValidationError(
translation_domain=DOMAIN, translation_key="invalid_device_id"
)
blue_current_config_entry: ConfigEntry | None = None
for config_entry_id in device.config_entries:
config_entry = hass.config_entries.async_get_entry(config_entry_id)
if not config_entry or config_entry.domain != DOMAIN:
# Not the blue_current config entry.
continue
if config_entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError(
translation_domain=DOMAIN, translation_key="config_entry_not_loaded"
)
blue_current_config_entry = config_entry
break
if not blue_current_config_entry:
# The device is not connected to a valid blue_current config entry.
raise ServiceValidationError(
translation_domain=DOMAIN, translation_key="no_config_entry"
)
connector = blue_current_config_entry.runtime_data
# Get the evse_id from the identifier of the device.
evse_id = next(
identifier[1]
for identifier in device.identifiers
if identifier[0] == DOMAIN
)
await connector.client.start_session(evse_id, charging_card_id)
hass.services.async_register(
DOMAIN,
SERVICE_START_CHARGE_SESSION,
start_charge_session,
SERVICE_START_CHARGE_SESSION_SCHEMA,
)
return True
async def async_unload_entry( async def async_unload_entry(
hass: HomeAssistant, config_entry: BlueCurrentConfigEntry hass: HomeAssistant, config_entry: BlueCurrentConfigEntry
) -> bool: ) -> bool:

View File

@@ -4,6 +4,7 @@
"codeowners": ["@gleeuwen", "@NickKoepr", "@jtodorova23"], "codeowners": ["@gleeuwen", "@NickKoepr", "@jtodorova23"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/blue_current", "documentation": "https://www.home-assistant.io/integrations/blue_current",
"integration_type": "hub",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["bluecurrent_api"], "loggers": ["bluecurrent_api"],
"requirements": ["bluecurrent-api==1.3.2"] "requirements": ["bluecurrent-api==1.3.2"]

View File

@@ -0,0 +1,79 @@
"""The Blue Current integration."""
from __future__ import annotations
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.const import CONF_DEVICE_ID
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv, device_registry as dr
from .const import BCU_APP, CHARGING_CARD_ID, DOMAIN, SERVICE_START_CHARGE_SESSION
SERVICE_START_CHARGE_SESSION_SCHEMA = vol.Schema(
{
vol.Required(CONF_DEVICE_ID): cv.string,
# When no charging card is provided, use no charging card (BCU_APP = no charging card).
vol.Optional(CHARGING_CARD_ID, default=BCU_APP): cv.string,
}
)
async def start_charge_session(service_call: ServiceCall) -> None:
"""Start a charge session with the provided device and charge card ID."""
# When no charge card is provided, use the default charge card set in the config flow.
charging_card_id = service_call.data[CHARGING_CARD_ID]
device_id = service_call.data[CONF_DEVICE_ID]
# Get the device based on the given device ID.
device = dr.async_get(service_call.hass).devices.get(device_id)
if device is None:
raise ServiceValidationError(
translation_domain=DOMAIN, translation_key="invalid_device_id"
)
blue_current_config_entry: ConfigEntry | None = None
for config_entry_id in device.config_entries:
config_entry = service_call.hass.config_entries.async_get_entry(config_entry_id)
if not config_entry or config_entry.domain != DOMAIN:
# Not the blue_current config entry.
continue
if config_entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError(
translation_domain=DOMAIN, translation_key="config_entry_not_loaded"
)
blue_current_config_entry = config_entry
break
if not blue_current_config_entry:
# The device is not connected to a valid blue_current config entry.
raise ServiceValidationError(
translation_domain=DOMAIN, translation_key="no_config_entry"
)
connector = blue_current_config_entry.runtime_data
# Get the evse_id from the identifier of the device.
evse_id = next(
identifier[1] for identifier in device.identifiers if identifier[0] == DOMAIN
)
await connector.client.start_session(evse_id, charging_card_id)
@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Register the services."""
hass.services.async_register(
DOMAIN,
SERVICE_START_CHARGE_SESSION,
start_charge_session,
SERVICE_START_CHARGE_SESSION_SCHEMA,
)

View File

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

View File

@@ -5,6 +5,7 @@
"codeowners": ["@thrawnarn", "@LouisChrist"], "codeowners": ["@thrawnarn", "@LouisChrist"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/bluesound", "documentation": "https://www.home-assistant.io/integrations/bluesound",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["pyblu==2.0.5"], "requirements": ["pyblu==2.0.5"],
"zeroconf": [ "zeroconf": [

View File

@@ -4,6 +4,7 @@
"codeowners": ["@gerard33", "@rikroe"], "codeowners": ["@gerard33", "@rikroe"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive", "documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
"integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["bimmer_connected"], "loggers": ["bimmer_connected"],
"requirements": ["bimmer-connected[china]==0.17.3"] "requirements": ["bimmer-connected[china]==0.17.3"]

View File

@@ -14,6 +14,7 @@
} }
], ],
"documentation": "https://www.home-assistant.io/integrations/bond", "documentation": "https://www.home-assistant.io/integrations/bond",
"integration_type": "hub",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["bond_async"], "loggers": ["bond_async"],
"requirements": ["bond-async==0.2.1"], "requirements": ["bond-async==0.2.1"],

View File

@@ -5,6 +5,7 @@
"codeowners": ["@tschamm"], "codeowners": ["@tschamm"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/bosch_shc", "documentation": "https://www.home-assistant.io/integrations/bosch_shc",
"integration_type": "hub",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["boschshcpy"], "loggers": ["boschshcpy"],
"requirements": ["boschshcpy==0.2.107"], "requirements": ["boschshcpy==0.2.107"],

View File

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

View File

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

View File

@@ -4,6 +4,7 @@
"codeowners": ["@mjj4791", "@ties", "@Robbie1221"], "codeowners": ["@mjj4791", "@ties", "@Robbie1221"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/buienradar", "documentation": "https://www.home-assistant.io/integrations/buienradar",
"integration_type": "service",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["buienradar", "vincenty"], "loggers": ["buienradar", "vincenty"],
"requirements": ["buienradar==1.0.6"] "requirements": ["buienradar==1.0.6"]

View File

@@ -4,6 +4,7 @@
"codeowners": [], "codeowners": [],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/caldav", "documentation": "https://www.home-assistant.io/integrations/caldav",
"integration_type": "service",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["caldav", "vobject"], "loggers": ["caldav", "vobject"],
"requirements": ["caldav==2.1.0", "icalendar==6.3.1", "vobject==0.9.9"] "requirements": ["caldav==2.1.0", "icalendar==6.3.1", "vobject==0.9.9"]

View File

@@ -5,6 +5,7 @@
"config_flow": true, "config_flow": true,
"dependencies": ["ffmpeg"], "dependencies": ["ffmpeg"],
"documentation": "https://www.home-assistant.io/integrations/canary", "documentation": "https://www.home-assistant.io/integrations/canary",
"integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["canary"], "loggers": ["canary"],
"requirements": ["py-canary==0.5.4"], "requirements": ["py-canary==0.5.4"],

View File

@@ -4,6 +4,7 @@
"codeowners": ["@ocalvo"], "codeowners": ["@ocalvo"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/ccm15", "documentation": "https://www.home-assistant.io/integrations/ccm15",
"integration_type": "hub",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["py_ccm15==0.1.2"] "requirements": ["py_ccm15==0.1.2"]
} }

View File

@@ -4,5 +4,6 @@
"codeowners": ["@jjlawren"], "codeowners": ["@jjlawren"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/cert_expiry", "documentation": "https://www.home-assistant.io/integrations/cert_expiry",
"integration_type": "service",
"iot_class": "cloud_polling" "iot_class": "cloud_polling"
} }

View File

@@ -4,6 +4,7 @@
"codeowners": ["@ludeeus", "@ctalkington"], "codeowners": ["@ludeeus", "@ctalkington"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/cloudflare", "documentation": "https://www.home-assistant.io/integrations/cloudflare",
"integration_type": "service",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["pycfdns"], "loggers": ["pycfdns"],
"requirements": ["pycfdns==3.0.0"], "requirements": ["pycfdns==3.0.0"],

View File

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

View File

@@ -1,9 +1,10 @@
{ {
"domain": "control4", "domain": "control4",
"name": "Control4", "name": "Control4",
"codeowners": ["@lawtancool"], "codeowners": ["@lawtancool", "@davidrecordon"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/control4", "documentation": "https://www.home-assistant.io/integrations/control4",
"integration_type": "hub",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["pyControl4"], "loggers": ["pyControl4"],
"requirements": ["pyControl4==1.5.0"], "requirements": ["pyControl4==1.5.0"],

View File

@@ -4,6 +4,7 @@
"codeowners": ["@fredrike"], "codeowners": ["@fredrike"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/daikin", "documentation": "https://www.home-assistant.io/integrations/daikin",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["pydaikin"], "loggers": ["pydaikin"],
"requirements": ["pydaikin==2.17.1"], "requirements": ["pydaikin==2.17.1"],

View File

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

View File

@@ -4,6 +4,7 @@
"codeowners": ["@ol-iver", "@starkillerOG"], "codeowners": ["@ol-iver", "@starkillerOG"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/denonavr", "documentation": "https://www.home-assistant.io/integrations/denonavr",
"integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["denonavr"], "loggers": ["denonavr"],
"requirements": ["denonavr==1.2.0"], "requirements": ["denonavr==1.2.0"],

View File

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

View File

@@ -4,6 +4,7 @@
"codeowners": ["@gjohansson-ST"], "codeowners": ["@gjohansson-ST"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/dnsip", "documentation": "https://www.home-assistant.io/integrations/dnsip",
"integration_type": "service",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"requirements": ["aiodns==3.6.0"] "requirements": ["aiodns==3.6.0"]
} }

View File

@@ -5,6 +5,7 @@
"config_flow": true, "config_flow": true,
"dependencies": ["http", "repairs"], "dependencies": ["http", "repairs"],
"documentation": "https://www.home-assistant.io/integrations/doorbird", "documentation": "https://www.home-assistant.io/integrations/doorbird",
"integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["doorbirdpy"], "loggers": ["doorbirdpy"],
"requirements": ["DoorBirdPy==3.0.11"], "requirements": ["DoorBirdPy==3.0.11"],

View File

@@ -5,6 +5,7 @@
"config_flow": true, "config_flow": true,
"dependencies": ["mqtt"], "dependencies": ["mqtt"],
"documentation": "https://www.home-assistant.io/integrations/drop_connect", "documentation": "https://www.home-assistant.io/integrations/drop_connect",
"integration_type": "hub",
"iot_class": "local_push", "iot_class": "local_push",
"mqtt": ["drop_connect/discovery/#"], "mqtt": ["drop_connect/discovery/#"],
"requirements": ["dropmqttapi==1.0.3"] "requirements": ["dropmqttapi==1.0.3"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@sarahseidman"], "codeowners": ["@sarahseidman"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/droplet", "documentation": "https://www.home-assistant.io/integrations/droplet",
"integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"quality_scale": "bronze", "quality_scale": "bronze",
"requirements": ["pydroplet==2.3.4"], "requirements": ["pydroplet==2.3.4"],

View File

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

View File

@@ -4,6 +4,7 @@
"codeowners": ["@cereal2nd"], "codeowners": ["@cereal2nd"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/duotecno", "documentation": "https://www.home-assistant.io/integrations/duotecno",
"integration_type": "hub",
"iot_class": "local_push", "iot_class": "local_push",
"loggers": ["pyduotecno", "pyduotecno-node", "pyduotecno-unit"], "loggers": ["pyduotecno", "pyduotecno-node", "pyduotecno-unit"],
"requirements": ["pyDuotecno==2024.10.1"], "requirements": ["pyDuotecno==2024.10.1"],

View File

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

View File

@@ -7,6 +7,7 @@
"homekit": { "homekit": {
"models": ["EB", "ecobee*"] "models": ["EB", "ecobee*"]
}, },
"integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["pyecobee"], "loggers": ["pyecobee"],
"requirements": ["python-ecobee-api==0.3.2"], "requirements": ["python-ecobee-api==0.3.2"],

View File

@@ -4,6 +4,7 @@
"codeowners": ["@pjanuario"], "codeowners": ["@pjanuario"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/ecoforest", "documentation": "https://www.home-assistant.io/integrations/ecoforest",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["pyecoforest"], "loggers": ["pyecoforest"],
"requirements": ["pyecoforest==0.4.0"] "requirements": ["pyecoforest==0.4.0"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@w1ll1am23"], "codeowners": ["@w1ll1am23"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/econet", "documentation": "https://www.home-assistant.io/integrations/econet",
"integration_type": "hub",
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["paho_mqtt", "pyeconet"], "loggers": ["paho_mqtt", "pyeconet"],
"requirements": ["pyeconet==0.1.28"] "requirements": ["pyeconet==0.1.28"]

View File

@@ -5,6 +5,7 @@
"config_flow": true, "config_flow": true,
"dependencies": ["application_credentials", "http"], "dependencies": ["application_credentials", "http"],
"documentation": "https://www.home-assistant.io/integrations/ekeybionyx", "documentation": "https://www.home-assistant.io/integrations/ekeybionyx",
"integration_type": "hub",
"iot_class": "local_push", "iot_class": "local_push",
"quality_scale": "bronze", "quality_scale": "bronze",
"requirements": ["ekey-bionyxpy==1.0.0"] "requirements": ["ekey-bionyxpy==1.0.0"]

View File

@@ -4,6 +4,7 @@
"codeowners": ["@jafar-atili"], "codeowners": ["@jafar-atili"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/electrasmart", "documentation": "https://www.home-assistant.io/integrations/electrasmart",
"integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"requirements": ["pyElectra==1.2.4"] "requirements": ["pyElectra==1.2.4"]
} }

View File

@@ -5,6 +5,7 @@
"config_flow": true, "config_flow": true,
"dependencies": ["recorder"], "dependencies": ["recorder"],
"documentation": "https://www.home-assistant.io/integrations/elvia", "documentation": "https://www.home-assistant.io/integrations/elvia",
"integration_type": "service",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"requirements": ["elvia==0.1.0"] "requirements": ["elvia==0.1.0"]
} }

View File

@@ -42,11 +42,11 @@
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]" "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
}, },
"data_description": { "data_description": {
"api_key": "The API key for authenticating with Firefly", "api_key": "The API key for authenticating with Firefly III",
"url": "[%key:common::config_flow::data::url%]", "url": "[%key:common::config_flow::data::url%]",
"verify_ssl": "Verify the SSL certificate of the Firefly instance" "verify_ssl": "Verify the SSL certificate of the Firefly III instance"
}, },
"description": "You can create an API key in the Firefly UI. Go to **Options > Profile** and select the **OAuth** tab. Create a new personal access token and copy it (it will only display once)." "description": "You can create an API key in the Firefly III UI. Go to **Options > Profile** and select the **OAuth** tab. Create a new personal access token and copy it (it will only display once)."
} }
} }
}, },
@@ -84,13 +84,13 @@
}, },
"exceptions": { "exceptions": {
"cannot_connect": { "cannot_connect": {
"message": "An error occurred while trying to connect to the Firefly instance: {error}" "message": "An error occurred while trying to connect to the Firefly III instance: {error}"
}, },
"invalid_auth": { "invalid_auth": {
"message": "An error occurred while trying to authenticate: {error}" "message": "An error occurred while trying to authenticate: {error}"
}, },
"timeout_connect": { "timeout_connect": {
"message": "A timeout occurred while trying to connect to the Firefly instance: {error}" "message": "A timeout occurred while trying to connect to the Firefly III instance: {error}"
} }
} }
} }

View File

@@ -5,6 +5,7 @@
"config_flow": true, "config_flow": true,
"dependencies": ["application_credentials", "http"], "dependencies": ["application_credentials", "http"],
"documentation": "https://www.home-assistant.io/integrations/fitbit", "documentation": "https://www.home-assistant.io/integrations/fitbit",
"integration_type": "service",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["fitbit", "fitbit_web_api"], "loggers": ["fitbit", "fitbit_web_api"],
"requirements": ["fitbit==0.3.1", "fitbit-web-api==2.13.5"] "requirements": ["fitbit==0.3.1", "fitbit-web-api==2.13.5"]

View File

@@ -7,7 +7,7 @@
"integration_type": "hub", "integration_type": "hub",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["pyfritzhome"], "loggers": ["pyfritzhome"],
"requirements": ["pyfritzhome==0.6.17"], "requirements": ["pyfritzhome==0.6.18"],
"ssdp": [ "ssdp": [
{ {
"st": "urn:schemas-upnp-org:device:fritzbox:1" "st": "urn:schemas-upnp-org:device:fritzbox:1"

View File

@@ -12,6 +12,7 @@
"config_flow": true, "config_flow": true,
"dependencies": ["bluetooth_adapters"], "dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/gardena_bluetooth", "documentation": "https://www.home-assistant.io/integrations/gardena_bluetooth",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["bleak", "bleak_esphome", "gardena_bluetooth"], "loggers": ["bleak", "bleak_esphome", "gardena_bluetooth"],
"requirements": ["gardena-bluetooth==1.6.0"] "requirements": ["gardena-bluetooth==1.6.0"]

View File

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

View File

@@ -8,5 +8,5 @@
"integration_type": "service", "integration_type": "service",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["googleapiclient"], "loggers": ["googleapiclient"],
"requirements": ["gcal-sync==8.0.0", "oauth2client==4.1.3", "ical==11.1.0"] "requirements": ["gcal-sync==8.0.0", "oauth2client==4.1.3", "ical==12.1.1"]
} }

View File

@@ -8,5 +8,5 @@
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["google_air_quality_api"], "loggers": ["google_air_quality_api"],
"quality_scale": "bronze", "quality_scale": "bronze",
"requirements": ["google_air_quality_api==2.0.0"] "requirements": ["google_air_quality_api==2.0.2"]
} }

View File

@@ -88,16 +88,16 @@
"1b_good_air_quality": "1B - Good air quality", "1b_good_air_quality": "1B - Good air quality",
"2_cyan": "2 - Cyan", "2_cyan": "2 - Cyan",
"2_light_green": "2 - Light green", "2_light_green": "2 - Light green",
"2_orange": "4 - Orange",
"2_red": "5 - Red",
"2_yellow": "3 - Yellow",
"2a_acceptable_air_quality": "2A - Acceptable air quality", "2a_acceptable_air_quality": "2A - Acceptable air quality",
"2b_acceptable_air_quality": "2B - Acceptable air quality", "2b_acceptable_air_quality": "2B - Acceptable air quality",
"3_green": "3 - Green", "3_green": "3 - Green",
"3_yellow": "3 - Yellow",
"3a_aggravated_air_quality": "3A - Aggravated air quality", "3a_aggravated_air_quality": "3A - Aggravated air quality",
"3b_bad_air_quality": "3B - Bad air quality", "3b_bad_air_quality": "3B - Bad air quality",
"4_orange": "4 - Orange",
"4_yellow_watch": "4 - Yellow/Watch", "4_yellow_watch": "4 - Yellow/Watch",
"5_orange_alert": "5 - Orange/Alert", "5_orange_alert": "5 - Orange/Alert",
"5_red": "5 - Red",
"6_red_alert": "6 - Red/Alert+", "6_red_alert": "6 - Red/Alert+",
"10_33": "10-33% of guideline", "10_33": "10-33% of guideline",
"33_66": "33-66% of guideline", "33_66": "33-66% of guideline",

View File

@@ -908,12 +908,21 @@ class StartStopTrait(_Trait):
} }
if domain in COVER_VALVE_DOMAINS: if domain in COVER_VALVE_DOMAINS:
assumed_state_or_set_position = bool(
(
self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
& COVER_VALVE_SET_POSITION_FEATURE[domain]
)
or self.state.attributes.get(ATTR_ASSUMED_STATE)
)
return { return {
"isRunning": state "isRunning": state
in ( in (
COVER_VALVE_STATES[domain]["closing"], COVER_VALVE_STATES[domain]["closing"],
COVER_VALVE_STATES[domain]["opening"], COVER_VALVE_STATES[domain]["opening"],
) )
or assumed_state_or_set_position
} }
raise NotImplementedError(f"Unsupported domain {domain}") raise NotImplementedError(f"Unsupported domain {domain}")
@@ -975,11 +984,23 @@ class StartStopTrait(_Trait):
"""Execute a StartStop command.""" """Execute a StartStop command."""
domain = self.state.domain domain = self.state.domain
if command == COMMAND_START_STOP: if command == COMMAND_START_STOP:
assumed_state_or_set_position = bool(
(
self.state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
& COVER_VALVE_SET_POSITION_FEATURE[domain]
)
or self.state.attributes.get(ATTR_ASSUMED_STATE)
)
if params["start"] is False: if params["start"] is False:
if self.state.state in ( if (
self.state.state
in (
COVER_VALVE_STATES[domain]["closing"], COVER_VALVE_STATES[domain]["closing"],
COVER_VALVE_STATES[domain]["opening"], COVER_VALVE_STATES[domain]["opening"],
) or self.state.attributes.get(ATTR_ASSUMED_STATE): )
or assumed_state_or_set_position
):
await self.hass.services.async_call( await self.hass.services.async_call(
domain, domain,
SERVICE_STOP_COVER_VALVE[domain], SERVICE_STOP_COVER_VALVE[domain],
@@ -992,7 +1013,14 @@ class StartStopTrait(_Trait):
ERR_ALREADY_STOPPED, ERR_ALREADY_STOPPED,
f"{FRIENDLY_DOMAIN[domain]} is already stopped", f"{FRIENDLY_DOMAIN[domain]} is already stopped",
) )
else: elif (
self.state.state
in (
COVER_VALVE_STATES[domain]["open"],
COVER_VALVE_STATES[domain]["closed"],
)
or assumed_state_or_set_position
):
await self.hass.services.async_call( await self.hass.services.async_call(
domain, domain,
SERVICE_TOGGLE_COVER_VALVE[domain], SERVICE_TOGGLE_COVER_VALVE[domain],

View File

@@ -27,6 +27,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="eBatChargeToday", api_key="eBatChargeToday",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_battery_charge_lifetime", key="mix_battery_charge_lifetime",
@@ -42,6 +43,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="eBatDisChargeToday", api_key="eBatDisChargeToday",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_battery_discharge_lifetime", key="mix_battery_discharge_lifetime",
@@ -57,6 +59,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="epvToday", api_key="epvToday",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_solar_generation_lifetime", key="mix_solar_generation_lifetime",
@@ -72,6 +75,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pDischarge1", api_key="pDischarge1",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_battery_voltage", key="mix_battery_voltage",
@@ -101,6 +105,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="elocalLoadToday", api_key="elocalLoadToday",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_load_consumption_lifetime", key="mix_load_consumption_lifetime",
@@ -116,6 +121,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="etoGridToday", api_key="etoGridToday",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_export_to_grid_lifetime", key="mix_export_to_grid_lifetime",
@@ -132,6 +138,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="chargePower", api_key="chargePower",
native_unit_of_measurement=UnitOfPower.KILO_WATT, native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_load_consumption", key="mix_load_consumption",
@@ -139,6 +146,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pLocalLoad", api_key="pLocalLoad",
native_unit_of_measurement=UnitOfPower.KILO_WATT, native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_wattage_pv_1", key="mix_wattage_pv_1",
@@ -146,6 +154,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pPv1", api_key="pPv1",
native_unit_of_measurement=UnitOfPower.KILO_WATT, native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_wattage_pv_2", key="mix_wattage_pv_2",
@@ -153,6 +162,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pPv2", api_key="pPv2",
native_unit_of_measurement=UnitOfPower.KILO_WATT, native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_wattage_pv_all", key="mix_wattage_pv_all",
@@ -160,6 +170,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="ppv", api_key="ppv",
native_unit_of_measurement=UnitOfPower.KILO_WATT, native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_export_to_grid", key="mix_export_to_grid",
@@ -167,6 +178,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pactogrid", api_key="pactogrid",
native_unit_of_measurement=UnitOfPower.KILO_WATT, native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_import_from_grid", key="mix_import_from_grid",
@@ -174,6 +186,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pactouser", api_key="pactouser",
native_unit_of_measurement=UnitOfPower.KILO_WATT, native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_battery_discharge_kw", key="mix_battery_discharge_kw",
@@ -181,6 +194,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pdisCharge1", api_key="pdisCharge1",
native_unit_of_measurement=UnitOfPower.KILO_WATT, native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_grid_voltage", key="mix_grid_voltage",
@@ -196,6 +210,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="eCharge", api_key="eCharge",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_load_consumption_solar_today", key="mix_load_consumption_solar_today",
@@ -203,6 +218,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="eChargeToday", api_key="eChargeToday",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_self_consumption_today", key="mix_self_consumption_today",
@@ -210,6 +226,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="eChargeToday1", api_key="eChargeToday1",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_load_consumption_battery_today", key="mix_load_consumption_battery_today",
@@ -217,6 +234,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="echarge1", api_key="echarge1",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="mix_import_from_grid_today", key="mix_import_from_grid_today",
@@ -224,6 +242,7 @@ MIX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="etouser", api_key="etouser",
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY, device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL_INCREASING,
), ),
# This sensor is manually created using the most recent X-Axis value from the chartData # This sensor is manually created using the most recent X-Axis value from the chartData
GrowattSensorEntityDescription( GrowattSensorEntityDescription(

View File

@@ -79,6 +79,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="ppv1", api_key="ppv1",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
precision=1, precision=1,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
@@ -122,6 +123,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="ppv2", api_key="ppv2",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
precision=1, precision=1,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
@@ -165,6 +167,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="ppv3", api_key="ppv3",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
precision=1, precision=1,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
@@ -208,6 +211,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="ppv4", api_key="ppv4",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
precision=1, precision=1,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
@@ -234,6 +238,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="ppv", api_key="ppv",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
precision=1, precision=1,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
@@ -258,6 +263,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pac", api_key="pac",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
precision=1, precision=1,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
@@ -323,6 +329,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="bdc1DischargePower", api_key="bdc1DischargePower",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="tlx_battery_1_discharge_total", key="tlx_battery_1_discharge_total",
@@ -339,6 +346,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="bdc2DischargePower", api_key="bdc2DischargePower",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="tlx_battery_2_discharge_total", key="tlx_battery_2_discharge_total",
@@ -372,6 +380,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="bdc1ChargePower", api_key="bdc1ChargePower",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="tlx_battery_1_charge_total", key="tlx_battery_1_charge_total",
@@ -388,6 +397,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="bdc2ChargePower", api_key="bdc2ChargePower",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
key="tlx_battery_2_charge_total", key="tlx_battery_2_charge_total",
@@ -445,6 +455,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pacToLocalLoad", api_key="pacToLocalLoad",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
precision=1, precision=1,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
@@ -453,6 +464,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pacToUserTotal", api_key="pacToUserTotal",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
precision=1, precision=1,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
@@ -461,6 +473,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pacToGridTotal", api_key="pacToGridTotal",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
precision=1, precision=1,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
@@ -545,6 +558,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="psystem", api_key="psystem",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
precision=1, precision=1,
), ),
GrowattSensorEntityDescription( GrowattSensorEntityDescription(
@@ -553,6 +567,7 @@ TLX_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="pself", api_key="pself",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
precision=1, precision=1,
), ),
) )

View File

@@ -50,5 +50,6 @@ TOTAL_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
api_key="nominalPower", api_key="nominalPower",
native_unit_of_measurement=UnitOfPower.WATT, native_unit_of_measurement=UnitOfPower.WATT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
state_class=SensorStateClass.MEASUREMENT,
), ),
) )

View File

@@ -28,6 +28,9 @@
} }
}, },
"number": { "number": {
"bbw_dose": {
"default": "mdi:weight-gram"
},
"coffee_temp": { "coffee_temp": {
"default": "mdi:thermometer-water" "default": "mdi:thermometer-water"
}, },
@@ -51,6 +54,14 @@
} }
}, },
"select": { "select": {
"bbw_dose_mode": {
"default": "mdi:all-inclusive-box",
"state": {
"continuous": "mdi:all-inclusive-box",
"dose1": "mdi:numeric-1-box",
"dose2": "mdi:numeric-2-box"
}
},
"prebrew_infusion_select": { "prebrew_infusion_select": {
"default": "mdi:water-pump-off", "default": "mdi:water-pump-off",
"state": { "state": {

View File

@@ -37,5 +37,5 @@
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["pylamarzocco"], "loggers": ["pylamarzocco"],
"quality_scale": "platinum", "quality_scale": "platinum",
"requirements": ["pylamarzocco==2.2.3"] "requirements": ["pylamarzocco==2.2.4"]
} }

View File

@@ -5,9 +5,14 @@ from dataclasses import dataclass
from typing import Any, cast from typing import Any, cast
from pylamarzocco import LaMarzoccoMachine from pylamarzocco import LaMarzoccoMachine
from pylamarzocco.const import ModelName, PreExtractionMode, WidgetType from pylamarzocco.const import DoseMode, ModelName, PreExtractionMode, WidgetType
from pylamarzocco.exceptions import RequestNotSuccessful from pylamarzocco.exceptions import RequestNotSuccessful
from pylamarzocco.models import CoffeeBoiler, PreBrewing, SteamBoilerTemperature from pylamarzocco.models import (
BrewByWeightDoses,
CoffeeBoiler,
PreBrewing,
SteamBoilerTemperature,
)
from homeassistant.components.number import ( from homeassistant.components.number import (
NumberDeviceClass, NumberDeviceClass,
@@ -18,6 +23,7 @@ from homeassistant.const import (
PRECISION_TENTHS, PRECISION_TENTHS,
PRECISION_WHOLE, PRECISION_WHOLE,
EntityCategory, EntityCategory,
UnitOfMass,
UnitOfTemperature, UnitOfTemperature,
UnitOfTime, UnitOfTime,
) )
@@ -219,6 +225,72 @@ ENTITIES: tuple[LaMarzoccoNumberEntityDescription, ...] = (
) )
), ),
), ),
LaMarzoccoNumberEntityDescription(
key="bbw_dose_1",
translation_key="bbw_dose",
translation_placeholders={"dose": "Dose 1"},
device_class=NumberDeviceClass.WEIGHT,
native_unit_of_measurement=UnitOfMass.GRAMS,
native_step=PRECISION_TENTHS,
native_min_value=5,
native_max_value=100,
entity_category=EntityCategory.CONFIG,
set_value_fn=(
lambda machine, value: machine.set_brew_by_weight_dose(
dose=DoseMode.DOSE_1,
value=value,
)
),
native_value_fn=(
lambda machine: cast(
BrewByWeightDoses,
machine.dashboard.config[WidgetType.CM_BREW_BY_WEIGHT_DOSES],
).doses.dose_1.dose
),
available_fn=lambda coordinator: (
cast(
BrewByWeightDoses,
coordinator.device.dashboard.config[WidgetType.CM_BREW_BY_WEIGHT_DOSES],
).scale_connected
),
supported_fn=(
lambda coordinator: coordinator.device.dashboard.model_name
in (ModelName.LINEA_MINI, ModelName.LINEA_MINI_R)
),
),
LaMarzoccoNumberEntityDescription(
key="bbw_dose_2",
translation_key="bbw_dose",
translation_placeholders={"dose": "Dose 2"},
device_class=NumberDeviceClass.WEIGHT,
native_unit_of_measurement=UnitOfMass.GRAMS,
native_step=PRECISION_TENTHS,
native_min_value=5,
native_max_value=100,
entity_category=EntityCategory.CONFIG,
set_value_fn=(
lambda machine, value: machine.set_brew_by_weight_dose(
dose=DoseMode.DOSE_2,
value=value,
)
),
native_value_fn=(
lambda machine: cast(
BrewByWeightDoses,
machine.dashboard.config[WidgetType.CM_BREW_BY_WEIGHT_DOSES],
).doses.dose_2.dose
),
available_fn=lambda coordinator: (
cast(
BrewByWeightDoses,
coordinator.device.dashboard.config[WidgetType.CM_BREW_BY_WEIGHT_DOSES],
).scale_connected
),
supported_fn=(
lambda coordinator: coordinator.device.dashboard.model_name
in (ModelName.LINEA_MINI, ModelName.LINEA_MINI_R)
),
),
) )

View File

@@ -5,6 +5,7 @@ from dataclasses import dataclass
from typing import Any, cast from typing import Any, cast
from pylamarzocco.const import ( from pylamarzocco.const import (
DoseMode,
ModelName, ModelName,
PreExtractionMode, PreExtractionMode,
SmartStandByType, SmartStandByType,
@@ -13,7 +14,7 @@ from pylamarzocco.const import (
) )
from pylamarzocco.devices import LaMarzoccoMachine from pylamarzocco.devices import LaMarzoccoMachine
from pylamarzocco.exceptions import RequestNotSuccessful from pylamarzocco.exceptions import RequestNotSuccessful
from pylamarzocco.models import PreBrewing, SteamBoilerLevel from pylamarzocco.models import BrewByWeightDoses, PreBrewing, SteamBoilerLevel
from homeassistant.components.select import SelectEntity, SelectEntityDescription from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
@@ -50,6 +51,14 @@ STANDBY_MODE_HA_TO_LM = {
STANDBY_MODE_LM_TO_HA = {value: key for key, value in STANDBY_MODE_HA_TO_LM.items()} STANDBY_MODE_LM_TO_HA = {value: key for key, value in STANDBY_MODE_HA_TO_LM.items()}
DOSE_MODE_HA_TO_LM = {
"continuous": DoseMode.CONTINUOUS,
"dose1": DoseMode.DOSE_1,
"dose2": DoseMode.DOSE_2,
}
DOSE_MODE_LM_TO_HA = {value: key for key, value in DOSE_MODE_HA_TO_LM.items()}
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
class LaMarzoccoSelectEntityDescription( class LaMarzoccoSelectEntityDescription(
@@ -117,6 +126,31 @@ ENTITIES: tuple[LaMarzoccoSelectEntityDescription, ...] = (
machine.schedule.smart_wake_up_sleep.smart_stand_by_after machine.schedule.smart_wake_up_sleep.smart_stand_by_after
], ],
), ),
LaMarzoccoSelectEntityDescription(
key="bbw_dose_mode",
translation_key="bbw_dose_mode",
entity_category=EntityCategory.CONFIG,
options=["continuous", "dose1", "dose2"],
select_option_fn=lambda machine, option: machine.set_brew_by_weight_dose_mode(
mode=DOSE_MODE_HA_TO_LM[option]
),
current_option_fn=lambda machine: DOSE_MODE_LM_TO_HA[
cast(
BrewByWeightDoses,
machine.dashboard.config[WidgetType.CM_BREW_BY_WEIGHT_DOSES],
).mode
],
available_fn=lambda coordinator: (
cast(
BrewByWeightDoses,
coordinator.device.dashboard.config[WidgetType.CM_BREW_BY_WEIGHT_DOSES],
).scale_connected
),
supported_fn=(
lambda coordinator: coordinator.device.dashboard.model_name
in (ModelName.LINEA_MINI, ModelName.LINEA_MINI_R)
),
),
) )

View File

@@ -87,6 +87,9 @@
} }
}, },
"number": { "number": {
"bbw_dose": {
"name": "Brew by weight {dose}"
},
"coffee_temp": { "coffee_temp": {
"name": "Coffee target temperature" "name": "Coffee target temperature"
}, },
@@ -107,6 +110,14 @@
} }
}, },
"select": { "select": {
"bbw_dose_mode": {
"name": "Brew by weight dose mode",
"state": {
"continuous": "Continuous",
"dose1": "Dose 1",
"dose2": "Dose 2"
}
},
"prebrew_infusion_select": { "prebrew_infusion_select": {
"name": "Prebrew/-infusion mode", "name": "Prebrew/-infusion mode",
"state": { "state": {

View File

@@ -1,6 +1,5 @@
"""Support for LCN climate control.""" """Support for LCN climate control."""
import asyncio
from collections.abc import Iterable from collections.abc import Iterable
from datetime import timedelta from datetime import timedelta
from functools import partial from functools import partial
@@ -172,14 +171,14 @@ class LcnClimate(LcnEntity, ClimateEntity):
async def async_update(self) -> None: async def async_update(self) -> None:
"""Update the state of the entity.""" """Update the state of the entity."""
self._attr_available = any( self._attr_available = any(
await asyncio.gather( [
self.device_connection.request_status_variable( await self.device_connection.request_status_variable(
self.variable, SCAN_INTERVAL.seconds self.variable, SCAN_INTERVAL.seconds
), ),
self.device_connection.request_status_variable( await self.device_connection.request_status_variable(
self.setpoint, SCAN_INTERVAL.seconds self.setpoint, SCAN_INTERVAL.seconds
), ),
) ]
) )
def input_received(self, input_obj: InputType) -> None: def input_received(self, input_obj: InputType) -> None:

View File

@@ -1,6 +1,5 @@
"""Support for LCN covers.""" """Support for LCN covers."""
import asyncio
from collections.abc import Coroutine, Iterable from collections.abc import Coroutine, Iterable
from datetime import timedelta from datetime import timedelta
from functools import partial from functools import partial
@@ -134,14 +133,14 @@ class LcnOutputsCover(LcnEntity, CoverEntity):
"""Update the state of the entity.""" """Update the state of the entity."""
if not self.device_connection.is_group: if not self.device_connection.is_group:
self._attr_available = any( self._attr_available = any(
await asyncio.gather( [
self.device_connection.request_status_output( await self.device_connection.request_status_output(
pypck.lcn_defs.OutputPort["OUTPUTUP"], SCAN_INTERVAL.seconds pypck.lcn_defs.OutputPort["OUTPUTUP"], SCAN_INTERVAL.seconds
), ),
self.device_connection.request_status_output( await self.device_connection.request_status_output(
pypck.lcn_defs.OutputPort["OUTPUTDOWN"], SCAN_INTERVAL.seconds pypck.lcn_defs.OutputPort["OUTPUTDOWN"], SCAN_INTERVAL.seconds
), ),
) ]
) )
def input_received(self, input_obj: InputType) -> None: def input_received(self, input_obj: InputType) -> None:
@@ -274,7 +273,7 @@ class LcnRelayCover(LcnEntity, CoverEntity):
self.motor, self.positioning_mode, SCAN_INTERVAL.seconds self.motor, self.positioning_mode, SCAN_INTERVAL.seconds
) )
) )
self._attr_available = any(await asyncio.gather(*coros)) self._attr_available = any([await coro for coro in coros])
def input_received(self, input_obj: InputType) -> None: def input_received(self, input_obj: InputType) -> None:
"""Set cover states when LCN input object (command) is received.""" """Set cover states when LCN input object (command) is received."""

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/local_calendar", "documentation": "https://www.home-assistant.io/integrations/local_calendar",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["ical"], "loggers": ["ical"],
"requirements": ["ical==11.1.0"] "requirements": ["ical==12.1.1"]
} }

View File

@@ -5,5 +5,5 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/local_todo", "documentation": "https://www.home-assistant.io/integrations/local_todo",
"iot_class": "local_polling", "iot_class": "local_polling",
"requirements": ["ical==11.1.0"] "requirements": ["ical==12.1.1"]
} }

View File

@@ -499,4 +499,53 @@ DISCOVERY_SCHEMAS = [
entity_class=MatterBinarySensor, entity_class=MatterBinarySensor,
required_attributes=(clusters.WindowCovering.Attributes.ConfigStatus,), required_attributes=(clusters.WindowCovering.Attributes.ConfigStatus,),
), ),
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
key="ThermostatRemoteSensing_LocalTemperature",
translation_key="thermostat_remote_sensing_local_temperature",
entity_category=EntityCategory.DIAGNOSTIC,
# LocalTemperature bit from RemoteSensing attribute
device_to_ha=lambda x: bool(
x
& clusters.Thermostat.Bitmaps.RemoteSensingBitmap.kLocalTemperature # Calculated Local Temperature is derived from a remote node
),
),
entity_class=MatterBinarySensor,
required_attributes=(clusters.Thermostat.Attributes.RemoteSensing,),
allow_multi=True,
),
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
key="ThermostatRemoteSensing_OutdoorTemperature",
translation_key="thermostat_remote_sensing_outdoor_temperature",
entity_category=EntityCategory.DIAGNOSTIC,
# OutdoorTemperature bit from RemoteSensing attribute
device_to_ha=lambda x: bool(
x
& clusters.Thermostat.Bitmaps.RemoteSensingBitmap.kOutdoorTemperature # OutdoorTemperature is derived from a remote node
),
),
entity_class=MatterBinarySensor,
required_attributes=(clusters.Thermostat.Attributes.RemoteSensing,),
allow_multi=True,
),
MatterDiscoverySchema(
platform=Platform.BINARY_SENSOR,
entity_description=MatterBinarySensorEntityDescription(
key="ThermostatRemoteSensing_Occupancy",
translation_key="thermostat_remote_sensing_occupancy",
entity_category=EntityCategory.DIAGNOSTIC,
# Occupancy bit from RemoteSensing attribute
device_to_ha=lambda x: bool(
x
& clusters.Thermostat.Bitmaps.RemoteSensingBitmap.kOccupancy # Occupancy is derived from a remote node
),
),
entity_class=MatterBinarySensor,
required_attributes=(clusters.Thermostat.Attributes.RemoteSensing,),
featuremap_contains=clusters.Thermostat.Bitmaps.Feature.kOccupancy,
allow_multi=True,
),
] ]

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