Compare commits

..

200 Commits

Author SHA1 Message Date
Franck Nijhof aaedff9502 Remove dead URL no-op in Pushbullet notify 2026-06-29 16:14:24 +00:00
Franck Nijhof cb1f8ce440 Guard the probatio shim import for pre-install scripts 2026-06-29 18:05:15 +02:00
Franck Nijhof e0f5c99fdf Activate the probatio shim for script entrypoints 2026-06-29 18:05:15 +02:00
Franck Nijhof 2952bf3e1a Use probatio as the data validation engine 2026-06-29 18:05:15 +02:00
Allen Porter 1186f153cb Add bump-dependency development skill and PyPI resolver (#172221)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2026-06-29 16:43:55 +01:00
Jan Rieger bc9c270117 Cleanup gpsd strings (#175125) 2026-06-29 16:12:51 +02:00
Manuel Stahl 8922131056 Remove unused translations from stiebel_eltron integration (#175112) 2026-06-29 17:00:01 +03:00
Franck Nijhof e0ee456bfa Fix invalid marker usage in LIFX service schemas (#175117) 2026-06-29 15:26:04 +02:00
Maciej Bieniek ebeb98dd83 Add missing translation key in the NextDNS integration (#175120) 2026-06-29 15:15:57 +02:00
Onero-testdev 49cff5f980 Add Candle Warmer Lamp support to switchbot (#173585)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 15:15:26 +02:00
TimL c24186e571 Add Bluetooth proxy support for SMLIGHT (#174710)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-06-29 15:12:49 +02:00
FuNK3Y c9ea8baf61 Bump ical to 13.3.0 (#175090)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 14:53:58 +02:00
Guillaume Winter f21426dfa6 Add fan speed sensor to Freebox integration (#175081) 2026-06-29 14:50:56 +02:00
Ariel Ebersberger a450999646 Rename "advanced_settings" section to "additional_settings" in Autoskope (#175106) 2026-06-29 14:50:18 +02:00
Ariel Ebersberger 5b8ff19d8d Rename "advanced_settings" section to "additional_settings" in Telegram bot (#175107) 2026-06-29 14:48:47 +02:00
Ronald van der Meer 25d505bcf3 Fix Duco ventilation sensors not being created for valve nodes (#174971) 2026-06-29 14:47:19 +02:00
Franck Nijhof 91cb829881 Fix invalid use of Exclusive marker in Kira name schema (#175115) 2026-06-29 14:44:02 +02:00
renovate[bot] 4fdc4e6219 Update ruff to v0.15.18 (#175087) 2026-06-29 14:41:47 +02:00
Ariel Ebersberger ca7ae00c7e Rename "advanced_settings" section to "additional_settings" in History Stats (#175109) 2026-06-29 14:32:57 +02:00
Ariel Ebersberger ff460901b7 Rename "advanced_options" section to "additional_options" in DNS IP (#175105) 2026-06-29 13:14:58 +02:00
Ariel Ebersberger 30512f08a8 Rename "advanced" step to "additional" in OpenAI Conversation (#175104) 2026-06-29 13:14:19 +02:00
Ariel Ebersberger 9dd1a59d50 Rename "advanced" step to "additional" in Anthropic (#175101) 2026-06-29 13:14:01 +02:00
TrojanHorsePower 91aded4474 Update vsure to 2.8.0 (#175060) 2026-06-29 11:46:21 +02:00
Erwin Douna 543eab3354 Portainer add utility.py for duplicate code (#175082) 2026-06-29 11:04:02 +02:00
Ariel Ebersberger 47b331a869 Rename "Advanced settings" title in OpenAI Conversation (#175096) 2026-06-29 11:00:21 +02:00
Ariel Ebersberger 696dd45803 Rename "Advanced settings" title in Anthropic (#175095) 2026-06-29 10:59:53 +02:00
Ariel Ebersberger f92239877f Rename "Advanced migration/setup" options in ZHA (#175094) 2026-06-29 10:59:35 +02:00
Ariel Ebersberger 45ceb13937 Rename "Advanced settings" sections in Scrape (#175098) 2026-06-29 10:58:26 +02:00
Ariel Ebersberger c5aeee8097 Rename "(advanced)" service name in Music Assistant (#175097) 2026-06-29 10:58:04 +02:00
Erik Montnemery bfc750b608 Tell bots that we like small try-clauses (#174654) 2026-06-29 11:16:46 +03:00
Karl Beecken 60f3bb58a0 Bump teltasync to 0.4.0 (#175084) 2026-06-29 01:10:28 +02:00
Nathan Spencer f56e13c249 Add router serial number to device info (#175063) 2026-06-28 22:21:15 +02:00
Ludovic BOUÉ fed2df4f7b Bump python-roborock to 5.22.0 (#175074)
Co-authored-by: Ludovic BOUÉ <938089+lboue@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 22:19:04 +02:00
Michael 8662982236 Migrate govee_ble to UnitOfDensity and UnitOfRatio enums (#175069) 2026-06-28 19:28:07 +02:00
Michael c618496c95 Migrate govee_ble to UnitOfDensity and UnitOfRatio enums (#175070) 2026-06-28 19:26:32 +02:00
Michael 40ef9828c4 Migrate sensirion_ble to UnitOfDensity and UnitOfRatio enums (#175072) 2026-06-28 19:25:51 +02:00
Ludovic BOUÉ ac92775538 Roborock Q10 DND switch (#174851)
Co-authored-by: Ludovic BOUÉ <938089+lboue@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 19:00:32 +02:00
Michael 4fbff0bd82 Migrate qingping to UnitOfDensity and UnitOfRatio enums (#175071) 2026-06-28 18:55:04 +02:00
Michael a076fcea2d Migrate xiaomi_ble to UnitOfDensity and UnitOfRatio enums (#175073) 2026-06-28 18:47:18 +02:00
Michael d3f6fff3be Migrate bthome to UnitOfDensity and UnitOfRatio enums (#175068) 2026-06-28 18:43:54 +02:00
renovate[bot] ea55fa2a2a Update coverage to 7.14.2 (#175051)
Co-authored-by: Mick Vleeshouwer <mick@imick.nl>
2026-06-28 15:21:19 +02:00
TimL cb35849a63 Add reconfiguration flow to SMLIGHT integration (#174952) 2026-06-28 14:26:45 +02:00
Mick Vleeshouwer e701afd7a7 Bump pyoverkiz to 2.0.3 (#175058) 2026-06-28 14:38:43 +03:00
Tom 3c285f766a Limit amount of ProxmoxVE entities enabled by default (#175004) 2026-06-28 12:30:55 +02:00
Manu 680b652ef2 Catch timeout errors in Steam config flow (#175052) 2026-06-28 08:09:45 +02:00
Michael bf09fa8c0d Update victron_ble test snapshots (#175043) 2026-06-28 03:08:08 +02:00
Michael 1469429510 Migrate alexa_devices to UnitOfDensity / UnitOfRatio enums (#174992) 2026-06-28 01:04:52 +02:00
Michael 476224c6f0 Migrate drop_connect to UnitOfDensity / UnitOfRatio enums (#175000) 2026-06-27 23:50:55 +02:00
Michael 7053d81920 Migrate ecobee to UnitOfDensity / UnitOfRatio enums (#175001) 2026-06-27 23:50:35 +02:00
Michael 1b2776d1f0 Migrate emoncms to UnitOfDensity / UnitOfRatio enums (#175002) 2026-06-27 23:50:29 +02:00
Michael f8a61d760c Migrate epion to UnitOfDensity / UnitOfRatio enums (#175003) 2026-06-27 23:49:34 +02:00
Michael e65d491849 Migrate foobot to UnitOfDensity / UnitOfRatio enums (#175006) 2026-06-27 23:49:19 +02:00
Michael efd615a461 Migrate homematic to UnitOfDensity / UnitOfRatio enums (#175009) 2026-06-27 23:47:10 +02:00
Michael 6e5e16c29d Migrate intelliclima to UnitOfDensity / UnitOfRatio enums (#175010) 2026-06-27 23:46:53 +02:00
Michael a1f78fbab4 Migrate omnilogic to UnitOfDensity / UnitOfRatio enums (#175018) 2026-06-27 23:45:46 +02:00
Michael f774c211a5 Migrate freshr to UnitOfDensity / UnitOfRatio enums (#175007) 2026-06-27 23:45:25 +02:00
Michael 3e23c9d13f Migrate lcn to UnitOfDensity / UnitOfRatio enums (#175011) 2026-06-27 23:42:53 +02:00
Michael 4c26f53358 Migrate lg_thinq to UnitOfDensity / UnitOfRatio enums (#175012) 2026-06-27 23:40:45 +02:00
Michael f5cae385fd Migrate luftdaten to UnitOfDensity / UnitOfRatio enums (#175013) 2026-06-27 23:39:57 +02:00
Michael 3aa68c93a7 Migrate serial_pm to UnitOfDensity / UnitOfRatio enums (#175025) 2026-06-27 23:39:55 +02:00
Michael d57539bfa3 Migrate microbees to UnitOfDensity / UnitOfRatio enums (#175014) 2026-06-27 23:39:41 +02:00
Michael ac0377885f Migrate vallox to UnitOfDensity / UnitOfRatio enums (#175033) 2026-06-27 23:39:40 +02:00
Michael b9676496d5 Migrate mill to UnitOfDensity / UnitOfRatio enums (#175015) 2026-06-27 23:39:13 +02:00
Michael 7435d6fe54 Migrate netatmo to UnitOfDensity / UnitOfRatio enums (#175017) 2026-06-27 23:39:01 +02:00
Michael 57f7c4305a Migrate awair to UnitOfDensity / UnitOfRatio enums (#175016) 2026-06-27 23:38:50 +02:00
Michael 24b1f5d1cf Migrate ondilo_ico to UnitOfDensity / UnitOfRatio enums (#175019) 2026-06-27 23:38:34 +02:00
Michael 915230df44 Migrate opensensemap to UnitOfDensity / UnitOfRatio enums (#175020) 2026-06-27 23:38:23 +02:00
Michael ca9735bb5d Migrate purpleair to UnitOfDensity / UnitOfRatio enums (#175022) 2026-06-27 23:38:14 +02:00
Michael 9cecc5516f Migrate renson to UnitOfDensity / UnitOfRatio enums (#175023) 2026-06-27 23:38:08 +02:00
Michael e75b7fb83b Migrate smartthings to UnitOfDensity / UnitOfRatio enums (#175026) 2026-06-27 23:37:53 +02:00
Michael 089bcd9183 Migrate switchbot to UnitOfDensity / UnitOfRatio enums (#175027) 2026-06-27 23:37:44 +02:00
Michael 363d477dac Migrate switchbot_cloud to UnitOfDensity / UnitOfRatio enums (#175028) 2026-06-27 23:37:32 +02:00
Michael 16772c1ecf Migrate tasmota to UnitOfDensity / UnitOfRatio enums (#175030) 2026-06-27 23:37:23 +02:00
Michael d1007086bd Migrate uhoo to UnitOfDensity / UnitOfRatio enums (#175032) 2026-06-27 23:37:16 +02:00
Michael 566f004401 Migrate venstar to UnitOfDensity / UnitOfRatio enums (#175034) 2026-06-27 23:37:04 +02:00
Michael 41c9a124b1 Migrate pooldose to UnitOfDensity / UnitOfRatio enums (#175031) 2026-06-27 23:33:29 +02:00
Michael dc9750a5cd Migrate xiaomi_miio to UnitOfDensity / UnitOfRatio enums (#175036) 2026-06-27 23:29:00 +02:00
Michael 1e43debac6 Migrate vesync to UnitOfDensity / UnitOfRatio enums (#175035) 2026-06-27 23:28:14 +02:00
Michael d319440508 Migrate openweathermap to UnitOfDensity / UnitOfRatio enums (#175021) 2026-06-27 23:27:15 +02:00
Michael 09c815205b Migrate yolink to UnitOfDensity / UnitOfRatio enums (#175037) 2026-06-27 23:25:46 +02:00
Michael 87809394ef Migrate matter (remaining files) to UnitOfRatio enum (#175038) 2026-06-27 23:25:01 +02:00
Michael 4cfc1d893e Migrate tuya (remaining files) to UnitOfRatio enum (#175039) 2026-06-27 23:24:04 +02:00
Michael 953ebba1f1 Migrate sensibo to UnitOfDensity / UnitOfRatio enums (#175024) 2026-06-27 23:22:29 +02:00
Michael ee96a2e4f1 Migrate google_air_quality to UnitOfDensity / UnitOfRatio enums (#175008) 2026-06-27 23:19:29 +02:00
Michael 5af703b5ed Migrate zwave_js to UnitOfDensity / UnitOfRatio enums (#175040) 2026-06-27 23:19:07 +02:00
Michael 1301d36442 Migrate overkiz (remaining files) to UnitOfRatio enum (#175041) 2026-06-27 23:18:51 +02:00
Michael 956f3fd821 Migrate fibaro to UnitOfDensity / UnitOfRatio enums (#175005) 2026-06-27 23:07:58 +02:00
Michael 8797a2ec5e Migrate deconz to UnitOfDensity / UnitOfRatio enums (#174999) 2026-06-27 21:32:37 +02:00
Michael 528ea500a9 Migrate compit to UnitOfDensity / UnitOfRatio enums (#174998) 2026-06-27 21:31:04 +02:00
Michael 433ff53630 Migrate broadlink to UnitOfDensity / UnitOfRatio enums (#174997) 2026-06-27 21:30:37 +02:00
Michael eebb8b5b91 Migrate bosch_shc to UnitOfDensity / UnitOfRatio enums (#174996) 2026-06-27 21:30:10 +02:00
Michael e0c881758b Migrate aranet to UnitOfDensity / UnitOfRatio enums (#174995) 2026-06-27 21:29:42 +02:00
Michael 46cf3e8289 Migrate airthings to UnitOfDensity / UnitOfRatio enums (#174986) 2026-06-27 21:27:51 +02:00
Michael 1d7ea1fc72 Migrate airobot to UnitOfDensity / UnitOfRatio enums (#174985) 2026-06-27 21:27:20 +02:00
Michael 9219643803 Migrate arve to UnitOfDensity / UnitOfRatio enums (#174982) 2026-06-27 21:26:52 +02:00
Michael b202a0d7ab Migrate altruist to UnitOfDensity / UnitOfRatio enums (#174993) 2026-06-27 21:26:10 +02:00
Michael 67801ab604 Migrate ambient_network to UnitOfDensity / UnitOfRatio enums (#174994) 2026-06-27 21:25:41 +02:00
Michael 227aee5232 Migrate airvisual to UnitOfDensity / UnitOfRatio enums (#174987) 2026-06-27 21:25:05 +02:00
Michael e465549f03 Migrate airvisual_pro to UnitOfDensity / UnitOfRatio enums (#174988) 2026-06-27 21:24:31 +02:00
Michael 6efd9093ce Migrate airzone_cloud to UnitOfDensity / UnitOfRatio enums (#174989) 2026-06-27 21:13:37 +02:00
Michael 7044dae281 Migrate airgradient to UnitOfDensity / UnitOfRatio enums (#174984) 2026-06-27 19:01:03 +02:00
Manu 9d34e83f71 Remove Clementine Music Player (#174970) 2026-06-27 17:39:48 +02:00
Manu ff45b81145 Remove Microsoft Face (#174977) 2026-06-27 17:37:38 +02:00
Willem-Jan van Rootselaar 486385e308 Bump python-bsblan to version 6.1.4 (#174974) 2026-06-27 18:02:38 +03:00
Raj Laud 11113ce1f4 Add Orion XS and Inverter support to Victron BLE (#169834)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-27 16:48:19 +02:00
Øyvind Matheson Wergeland 31dc1f9519 Drop nobo_hub custom select device class (#170135)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-06-27 16:44:57 +02:00
On Freund 926900d0ca Bump pymonoprice to 0.6.1 (#174951)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 09:49:03 -04:00
OzGav 06b429d0b7 Update music-assistant-client to 1.3.6 (#174968) 2026-06-27 15:31:17 +02:00
Åke Strandberg 9fb2d14fdb Handle SSE updates of single miele devices (#174955) 2026-06-27 15:26:57 +02:00
Manu 0e6279e273 Remove Watson TTS integration (#174967) 2026-06-27 15:17:41 +02:00
TimL 02ddd4c3f4 Bump Pysmlight to v0.5.0 (#174947) 2026-06-27 15:58:27 +03:00
Michael c147321aba Migrate demo to UnitOfRatio enums (#174966) 2026-06-27 15:56:34 +03:00
Michael f0e75ebb83 Bump aioimmich to 0.15.1 (#174962) 2026-06-27 15:55:18 +03:00
Manu 7ae85d8d97 Remove BlinkStick (#174961) 2026-06-27 14:29:30 +02:00
g4bri3lDev 43802961e9 Fix swallowed exception in opendisplay upload_image action handler (#170660) 2026-06-27 14:19:47 +02:00
Øyvind Matheson Wergeland 97844d7ee0 Filter already-configured hubs from nobo_hub config flow (#169593)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-27 14:18:53 +02:00
renovate[bot] e8e5eedd7e Update pytest-github-actions-annotate-failures to 0.4.2 (#174941) 2026-06-27 10:41:27 +02:00
Erwin Douna bbf7a60a9e ProxmoxVE refactor to use text selectors (#174949) 2026-06-27 10:19:38 +02:00
Erwin Douna fac6e8feb6 MELCloud Home add current password config flow (#174950) 2026-06-27 09:26:20 +02:00
Arie Catsman 86d282ac2a remove pyenphase library deprecated data fields from enphase_envoy fixtures (#174928) 2026-06-27 08:30:48 +02:00
Manuel Stahl cb407b8163 Use async config flow in stiebel_eltron integration (#174937) 2026-06-26 21:25:53 -04:00
Manu 61b3dda20f Remove Dovado integration (#174933) 2026-06-26 21:23:51 -04:00
Manu 951ec34e7c Remove Logentries (#174939) 2026-06-26 21:23:29 -04:00
Manu 57a441aa4a Remove ATEN Rack PDU integration (#174940) 2026-06-26 21:23:07 -04:00
Raphael Hehl 1e71607063 Bump uiprotect to 15.3.0 (#174938) 2026-06-27 02:48:58 +02:00
Manuel Stahl 952fdec1ad Extract Modbus schemas to schemas.py (#174935)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-26 20:26:56 -04:00
Erwin Douna c9bc041cac ProxmoxVE add typing to auth kwargs (#174930) 2026-06-26 23:56:55 +02:00
Erwin Douna 2047042803 ProxmoxVE add stale device removal (#174932) 2026-06-26 23:51:13 +02:00
Manuel Stahl 9f92c8438e Use async coordinator in stiebel_eltron integration (#174850)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 17:32:25 -04:00
Allen Porter a0c6d4fb52 Refactor Roborock time platform to use library property APIs (#174921) 2026-06-26 17:29:02 -04:00
Manu f315273404 Remove Greenwave Reality (#174929) 2026-06-26 22:28:43 +02:00
Michael 38baeba81c Check for supported fan speed modes in Synology DSM (#174925) 2026-06-26 21:45:32 +02:00
Raphael Hehl d246a7a7e5 Bump uiprotect to 15.2.0 (#174922) 2026-06-26 21:44:09 +02:00
Simone Chemelli 29f12d6e80 Bump aioamazondevices to 14.1.8 (#174924) 2026-06-26 20:47:21 +02:00
Jordan Harvey e01ed866d9 Bump pyanglianwater to 3.2.3 (#174902) 2026-06-26 18:30:37 +02:00
Arie Catsman 3ce5630809 remove enphase_envoy test for to-be-deprecated pyenphase library data (#174908) 2026-06-26 18:29:24 +02:00
Ronald van der Meer 344ae6f604 Fix Duco ventilation state select not being created for valve nodes (#174901) 2026-06-26 18:28:28 +02:00
Simone Chemelli e8b1acea6c Clarify and align domain/platform usage in Alexa Devices (#174914) 2026-06-26 18:27:08 +02:00
Simone Chemelli 24d6a0420c Clarify and align domain/platform usage in Fritz (#174915) 2026-06-26 18:26:40 +02:00
TheOtherAdam 393424fa88 Allow disabling managed log file (#170374)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-26 16:45:55 +01:00
Manu 585dd72366 Add reconfigure to Steam integration (#174696) 2026-06-26 17:01:36 +02:00
A. Gideonse 7e2343243d Add charge/discharge remaining time to Indevolt (#174600) 2026-06-26 16:42:47 +02:00
Simone Chemelli de8ed15ea8 Handle all login exceptions in Vodafone Station (#174852)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-06-26 16:34:52 +02:00
Allen Porter 9a21d92908 Bump voluptuous-openapi to 0.4.1 (#174912) 2026-06-26 16:12:28 +02:00
GSzabados 12ea0c4d77 Update units UnitOfDensity / UnitOfRatio for airnow (#174905) 2026-06-26 14:57:50 +02:00
prana-dev-official 94c4483735 Update units UnitOfRatio enums for Prana (#174907)
Co-authored-by: yuriipopow <yurapopov522@gmail.com>
2026-06-26 14:23:04 +02:00
GSzabados 053efdf662 Update units UnitOfDensity / UnitOfRatio for airthings_ble (#174906) 2026-06-26 14:16:49 +02:00
GSzabados 3c9f55f7b2 Update units UnitOfDensity / UnitOfRatio for ambient_station (#174900) 2026-06-26 12:53:23 +02:00
GSzabados a46b434930 Update measurement units UnitOfDensity / UnitOfRatio for Tomorrow.io (#174899) 2026-06-26 12:42:49 +02:00
Renat Sibgatulin 031e764957 Migrate the airq to the new enums (UnitOfDensity / UnitOfRatio) (#174896) 2026-06-26 12:26:52 +02:00
Paulus Schoutsen d9f0faf365 Fix Roborock time entity crash when timer value is missing (#174873)
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-26 06:25:55 -04:00
Joost Lekkerkerker a14e5d8a0c Change Airthings BLE level entities in enum device class (#174815) 2026-06-26 12:14:49 +02:00
Manu f356a1cd0d Remove ThermoWorks Smoke (#174845) 2026-06-26 11:39:56 +02:00
starkillerOG 50c12d85f8 Add Reolink push command IDs (#174876) 2026-06-26 11:38:48 +02:00
javicalle a88b43d845 Migrate rflink to UnitOfRatio enum (#174886) 2026-06-26 11:21:21 +02:00
GSzabados b9e59522e3 Update measurement units UnitOfDensity / UnitOfRatio for EcoWitt sensors (#174887) 2026-06-26 11:19:21 +02:00
Ludovic BOUÉ 1484384d63 Bump roborock dependencies to 5.21.0 (#174841) 2026-06-26 11:06:00 +02:00
Paulus Schoutsen 1d1ab798df Fix Roborock number entity crash when volume is None (#174872)
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-26 11:05:12 +02:00
Erwin Douna 926d2f1e21 Shelly refactor UnitsOf (#174883) 2026-06-26 11:01:23 +02:00
dependabot[bot] b341228b4b Bump actions/checkout from 6.0.3 to 7.0.0 (#174875)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-26 10:58:47 +02:00
renovate[bot] 241f850e90 Update pytest-github-actions-annotate-failures to 0.4.1 (#174868) 2026-06-26 10:34:57 +02:00
Rafa PA 257040ac51 [aemet] Increase weather update interval to 20 minutes (#174803) 2026-06-26 10:01:09 +02:00
Arie Catsman e3c17026d0 Update enphase_envoy diagnostics for pyenphase lib v3.0.0 (#174524) 2026-06-26 10:00:35 +02:00
Stefan Agner 9e4550dd14 Fix hassio job subscribe returning None instead of unsubscribe callback (#174063)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-26 09:56:50 +02:00
starkillerOG 37f441d3da Bump reolink_aio to 0.21.3 (#174879) 2026-06-26 09:53:04 +02:00
Maciej Bieniek 0099100d14 Use UnitOfDensity enum in GIOS (#174855) 2026-06-26 07:59:40 +02:00
Maciej Bieniek e20f74dac5 Use new unit enums in NAM (#174856) 2026-06-26 07:59:37 +02:00
John Hillery f7aa6ef384 Bump nexia to 2.13.0 (#174652) 2026-06-26 07:07:25 +02:00
Raphael Hehl 71342ef1f6 Bump uiprotect to 15.1.0 (#174846) 2026-06-25 22:27:17 -04:00
Simone Chemelli 544dccd50b Bump aioamazondevices to 14.1.6 (#174848) 2026-06-25 22:26:39 -04:00
Manu 5f6508c424 Remove Mycroft integration (#174849) 2026-06-25 22:25:47 -04:00
Maciej Bieniek 70f3526be3 Use UnitOfDensity / UnitOfRatio enums in Airly (#174854) 2026-06-25 22:25:28 -04:00
Mick Vleeshouwer f5f2ecadfd Migrate to UnitOfRatio enum in Overkiz (#174862) 2026-06-25 22:25:11 -04:00
Mick Vleeshouwer a8de57a1c6 Set RTS command duration for Overkiz Rexel client (#174863) 2026-06-25 22:24:43 -04:00
Erwin Douna 8b08f10f78 Tuya refactor UnitOfs (#174835) 2026-06-25 20:33:07 +02:00
Erik Montnemery 34679b033a Improve tests of entity limits (#174793) 2026-06-25 20:23:50 +02:00
Paul Bottein 014f785050 Fix missing translated names for Xiaomi Miio select entities (#174810) 2026-06-25 20:23:13 +02:00
epenet 1c53ada438 Bump tuya-device-handlers to 0.0.24 (#174840)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2026-06-25 20:22:42 +02:00
Erwin Douna a88de3efab Matter refactor to UnitOfs (#174824) 2026-06-25 19:54:53 +02:00
Nicolas Mowen 10ce428387 Handle case where GetLiveContext includes an entity with StrEnum key (#174822) 2026-06-25 19:53:32 +02:00
Erwin Douna dae8b60ec4 Tradfri refactor UnitOfs (#174834) 2026-06-25 19:08:12 +02:00
Bram Kragten e098cc384c Update frontend to 20260624.1 (#174831) 2026-06-25 18:47:17 +02:00
J. Nick Koston 6007bfa1cd Bump aioesphomeapi to 45.5.2 (#174826) 2026-06-25 18:31:51 +02:00
epenet 16262362e1 Bump tuya-device-sharing-sdk to 0.2.10 (#174827) 2026-06-25 17:57:33 +02:00
Erik Montnemery 01350e8f15 Fix exception in legacy sun condition (#174811) 2026-06-25 17:40:36 +02:00
Manu 8977fc6f67 Migrate entity unique_id in Steam integration (#174701) 2026-06-25 16:54:53 +02:00
Robert Resch 6411cc5c48 Improve the gate on check requirements aw to avoid useless runs (#174599) 2026-06-25 16:48:51 +02:00
Erik Montnemery 9ce56183ea Add WS command recorder/entity_options/get (#174134) 2026-06-25 15:59:29 +02:00
MoonDevLT 5117c0b964 Migrate lunatone to UnitOfRatio enums (#174817) 2026-06-25 15:01:10 +02:00
Christian Lackas 7dd5e188bb Migrate homematicip_cloud to UnitOfDensity / UnitOfRatio enums (#174813) 2026-06-25 14:15:36 +02:00
Christian Lackas ec80260c4c Migrate vicare to UnitOfDensity / UnitOfRatio enums (#174812) 2026-06-25 14:14:30 +02:00
Erwin Douna 10ceac63f6 Homekit controller refactor UnitOf (#174806) 2026-06-25 13:35:51 +02:00
Franck Nijhof 847f4dc287 Add missing unit of measurement to Home Connect battery sensor (#174694)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-06-25 13:35:30 +02:00
Michael Hansen 8d4c8114d4 Bump intents and fix broken tests (#174689) 2026-06-25 12:58:39 +02:00
Erik Montnemery b6b165fd00 Improve tests of sun conditions and triggers (#174805)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-25 12:56:49 +02:00
Simone Chemelli 7c99cf6385 Fix async_get_entity_id() params for Alexa Devices (#174641) 2026-06-25 12:32:55 +02:00
Erik Montnemery d6b743b93e Catch errors when evaluating automation conditions (#174799) 2026-06-25 11:15:01 +02:00
Joost Lekkerkerker 0cae5e41b4 Add entity category to Mealie item count sensors (#174795) 2026-06-25 10:57:06 +02:00
Erwin Douna 6fce245dfa Portainer refactor to UnitOfRatio (#174801) 2026-06-25 10:50:12 +02:00
Thomas D 44ba231bf6 Migrate to UnitOfRatio in the Qbus integration (#174800) 2026-06-25 10:49:56 +02:00
517 changed files with 13013 additions and 7976 deletions
+98
View File
@@ -0,0 +1,98 @@
---
name: bump-dependency
description: Bumps a Python package dependency across Home Assistant Core integrations, regenerates core requirement files, runs verification tests and prek lint, and prepares a pull request with proper release/compare links.
---
# Bump Python Package Dependency in Home Assistant Core
Follow these systematic steps to successfully bump a python package requirement in the repository, regenerate necessary derivative files, verify the integration, and raise a pull request.
## Gotchas & Non-Obvious Constraints
- **PR Template Integrity**: Follow Home Assistant's Pull Request template (`.github/PULL_REQUEST_TEMPLATE.md`) exactly as written, including any instructions inside the template itself. Preserve all sections, comments, and unchecked checkboxes unless the template explicitly says otherwise; the only allowed removal is the **Breaking change** section when the template instructs you to remove it if not applicable.
- **GitHub Tag Volatility**: Release tags on GitHub are highly inconsistent (e.g., `v1.2.3` vs `1.2.3` vs `release-1.2.3`). Always use the automated resolver `resolve_dependency.py` to check HEAD status for correct tags before hardcoding comparison URLs.
## Step-by-Step Workflow Checklist
### Phase A: Research and Plan
- [ ] **1. Identify Targets**: Note the requested target package and target version to bump.
- [ ] **2. Discover Codebase References**: Search the codebase to find all `manifest.json` and requirements files referencing the package.
- [ ] **3. Resolve Version/Tag Details**: Run the integrated validation helper script to resolve version details, GitHub repo, release tag format, and formatted PR links:
```bash
uv run python3 ./.claude/skills/bump-dependency/scripts/resolve_dependency.py <package> <old_version> [--new-version <new_version>]
```
- [ ] **4. Plan-Validate-Execute (Draft Plan)**: Before modifying any files, write a brief, structured plan outlining the integrations to change, old version, new version, and the resolved comparison link. Show this draft plan to the user.
### Phase B: Execute and Validate (Local Changes)
- [ ] **5. Check Uncommitted Changes**: Check for any uncommitted changes in the repository. If they exist, ask the user whether to stash, commit, or discard them before proceeding.
- [ ] **6. Git Branch Setup**: Create a clean branch starting from the latest `upstream/dev`:
```bash
git fetch upstream dev
git checkout -b bump-<package>-to-<version> upstream/dev
```
- [ ] **7. Apply Bump to manifests**: Update the version constraint string in all identified `manifest.json` files (e.g., change `"package==1.0.0"` to `"package==1.1.0"`).
- [ ] **8. Regenerate Core Requirements**: Run the requirements generator to update all derivative requirements and constraint files:
```bash
uv run python3 -m script.gen_requirements_all
```
- [ ] **9. Validate Requirements**: Check `git diff` to ensure that only the targeted `manifest.json` files and `requirements_all.txt` (and potentially standard constraints) were modified. No unrelated files must be affected.
- [ ] **10. Local Venv Verification**: Install the exact targeted package version directly inside the virtual environment:
```bash
uv pip install "<package>==<version>"
```
### Phase C: Validation Loop (Tests & Lint)
- [ ] **11. Run Integration Tests**: Execute the pytest suite for all integrations that consume the bumped package:
```bash
uv run pytest tests/components/<integration_name>
```
- *Validation Loop*: If tests fail, analyze the error, apply appropriate fixes, and re-run pytest until all tests pass cleanly.
- [ ] **12. Run prek Lint Checks**: Run the local prek hooks on modified files:
```bash
uv run prek run
```
- *Validation Loop*: If prek checks report any formatting or linting violations, fix them and repeat `uv run prek run` until it passes completely without errors.
### Phase D: User Confirmation & PR Creation
- [ ] **13. Commit Changes**: Commit the clean changes:
```bash
git add <modified_files>
git commit -m "Bump <package> to <version>"
```
- [ ] **14. Push Branch**: Push the local branch to your origin remote:
```bash
git push origin bump-<package>-to-<version>
```
- [ ] **15. PR Description Preparation**: Generate the pull request body from `.github/PULL_REQUEST_TEMPLATE.md`:
- **Proposed change**: Describe the package, old version, new version, target/source branches, and insert the resolved PyPI, changelog, and comparison diff links.
- **Type of change**: Check only 1 box in this section, and mark the `Dependency upgrade` checkbox as checked: `[x] Dependency upgrade`.
- **Breaking change**: You may remove the "Breaking change" section entirely from the template.
- **Validation checklists**: Mark `The code change is tested` checkbox as checked: `[x] The code change is tested`.
- **Keep remaining template intact**: Do NOT remove any other commented-out blocks, headers, or unchecked checkboxes in the template.
- [ ] **16. Mandatory Review Presentation**: Format the PR proposal using the **PR Presentation Template** below and display it to the user. **Stop and wait for the user to review and explicitly confirm/approve the PR template and draft details before creating the PR.**
- [ ] **17. Raise Pull Request**: Once the user approves, create the Pull Request using the GitHub CLI:
```bash
gh pr create --repo home-assistant/core --base dev --head <username>:bump-<package>-to-<version> --title "Bump <package> to <version>" --body-file <pr_body_file>
```
## PR Presentation Template
```markdown
### 🚀 Dependency Bump Pull Request Draft Review
- **Package**: `<package_name>` (`<old_version>` → `<new_version>`)
- **PR Title**: `Bump <package_name> to <new_version>`
- **Target Branch**: `dev`
- **Head Branch**: `<fork_username>:bump-<package_name>-to-<new_version>`
#### 🔗 PyPI & GitHub Links
- **PyPI Release**: https://pypi.org/project/<package_name>/<new_version>/
- **Changelog Link**: `<changelog_url>`
- **Comparison Diff**: `<compare_url>`
#### 📁 Modified Files
- `<list_of_modified_files>`
#### 📝 Proposed PR Body
<render the complete filled PR template body here, showing all checks and modifications for user approval>
```
@@ -0,0 +1,205 @@
#!/usr/bin/env python3
# ruff: noqa: T201, D103, BLE001
"""Helper script to resolve package details, GitHub repo, release tags, and diff links from PyPI."""
import argparse
import json
import re
import sys
import urllib.error
import urllib.parse
import urllib.request
from packaging.version import Version
_VERSION_MATCH = r"^[a-zA-Z0-9_\-\.\+\!\*]+$"
_REPO_MATCH = r"https?://(?:www\.)?github\.com/([^/]+)/([^/]+)"
# Project owner or repo name
_NAME_MATCH = r"^[a-zA-Z0-9_\-\.]+$"
def get_pypi_data(package_name):
# Sanitize and URL-quote the package name to prevent URL path injection
safe_package_name = urllib.parse.quote(package_name)
url = f"https://pypi.org/pypi/{safe_package_name}/json"
req = urllib.request.Request(
url,
headers={
"User-Agent": "HomeAssistant-Skill-Resolver/1.0",
"Accept": "application/json",
},
)
try:
with urllib.request.urlopen(req, timeout=10) as response:
return json.loads(response.read().decode())
except urllib.error.HTTPError as e:
print(f"Error fetching PyPI data (HTTP {e.code}): {e.reason}", file=sys.stderr)
sys.exit(1)
except urllib.error.URLError as e:
print(f"Network error fetching PyPI data: {e.reason}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"Unexpected error fetching PyPI data: {e}", file=sys.stderr)
sys.exit(1)
def find_github_repo(info):
urls = []
if info.get("home_page"):
urls.append(info["home_page"])
if info.get("project_urls"):
urls.extend(info["project_urls"].values())
for u in urls:
if not u:
continue
cleaned_url = u.replace("git+", "")
m = re.search(_REPO_MATCH, cleaned_url, re.IGNORECASE)
if m:
owner, repo = m.groups()
# Strip query parameters, hashes, trailing slashes, and .git extension
repo = repo.split("?")[0].split("#")[0].split("/")[0]
repo = repo.removesuffix(".git")
# Validate that the owner and repo name conform to valid formats,
# preventing prompt injection payloads embedded in repository URLs.
if re.match(_NAME_MATCH, owner) and re.match(_NAME_MATCH, repo):
return f"https://github.com/{owner}/{repo}"
return None
def check_github_tag(repo_url, version):
# Try with 'v' prefix, without prefix, and with 'release-' prefix
tag_options = [f"v{version}", version, f"release-{version}"]
for tag in tag_options:
# Check both tree and releases paths on GitHub
for path_template in [f"tree/{tag}", f"releases/tag/{tag}"]:
url = f"{repo_url}/{path_template}"
try:
req = urllib.request.Request(
url,
method="HEAD",
headers={"User-Agent": "HomeAssistant-Skill-Resolver/1.0"},
)
with urllib.request.urlopen(req, timeout=10) as resp:
if resp.status == 200:
return tag
except urllib.error.HTTPError as e:
# If it's a 404, we continue checking other tag/path options
if e.code == 404:
continue
# For non-404 HTTP errors (like 403 Forbidden/429 rate limit), exit with error to avoid false reports
print(
f"\n[ERROR] HTTP error contacting GitHub ({e.code} {e.reason}) for tag '{tag}'",
file=sys.stderr,
)
sys.exit(1)
except urllib.error.URLError as e:
# Connection or timeout error
print(
f"\n[ERROR] Network error contacting GitHub: {e.reason}",
file=sys.stderr,
)
sys.exit(1)
except Exception as e:
# Unexpected exceptions
print(
f"\n[ERROR] Unexpected error verifying tag '{tag}': {e}",
file=sys.stderr,
)
sys.exit(1)
return None
def main():
parser = argparse.ArgumentParser(
description="Resolve PyPI package info and GitHub release diffs."
)
parser.add_argument("package", help="Name of the PyPI package")
parser.add_argument(
"old_version", help="Current version installed in Home Assistant"
)
parser.add_argument(
"--new-version", help="Target version to bump to (defaults to latest on PyPI)"
)
args = parser.parse_args()
if not re.match(_NAME_MATCH, args.package):
print(f"[ERROR] Invalid package name format: '{args.package}'", file=sys.stderr)
sys.exit(1)
if not re.match(_VERSION_MATCH, args.old_version):
print(
f"[ERROR] Invalid old version format: '{args.old_version}'", file=sys.stderr
)
sys.exit(1)
if args.new_version and not re.match(_VERSION_MATCH, args.new_version):
print(
f"[ERROR] Invalid target version format: '{args.new_version}'",
file=sys.stderr,
)
sys.exit(1)
data = get_pypi_data(args.package)
info = data.get("info", {})
# Filter out pre-releases from the releases list to identify the latest stable version
stable_versions = []
for v in data.get("releases", {}):
try:
ver = Version(v)
if not ver.is_prerelease:
stable_versions.append(ver)
except Exception:
continue
latest = str(max(stable_versions)) if stable_versions else info.get("version")
if latest and not re.match(_VERSION_MATCH, latest):
print("[ERROR] Invalid latest version resolved from PyPI.", file=sys.stderr)
sys.exit(1)
target_version = args.new_version or latest
if not target_version:
print("[ERROR] Could not resolve a target version from PyPI.", file=sys.stderr)
sys.exit(1)
github_repo = find_github_repo(info)
print("--- RESOLVED DEPENDENCY DETAILS ---")
print(f"Package: {args.package}")
print(f"Current Version: {args.old_version}")
print(f"Latest (PyPI): {latest}")
print(f"Target Version: {target_version}")
if github_repo:
print(f"GitHub Repository: {github_repo}")
# Verify tag formats on GitHub
old_tag = check_github_tag(github_repo, args.old_version)
new_tag = check_github_tag(github_repo, target_version)
if not old_tag or not new_tag:
missing_tags = []
if not old_tag:
missing_tags.append(args.old_version)
if not new_tag:
missing_tags.append(target_version)
print(
f"\n[ERROR] Could not resolve GitHub release tag for version(s): {', '.join(missing_tags)}",
file=sys.stderr,
)
sys.exit(1)
changelog_url = f"{github_repo}/releases/tag/{new_tag}"
compare_url = f"{github_repo}/compare/{old_tag}...{new_tag}"
print("\n--- PR DOCUMENTATION LINKS ---")
print(f"Changelog Link: {changelog_url}")
print(f"Comparison Diff Link: {compare_url}")
else:
print("\n[WARNING] GitHub repository could not be resolved from PyPI metadata.")
print("Please resolve the release notes and diff links manually.")
if __name__ == "__main__":
main()
+1
View File
@@ -54,3 +54,4 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- Keep comments concise. Prefer one short line stating the non-obvious constraint, or no comment at all.
- Do not add comments that just restate the code on the following line(s) (e.g. `# Check if initialized` above `if self.initialized:`). Comments should only explain why (non-obvious constraints, surprising behavior, or workarounds), never what. Never add comments that justify a change by referencing what the code looked like before.
- Do not add section or divider comments (e.g. `# --- XYZ Triggers ---`) inside or outside of functions, since those can easily become stale and be misleading.
- When catching exceptions, try-clauses should be as small as possible, i.e. avoid wrapping large blocks of code in a try-clause, and avoid catching exceptions from functions that are not expected to raise them.
@@ -12,6 +12,7 @@ on:
types: [opened, synchronize, reopened]
paths:
- "requirements*.txt"
- "**/requirements*.txt"
- "homeassistant/package_constraints.txt"
workflow_dispatch:
inputs:
@@ -58,6 +59,7 @@ jobs:
echo "head_sha=${HEAD_SHA}" >> "${GITHUB_OUTPUT}"
- name: Run deterministic checks
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ inputs.pull_request_number || github.event.pull_request.number }}
HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
run: |
+54 -125
View File
@@ -1,5 +1,5 @@
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"7b142e96e0f8b454cdcc9c0c25070cf9a52c44d83a6b1fbc3ad6725b6567337c","body_hash":"3894ded07d5934ac5f29d160ffb1f9115cf72b6da8a7e453a4d4f69e8641a48e","compiler_version":"v0.79.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"v0.79.6","version":"v0.79.6"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"36a7fc263a2ce868d74a266f23eb7772d82fd397806464384fe087479ddd4a70","body_hash":"bba8c011f2b82bb4d9847a359f43f0e7d91245b280678c20e5112b3c9e77d5cd","compiler_version":"v0.79.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}}
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"5c2fe865bb4dc46e1450f6ee0d0541d759aea73a","version":"v0.79.6"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]}
# ___ _ _
# / _ \ | | (_)
# | |_| | __ _ ___ _ __ | |_ _ ___
@@ -36,7 +36,7 @@
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@@ -92,7 +92,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -344,9 +344,8 @@ jobs:
agent:
needs:
- activation
- extract_pr_number
- gate
if: needs.activation.outputs.daily_effective_workflow_exceeded != 'true'
- prepare
if: (needs.prepare.outputs.skip != 'true') && (needs.activation.outputs.daily_effective_workflow_exceeded != 'true')
runs-on: ubuntu-latest
permissions:
actions: read
@@ -383,7 +382,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -489,15 +488,15 @@ jobs:
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_f496a449c5dccca1_EOF'
{"add_comment":{"max":1,"target":"${{ needs.extract_pr_number.outputs.pr_number }}"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
GH_AW_SAFE_OUTPUTS_CONFIG_f496a449c5dccca1_EOF
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_823c5547a5e52957_EOF'
{"add_comment":{"max":1,"target":"${{ needs.prepare.outputs.pr_number }}"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
GH_AW_SAFE_OUTPUTS_CONFIG_823c5547a5e52957_EOF
- name: Generate Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
"description_suffixes": {
"add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Target: ${{ needs.extract_pr_number.outputs.pr_number }}. Supports reply_to_id for discussion threading."
"add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Target: ${{ needs.prepare.outputs.pr_number }}. Supports reply_to_id for discussion threading."
},
"repo_params": {},
"dynamic_tools": []
@@ -994,8 +993,7 @@ jobs:
- activation
- agent
- detection
- extract_pr_number
- gate
- prepare
- safe_outputs
if: >
always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
@@ -1018,7 +1016,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -1208,7 +1206,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -1429,111 +1427,6 @@ jobs:
}
}
extract_pr_number:
needs: gate
if: needs.gate.outputs.skip != 'true' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
outputs:
pr_number: ${{ steps.extract.outputs.pr_number }}
steps:
- name: Configure GH_HOST for enterprise compatibility
id: ghes-host-config
shell: bash
# zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
run: |
# Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
# GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
GH_HOST="${GITHUB_SERVER_URL#https://}"
GH_HOST="${GH_HOST#http://}"
echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- name: Download deterministic-results artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
name: check-requirements-deterministic
path: /tmp/deterministic
run-id: ${{ github.event.workflow_run.id }}
- name: Extract PR number from artifact
id: extract
run: |
PR=$(jq -r '.pr_number' /tmp/deterministic/results.json)
echo "pr_number=${PR}" >> "${GITHUB_OUTPUT}"
gate:
needs: activation
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
pull-requests: read
outputs:
skip: ${{ steps.gate.outputs.skip }}
steps:
- name: Configure GH_HOST for enterprise compatibility
id: ghes-host-config
shell: bash
# zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
run: |
# Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
# GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
GH_HOST="${GITHUB_SERVER_URL#https://}"
GH_HOST="${GH_HOST#http://}"
echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- name: Download deterministic-results artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
name: check-requirements-deterministic
path: /tmp/gate
run-id: ${{ github.event.workflow_run.id }}
- name: Decide whether requirements changed since the last comment
id: gate
run: |
PR=$(jq -r '.pr_number' /tmp/gate/results.json)
HEAD=$(jq -r '.head_sha // empty' /tmp/gate/results.json)
if [ -z "${HEAD}" ]; then
echo "Artifact has no head_sha; running the agent."
exit 0
fi
# Recover the commit recorded in the most recent requirements-check
# comment from the "Checked at commit" link
PRIOR=$(gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${PR}/comments" \
--jq '.[] | select(.body | contains("<!-- requirements-check -->")) | .body' \
| grep -oiE '/commit/[0-9a-f]{40}' \
| grep -oiE '[0-9a-f]{40}' | tail -1 || true)
if [ -z "${PRIOR}" ]; then
echo "No previous comment with a recorded commit; running the agent."
exit 0
fi
if [ "${PRIOR}" = "${HEAD}" ]; then
echo "Head ${HEAD} unchanged since the last comment; skipping the agent."
echo "skip=true" >> "${GITHUB_OUTPUT}"
exit 0
fi
# List files changed between the recorded commit and the current head.
# Tracked patterns mirror script/check_requirements/diff.py TRACKED_PATTERNS.
CHANGED=$(gh api "repos/${GITHUB_REPOSITORY}/compare/${PRIOR}...${HEAD}" \
--jq '.files[].filename' 2>/dev/null) || {
echo "Could not compare ${PRIOR}...${HEAD}; running the agent."
exit 0
}
TRACKED=$(printf '%s\n' "${CHANGED}" \
| grep -Ex 'requirements.*\.txt|homeassistant/package_constraints\.txt' || true)
if [ -z "${TRACKED}" ]; then
echo "No tracked requirement files changed since ${PRIOR}; skipping the agent."
echo "skip=true" >> "${GITHUB_OUTPUT}"
else
echo "Tracked requirement files changed since ${PRIOR}; running the agent:"
printf '%s\n' "${TRACKED}"
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
pre_activation:
runs-on: ubuntu-slim
outputs:
@@ -1545,7 +1438,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -1568,12 +1461,48 @@ jobs:
const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs');
await main();
prepare:
needs: activation
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
outputs:
pr_number: ${{ steps.prepare.outputs.pr_number }}
skip: ${{ steps.prepare.outputs.skip }}
steps:
- name: Configure GH_HOST for enterprise compatibility
id: ghes-host-config
shell: bash
# zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
run: |
# Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
# GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
GH_HOST="${GITHUB_SERVER_URL#https://}"
GH_HOST="${GH_HOST#http://}"
echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- name: Download deterministic-results artifact
id: download
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
name: check-requirements-deterministic
path: /tmp/deterministic
run-id: ${{ github.event.workflow_run.id }}
- name: Resolve skip and PR number from the artifact
id: prepare
run: |
echo "skip=$(jq -r '.skip_aw' /tmp/deterministic/results.json)" >> "${GITHUB_OUTPUT}"
echo "pr_number=$(jq -r '.pr_number' /tmp/deterministic/results.json)" >> "${GITHUB_OUTPUT}"
safe_outputs:
needs:
- activation
- agent
- detection
- extract_pr_number
- prepare
if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
runs-on: ubuntu-slim
permissions:
@@ -1609,7 +1538,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@@ -1654,7 +1583,7 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,binstar.org,bootstrap.pypa.io,conda.anaconda.org,conda.binstar.org,files.pythonhosted.org,github.com,host.docker.internal,pip.pypa.io,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,telemetry.enterprise.githubcopilot.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"target\":\"${{ needs.extract_pr_number.outputs.pr_number }}\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"target\":\"${{ needs.prepare.outputs.pr_number }}\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
+15 -68
View File
@@ -15,94 +15,41 @@ tools:
github:
toolsets: [repos, pull_requests]
min-integrity: unapproved
if: needs.prepare.outputs.skip != 'true'
safe-outputs:
add-comment:
max: 1
target: "${{ needs.extract_pr_number.outputs.pr_number }}"
target: "${{ needs.prepare.outputs.pr_number }}"
needs:
- extract_pr_number
- prepare
jobs:
gate:
# Skip the (token-spending) agent when no tracked requirement file changed
prepare:
# The deterministic stage always uploads an artifact; its `skip_aw` flag is
# true when no tracked requirement file changed since the last comment,
# which is our cue to skip the (token-spending) agent. Recover the PR number
# to comment on either way.
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
pull-requests: read
outputs:
skip: ${{ steps.gate.outputs.skip }}
steps:
- name: Download deterministic-results artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: check-requirements-deterministic
path: /tmp/gate
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Decide whether requirements changed since the last comment
id: gate
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR=$(jq -r '.pr_number' /tmp/gate/results.json)
HEAD=$(jq -r '.head_sha // empty' /tmp/gate/results.json)
if [ -z "${HEAD}" ]; then
echo "Artifact has no head_sha; running the agent."
exit 0
fi
# Recover the commit recorded in the most recent requirements-check
# comment from the "Checked at commit" link
PRIOR=$(gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${PR}/comments" \
--jq '.[] | select(.body | contains("<!-- requirements-check -->")) | .body' \
| grep -oiE '/commit/[0-9a-f]{40}' \
| grep -oiE '[0-9a-f]{40}' | tail -1 || true)
if [ -z "${PRIOR}" ]; then
echo "No previous comment with a recorded commit; running the agent."
exit 0
fi
if [ "${PRIOR}" = "${HEAD}" ]; then
echo "Head ${HEAD} unchanged since the last comment; skipping the agent."
echo "skip=true" >> "${GITHUB_OUTPUT}"
exit 0
fi
# List files changed between the recorded commit and the current head.
# Tracked patterns mirror script/check_requirements/diff.py TRACKED_PATTERNS.
CHANGED=$(gh api "repos/${GITHUB_REPOSITORY}/compare/${PRIOR}...${HEAD}" \
--jq '.files[].filename' 2>/dev/null) || {
echo "Could not compare ${PRIOR}...${HEAD}; running the agent."
exit 0
}
TRACKED=$(printf '%s\n' "${CHANGED}" \
| grep -Ex 'requirements.*\.txt|homeassistant/package_constraints\.txt' || true)
if [ -z "${TRACKED}" ]; then
echo "No tracked requirement files changed since ${PRIOR}; skipping the agent."
echo "skip=true" >> "${GITHUB_OUTPUT}"
else
echo "Tracked requirement files changed since ${PRIOR}; running the agent:"
printf '%s\n' "${TRACKED}"
fi
extract_pr_number:
needs: gate
if: needs.gate.outputs.skip != 'true' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
permissions:
actions: read
outputs:
pr_number: ${{ steps.extract.outputs.pr_number }}
skip: ${{ steps.prepare.outputs.skip }}
pr_number: ${{ steps.prepare.outputs.pr_number }}
steps:
- name: Download deterministic-results artifact
id: download
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: check-requirements-deterministic
path: /tmp/deterministic
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Extract PR number from artifact
id: extract
- name: Resolve skip and PR number from the artifact
id: prepare
run: |
PR=$(jq -r '.pr_number' /tmp/deterministic/results.json)
echo "pr_number=${PR}" >> "${GITHUB_OUTPUT}"
echo "skip=$(jq -r '.skip_aw' /tmp/deterministic/results.json)" >> "${GITHUB_OUTPUT}"
echo "pr_number=$(jq -r '.pr_number' /tmp/deterministic/results.json)" >> "${GITHUB_OUTPUT}"
concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.id }}
cancel-in-progress: true
+1 -1
View File
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.17
rev: v0.15.18
hooks:
- id: ruff-check
args:
+1
View File
@@ -43,3 +43,4 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- Keep comments concise. Prefer one short line stating the non-obvious constraint, or no comment at all.
- Do not add comments that just restate the code on the following line(s) (e.g. `# Check if initialized` above `if self.initialized:`). Comments should only explain why (non-obvious constraints, surprising behavior, or workarounds), never what. Never add comments that justify a change by referencing what the code looked like before.
- Do not add section or divider comments (e.g. `# --- XYZ Triggers ---`) inside or outside of functions, since those can easily become stale and be misleading.
- When catching exceptions, try-clauses should be as small as possible, i.e. avoid wrapping large blocks of code in a try-clause, and avoid catching exceptions from functions that are not expected to raise them.
Generated
-2
View File
@@ -181,7 +181,6 @@ CLAUDE.md @home-assistant/core
/tests/components/asuswrt/ @kennedyshead @ollo69 @Vaskivskyi
/homeassistant/components/atag/ @MatsNL
/tests/components/atag/ @MatsNL
/homeassistant/components/aten_pe/ @mtdcr
/homeassistant/components/atome/ @baqs
/homeassistant/components/august/ @bdraco
/tests/components/august/ @bdraco
@@ -1987,7 +1986,6 @@ CLAUDE.md @home-assistant/core
/tests/components/waterfurnace/ @sdague @masterkoppa
/homeassistant/components/watergate/ @adam-the-hero
/tests/components/watergate/ @adam-the-hero
/homeassistant/components/watson_tts/ @rutkai
/homeassistant/components/watts/ @theobld-ww @devender-verma-ww @ssi-spyro
/tests/components/watts/ @theobld-ww @devender-verma-ww @ssi-spyro
/homeassistant/components/watttime/ @bachya
+8
View File
@@ -1 +1,9 @@
"""Init file for Home Assistant."""
from probatio.compat import install_as_voluptuous
# Probatio replaces voluptuous as the validation engine. Custom integrations and a
# few dependencies still import voluptuous directly, so alias it to probatio in
# sys.modules before anything imports it. This must run before the first
# `import voluptuous`, hence the package __init__.
install_as_voluptuous()
+36 -3
View File
@@ -66,6 +66,7 @@ from .const import (
BASE_PLATFORMS,
FORMAT_DATETIME,
KEY_DATA_LOGGING as DATA_LOGGING,
KEY_DATA_LOGGING_DISABLED_REASON as DATA_LOGGING_DISABLED_REASON,
SIGNAL_BOOTSTRAP_INTEGRATIONS,
)
from .core_config import async_process_ha_core_config
@@ -129,6 +130,11 @@ SETUP_ORDER_SORT_KEY = partial(contains, BASE_PLATFORMS)
ERROR_LOG_FILENAME = "home-assistant.log"
ENV_DISABLE_LOG_FILE = "HA_DISABLE_LOG_FILE"
ENV_DUPLICATE_LOG_FILE = "HA_DUPLICATE_LOG_FILE"
ENV_SUPERVISOR = "SUPERVISOR"
LOG_FILE_DISABLED_REASON_ENVIRONMENT = "environment"
LOG_FILE_DISABLED_REASON_SUPERVISOR = "supervisor"
# hass.data key for logging information.
DATA_REGISTRIES_LOADED: HassKey[None] = HassKey("bootstrap_registries_loaded")
@@ -642,10 +648,12 @@ async def async_enable_logging(
logger.setLevel(logging.INFO if verbose else logging.WARNING)
if log_file is None:
disabled_log_file_reason = _log_file_disabled_reason()
default_log_path = hass.config.path(ERROR_LOG_FILENAME)
if "SUPERVISOR" in os.environ and "HA_DUPLICATE_LOG_FILE" not in os.environ:
if disabled_log_file_reason:
# Rename the default log file if it exists, since previous versions created
# it even on Supervisor
# it before Supervisor disabled duplicate file logging or
# HA_DISABLE_LOG_FILE disabled the log file.
def rename_old_file() -> None:
"""Rename old log file in executor."""
if os.path.isfile(default_log_path):
@@ -657,6 +665,7 @@ async def async_enable_logging(
else:
err_log_path = default_log_path
else:
disabled_log_file_reason = None
err_log_path = os.path.abspath(log_file)
if err_log_path:
@@ -669,10 +678,34 @@ async def async_enable_logging(
# Save the log file location for access by other components.
hass.data[DATA_LOGGING] = err_log_path
elif disabled_log_file_reason == LOG_FILE_DISABLED_REASON_ENVIRONMENT:
hass.data[DATA_LOGGING_DISABLED_REASON] = disabled_log_file_reason
async_activate_log_queue_handler(hass)
def _log_file_disabled_reason() -> str | None:
"""Return why the log file is disabled."""
if ENV_SUPERVISOR in os.environ and ENV_DUPLICATE_LOG_FILE not in os.environ:
return LOG_FILE_DISABLED_REASON_SUPERVISOR
disable_log_file = os.environ.get(ENV_DISABLE_LOG_FILE)
if disable_log_file is None:
return None
try:
if cv.boolean(disable_log_file):
return LOG_FILE_DISABLED_REASON_ENVIRONMENT
except vol.Invalid:
_LOGGER.warning(
"Ignoring invalid %s value: %s. Expected a boolean value: "
"1/0, true/false, yes/no, on/off, or enable/disable",
ENV_DISABLE_LOG_FILE,
disable_log_file,
)
return None
def _create_log_file(
err_log_path: str, log_rotate_days: int | None
) -> RotatingFileHandler | TimedRotatingFileHandler:
@@ -734,7 +767,7 @@ def _get_domains(hass: core.HomeAssistant, config: dict[str, Any]) -> set[str]:
domains.update(DEFAULT_INTEGRATIONS_RECOVERY_MODE)
# Add domains depending on if the Supervisor is used or not
if "SUPERVISOR" in os.environ:
if ENV_SUPERVISOR in os.environ:
domains.update(DEFAULT_INTEGRATIONS_SUPERVISOR)
return domains
-3
View File
@@ -7,9 +7,6 @@
"azure_event_hub",
"azure_service_bus",
"azure_storage",
"microsoft_face_detect",
"microsoft_face_identify",
"microsoft_face",
"microsoft",
"onedrive",
"onedrive_for_business",
@@ -27,7 +27,7 @@ from .const import CONDITIONS_MAP, DOMAIN, FORECAST_MAP
_LOGGER = logging.getLogger(__name__)
API_TIMEOUT: Final[int] = 120
WEATHER_UPDATE_INTERVAL = timedelta(minutes=10)
WEATHER_UPDATE_INTERVAL = timedelta(minutes=20)
type AemetConfigEntry = ConfigEntry[AemetData]
@@ -12,7 +12,7 @@ from homeassistant.components.number import (
NumberEntity,
NumberEntityDescription,
)
from homeassistant.const import PERCENTAGE, EntityCategory
from homeassistant.const import EntityCategory, UnitOfRatio
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -40,7 +40,7 @@ DISPLAY_BRIGHTNESS = AirGradientNumberEntityDescription(
native_min_value=0,
native_max_value=100,
native_step=1,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
value_fn=lambda config: config.display_brightness,
set_value_fn=lambda client, value: client.set_display_brightness(value),
)
@@ -52,7 +52,7 @@ LED_BAR_BRIGHTNESS = AirGradientNumberEntityDescription(
native_min_value=0,
native_max_value=100,
native_step=1,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
value_fn=lambda config: config.led_bar_brightness,
set_value_fn=lambda client, value: client.set_led_bar_brightness(value),
)
+11 -12
View File
@@ -19,11 +19,10 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
UnitOfDensity,
UnitOfRatio,
UnitOfTemperature,
UnitOfTime,
)
@@ -57,21 +56,21 @@ MEASUREMENT_SENSOR_TYPES: tuple[AirGradientMeasurementSensorEntityDescription, .
AirGradientMeasurementSensorEntityDescription(
key="pm01",
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.pm01,
),
AirGradientMeasurementSensorEntityDescription(
key="pm02",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.pm02,
),
AirGradientMeasurementSensorEntityDescription(
key="pm10",
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.pm10,
),
@@ -85,7 +84,7 @@ MEASUREMENT_SENSOR_TYPES: tuple[AirGradientMeasurementSensorEntityDescription, .
AirGradientMeasurementSensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.relative_humidity,
),
@@ -113,7 +112,7 @@ MEASUREMENT_SENSOR_TYPES: tuple[AirGradientMeasurementSensorEntityDescription, .
AirGradientMeasurementSensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.rco2,
),
@@ -144,7 +143,7 @@ MEASUREMENT_SENSOR_TYPES: tuple[AirGradientMeasurementSensorEntityDescription, .
key="pm02_raw",
translation_key="raw_pm02",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
value_fn=lambda status: status.raw_pm02,
@@ -190,7 +189,7 @@ CONFIG_LED_BAR_SENSOR_TYPES: tuple[AirGradientConfigSensorEntityDescription, ...
AirGradientConfigSensorEntityDescription(
key="led_bar_brightness",
translation_key="led_bar_brightness",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda config: config.led_bar_brightness,
),
@@ -216,9 +215,9 @@ CONFIG_DISPLAY_SENSOR_TYPES: tuple[AirGradientConfigSensorEntityDescription, ...
AirGradientConfigSensorEntityDescription(
key="display_brightness",
translation_key="display_brightness",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda config: config.display_brightness,
value_fn=lambda config: config.led_bar_brightness,
),
)
+10 -10
View File
@@ -11,10 +11,10 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONF_NAME,
PERCENTAGE,
UnitOfDensity,
UnitOfPressure,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
@@ -76,14 +76,14 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_PM1,
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
AirlySensorEntityDescription(
key=ATTR_API_PM25,
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
@@ -94,7 +94,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_PM10,
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
@@ -105,7 +105,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_HUMIDITY,
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
@@ -126,7 +126,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_CO,
device_class=SensorDeviceClass.CO,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
@@ -137,7 +137,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_NO2,
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
@@ -148,7 +148,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_SO2,
device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
@@ -159,7 +159,7 @@ SENSOR_TYPES: tuple[AirlySensorEntityDescription, ...] = (
AirlySensorEntityDescription(
key=ATTR_API_O3,
device_class=SensorDeviceClass.OZONE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
attrs=lambda data: {
+4 -8
View File
@@ -12,11 +12,7 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
ATTR_TIME,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
)
from homeassistant.const import ATTR_TIME, UnitOfDensity, UnitOfRatio
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -95,7 +91,7 @@ SENSOR_TYPES: tuple[AirNowEntityDescription, ...] = (
AirNowEntityDescription(
key=ATTR_API_PM10,
translation_key="pm10",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.PM10,
value_fn=lambda data: data.get(ATTR_API_PM10),
@@ -104,7 +100,7 @@ SENSOR_TYPES: tuple[AirNowEntityDescription, ...] = (
AirNowEntityDescription(
key=ATTR_API_PM25,
translation_key="pm25",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.PM25,
value_fn=lambda data: data.get(ATTR_API_PM25),
@@ -113,7 +109,7 @@ SENSOR_TYPES: tuple[AirNowEntityDescription, ...] = (
AirNowEntityDescription(
key=ATTR_API_O3,
translation_key="o3",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda data: data.get(ATTR_API_O3),
extra_state_attributes_fn=None,
+3 -4
View File
@@ -14,9 +14,8 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
EntityCategory,
UnitOfRatio,
UnitOfTemperature,
UnitOfTime,
)
@@ -59,7 +58,7 @@ SENSOR_TYPES: tuple[AirobotSensorEntityDescription, ...] = (
AirobotSensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.hum_air,
),
@@ -75,7 +74,7 @@ SENSOR_TYPES: tuple[AirobotSensorEntityDescription, ...] = (
AirobotSensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.co2,
supported_fn=lambda status: status.has_co2_sensor,
+2 -2
View File
@@ -8,7 +8,7 @@ from typing import override
from aioairq.core import AirQ
from homeassistant.components.number import NumberEntity, NumberEntityDescription
from homeassistant.const import PERCENTAGE
from homeassistant.const import UnitOfRatio
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -32,7 +32,7 @@ AIRQ_LED_BRIGHTNESS = AirQBrightnessDescription(
native_min_value=0.0,
native_max_value=100.0,
native_step=1.0,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
value=lambda data: data["brightness"],
set_value=lambda device, value: device.set_current_brightness(value),
)
+47 -51
View File
@@ -12,13 +12,9 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_GRAMS_PER_CUBIC_METER,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
UnitOfDensity,
UnitOfPressure,
UnitOfRatio,
UnitOfSoundPressure,
UnitOfTemperature,
)
@@ -44,70 +40,70 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
AirQEntityDescription(
key="c2h4o",
translation_key="acetaldehyde",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("c2h4o"),
),
AirQEntityDescription(
key="nh3_MR100",
translation_key="ammonia",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("nh3_MR100"),
),
AirQEntityDescription(
key="ash3",
translation_key="arsine",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ash3"),
),
AirQEntityDescription(
key="br2",
translation_key="bromine",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("br2"),
),
AirQEntityDescription(
key="ch4s",
translation_key="methanethiol",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ch4s"),
),
AirQEntityDescription(
key="cl2_M20",
translation_key="chlorine",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("cl2_M20"),
),
AirQEntityDescription(
key="clo2",
translation_key="chlorine_dioxide",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("clo2"),
),
AirQEntityDescription(
key="co",
translation_key="carbon_monoxide",
native_unit_of_measurement=CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MILLIGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("co"),
),
AirQEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("co2"),
),
AirQEntityDescription(
key="cs2",
translation_key="carbon_disulfide",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("cs2"),
),
@@ -122,182 +118,182 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
AirQEntityDescription(
key="ethanol",
translation_key="ethanol",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ethanol"),
),
AirQEntityDescription(
key="c2h4",
translation_key="ethylene",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("c2h4"),
),
AirQEntityDescription(
key="ch2o_M10",
translation_key="formaldehyde",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ch2o_M10"),
),
AirQEntityDescription(
key="f2",
translation_key="fluorine",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("f2"),
),
AirQEntityDescription(
key="h2s",
translation_key="hydrogen_sulfide",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("h2s"),
),
AirQEntityDescription(
key="hcl",
translation_key="hydrochloric_acid",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("hcl"),
),
AirQEntityDescription(
key="hcn",
translation_key="hydrogen_cyanide",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("hcn"),
),
AirQEntityDescription(
key="hf",
translation_key="hydrogen_fluoride",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("hf"),
),
AirQEntityDescription(
key="health",
translation_key="health_index",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("health", 0.0) / 10.0,
),
AirQEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("humidity"),
),
AirQEntityDescription(
key="humidity_abs",
device_class=SensorDeviceClass.ABSOLUTE_HUMIDITY,
native_unit_of_measurement=CONCENTRATION_GRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.GRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("humidity_abs"),
),
AirQEntityDescription(
key="h2_M1000",
translation_key="hydrogen",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("h2_M1000"),
),
AirQEntityDescription(
key="h2o2",
translation_key="hydrogen_peroxide",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("h2o2"),
),
AirQEntityDescription(
key="ch4_MIPEX",
translation_key="methane",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ch4_MIPEX"),
),
AirQEntityDescription(
key="mold",
translation_key="mold",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("mold"),
),
AirQEntityDescription(
key="n2o",
device_class=SensorDeviceClass.NITROUS_OXIDE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("n2o"),
),
AirQEntityDescription(
key="no_M250",
device_class=SensorDeviceClass.NITROGEN_MONOXIDE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("no_M250"),
),
AirQEntityDescription(
key="no2",
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("no2"),
),
AirQEntityDescription(
key="acid_M100",
translation_key="organic_acid",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("acid_M100"),
),
AirQEntityDescription(
key="oxygen",
translation_key="oxygen",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("oxygen"),
),
AirQEntityDescription(
key="o3",
device_class=SensorDeviceClass.OZONE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("o3"),
),
AirQEntityDescription(
key="performance",
translation_key="performance_index",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("performance", 0.0) / 10.0,
),
AirQEntityDescription(
key="ph3",
translation_key="hydrogen_phosphide",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("ph3"),
),
AirQEntityDescription(
key="pm1",
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("pm1"),
),
AirQEntityDescription(
key="pm2_5",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("pm2_5"),
),
AirQEntityDescription(
key="pm10",
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("pm10"),
),
@@ -319,42 +315,42 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
AirQEntityDescription(
key="c3h8_MIPEX",
translation_key="propane",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("c3h8_MIPEX"),
),
AirQEntityDescription(
key="r32",
translation_key="r32",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("r32"),
),
AirQEntityDescription(
key="r454b",
translation_key="r454b",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("r454b"),
),
AirQEntityDescription(
key="r454c",
translation_key="r454c",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("r454c"),
),
AirQEntityDescription(
key="sih4",
translation_key="silicon_hydride",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("sih4"),
),
AirQEntityDescription(
key="so2",
device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("so2"),
),
@@ -391,7 +387,7 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
AirQEntityDescription(
key="tvoc",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("tvoc"),
),
@@ -399,14 +395,14 @@ SENSOR_TYPES: list[AirQEntityDescription] = [
key="tvoc_ionsc",
translation_key="industrial_volatile_organic_compounds",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("tvoc_ionsc"),
),
AirQEntityDescription(
key="virus",
translation_key="virus_index",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value=lambda data: data.get("virus", 0.0),
),
+9 -11
View File
@@ -11,14 +11,12 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS,
EntityCategory,
UnitOfDensity,
UnitOfPressure,
UnitOfRatio,
UnitOfSoundPressure,
UnitOfTemperature,
)
@@ -49,7 +47,7 @@ SENSORS: dict[str, SensorEntityDescription] = {
"humidity": SensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
@@ -70,7 +68,7 @@ SENSORS: dict[str, SensorEntityDescription] = {
"battery": SensorEntityDescription(
key="battery",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
@@ -78,20 +76,20 @@ SENSORS: dict[str, SensorEntityDescription] = {
"co2": SensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"voc": SensorEntityDescription(
key="voc",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"light": SensorEntityDescription(
key="light",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
translation_key="light",
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
@@ -126,14 +124,14 @@ SENSORS: dict[str, SensorEntityDescription] = {
),
"pm1": SensorEntityDescription(
key="pm1",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM1,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"pm25": SensorEntityDescription(
key="pm25",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
@@ -1,6 +1,8 @@
"""Support for airthings ble sensors."""
from collections.abc import Callable
import dataclasses
from dataclasses import dataclass
import logging
from typing import override
@@ -13,13 +15,11 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
EntityCategory,
Platform,
UnitOfPressure,
UnitOfRatio,
UnitOfSoundPressure,
UnitOfTemperature,
)
@@ -46,87 +46,108 @@ CONNECTIVITY_MODE_MAP = {
AirthingsConnectivityMode.NOT_CONFIGURED.value: "not_configured",
}
SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
"radon_1day_avg": SensorEntityDescription(
def get_connectivity_mode(value: str | float | None) -> str | None:
"""Get connectivity mode."""
if not isinstance(value, str):
return None
return CONNECTIVITY_MODE_MAP.get(value)
@dataclass(frozen=True, kw_only=True)
class AirthingsBLESensorEntityDescription(SensorEntityDescription):
"""Describes Airthings BLE sensor entity."""
value_fn: Callable[[str | float | None], str | float | None] = lambda x: x
SENSORS_MAPPING_TEMPLATE: dict[str, AirthingsBLESensorEntityDescription] = {
"radon_1day_avg": AirthingsBLESensorEntityDescription(
key="radon_1day_avg",
translation_key="radon_1day_avg",
native_unit_of_measurement=VOLUME_BECQUEREL,
suggested_display_precision=0,
state_class=SensorStateClass.MEASUREMENT,
),
"radon_longterm_avg": SensorEntityDescription(
"radon_longterm_avg": AirthingsBLESensorEntityDescription(
key="radon_longterm_avg",
translation_key="radon_longterm_avg",
native_unit_of_measurement=VOLUME_BECQUEREL,
suggested_display_precision=0,
state_class=SensorStateClass.MEASUREMENT,
),
"radon_1day_level": SensorEntityDescription(
"radon_1day_level": AirthingsBLESensorEntityDescription(
key="radon_1day_level",
translation_key="radon_1day_level",
device_class=SensorDeviceClass.ENUM,
options=["good", "fair", "poor"],
value_fn=lambda value: value if value != "unknown" else None,
),
"radon_longterm_level": SensorEntityDescription(
"radon_longterm_level": AirthingsBLESensorEntityDescription(
key="radon_longterm_level",
translation_key="radon_longterm_level",
device_class=SensorDeviceClass.ENUM,
options=["good", "fair", "poor"],
value_fn=lambda value: value if value != "unknown" else None,
),
"temperature": SensorEntityDescription(
"temperature": AirthingsBLESensorEntityDescription(
key="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"humidity": SensorEntityDescription(
"humidity": AirthingsBLESensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"pressure": SensorEntityDescription(
"pressure": AirthingsBLESensorEntityDescription(
key="pressure",
device_class=SensorDeviceClass.ATMOSPHERIC_PRESSURE,
native_unit_of_measurement=UnitOfPressure.MBAR,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
),
"battery": SensorEntityDescription(
"battery": AirthingsBLESensorEntityDescription(
key="battery",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=0,
),
"co2": SensorEntityDescription(
"co2": AirthingsBLESensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"voc": SensorEntityDescription(
"voc": AirthingsBLESensorEntityDescription(
key="voc",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"illuminance": SensorEntityDescription(
"illuminance": AirthingsBLESensorEntityDescription(
key="illuminance",
translation_key="illuminance",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"lux": SensorEntityDescription(
"lux": AirthingsBLESensorEntityDescription(
key="lux",
device_class=SensorDeviceClass.ILLUMINANCE,
native_unit_of_measurement=LIGHT_LUX,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"noise": SensorEntityDescription(
"noise": AirthingsBLESensorEntityDescription(
key="noise",
translation_key="ambient_noise",
device_class=SensorDeviceClass.SOUND_PRESSURE,
@@ -134,13 +155,14 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"connectivity_mode": SensorEntityDescription(
"connectivity_mode": AirthingsBLESensorEntityDescription(
key="connectivity_mode",
translation_key="connectivity_mode",
device_class=SensorDeviceClass.ENUM,
options=list(CONNECTIVITY_MODE_MAP.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
value_fn=get_connectivity_mode,
),
}
@@ -228,12 +250,13 @@ class AirthingsSensor(
"""Airthings BLE sensors for the device."""
_attr_has_entity_name = True
entity_description: AirthingsBLESensorEntityDescription
def __init__(
self,
coordinator: AirthingsBLEDataUpdateCoordinator,
airthings_device: AirthingsDevice,
entity_description: SensorEntityDescription,
entity_description: AirthingsBLESensorEntityDescription,
) -> None:
"""Populate the airthings entity with relevant data."""
super().__init__(coordinator)
@@ -272,11 +295,4 @@ class AirthingsSensor(
def native_value(self) -> StateType:
"""Return the value reported by the sensor."""
value = self.coordinator.data.sensors[self.entity_description.key]
# Map connectivity mode to enum values
if self.entity_description.key == "connectivity_mode":
if not isinstance(value, str):
return None
return CONNECTIVITY_MODE_MAP.get(value)
return value
return self.entity_description.value_fn(value)
@@ -45,13 +45,23 @@
"name": "Radon 1-day average"
},
"radon_1day_level": {
"name": "Radon 1-day level"
"name": "Radon 1-day level",
"state": {
"fair": "Fair",
"good": "Good",
"poor": "Poor"
}
},
"radon_longterm_avg": {
"name": "Radon longterm average"
},
"radon_longterm_level": {
"name": "Radon longterm level"
"name": "Radon longterm level",
"state": {
"fair": "[%key:component::airthings_ble::entity::sensor::radon_1day_level::state::fair%]",
"good": "[%key:component::airthings_ble::entity::sensor::radon_1day_level::state::good%]",
"poor": "[%key:component::airthings_ble::entity::sensor::radon_1day_level::state::poor%]"
}
}
}
},
+8 -9
View File
@@ -12,14 +12,13 @@ from homeassistant.const import (
ATTR_LATITUDE,
ATTR_LONGITUDE,
ATTR_STATE,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
CONF_COUNTRY,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_SHOW_ON_MAP,
CONF_STATE,
UnitOfDensity,
UnitOfRatio,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -94,12 +93,12 @@ POLLUTANT_LEVELS = {
}
POLLUTANT_UNITS = {
"co": CONCENTRATION_PARTS_PER_MILLION,
"n2": CONCENTRATION_PARTS_PER_BILLION,
"o3": CONCENTRATION_PARTS_PER_BILLION,
"p1": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"p2": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
"s2": CONCENTRATION_PARTS_PER_BILLION,
"co": UnitOfRatio.PARTS_PER_MILLION,
"n2": UnitOfRatio.PARTS_PER_BILLION,
"o3": UnitOfRatio.PARTS_PER_BILLION,
"p1": UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
"p2": UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
"s2": UnitOfRatio.PARTS_PER_BILLION,
}
@@ -11,10 +11,9 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
EntityCategory,
UnitOfDensity,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
@@ -57,21 +56,21 @@ SENSOR_DESCRIPTIONS = (
key="battery_level",
device_class=SensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda settings, status, measurements, history: status["battery"],
),
AirVisualProMeasurementDescription(
key="carbon_dioxide",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda settings, status, measurements, history: measurements["co2"],
),
AirVisualProMeasurementDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda settings, status, measurements, history: measurements[
"humidity"
@@ -80,21 +79,21 @@ SENSOR_DESCRIPTIONS = (
AirVisualProMeasurementDescription(
key="particulate_matter_0_1",
translation_key="pm01",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda settings, status, measurements, history: measurements["pm0_1"],
),
AirVisualProMeasurementDescription(
key="particulate_matter_1_0",
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda settings, status, measurements, history: measurements["pm1_0"],
),
AirVisualProMeasurementDescription(
key="particulate_matter_2_5",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda settings, status, measurements, history: measurements["pm2_5"],
),
@@ -110,7 +109,7 @@ SENSOR_DESCRIPTIONS = (
AirVisualProMeasurementDescription(
key="voc",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda settings, status, measurements, history: measurements["voc"],
),
@@ -35,13 +35,13 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
UnitOfDensity,
UnitOfElectricCurrent,
UnitOfInformation,
UnitOfPressure,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
@@ -141,7 +141,7 @@ WEBSERVER_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = (
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
key=AZD_CPU_USAGE,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
translation_key="cpu_usage",
),
@@ -172,19 +172,19 @@ ZONE_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = (
SensorEntityDescription(
device_class=SensorDeviceClass.PM1,
key=AZD_AQ_PM_1,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
device_class=SensorDeviceClass.PM25,
key=AZD_AQ_PM_2P5,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
device_class=SensorDeviceClass.PM10,
key=AZD_AQ_PM_10,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
@@ -196,20 +196,20 @@ ZONE_SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = (
SensorEntityDescription(
device_class=SensorDeviceClass.HUMIDITY,
key=AZD_HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
device_class=SensorDeviceClass.BATTERY,
key=AZD_THERMOSTAT_BATTERY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
key=AZD_THERMOSTAT_COVERAGE,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
translation_key="thermostat_coverage",
),
@@ -13,7 +13,7 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.const import EntityCategory
from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
import homeassistant.helpers.entity_registry as er
@@ -118,7 +118,7 @@ async def async_setup_entry(
for serial_num in coordinator.data:
unique_id = f"{serial_num}-{sensor_desc.key}"
if entity_id := entity_registry.async_get_entity_id(
BINARY_SENSOR_DOMAIN, DOMAIN, unique_id
Platform.BINARY_SENSOR, DOMAIN, unique_id
):
_LOGGER.debug("Removing deprecated entity %s", entity_id)
entity_registry.async_remove(entity_id)
@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "platinum",
"requirements": ["aioamazondevices==14.1.3"]
"requirements": ["aioamazondevices==14.1.8"]
}
@@ -18,13 +18,7 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
UnitOfTemperature,
)
from homeassistant.const import LIGHT_LUX, UnitOfDensity, UnitOfRatio, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
@@ -97,25 +91,25 @@ SENSORS: Final = (
AmazonSensorEntityDescription(
key="Humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
AmazonSensorEntityDescription(
key="PM10",
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
AmazonSensorEntityDescription(
key="PM25",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
AmazonSensorEntityDescription(
key="CO",
device_class=SensorDeviceClass.CO,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
AmazonSensorEntityDescription(
@@ -7,7 +7,7 @@ from aioamazondevices.const.schedules import (
NOTIFICATION_TIMER,
)
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
import homeassistant.helpers.entity_registry as er
@@ -28,7 +28,7 @@ async def async_update_unique_id(
for serial_num in coordinator.data:
unique_id = f"{serial_num}-{old_key}"
if entity_id := entity_registry.async_get_entity_id(
DOMAIN, platform, unique_id
platform, DOMAIN, unique_id
):
_LOGGER.debug("Updating unique_id for %s", entity_id)
new_unique_id = unique_id.replace(old_key, new_key)
@@ -48,7 +48,7 @@ async def async_remove_entity_from_virtual_group(
for serial_num in coordinator.data:
unique_id = f"{serial_num}-{key}"
entity_id = entity_registry.async_get_entity_id(DOMAIN, platform, unique_id)
entity_id = entity_registry.async_get_entity_id(platform, DOMAIN, unique_id)
is_group = coordinator.data[serial_num].device_family == SPEAKER_GROUP_FAMILY
if entity_id and is_group:
entity_registry.async_remove(entity_id)
@@ -70,7 +70,7 @@ async def async_remove_unsupported_notification_sensors(
):
unique_id = f"{serial_num}-{notification_key}"
entity_id = entity_registry.async_get_entity_id(
DOMAIN, SENSOR_DOMAIN, unique_id=unique_id
Platform.SENSOR, DOMAIN, unique_id=unique_id
)
is_unsupported = not coordinator.data[serial_num].notifications_supported
+11 -12
View File
@@ -12,12 +12,11 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
UnitOfDensity,
UnitOfPressure,
UnitOfRatio,
UnitOfSoundPressure,
UnitOfTemperature,
)
@@ -45,7 +44,7 @@ SENSOR_DESCRIPTIONS = [
device_class=SensorDeviceClass.HUMIDITY,
key="BME280_humidity",
translation_key="humidity",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "BME280"},
),
@@ -70,7 +69,7 @@ SENSOR_DESCRIPTIONS = [
device_class=SensorDeviceClass.HUMIDITY,
key="BME680_humidity",
translation_key="humidity",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "BME680"},
),
@@ -129,7 +128,7 @@ SENSOR_DESCRIPTIONS = [
device_class=SensorDeviceClass.HUMIDITY,
key="HTU21D_humidity",
translation_key="humidity",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "HTU21D"},
),
@@ -145,21 +144,21 @@ SENSOR_DESCRIPTIONS = [
device_class=SensorDeviceClass.PM10,
translation_key="pm_10",
key="SDS_P1",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_display_precision=2,
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.PM25,
translation_key="pm_25",
key="SDS_P2",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_display_precision=2,
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.HUMIDITY,
key="SHT3X_humidity",
translation_key="humidity",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
suggested_display_precision=2,
translation_placeholders={"sensor_name": "SHT3X"},
),
@@ -194,7 +193,7 @@ SENSOR_DESCRIPTIONS = [
),
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
translation_key="co2",
key="CCS_CO2",
suggested_display_precision=2,
@@ -203,7 +202,7 @@ SENSOR_DESCRIPTIONS = [
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
key="CCS_TVOC",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
suggested_display_precision=2,
),
AltruistSensorEntityDescription(
@@ -215,7 +214,7 @@ SENSOR_DESCRIPTIONS = [
AltruistSensorEntityDescription(
device_class=SensorDeviceClass.CO2,
translation_key="co2",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
key="SCD4x_co2",
suggested_display_precision=2,
translation_placeholders={"sensor_name": "SCD4x"},
@@ -10,15 +10,14 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
CONF_MAC,
DEGREE,
PERCENTAGE,
UnitOfDensity,
UnitOfIrradiance,
UnitOfLength,
UnitOfPrecipitationDepth,
UnitOfPressure,
UnitOfRatio,
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolumetricFlux,
@@ -94,7 +93,7 @@ SENSOR_DESCRIPTIONS = (
),
SensorEntityDescription(
key=TYPE_CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=2,
@@ -134,7 +133,7 @@ SENSOR_DESCRIPTIONS = (
),
SensorEntityDescription(
key=TYPE_HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
@@ -188,14 +187,14 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_PM25_24H,
translation_key="pm25_24h_average",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
suggested_display_precision=1,
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key=TYPE_PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=1,
@@ -11,15 +11,14 @@ from homeassistant.components.sensor import (
)
from homeassistant.const import (
ATTR_NAME,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
DEGREE,
LIGHT_LUX,
PERCENTAGE,
UnitOfDensity,
UnitOfIrradiance,
UnitOfLength,
UnitOfPrecipitationDepth,
UnitOfPressure,
UnitOfRatio,
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolumetricFlux,
@@ -157,13 +156,13 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_PM25_IN_AQIN,
translation_key="pm25_indoor_aqin",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_PM25_IN_24H_AQIN,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
translation_key="pm25_indoor_24h_aqin",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
@@ -171,28 +170,28 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_PM10_IN_AQIN,
translation_key="pm10_indoor_aqin",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_PM10_IN_24H_AQIN,
translation_key="pm10_indoor_24h_aqin",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_CO2_IN_AQIN,
translation_key="co2_indoor_aqin",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_CO2_IN_24H_AQIN,
translation_key="co2_indoor_24h_aqin",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -206,7 +205,7 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_PM_IN_HUMIDITY_AQIN,
translation_key="pm_indoor_humidity_aqin",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -250,7 +249,7 @@ SENSOR_DESCRIPTIONS = (
),
SensorEntityDescription(
key=TYPE_CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -291,83 +290,83 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_HUMIDITY10,
translation_key="humidity_10",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY1,
translation_key="humidity_1",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY2,
translation_key="humidity_2",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY3,
translation_key="humidity_3",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY4,
translation_key="humidity_4",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY5,
translation_key="humidity_5",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY6,
translation_key="humidity_6",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY7,
translation_key="humidity_7",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY8,
translation_key="humidity_8",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY9,
translation_key="humidity_9",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_HUMIDITYIN,
translation_key="humidity_indoor",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -417,95 +416,95 @@ SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=TYPE_PM25_24H,
translation_key="pm25_24h_average",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
),
SensorEntityDescription(
key=TYPE_PM25_IN,
translation_key="pm25_indoor",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_PM25_IN_24H,
translation_key="pm25_indoor_24h_average",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
),
SensorEntityDescription(
key=TYPE_PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM10,
translation_key="soil_humidity_10",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM1,
translation_key="soil_humidity_1",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM2,
translation_key="soil_humidity_2",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM3,
translation_key="soil_humidity_3",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM4,
translation_key="soil_humidity_4",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM5,
translation_key="soil_humidity_5",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM6,
translation_key="soil_humidity_6",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM7,
translation_key="soil_humidity_7",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM8,
translation_key="soil_humidity_8",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_SOILHUM9,
translation_key="soil_humidity_9",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -9,5 +9,5 @@
"iot_class": "cloud_polling",
"loggers": ["pyanglianwater"],
"quality_scale": "bronze",
"requirements": ["pyanglianwater==3.2.2"]
"requirements": ["pyanglianwater==3.2.3"]
}
@@ -6,8 +6,8 @@ import logging
from typing import TYPE_CHECKING, Any, cast, override
import anthropic
from probatio import to_openapi as convert
import voluptuous as vol
from voluptuous_openapi import convert
from homeassistant.components.zone import ENTITY_ID_HOME
from homeassistant.config_entries import (
@@ -298,7 +298,7 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
):
self.options.pop(CONF_LLM_HASS_API)
if not errors:
return await self.async_step_advanced()
return await self.async_step_additional()
return self.async_show_form(
step_id="init",
@@ -308,10 +308,10 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
errors=errors or None,
)
async def async_step_advanced(
async def async_step_additional(
self, user_input: dict[str, Any] | None = None
) -> SubentryFlowResult:
"""Manage advanced options."""
"""Manage additional options."""
errors: dict[str, str] = {}
description_placeholders: dict[str, str] = {}
@@ -360,7 +360,7 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
return await self.async_step_model()
return self.async_show_form(
step_id="advanced",
step_id="additional",
data_schema=self.add_suggested_values_to_schema(
vol.Schema(step_schema), self.options
),
+1 -1
View File
@@ -103,8 +103,8 @@ from anthropic.types.web_fetch_tool_result_block import (
from anthropic.types.web_fetch_tool_result_block_param import (
Content as WebFetchToolResultBlockParamContentParam,
)
from probatio import to_openapi as convert
import voluptuous as vol
from voluptuous_openapi import convert
from homeassistant.components import conversation
from homeassistant.config_entries import ConfigSubentry
@@ -48,16 +48,16 @@
"user": "Add AI task"
},
"step": {
"advanced": {
"additional": {
"data": {
"chat_model": "[%key:common::generic::model%]",
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data::prompt_caching%]"
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::additional::data::prompt_caching%]"
},
"data_description": {
"chat_model": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::chat_model%]",
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::advanced::data_description::prompt_caching%]"
"chat_model": "[%key:component::anthropic::config_subentries::conversation::step::additional::data_description::chat_model%]",
"prompt_caching": "[%key:component::anthropic::config_subentries::conversation::step::additional::data_description::prompt_caching%]"
},
"title": "[%key:component::anthropic::config_subentries::conversation::step::advanced::title%]"
"title": "[%key:component::anthropic::config_subentries::conversation::step::additional::title%]"
},
"init": {
"data": {
@@ -115,7 +115,7 @@
"user": "Add conversation agent"
},
"step": {
"advanced": {
"additional": {
"data": {
"chat_model": "[%key:common::generic::model%]",
"prompt_caching": "Caching strategy"
@@ -124,7 +124,7 @@
"chat_model": "The model to serve the responses.",
"prompt_caching": "Optimize your API cost and response times based on your usage."
},
"title": "Advanced settings"
"title": "Additional settings"
},
"init": {
"data": {
+4 -5
View File
@@ -23,10 +23,9 @@ from homeassistant.const import (
ATTR_MODEL,
ATTR_NAME,
ATTR_SW_VERSION,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
EntityCategory,
UnitOfPressure,
UnitOfRatio,
UnitOfTemperature,
UnitOfTime,
)
@@ -62,7 +61,7 @@ SENSOR_DESCRIPTIONS = {
key="humidity",
name="Humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
"pressure": AranetSensorEntityDescription(
@@ -83,7 +82,7 @@ SENSOR_DESCRIPTIONS = {
key="co2",
name="Carbon Dioxide",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
"radiation_rate": AranetSensorEntityDescription(
@@ -115,7 +114,7 @@ SENSOR_DESCRIPTIONS = {
key="battery",
name="Battery",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
),
+5 -10
View File
@@ -12,12 +12,7 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
UnitOfTemperature,
)
from homeassistant.const import UnitOfDensity, UnitOfRatio, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -35,7 +30,7 @@ class ArveDeviceEntityDescription(SensorEntityDescription):
SENSORS: tuple[ArveDeviceEntityDescription, ...] = (
ArveDeviceEntityDescription(
key="CO2",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
value_fn=lambda arve_data: arve_data.co2,
state_class=SensorStateClass.MEASUREMENT,
@@ -48,21 +43,21 @@ SENSORS: tuple[ArveDeviceEntityDescription, ...] = (
),
ArveDeviceEntityDescription(
key="Humidity",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
value_fn=lambda arve_data: arve_data.humidity,
state_class=SensorStateClass.MEASUREMENT,
),
ArveDeviceEntityDescription(
key="PM10",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM10,
value_fn=lambda arve_data: arve_data.pm10,
state_class=SensorStateClass.MEASUREMENT,
),
ArveDeviceEntityDescription(
key="PM25",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
value_fn=lambda arve_data: arve_data.pm25,
state_class=SensorStateClass.MEASUREMENT,
@@ -1 +0,0 @@
"""The ATEN PE component."""
@@ -1,9 +0,0 @@
{
"domain": "aten_pe",
"name": "ATEN Rack PDU",
"codeowners": ["@mtdcr"],
"documentation": "https://www.home-assistant.io/integrations/aten_pe",
"iot_class": "local_polling",
"quality_scale": "legacy",
"requirements": ["atenpdu==0.3.6"]
}
-119
View File
@@ -1,119 +0,0 @@
"""The ATEN PE switch component."""
import logging
from typing import Any, override
from atenpdu import AtenPE, AtenPEError
import voluptuous as vol
from homeassistant.components.switch import (
PLATFORM_SCHEMA as SWITCH_PLATFORM_SCHEMA,
SwitchDeviceClass,
SwitchEntity,
)
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
_LOGGER = logging.getLogger(__name__)
CONF_AUTH_KEY = "auth_key"
CONF_COMMUNITY = "community"
CONF_PRIV_KEY = "priv_key"
DEFAULT_COMMUNITY = "private"
DEFAULT_PORT = "161"
DEFAULT_USERNAME = "administrator"
PLATFORM_SCHEMA = SWITCH_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_COMMUNITY, default=DEFAULT_COMMUNITY): cv.string,
vol.Optional(CONF_USERNAME, default=DEFAULT_USERNAME): cv.string,
vol.Optional(CONF_AUTH_KEY): cv.string,
vol.Optional(CONF_PRIV_KEY): cv.string,
}
)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the ATEN PE switch."""
node = config[CONF_HOST]
serv = config[CONF_PORT]
dev = AtenPE(
node=node,
serv=serv,
community=config[CONF_COMMUNITY],
username=config[CONF_USERNAME],
authkey=config.get(CONF_AUTH_KEY),
privkey=config.get(CONF_PRIV_KEY),
)
try:
await hass.async_add_executor_job(dev.initialize)
mac = await dev.deviceMAC()
outlets = dev.outlets()
name = await dev.deviceName()
model = await dev.modelName()
sw_version = await dev.deviceFWversion()
except AtenPEError as exc:
_LOGGER.error("Failed to initialize %s:%s: %s", node, serv, str(exc))
raise PlatformNotReady from exc
info = DeviceInfo(
connections={(CONNECTION_NETWORK_MAC, mac)},
manufacturer="ATEN",
model=model,
name=name,
sw_version=sw_version,
)
async_add_entities(
(AtenSwitch(dev, info, mac, outlet.id, outlet.name) for outlet in outlets), True
)
class AtenSwitch(SwitchEntity):
"""Represents an ATEN PE switch."""
_attr_device_class = SwitchDeviceClass.OUTLET
def __init__(
self, device: AtenPE, info: DeviceInfo, mac: str, outlet: str, name: str
) -> None:
"""Initialize an ATEN PE switch."""
self._device = device
self._outlet = outlet
self._attr_device_info = info
self._attr_unique_id = f"{mac}-{outlet}"
self._attr_name = name or f"Outlet {outlet}"
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
await self._device.setOutletStatus(self._outlet, "on")
self._attr_is_on = True
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
await self._device.setOutletStatus(self._outlet, "off")
self._attr_is_on = False
async def async_update(self) -> None:
"""Process update from entity."""
status = await self._device.displayOutletStatus(self._outlet)
if status == "on":
self._attr_is_on = True
elif status == "off":
self._attr_is_on = False
+2 -2
View File
@@ -74,8 +74,8 @@ from ipaddress import ip_address
from typing import TYPE_CHECKING, Any, cast
from aiohttp import web
from probatio import serialize
import voluptuous as vol
import voluptuous_serialize
from homeassistant import data_entry_flow
from homeassistant.auth import AuthManagerFlowManager, InvalidAuthError
@@ -263,7 +263,7 @@ def _prepare_result_json(result: AuthFlowResult) -> dict[str, Any]:
if (schema := result["data_schema"]) is None:
data["data_schema"] = []
else:
data["data_schema"] = voluptuous_serialize.convert(schema)
data["data_schema"] = serialize(schema)
return data
@@ -3,8 +3,8 @@
import logging
from typing import Any, override
from probatio import serialize
import voluptuous as vol
import voluptuous_serialize
from homeassistant import data_entry_flow
from homeassistant.components import websocket_api
@@ -153,6 +153,6 @@ def _prepare_result_json(result: data_entry_flow.FlowResult) -> dict[str, Any]:
if (schema := result["data_schema"]) is None:
data["data_schema"] = []
else:
data["data_schema"] = voluptuous_serialize.convert(schema)
data["data_schema"] = serialize(schema)
return data
+29 -12
View File
@@ -731,17 +731,32 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
trace_element = TraceElement(variables, trigger_path)
trace_append_element(trace_element)
if (
not skip_condition
and self._condition is not None
and not self._condition.async_check(variables=variables)
):
self._logger.debug(
"Conditions not met, aborting automation. Condition summary: %s",
trace_get(clear=False),
)
script_execution_set("failed_conditions")
return None
if not skip_condition and self._condition is not None:
try:
conditions_pass = self._condition.async_check(variables=variables)
except (vol.Invalid, HomeAssistantError) as err:
self._logger.error(
"Error while checking conditions of automation %s: %s",
self.entity_id,
err,
)
automation_trace.set_error(err)
return None
except Exception as err:
self._logger.exception(
"Unexpected error while checking conditions of automation %s",
self.entity_id,
)
automation_trace.set_error(err)
return None
if not conditions_pass:
self._logger.debug(
"Conditions not met, aborting automation. Condition summary: %s",
trace_get(clear=False),
)
script_execution_set("failed_conditions")
return None
self.async_set_context(trigger_context)
event_data = {
@@ -794,7 +809,9 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
)
automation_trace.set_error(err)
except Exception as err:
self._logger.exception("While executing automation %s", self.entity_id)
self._logger.exception(
"Unexpected error while executing automation %s", self.entity_id
)
automation_trace.set_error(err)
return None
@@ -17,7 +17,7 @@ from homeassistant.helpers.selector import (
TextSelectorType,
)
from .const import DEFAULT_HOST, DOMAIN, SECTION_ADVANCED_SETTINGS
from .const import DEFAULT_HOST, DOMAIN, SECTION_ADDITIONAL_SETTINGS
STEP_USER_DATA_SCHEMA = vol.Schema(
{
@@ -25,7 +25,7 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
vol.Required(CONF_PASSWORD): TextSelector(
TextSelectorConfig(type=TextSelectorType.PASSWORD)
),
vol.Required(SECTION_ADVANCED_SETTINGS): section(
vol.Required(SECTION_ADDITIONAL_SETTINGS): section(
vol.Schema(
{
vol.Required(CONF_HOST, default=DEFAULT_HOST): TextSelector(
@@ -79,7 +79,7 @@ class AutoskopeConfigFlow(ConfigFlow, domain=DOMAIN):
errors: dict[str, str] = {}
if user_input is not None:
username = user_input[CONF_USERNAME].lower()
host = user_input[SECTION_ADVANCED_SETTINGS][CONF_HOST].lower()
host = user_input[SECTION_ADDITIONAL_SETTINGS][CONF_HOST].lower()
try:
cv.url(host)
+1 -1
View File
@@ -5,5 +5,5 @@ from datetime import timedelta
DOMAIN = "autoskope"
DEFAULT_HOST = "https://portal.autoskope.de"
SECTION_ADVANCED_SETTINGS = "advanced_settings"
SECTION_ADDITIONAL_SETTINGS = "additional_settings"
UPDATE_INTERVAL = timedelta(seconds=60)
@@ -31,7 +31,7 @@
},
"description": "Enter your Autoskope credentials.",
"sections": {
"advanced_settings": {
"additional_settings": {
"data": {
"host": "API endpoint"
},
+9 -12
View File
@@ -16,12 +16,9 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_CONNECTIONS,
ATTR_SW_VERSION,
CONCENTRATION_GRAMS_PER_CUBIC_METER,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
UnitOfDensity,
UnitOfRatio,
UnitOfSoundPressure,
UnitOfTemperature,
)
@@ -61,7 +58,7 @@ class AwairSensorEntityDescription(SensorEntityDescription):
SENSOR_TYPE_SCORE = AwairSensorEntityDescription(
key=API_SCORE,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
translation_key="score",
unique_id_tag="score", # matches legacy format
state_class=SensorStateClass.MEASUREMENT,
@@ -71,7 +68,7 @@ SENSOR_TYPES: tuple[AwairSensorEntityDescription, ...] = (
AwairSensorEntityDescription(
key=API_HUMID,
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
unique_id_tag="HUMID", # matches legacy format
state_class=SensorStateClass.MEASUREMENT,
),
@@ -93,7 +90,7 @@ SENSOR_TYPES: tuple[AwairSensorEntityDescription, ...] = (
AwairSensorEntityDescription(
key=API_VOC,
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
unique_id_tag="VOC", # matches legacy format
state_class=SensorStateClass.MEASUREMENT,
),
@@ -107,7 +104,7 @@ SENSOR_TYPES: tuple[AwairSensorEntityDescription, ...] = (
AwairSensorEntityDescription(
key=API_CO2,
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
unique_id_tag="CO2", # matches legacy format
state_class=SensorStateClass.MEASUREMENT,
),
@@ -123,7 +120,7 @@ SENSOR_TYPES: tuple[AwairSensorEntityDescription, ...] = (
AwairSensorEntityDescription(
key=API_ABS_HUMID,
device_class=SensorDeviceClass.ABSOLUTE_HUMIDITY,
native_unit_of_measurement=CONCENTRATION_GRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.GRAMS_PER_CUBIC_METER,
unique_id_tag="absolute_humidity",
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
@@ -134,14 +131,14 @@ SENSOR_TYPES_DUST: tuple[AwairSensorEntityDescription, ...] = (
AwairSensorEntityDescription(
key=API_PM25,
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
unique_id_tag="PM25", # matches legacy format
state_class=SensorStateClass.MEASUREMENT,
),
AwairSensorEntityDescription(
key=API_PM10,
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
unique_id_tag="PM10", # matches legacy format
state_class=SensorStateClass.MEASUREMENT,
),
@@ -1 +0,0 @@
"""The BlinkStick integration."""
@@ -1,87 +0,0 @@
"""Support for BlinkStick lights."""
# mypy: ignore-errors
from typing import Any
# from blinkstick import blinkstick
import voluptuous as vol
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_HS_COLOR,
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
ColorMode,
LightEntity,
)
from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import color as color_util
CONF_SERIAL = "serial"
DEFAULT_NAME = "Blinkstick"
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_SERIAL): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
}
)
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up BlinkStick device specified by serial number."""
name = config[CONF_NAME]
serial = config[CONF_SERIAL]
stick = blinkstick.find_by_serial(serial)
add_entities([BlinkStickLight(stick, name)], True)
class BlinkStickLight(LightEntity):
"""Representation of a BlinkStick light."""
_attr_color_mode = ColorMode.HS
_attr_supported_color_modes = {ColorMode.HS}
def __init__(self, stick, name):
"""Initialize the light."""
self._stick = stick
self._attr_name = name
def update(self) -> None:
"""Read back the device state."""
rgb_color = self._stick.get_color()
hsv = color_util.color_RGB_to_hsv(*rgb_color)
self._attr_hs_color = hsv[:2]
self._attr_brightness = int(hsv[2])
self._attr_is_on = self.brightness is not None and self.brightness > 0
def turn_on(self, **kwargs: Any) -> None:
"""Turn the device on."""
if ATTR_HS_COLOR in kwargs:
self._attr_hs_color = kwargs[ATTR_HS_COLOR]
brightness: int = kwargs.get(ATTR_BRIGHTNESS, 255)
self._attr_brightness = brightness
self._attr_is_on = bool(brightness)
assert self.hs_color
rgb_color = color_util.color_hsv_to_RGB(
self.hs_color[0], self.hs_color[1], brightness / 255 * 100
)
self._stick.set_color(red=rgb_color[0], green=rgb_color[1], blue=rgb_color[2])
def turn_off(self, **kwargs: Any) -> None:
"""Turn the device off."""
self._stick.turn_off()
@@ -1,11 +0,0 @@
{
"domain": "blinksticklight",
"name": "BlinkStick",
"codeowners": [],
"disabled": "This integration is disabled because it uses non-open source code to operate.",
"documentation": "https://www.home-assistant.io/integrations/blinksticklight",
"iot_class": "local_polling",
"loggers": ["blinkstick"],
"quality_scale": "legacy",
"requirements": ["BlinkStick==1.2.0"]
}
@@ -1,5 +0,0 @@
extend = "../../../pyproject.toml"
lint.extend-ignore = [
"F821"
]
+4 -5
View File
@@ -13,10 +13,9 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
UnitOfEnergy,
UnitOfPower,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
@@ -58,13 +57,13 @@ SENSOR_DESCRIPTIONS: dict[str, SHCSensorEntityDescription] = {
HUMIDITY_SENSOR: SHCSensorEntityDescription(
key=HUMIDITY_SENSOR,
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
value_fn=lambda device: device.humidity,
),
PURITY_SENSOR: SHCSensorEntityDescription(
key=PURITY_SENSOR,
translation_key=PURITY_SENSOR,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
value_fn=lambda device: device.purity,
),
AIR_QUALITY_SENSOR: SHCSensorEntityDescription(
@@ -112,7 +111,7 @@ SENSOR_DESCRIPTIONS: dict[str, SHCSensorEntityDescription] = {
key=VALVE_TAPPET_SENSOR,
translation_key=VALVE_TAPPET_SENSOR,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
value_fn=lambda device: device.position,
attributes_fn=lambda device: {
"valve_tappet_state": device.valvestate.name,
+6 -6
View File
@@ -10,12 +10,12 @@ from homeassistant.components.sensor import (
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
PERCENTAGE,
UnitOfDensity,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfPower,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
@@ -37,25 +37,25 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
),
SensorEntityDescription(
key="pm10",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM10,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="pm2_5",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="pm1",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
device_class=SensorDeviceClass.PM1,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key="humidity",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
@@ -8,7 +8,7 @@
"iot_class": "local_polling",
"loggers": ["bsblan"],
"quality_scale": "silver",
"requirements": ["python-bsblan==6.1.3"],
"requirements": ["python-bsblan==6.1.4"],
"zeroconf": [
{
"name": "bsb-lan*",
+9 -10
View File
@@ -18,15 +18,13 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
DEGREE,
LIGHT_LUX,
PERCENTAGE,
REVOLUTIONS_PER_MINUTE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
EntityCategory,
UnitOfConductivity,
UnitOfDensity,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
@@ -34,6 +32,7 @@ from homeassistant.const import (
UnitOfMass,
UnitOfPower,
UnitOfPressure,
UnitOfRatio,
UnitOfSpeed,
UnitOfTemperature,
UnitOfTime,
@@ -63,7 +62,7 @@ SENSOR_DESCRIPTIONS = {
(BTHomeSensorDeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.BATTERY}_{Units.PERCENTAGE}",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
),
@@ -96,7 +95,7 @@ SENSOR_DESCRIPTIONS = {
): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.CO2}_{Units.CONCENTRATION_PARTS_PER_MILLION}",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
# Current (Ampere)
@@ -182,7 +181,7 @@ SENSOR_DESCRIPTIONS = {
(BTHomeSensorDeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.HUMIDITY}_{Units.PERCENTAGE}",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
# Illuminance (lux)
@@ -216,7 +215,7 @@ SENSOR_DESCRIPTIONS = {
(BTHomeSensorDeviceClass.MOISTURE, Units.PERCENTAGE): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.MOISTURE}_{Units.PERCENTAGE}",
device_class=SensorDeviceClass.MOISTURE,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
# Packet Id (-)
@@ -234,7 +233,7 @@ SENSOR_DESCRIPTIONS = {
): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.PM10}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}",
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
# PM2.5 (μg/m3)
@@ -244,7 +243,7 @@ SENSOR_DESCRIPTIONS = {
): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.PM25}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
# Power (Watt)
@@ -357,7 +356,7 @@ SENSOR_DESCRIPTIONS = {
): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
# Voltage (volt)
@@ -1 +0,0 @@
"""The clementine component."""
@@ -1,10 +0,0 @@
{
"domain": "clementine",
"name": "Clementine Music Player",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/clementine",
"iot_class": "local_polling",
"loggers": ["clementineremote"],
"quality_scale": "legacy",
"requirements": ["python-clementine-remote==1.0.1"]
}
@@ -1,166 +0,0 @@
"""Support for Clementine Music Player as media player."""
from datetime import timedelta
import time
from typing import override
from clementineremote import ClementineRemote
import voluptuous as vol
from homeassistant.components.media_player import (
PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
MediaPlayerEntity,
MediaPlayerEntityFeature,
MediaPlayerState,
MediaType,
)
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_HOST, CONF_NAME, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
DEFAULT_NAME = "Clementine Remote"
DEFAULT_PORT = 5500
SCAN_INTERVAL = timedelta(seconds=5)
PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_ACCESS_TOKEN): cv.positive_int,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
}
)
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Clementine platform."""
host = config[CONF_HOST]
port = config[CONF_PORT]
token = config.get(CONF_ACCESS_TOKEN)
client = ClementineRemote(host, port, token, reconnect=True)
add_entities([ClementineDevice(client, config[CONF_NAME])])
class ClementineDevice(MediaPlayerEntity):
"""Representation of Clementine Player."""
_attr_media_content_type = MediaType.MUSIC
_attr_supported_features = (
MediaPlayerEntityFeature.PAUSE
| MediaPlayerEntityFeature.VOLUME_STEP
| MediaPlayerEntityFeature.PREVIOUS_TRACK
| MediaPlayerEntityFeature.VOLUME_SET
| MediaPlayerEntityFeature.NEXT_TRACK
| MediaPlayerEntityFeature.SELECT_SOURCE
| MediaPlayerEntityFeature.PLAY
)
_attr_volume_step = 0.04
def __init__(self, client, name):
"""Initialize the Clementine device."""
self._client = client
self._attr_name = name
def update(self) -> None:
"""Retrieve the latest data from the Clementine Player."""
try:
client = self._client
if client.state == "Playing":
self._attr_state = MediaPlayerState.PLAYING
elif client.state == "Paused":
self._attr_state = MediaPlayerState.PAUSED
elif client.state == "Disconnected":
self._attr_state = MediaPlayerState.OFF
else:
self._attr_state = MediaPlayerState.PAUSED
if client.last_update and (time.time() - client.last_update > 40):
self._attr_state = MediaPlayerState.OFF
volume = float(client.volume) if client.volume else 0.0
self._attr_volume_level = volume / 100.0
if client.active_playlist_id in client.playlists:
self._attr_source = client.playlists[client.active_playlist_id]["name"]
else:
self._attr_source = "Unknown"
self._attr_source_list = [s["name"] for s in client.playlists.values()]
if client.current_track:
self._attr_media_title = client.current_track["title"]
self._attr_media_artist = client.current_track["track_artist"]
self._attr_media_album_name = client.current_track["track_album"]
self._attr_media_image_hash = client.current_track["track_id"]
else:
self._attr_media_image_hash = None
except Exception:
self._attr_state = MediaPlayerState.OFF
raise
@override
def select_source(self, source: str) -> None:
"""Select input source."""
client = self._client
sources = [s for s in client.playlists.values() if s["name"] == source]
if len(sources) == 1:
client.change_song(sources[0]["id"], 0)
@override
async def async_get_media_image(self) -> tuple[bytes | None, str | None]:
"""Fetch media image of current playing image."""
if self._client.current_track:
image = bytes(self._client.current_track["art"])
return (image, "image/png")
return None, None
@override
def mute_volume(self, mute: bool) -> None:
"""Send mute command."""
self._client.set_volume(0)
@override
def set_volume_level(self, volume: float) -> None:
"""Set volume level."""
self._client.set_volume(int(100 * volume))
def media_play_pause(self) -> None:
"""Simulate play pause media player."""
if self.state == MediaPlayerState.PLAYING:
self.media_pause()
else:
self.media_play()
@override
def media_play(self) -> None:
"""Send play command."""
self._attr_state = MediaPlayerState.PLAYING
self._client.play()
@override
def media_pause(self) -> None:
"""Send media pause command to media player."""
self._attr_state = MediaPlayerState.PAUSED
self._client.pause()
@override
def media_next_track(self) -> None:
"""Send next track command."""
self._client.next()
@override
def media_previous_track(self) -> None:
"""Send the previous track command."""
self._client.previous()
+1 -1
View File
@@ -42,8 +42,8 @@ from openai.types.responses.response_input_param import (
ImageGenerationCall as ImageGenerationCallParam,
)
from openai.types.responses.response_output_item import ImageGenerationCall
from probatio import to_openapi as convert
import voluptuous as vol
from voluptuous_openapi import convert
from homeassistant.components import conversation
from homeassistant.config_entries import ConfigEntry
+11 -12
View File
@@ -13,12 +13,11 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
UnitOfDensity,
UnitOfElectricCurrent,
UnitOfEnergy,
UnitOfPower,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
@@ -117,7 +116,7 @@ DESCRIPTIONS: dict[CompitParameter, SensorEntityDescription] = {
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
),
CompitParameter.BOILER_TEMPERATURE: SensorEntityDescription(
key=CompitParameter.BOILER_TEMPERATURE.value,
@@ -204,14 +203,14 @@ DESCRIPTIONS: dict[CompitParameter, SensorEntityDescription] = {
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
),
CompitParameter.CO2_PERCENT: SensorEntityDescription(
key=CompitParameter.CO2_PERCENT.value,
translation_key="co2_percent",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
),
CompitParameter.COLLECTOR_POWER: SensorEntityDescription(
key=CompitParameter.COLLECTOR_POWER.value,
@@ -290,7 +289,7 @@ DESCRIPTIONS: dict[CompitParameter, SensorEntityDescription] = {
translation_key="fuel_level",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
),
CompitParameter.HEATING1_TARGET_TEMPERATURE: SensorEntityDescription(
key=CompitParameter.HEATING1_TARGET_TEMPERATURE.value,
@@ -333,7 +332,7 @@ DESCRIPTIONS: dict[CompitParameter, SensorEntityDescription] = {
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
),
CompitParameter.LOWER_SOURCE_TEMPERATURE: SensorEntityDescription(
key=CompitParameter.LOWER_SOURCE_TEMPERATURE.value,
@@ -401,14 +400,14 @@ DESCRIPTIONS: dict[CompitParameter, SensorEntityDescription] = {
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
),
CompitParameter.PM4_LEVEL_MEASURED: SensorEntityDescription(
key=CompitParameter.PM4_LEVEL_MEASURED.value,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.PM4,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
),
CompitParameter.PM10_LEVEL: SensorEntityDescription(
key=CompitParameter.PM10_LEVEL.value,
@@ -422,7 +421,7 @@ DESCRIPTIONS: dict[CompitParameter, SensorEntityDescription] = {
key=CompitParameter.PM10_MEASURED.value,
device_class=SensorDeviceClass.PM10,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
),
CompitParameter.PM25_LEVEL: SensorEntityDescription(
key=CompitParameter.PM25_LEVEL.value,
@@ -436,7 +435,7 @@ DESCRIPTIONS: dict[CompitParameter, SensorEntityDescription] = {
key=CompitParameter.PM25_MEASURED.value,
device_class=SensorDeviceClass.PM25,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
),
CompitParameter.PROTECTION_TEMPERATURE: SensorEntityDescription(
key=CompitParameter.PROTECTION_TEMPERATURE.value,
@@ -805,6 +805,10 @@ class DefaultAgent(ConversationEntity):
else:
num_unmatched_entities += 1
# Literal text matched is the dominant signal
same_text_matched = (maybe_result is not None) and (
result.text_chunks_matched == maybe_result.text_chunks_matched
)
if (
(maybe_result is None) # first result
or (
@@ -813,22 +817,25 @@ class DefaultAgent(ConversationEntity):
)
or (
# More entities matched
num_matched_entities > best_num_matched_entities
same_text_matched
and (num_matched_entities > best_num_matched_entities)
)
or (
# Fewer unmatched entities
(num_matched_entities == best_num_matched_entities)
same_text_matched
and (num_matched_entities == best_num_matched_entities)
and (num_unmatched_entities < best_num_unmatched_entities)
)
or (
# Prefer unmatched ranges
(num_matched_entities == best_num_matched_entities)
same_text_matched
and (num_matched_entities == best_num_matched_entities)
and (num_unmatched_entities == best_num_unmatched_entities)
and (num_unmatched_ranges > best_num_unmatched_ranges)
)
or (
# Prefer match failures with entities
(result.text_chunks_matched == maybe_result.text_chunks_matched)
same_text_matched
and (num_unmatched_entities == best_num_unmatched_entities)
and (num_unmatched_ranges == best_num_unmatched_ranges)
and (
@@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/conversation",
"integration_type": "entity",
"quality_scale": "internal",
"requirements": ["hassil==3.8.0", "home-assistant-intents==2026.6.1"]
"requirements": ["hassil==3.8.0", "home-assistant-intents==2026.6.24"]
}
+12 -14
View File
@@ -35,15 +35,13 @@ from homeassistant.components.sensor import (
from homeassistant.const import (
ATTR_TEMPERATURE,
ATTR_VOLTAGE,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
EntityCategory,
UnitOfDensity,
UnitOfEnergy,
UnitOfPower,
UnitOfPressure,
UnitOfRatio,
UnitOfTemperature,
UnitOfTime,
)
@@ -138,7 +136,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = (
name_suffix="PPB",
old_unique_id_suffix="ppb",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
),
DeconzSensorDescription[AirQuality](
key="air_quality_formaldehyde",
@@ -149,7 +147,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = (
name_suffix="CH2O",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
),
DeconzSensorDescription[AirQuality](
key="air_quality_co2",
@@ -160,7 +158,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = (
name_suffix="CO2",
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
),
DeconzSensorDescription[AirQuality](
key="air_quality_pm2_5",
@@ -171,7 +169,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = (
name_suffix="PM25",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
),
DeconzSensorDescription[CarbonDioxide](
key="carbon_dioxide",
@@ -181,7 +179,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = (
instance_check=CarbonDioxide,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
),
DeconzSensorDescription[Consumption](
key="consumption",
@@ -210,7 +208,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = (
instance_check=Formaldehyde,
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
),
DeconzSensorDescription[GenericStatus](
key="status",
@@ -227,7 +225,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = (
instance_check=Humidity,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
suggested_display_precision=1,
),
DeconzSensorDescription[LightLevel](
@@ -248,7 +246,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = (
instance_check=Moisture,
device_class=SensorDeviceClass.MOISTURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
suggested_display_precision=1,
),
DeconzSensorDescription[ParticulateMatter](
@@ -260,7 +258,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = (
name_suffix="PM25",
device_class=SensorDeviceClass.PM25,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
),
DeconzSensorDescription[Power](
key="power",
@@ -310,7 +308,7 @@ ENTITY_DESCRIPTIONS: tuple[DeconzSensorDescription, ...] = (
old_unique_id_suffix="battery",
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
DeconzSensorDescription[SensorResources](
+6 -7
View File
@@ -12,11 +12,10 @@ from homeassistant.components.sensor import (
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
EntityCategory,
UnitOfEnergy,
UnitOfPower,
UnitOfRatio,
UnitOfTemperature,
UnitOfVolume,
)
@@ -52,7 +51,7 @@ async def async_setup_entry(
12,
SensorDeviceClass.BATTERY,
SensorStateClass.MEASUREMENT,
PERCENTAGE,
UnitOfRatio.PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_name="Battery",
),
@@ -63,7 +62,7 @@ async def async_setup_entry(
54,
SensorDeviceClass.HUMIDITY,
SensorStateClass.MEASUREMENT,
PERCENTAGE,
UnitOfRatio.PERCENTAGE,
),
DemoSensor(
"sensor_3",
@@ -72,7 +71,7 @@ async def async_setup_entry(
54,
SensorDeviceClass.CO,
SensorStateClass.MEASUREMENT,
CONCENTRATION_PARTS_PER_MILLION,
UnitOfRatio.PARTS_PER_MILLION,
),
DemoSensor(
"sensor_4",
@@ -81,7 +80,7 @@ async def async_setup_entry(
54,
SensorDeviceClass.CO2,
SensorStateClass.MEASUREMENT,
CONCENTRATION_PARTS_PER_MILLION,
UnitOfRatio.PARTS_PER_MILLION,
),
DemoSensor(
"battery_4",
@@ -90,7 +89,7 @@ async def async_setup_entry(
99,
SensorDeviceClass.BATTERY,
SensorStateClass.MEASUREMENT,
PERCENTAGE,
UnitOfRatio.PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_name="Battery",
),
@@ -9,8 +9,8 @@ import logging
from types import ModuleType
from typing import TYPE_CHECKING, Any, Literal, overload
from probatio import serialize
import voluptuous as vol
import voluptuous_serialize
from homeassistant.components import websocket_api
from homeassistant.components.websocket_api import ActiveConnection
@@ -318,7 +318,7 @@ async def _async_get_device_automation_capabilities(
if (extra_fields := capabilities.get("extra_fields")) is None:
capabilities["extra_fields"] = []
else:
capabilities["extra_fields"] = voluptuous_serialize.convert(
capabilities["extra_fields"] = serialize(
extra_fields, custom_serializer=cv.custom_serializer
)
@@ -20,7 +20,7 @@ from homeassistant.data_entry_flow import SectionConfig, section
from homeassistant.helpers import config_validation as cv
from .const import (
CONF_ADVANCED_OPTIONS,
CONF_ADDITIONAL_OPTIONS,
CONF_HOSTNAME,
CONF_IPV4,
CONF_IPV6,
@@ -39,7 +39,7 @@ from .const import (
DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOSTNAME, default=DEFAULT_HOSTNAME): cv.string,
vol.Required(CONF_ADVANCED_OPTIONS): section(
vol.Required(CONF_ADDITIONAL_OPTIONS): section(
vol.Schema(
{
vol.Optional(CONF_RESOLVER): cv.string,
@@ -117,13 +117,13 @@ class DnsIPConfigFlow(ConfigFlow, domain=DOMAIN):
if user_input:
hostname = user_input[CONF_HOSTNAME]
name = DEFAULT_NAME if hostname == DEFAULT_HOSTNAME else hostname
advanced_options = user_input[CONF_ADVANCED_OPTIONS]
resolver = advanced_options.get(CONF_RESOLVER, DEFAULT_RESOLVER)
resolver_ipv6 = advanced_options.get(
additional_options = user_input[CONF_ADDITIONAL_OPTIONS]
resolver = additional_options.get(CONF_RESOLVER, DEFAULT_RESOLVER)
resolver_ipv6 = additional_options.get(
CONF_RESOLVER_IPV6, DEFAULT_RESOLVER_IPV6
)
port = advanced_options.get(CONF_PORT, DEFAULT_PORT)
port_ipv6 = advanced_options.get(CONF_PORT_IPV6, DEFAULT_PORT)
port = additional_options.get(CONF_PORT, DEFAULT_PORT)
port_ipv6 = additional_options.get(CONF_PORT_IPV6, DEFAULT_PORT)
validate = await async_validate_hostname(
hostname, resolver, resolver_ipv6, port, port_ipv6
+1 -1
View File
@@ -12,7 +12,7 @@ CONF_PORT_IPV6 = "port_ipv6"
CONF_IPV4 = "ipv4"
CONF_IPV6 = "ipv6"
CONF_IPV6_V4 = "ipv6_v4"
CONF_ADVANCED_OPTIONS = "advanced_options"
CONF_ADDITIONAL_OPTIONS = "additional_options"
DEFAULT_HOSTNAME = "myip.opendns.com"
DEFAULT_IPV6 = False
+9 -9
View File
@@ -15,7 +15,7 @@
"hostname": "The hostname for which to perform the DNS query."
},
"sections": {
"advanced_options": {
"additional_options": {
"data": {
"port": "IPv4 port",
"port_ipv6": "IPv6 port",
@@ -63,16 +63,16 @@
"step": {
"init": {
"data": {
"port": "[%key:component::dnsip::config::step::user::sections::advanced_options::data::port%]",
"port_ipv6": "[%key:component::dnsip::config::step::user::sections::advanced_options::data::port_ipv6%]",
"resolver": "[%key:component::dnsip::config::step::user::sections::advanced_options::data::resolver%]",
"resolver_ipv6": "[%key:component::dnsip::config::step::user::sections::advanced_options::data::resolver_ipv6%]"
"port": "[%key:component::dnsip::config::step::user::sections::additional_options::data::port%]",
"port_ipv6": "[%key:component::dnsip::config::step::user::sections::additional_options::data::port_ipv6%]",
"resolver": "[%key:component::dnsip::config::step::user::sections::additional_options::data::resolver%]",
"resolver_ipv6": "[%key:component::dnsip::config::step::user::sections::additional_options::data::resolver_ipv6%]"
},
"data_description": {
"port": "[%key:component::dnsip::config::step::user::sections::advanced_options::data_description::port%]",
"port_ipv6": "[%key:component::dnsip::config::step::user::sections::advanced_options::data_description::port_ipv6%]",
"resolver": "[%key:component::dnsip::config::step::user::sections::advanced_options::data_description::resolver%]",
"resolver_ipv6": "[%key:component::dnsip::config::step::user::sections::advanced_options::data_description::resolver_ipv6%]"
"port": "[%key:component::dnsip::config::step::user::sections::additional_options::data_description::port%]",
"port_ipv6": "[%key:component::dnsip::config::step::user::sections::additional_options::data_description::port_ipv6%]",
"resolver": "[%key:component::dnsip::config::step::user::sections::additional_options::data_description::resolver%]",
"resolver_ipv6": "[%key:component::dnsip::config::step::user::sections::additional_options::data_description::resolver_ipv6%]"
},
"description": "Optionally change resolvers and ports."
}
@@ -1,87 +0,0 @@
"""Support for Dovado router."""
# mypy: ignore-errors
from datetime import timedelta
import logging
# import dovado
import voluptuous as vol
from homeassistant.const import (
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_USERNAME,
DEVICE_DEFAULT_NAME,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
DOMAIN = "dovado"
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_HOST): cv.string,
vol.Optional(CONF_PORT): cv.port,
}
)
},
extra=vol.ALLOW_EXTRA,
)
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Dovado component."""
hass.data[DOMAIN] = DovadoData(
dovado.Dovado(
config[DOMAIN][CONF_USERNAME],
config[DOMAIN][CONF_PASSWORD],
config[DOMAIN].get(CONF_HOST),
config[DOMAIN].get(CONF_PORT),
)
)
return True
class DovadoData:
"""Maintain a connection to the router."""
def __init__(self, client):
"""Set up a new Dovado connection."""
self._client = client
self.state = {}
@property
def name(self):
"""Name of the router."""
return self.state.get("product name", DEVICE_DEFAULT_NAME)
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Update device state."""
try:
self.state = self._client.state or {}
if not self.state:
return False
self.state.update(connected=self.state.get("modem status") == "CONNECTED")
except OSError as error:
_LOGGER.warning("Could not contact the router: %s", error)
return None
_LOGGER.debug("Received: %s", self.state)
return True
@property
def client(self):
"""Dovado client instance."""
return self._client
@@ -1,10 +0,0 @@
{
"domain": "dovado",
"name": "Dovado",
"codeowners": [],
"disabled": "This integration is disabled because it uses non-open source code to operate.",
"documentation": "https://www.home-assistant.io/integrations/dovado",
"iot_class": "local_polling",
"quality_scale": "legacy",
"requirements": ["dovado==0.4.1"]
}
-38
View File
@@ -1,38 +0,0 @@
"""Support for SMS notifications from the Dovado router."""
import logging
from typing import Any, override
from homeassistant.components.notify import ATTR_TARGET, BaseNotificationService
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import DOMAIN
_LOGGER = logging.getLogger(__name__)
def get_service(
hass: HomeAssistant,
config: ConfigType,
discovery_info: DiscoveryInfoType | None = None,
) -> DovadoSMSNotificationService:
"""Get the Dovado Router SMS notification service."""
return DovadoSMSNotificationService(hass.data[DOMAIN].client)
class DovadoSMSNotificationService(BaseNotificationService):
"""Implement the notification service for the Dovado SMS component."""
def __init__(self, client):
"""Initialize the service."""
self._client = client
@override
def send_message(self, message: str, **kwargs: Any) -> None:
"""Send SMS to the specified target phone number."""
if not (target := kwargs.get(ATTR_TARGET)):
_LOGGER.error("One target is required")
return
self._client.send_sms(target, message)
@@ -1,5 +0,0 @@
extend = "../../../pyproject.toml"
lint.extend-ignore = [
"F821"
]
-143
View File
@@ -1,143 +0,0 @@
"""Support for sensors from the Dovado router."""
from dataclasses import dataclass
from datetime import timedelta
import re
from typing import Any, override
import voluptuous as vol
from homeassistant.components.sensor import (
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
)
from homeassistant.const import CONF_SENSORS, PERCENTAGE, UnitOfInformation
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import DOMAIN
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
SENSOR_UPLOAD = "upload"
SENSOR_DOWNLOAD = "download"
SENSOR_SIGNAL = "signal"
SENSOR_NETWORK = "network"
SENSOR_SMS_UNREAD = "sms"
@dataclass(frozen=True, kw_only=True)
class DovadoSensorEntityDescription(SensorEntityDescription):
"""Describes Dovado sensor entity."""
identifier: str
SENSOR_TYPES: tuple[DovadoSensorEntityDescription, ...] = (
DovadoSensorEntityDescription(
identifier=SENSOR_NETWORK,
key="signal strength",
name="Network",
icon="mdi:access-point-network",
),
DovadoSensorEntityDescription(
identifier=SENSOR_SIGNAL,
key="signal strength",
name="Signal Strength",
native_unit_of_measurement=PERCENTAGE,
icon="mdi:signal",
),
DovadoSensorEntityDescription(
identifier=SENSOR_SMS_UNREAD,
key="sms unread",
name="SMS unread",
icon="mdi:message-text-outline",
),
DovadoSensorEntityDescription(
identifier=SENSOR_UPLOAD,
key="traffic modem tx",
name="Sent",
native_unit_of_measurement=UnitOfInformation.GIGABYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:cloud-upload",
),
DovadoSensorEntityDescription(
identifier=SENSOR_DOWNLOAD,
key="traffic modem rx",
name="Received",
native_unit_of_measurement=UnitOfInformation.GIGABYTES,
device_class=SensorDeviceClass.DATA_SIZE,
icon="mdi:cloud-download",
),
)
SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
{vol.Required(CONF_SENSORS): vol.All(cv.ensure_list, [vol.In(SENSOR_KEYS)])}
)
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Dovado sensor platform."""
dovado = hass.data[DOMAIN]
sensors = config[CONF_SENSORS]
entities = [
DovadoSensor(dovado, description)
for description in SENSOR_TYPES
if description.key in sensors
]
add_entities(entities)
class DovadoSensor(SensorEntity):
"""Representation of a Dovado sensor."""
entity_description: DovadoSensorEntityDescription
def __init__(self, data, description: DovadoSensorEntityDescription) -> None:
"""Initialize the sensor."""
self.entity_description = description
self._data = data
self._attr_name = f"{data.name} {description.name}"
self._attr_native_value = self._compute_state()
def _compute_state(self):
"""Compute the state of the sensor."""
state = self._data.state.get(self.entity_description.key)
sensor_identifier = self.entity_description.identifier
if sensor_identifier == SENSOR_NETWORK:
match = re.search(r"\((.+)\)", state)
return match.group(1) if match else None
if sensor_identifier == SENSOR_SIGNAL:
try:
return int(state.split()[0])
except ValueError:
return None
if sensor_identifier == SENSOR_SMS_UNREAD:
return int(state)
if sensor_identifier in [SENSOR_UPLOAD, SENSOR_DOWNLOAD]:
return round(float(state) / 1e6, 1)
return state
def update(self) -> None:
"""Update sensor values."""
self._data.update()
self._attr_native_value = self._compute_state()
@property
@override
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
return {k: v for k, v in self._data.state.items() if k not in ["date", "time"]}
@@ -12,11 +12,10 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
TEMPERATURE,
EntityCategory,
UnitOfPressure,
UnitOfRatio,
UnitOfTemperature,
UnitOfVolume,
UnitOfVolumeFlowRate,
@@ -141,7 +140,7 @@ SENSORS: list[DROPSensorEntityDescription] = [
DROPSensorEntityDescription(
key=BATTERY,
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
suggested_display_precision=0,
value_fn=lambda device: device.drop_api.battery(),
state_class=SensorStateClass.MEASUREMENT,
@@ -158,7 +157,7 @@ SENSORS: list[DROPSensorEntityDescription] = [
DROPSensorEntityDescription(
key=INLET_TDS,
translation_key=INLET_TDS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
value_fn=lambda device: device.drop_api.inlet_tds(),
@@ -166,7 +165,7 @@ SENSORS: list[DROPSensorEntityDescription] = [
DROPSensorEntityDescription(
key=OUTLET_TDS,
translation_key=OUTLET_TDS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
value_fn=lambda device: device.drop_api.outlet_tds(),
@@ -174,7 +173,7 @@ SENSORS: list[DROPSensorEntityDescription] = [
DROPSensorEntityDescription(
key=CARTRIDGE_1_LIFE,
translation_key=CARTRIDGE_1_LIFE,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=0,
@@ -183,7 +182,7 @@ SENSORS: list[DROPSensorEntityDescription] = [
DROPSensorEntityDescription(
key=CARTRIDGE_2_LIFE,
translation_key=CARTRIDGE_2_LIFE,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=0,
@@ -192,7 +191,7 @@ SENSORS: list[DROPSensorEntityDescription] = [
DROPSensorEntityDescription(
key=CARTRIDGE_3_LIFE,
translation_key=CARTRIDGE_3_LIFE,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
suggested_display_precision=0,
+14
View File
@@ -2,9 +2,23 @@
from datetime import timedelta
from duco_connectivity.models import NodeType
from homeassistant.const import Platform
DOMAIN = "duco"
PLATFORMS = [Platform.FAN, Platform.SELECT, Platform.SENSOR]
SCAN_INTERVAL = timedelta(seconds=10)
BOX_NODE_ID = 1
VENTILATION_CAPABLE_NODE_TYPES: tuple[NodeType, ...] = (
NodeType.BOX,
NodeType.VLV,
NodeType.VLVRH,
NodeType.VLVVOC,
NodeType.VLVCO2,
NodeType.VLVCO2RH,
NodeType.EAV,
NodeType.EAVRH,
NodeType.EAVVOC,
NodeType.EAVCO2,
)
+4 -3
View File
@@ -10,7 +10,6 @@ from duco_connectivity import (
KnownActionName,
Node,
NodeListActionItemList,
NodeType,
VentilationState,
)
@@ -19,7 +18,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN
from .const import DOMAIN, VENTILATION_CAPABLE_NODE_TYPES
from .coordinator import DucoConfigEntry, DucoCoordinator
from .entity import DucoEntity
@@ -71,7 +70,9 @@ async def async_setup_entry(
if node.node_id in known_nodes:
continue
if node.general.node_type is not NodeType.BOX:
# Duco advertises SetVentilationState broadly, so keep the select
# limited to the box and known valve node families.
if node.general.node_type not in VENTILATION_CAPABLE_NODE_TYPES:
continue
options = options_by_node.get(node.node_id)
+4 -4
View File
@@ -25,7 +25,7 @@ from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import dt as dt_util
from .const import BOX_NODE_ID, DOMAIN
from .const import BOX_NODE_ID, DOMAIN, VENTILATION_CAPABLE_NODE_TYPES
from .coordinator import DucoConfigEntry, DucoCoordinator
from .entity import DucoEntity
@@ -65,7 +65,7 @@ SENSOR_DESCRIPTIONS: tuple[DucoSensorEntityDescription, ...] = (
if node.ventilation and node.ventilation.state != VentilationState.UNKNOWN
else None
),
node_types=(NodeType.BOX,),
node_types=VENTILATION_CAPABLE_NODE_TYPES,
),
DucoSensorEntityDescription(
key="target_flow_level",
@@ -76,7 +76,7 @@ SENSOR_DESCRIPTIONS: tuple[DucoSensorEntityDescription, ...] = (
value_fn=lambda node: (
node.ventilation.flow_lvl_tgt if node.ventilation else None
),
node_types=(NodeType.BOX,),
node_types=VENTILATION_CAPABLE_NODE_TYPES,
),
DucoSensorEntityDescription(
key="time_state_end",
@@ -89,7 +89,7 @@ SENSOR_DESCRIPTIONS: tuple[DucoSensorEntityDescription, ...] = (
if node.ventilation and node.ventilation.time_state_end != 0
else None
),
node_types=(NodeType.BOX,),
node_types=VENTILATION_CAPABLE_NODE_TYPES,
),
DucoSensorEntityDescription(
key="co2",
+4 -9
View File
@@ -11,12 +11,7 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
UnitOfTemperature,
)
from homeassistant.const import UnitOfDensity, UnitOfRatio, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -42,14 +37,14 @@ SENSOR_TYPES: tuple[EcobeeSensorEntityDescription, ...] = (
),
EcobeeSensorEntityDescription(
key="humidity",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
runtime_key=None,
),
EcobeeSensorEntityDescription(
key="co2PPM",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
runtime_key="actualCO2",
@@ -57,7 +52,7 @@ SENSOR_TYPES: tuple[EcobeeSensorEntityDescription, ...] = (
EcobeeSensorEntityDescription(
key="vocPPM",
device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
runtime_key="actualVOC",
),
+11 -12
View File
@@ -14,18 +14,17 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
DEGREE,
LIGHT_LUX,
PERCENTAGE,
UV_INDEX,
EntityCategory,
UnitOfDensity,
UnitOfElectricPotential,
UnitOfIrradiance,
UnitOfLength,
UnitOfPrecipitationDepth,
UnitOfPressure,
UnitOfRatio,
UnitOfSpeed,
UnitOfTemperature,
UnitOfVolumetricFlux,
@@ -99,7 +98,7 @@ ECOWITT_SENSORS_MAPPING: Final = {
EcoWittSensorTypes.HUMIDITY: SensorEntityDescription(
key="HUMIDITY",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.DEGREE: SensorEntityDescription(
@@ -122,19 +121,19 @@ ECOWITT_SENSORS_MAPPING: Final = {
EcoWittSensorTypes.PM25: SensorEntityDescription(
key="PM25",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.PM10: SensorEntityDescription(
key="PM10",
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.BATTERY_PERCENTAGE: SensorEntityDescription(
key="BATTERY_PERCENTAGE",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
),
@@ -149,7 +148,7 @@ ECOWITT_SENSORS_MAPPING: Final = {
EcoWittSensorTypes.CO2_PPM: SensorEntityDescription(
key="CO2_PPM",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.LUX: SensorEntityDescription(
@@ -263,13 +262,13 @@ ECOWITT_SENSORS_MAPPING: Final = {
),
EcoWittSensorTypes.PERCENTAGE: SensorEntityDescription(
key="PERCENTAGE",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.SOIL_MOISTURE: SensorEntityDescription(
key="SOIL_MOISTURE",
device_class=SensorDeviceClass.MOISTURE,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.DISTANCE_MM: SensorEntityDescription(
@@ -286,13 +285,13 @@ ECOWITT_SENSORS_MAPPING: Final = {
EcoWittSensorTypes.PM1: SensorEntityDescription(
key="PM1",
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.PM4: SensorEntityDescription(
key="PM4",
device_class=SensorDeviceClass.PM4,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
}
+5 -6
View File
@@ -9,17 +9,16 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
CONF_URL,
PERCENTAGE,
UnitOfApparentPower,
UnitOfDensity,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfFrequency,
UnitOfPower,
UnitOfPressure,
UnitOfRatio,
UnitOfSoundPressure,
UnitOfSpeed,
UnitOfTemperature,
@@ -158,19 +157,19 @@ SENSORS: dict[str | None, SensorEntityDescription] = {
"μg/m³": SensorEntityDescription(
key="concentration|microgram_per_cubic_meter",
translation_key="concentration",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
"ppm": SensorEntityDescription(
key="concentration|microgram_parts_per_million",
translation_key="concentration",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
"%": SensorEntityDescription(
key="percent",
translation_key="percent",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
}
@@ -65,6 +65,14 @@ async def _get_fixture_collection(envoy: Envoy, serial: str) -> dict[str, Any]:
"/ivp/meters/readings",
"/ivp/pdm/device_data",
"/home",
"/inventory.json?deleted=1",
"/admin/lib/acb_config",
"/ivp/sc/sched",
"/admin/lib/network_display",
"/admin/lib/wireless_display",
"/ivp/ensemble/relay",
"/ivp/livedata/status",
"/ivp/pdm/energy",
]
for end_point in end_points:
@@ -134,16 +142,15 @@ async def async_get_config_entry_diagnostics(
"encharge_power": envoy_data.encharge_power,
"encharge_aggregate": envoy_data.encharge_aggregate,
"enpower": envoy_data.enpower,
"acb_power": envoy_data.acb_power,
"acb_inventory": envoy_data.acb_inventory,
"battery_aggregate": envoy_data.battery_aggregate,
"collar": envoy_data.collar,
"c6cc": envoy_data.c6cc,
"system_consumption": envoy_data.system_consumption,
"system_production": envoy_data.system_production,
"system_consumption_phases": envoy_data.system_consumption_phases,
"system_production_phases": envoy_data.system_production_phases,
"ctmeter_production": envoy_data.ctmeter_production,
"ctmeter_consumption": envoy_data.ctmeter_consumption,
"ctmeter_storage": envoy_data.ctmeter_storage,
"ctmeter_production_phases": envoy_data.ctmeter_production_phases,
"ctmeter_consumption_phases": envoy_data.ctmeter_consumption_phases,
"ctmeter_storage_phases": envoy_data.ctmeter_storage_phases,
"ctmeters": envoy_data.ctmeters,
"ctmeters_phases": envoy_data.ctmeters_phases,
"dry_contact_status": envoy_data.dry_contact_status,
+3 -8
View File
@@ -8,12 +8,7 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
UnitOfPressure,
UnitOfTemperature,
)
from homeassistant.const import UnitOfPressure, UnitOfRatio, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -26,7 +21,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
key="co2",
suggested_display_precision=0,
),
@@ -40,7 +35,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
key="humidity",
suggested_display_precision=1,
),
@@ -17,7 +17,7 @@
"mqtt": ["esphome/discover/#"],
"quality_scale": "platinum",
"requirements": [
"aioesphomeapi==45.3.1",
"aioesphomeapi==45.5.2",
"esphome-dashboard-api==1.3.0",
"bleak-esphome==3.9.4"
],
@@ -52,9 +52,7 @@ __all__ = [
"DoorbellEventType",
"EventDeviceClass",
"EventEntity",
"EventEntityCapabilityAttribute",
"EventEntityDescription",
"EventEntityStateAttribute",
]
# mypy: disallow-any-generics
+4 -5
View File
@@ -13,12 +13,11 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
Platform,
UnitOfEnergy,
UnitOfPower,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
@@ -39,20 +38,20 @@ MAIN_SENSOR_TYPES: dict[str, SensorEntityDescription] = {
"com.fibaro.smokeSensor": SensorEntityDescription(
key="com.fibaro.smokeSensor",
name="Smoke",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
icon="mdi:fire",
),
"CO2": SensorEntityDescription(
key="CO2",
name="CO2",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
device_class=SensorDeviceClass.CO2,
state_class=SensorStateClass.MEASUREMENT,
),
"com.fibaro.humiditySensor": SensorEntityDescription(
key="com.fibaro.humiditySensor",
name="Humidity",
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
),
+7 -9
View File
@@ -16,12 +16,10 @@ from homeassistant.components.sensor import (
)
from homeassistant.const import (
ATTR_TEMPERATURE,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
CONF_TOKEN,
CONF_USERNAME,
PERCENTAGE,
UnitOfDensity,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
@@ -44,7 +42,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
key="pm",
name=ATTR_PM2_5,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
icon="mdi:cloud",
),
SensorEntityDescription(
@@ -56,25 +54,25 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
key="hum",
name=ATTR_HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
icon="mdi:water-percent",
),
SensorEntityDescription(
key="co2",
name=ATTR_CARBON_DIOXIDE,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
icon="mdi:molecule-co2",
),
SensorEntityDescription(
key="voc",
name=ATTR_VOLATILE_ORGANIC_COMPOUNDS,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_BILLION,
icon="mdi:cloud",
),
SensorEntityDescription(
key="allpollu",
name=ATTR_FOOBOT_INDEX,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
icon="mdi:percent",
),
)
+13 -1
View File
@@ -126,6 +126,8 @@ class FreeboxRouter:
self.raids: dict[int, dict[str, Any]] = {}
self.sensors_temperature: dict[str, int] = {}
self.sensors_temperature_names: dict[str, str] = {}
self.sensors_fan: dict[str, int] = {}
self.sensors_fan_names: dict[str, str] = {}
self.sensors_connection: dict[str, float] = {}
self.call_list: list[dict[str, Any]] = []
self.home_granted = True
@@ -190,6 +192,12 @@ class FreeboxRouter:
self.sensors_temperature[sensor_id] = sensor.get("value")
self.sensors_temperature_names[sensor_id] = sensor["name"]
# Fan speed sensors (rpm). Name and id may vary under Freebox devices.
for fan in syst_datas.get("fans", []):
fan_id = fan["id"]
self.sensors_fan[fan_id] = fan.get("value")
self.sensors_fan_names[fan_id] = fan["name"]
# Connection sensors
connection_datas: dict[str, Any] = await self._api.connection.get_status()
for sensor_key in CONNECTION_SENSORS_KEYS:
@@ -321,7 +329,11 @@ class FreeboxRouter:
@property
def sensors(self) -> dict[str, Any]:
"""Return sensors."""
return {**self.sensors_temperature, **self.sensors_connection}
return {
**self.sensors_temperature,
**self.sensors_fan,
**self.sensors_connection,
}
@property
def call(self) -> Call:
@@ -11,6 +11,7 @@ from homeassistant.components.sensor import (
)
from homeassistant.const import (
PERCENTAGE,
REVOLUTIONS_PER_MINUTE,
EntityCategory,
UnitOfDataRate,
UnitOfTemperature,
@@ -93,6 +94,27 @@ async def async_setup_entry(
for sensor_id, sensor_name in router.sensors_temperature_names.items()
]
_LOGGER.debug(
"%s - %s - %s fan sensors",
router.name,
router.mac,
len(router.sensors_fan_names),
)
entities.extend(
FreeboxSensor(
router,
SensorEntityDescription(
key=fan_id,
name=fan_name,
native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
icon="mdi:fan",
),
)
for fan_id, fan_name in router.sensors_fan_names.items()
)
entities.extend(
[FreeboxSensor(router, description) for description in CONNECTION_SENSORS]
)
+3 -8
View File
@@ -13,12 +13,7 @@ from homeassistant.components.sensor import (
SensorStateClass,
StateType,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
UnitOfTemperature,
UnitOfVolumeFlowRate,
)
from homeassistant.const import UnitOfRatio, UnitOfTemperature, UnitOfVolumeFlowRate
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -54,14 +49,14 @@ _T2 = FreshrSensorEntityDescription(
_CO2 = FreshrSensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda r: r.co2,
)
_HUM = FreshrSensorEntityDescription(
key="hum",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda r: r.hum,
)
+3 -3
View File
@@ -10,7 +10,7 @@ from homeassistant.components.button import (
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.const import EntityCategory
from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
@@ -75,7 +75,7 @@ def repair_issue_cleanup(hass: HomeAssistant, avm_wrapper: AvmWrapper) -> None:
if (
(
entity_button := entity_registry.async_get_entity_id(
"button", DOMAIN, f"{avm_wrapper.unique_id}-cleanup"
Platform.BUTTON, DOMAIN, f"{avm_wrapper.unique_id}-cleanup"
)
)
and (entity_entry := entity_registry.async_get(entity_button))
@@ -102,7 +102,7 @@ def repair_issue_firmware_update(hass: HomeAssistant, avm_wrapper: AvmWrapper) -
if (
(
entity_button := entity_registry.async_get_entity_id(
"button", DOMAIN, f"{avm_wrapper.unique_id}-firmware_update"
Platform.BUTTON, DOMAIN, f"{avm_wrapper.unique_id}-firmware_update"
)
)
and (entity_entry := entity_registry.async_get(entity_button))
+2 -2
View File
@@ -7,13 +7,13 @@ from typing import override
from requests.exceptions import RequestException
from homeassistant.components.image import ImageEntity
from homeassistant.const import EntityCategory
from homeassistant.const import EntityCategory, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.util import dt as dt_util, slugify
from .const import DOMAIN, Platform
from .const import DOMAIN
from .coordinator import AvmWrapper, FritzConfigEntry
from .entity import FritzBoxBaseEntity
@@ -21,5 +21,5 @@
"integration_type": "system",
"preview_features": { "winter_mode": {} },
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20260624.0"]
"requirements": ["home-assistant-frontend==20260624.1"]
}
+10 -10
View File
@@ -14,7 +14,7 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
from homeassistant.const import UnitOfDensity
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -63,7 +63,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
key=ATTR_C6H6,
value=lambda sensors: sensors.c6h6.value if sensors.c6h6 else None,
suggested_display_precision=0,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
translation_key="c6h6",
),
@@ -72,7 +72,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.co.value if sensors.co else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.CO,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -80,7 +80,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.no.value if sensors.no else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.NITROGEN_MONOXIDE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -88,7 +88,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.no2.value if sensors.no2 else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.NITROGEN_DIOXIDE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -104,7 +104,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
translation_key=ATTR_NOX,
value=lambda sensors: sensors.nox.value if sensors.nox else None,
suggested_display_precision=0,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -112,7 +112,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.o3.value if sensors.o3 else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.OZONE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -128,7 +128,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.pm10.value if sensors.pm10 else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.PM10,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -144,7 +144,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.pm25.value if sensors.pm25 else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -160,7 +160,7 @@ SENSOR_TYPES: tuple[GiosSensorEntityDescription, ...] = (
value=lambda sensors: sensors.so2.value if sensors.so2 else None,
suggested_display_precision=0,
device_class=SensorDeviceClass.SULPHUR_DIOXIDE,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
GiosSensorEntityDescription(
@@ -8,5 +8,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"loggers": ["googleapiclient"],
"requirements": ["gcal-sync==8.0.0", "oauth2client==4.1.3", "ical==13.2.5"]
"requirements": ["gcal-sync==8.0.0", "oauth2client==4.1.3", "ical==13.3.0"]
}
@@ -14,11 +14,7 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.config_entries import ConfigSubentry
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
CONF_LATITUDE,
CONF_LONGITUDE,
)
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, UnitOfRatio
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -125,7 +121,7 @@ AIR_QUALITY_SENSOR_TYPES: tuple[AirQualitySensorEntityDescription, ...] = (
native_unit_of_measurement_fn=lambda x: x.pollutants.co.concentration.units,
exists_fn=lambda x: "co" in {p.code for p in x.pollutants},
value_fn=lambda x: x.pollutants.co.concentration.value,
suggested_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
suggested_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
),
AirQualitySensorEntityDescription(
key="nh3",
@@ -31,8 +31,8 @@ from google.genai.types import (
Tool,
ToolListUnion,
)
from probatio import to_openapi as convert
import voluptuous as vol
from voluptuous_openapi import convert
from homeassistant.components import conversation
from homeassistant.config_entries import ConfigSubentry
+6 -7
View File
@@ -19,10 +19,9 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
UnitOfDensity,
UnitOfRatio,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
@@ -44,13 +43,13 @@ SENSOR_DESCRIPTIONS = {
(DeviceClass.HUMIDITY, Units.PERCENTAGE): SensorEntityDescription(
key=f"{DeviceClass.HUMIDITY}_{Units.PERCENTAGE}",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
(DeviceClass.BATTERY, Units.PERCENTAGE): SensorEntityDescription(
key=f"{DeviceClass.BATTERY}_{Units.PERCENTAGE}",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
native_unit_of_measurement=UnitOfRatio.PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
(
@@ -69,13 +68,13 @@ SENSOR_DESCRIPTIONS = {
): SensorEntityDescription(
key=f"{DeviceClass.PM25}_{Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}",
device_class=SensorDeviceClass.PM25,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
native_unit_of_measurement=UnitOfDensity.MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
(DeviceClass.CO2, Units.CONCENTRATION_PARTS_PER_MILLION): SensorEntityDescription(
key=f"{DeviceClass.CO2}_{Units.CONCENTRATION_PARTS_PER_MILLION}",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
native_unit_of_measurement=UnitOfRatio.PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
),
}
@@ -27,25 +27,6 @@
"state": {
"2d_fix": "2D Fix",
"3d_fix": "3D Fix"
},
"state_attributes": {
"climb": {
"name": "[%key:component::gpsd::entity::sensor::climb::name%]"
},
"elevation": {
"name": "[%key:common::config_flow::data::elevation%]"
},
"gps_time": {
"name": "[%key:component::time_date::selector::display_options::options::time%]"
},
"latitude": { "name": "[%key:common::config_flow::data::latitude%]" },
"longitude": {
"name": "[%key:common::config_flow::data::longitude%]"
},
"mode": { "name": "[%key:common::config_flow::data::mode%]" },
"speed": {
"name": "[%key:component::sensor::entity_component::speed::name%]"
}
}
},
"time": {

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