Compare commits

...

164 Commits

Author SHA1 Message Date
Franck Nijhof 4022eb93de Bump version to 2026.7.0b1 2026-06-24 21:29:59 +00:00
Christian Lackas 28171dfe90 Bump homematicip to 2.13.2 (#174673) 2026-06-24 21:29:51 +00:00
Erwin Douna e9af932fbe Vera core group executor job (#174669) 2026-06-24 21:29:49 +00:00
Erwin Douna 3212e0f051 Tami4 group executor job (#174668) 2026-06-24 21:29:47 +00:00
TheJulianJES 87f0720450 Bump zha-quirks to 2.1.0 (#174662) 2026-06-24 21:29:45 +00:00
Brandon Rothweiler 534ff3f3dc Add missing scope and authorize param to Dropbox OAuth (#174587) 2026-06-24 21:29:43 +00:00
Franck Nijhof 1096c8af13 Bump version to 2026.7.0b0 2026-06-24 16:36:39 +00:00
Leonardo Merza cc2944d626 Fix ecobee active sensor reporting for custom presets and shared device names (#174417) 2026-06-24 17:21:45 +01:00
Mick Vleeshouwer 548ec5cacf Enable action queue to batch concurrent commands in Overkiz (#174275) 2026-06-24 17:19:15 +02:00
karwosts dc6eef2844 Fix date-only input_datetime timestamp attribute to use the correct TZ (#174357) 2026-06-24 17:15:09 +02:00
Stefan Agner 0808e30e37 Add repair when IPv6 is disabled for Matter (#174653)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-24 17:11:54 +02:00
Manu f0ed257f47 Refactor Steam integration config flow and tests (#174504)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-06-24 17:06:29 +02:00
Petro31 b4b710b474 Add restore state framework for template entities (#172847) 2026-06-24 16:54:56 +02:00
Bram Kragten 0004a82fe4 Update frontend to 20260624.0 (#174657) 2026-06-24 15:46:44 +01:00
epenet 0c4bc95bdd Migrate base entity attributes to StrEnum (#174633) 2026-06-24 15:38:49 +01:00
Manu 5fdab795e8 Remove Avi-on integration (#174649) 2026-06-24 16:35:22 +02:00
Manu 2193665909 Remove BeeWi SmartClim (#174651) 2026-06-24 16:32:05 +02:00
Martin Hjelmare c9d91d5812 Add enabled entity limit per config entry (#174194) 2026-06-24 16:07:54 +02:00
Erik Montnemery de9d9c66c1 Add additional sun conditions (#174537) 2026-06-24 16:00:08 +02:00
Paul Bottein dfcc4d1ae4 Fix friendly name for restored unavailable entities (#174614) 2026-06-24 14:47:39 +01:00
TimL d71812f09b Bump pysmlight to 0.4.0 (#174640) 2026-06-24 15:19:35 +02:00
Ariel Ebersberger a323ebe634 Reword "advanced operation" channel warning in Home Assistant Hardware (#174645) 2026-06-24 14:57:18 +02:00
Ariel Ebersberger 024bba55cf Reword "Configure advanced voice settings" in ElevenLabs (#174642) 2026-06-24 14:33:41 +02:00
Ariel Ebersberger a5546566e7 Rename "(advanced)" service names in Z-Wave (#174644) 2026-06-24 14:32:38 +02:00
Ariel Ebersberger 3d9994ee4f Rename "Local Risco Panel (advanced)" option in Risco (#174643) 2026-06-24 14:31:55 +02:00
Erik Montnemery c542f38387 Add checks for did_not_trigger calls to trigger tests (#174636)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:05:08 +01:00
wollew 49d6166b7e Bump pyvlx to 0.2.36 (#174638) 2026-06-24 13:21:45 +02:00
J. Nick Koston 7249190c64 Bump habluetooth to 6.23.1 (#174639) 2026-06-24 13:21:12 +02:00
epenet cebdde6ab4 Migrate device tracker entity attributes to StrEnum (#174621) 2026-06-24 12:53:43 +02:00
epenet 031f4cd965 Migrate remaining platform entity attributes to StrEnum (#174625) 2026-06-24 12:52:50 +02:00
Ariel Ebersberger a734f7110c Rename "Advanced options" to "Additional options" in DNS IP (#174628) 2026-06-24 12:38:16 +02:00
Ariel Ebersberger 395e949591 Rename "Advanced settings" to "Additional settings" in History Stats (#174629) 2026-06-24 12:38:00 +02:00
Ariel Ebersberger 484e60a1c4 Rename "Advanced options" to "Additional options" in SQL (#174631) 2026-06-24 12:19:38 +02:00
Ariel Ebersberger b7a234fbd9 Rename "Advanced settings" to "Additional settings" in Telegram bot (#174632) 2026-06-24 12:19:08 +02:00
Ariel Ebersberger a1982fbd54 Rename "Advanced settings" to "Additional settings" in Autoskope (#174630) 2026-06-24 12:18:53 +02:00
epenet c384cd9894 Migrate calendar entity attributes to StrEnum (#174615) 2026-06-24 12:10:37 +02:00
Tom 1aefd2a5ac Bump airOS dependency to support open wireless (#174559) 2026-06-24 12:00:38 +02:00
epenet e3605be5cd Migrate siren entity attributes to StrEnum (#174616) 2026-06-24 11:54:13 +02:00
epenet e87a41a01d Migrate text entity attributes to StrEnum (#174619) 2026-06-24 11:51:28 +02:00
epenet 190ff034aa Migrate vacuum entity attributes to StrEnum (#174617) 2026-06-24 11:46:29 +02:00
epenet b301925687 Migrate cover entity attributes to StrEnum (#174601) 2026-06-24 11:18:37 +02:00
epenet 7a0f5b066e Migrate humidifier entity attributes to StrEnum (#174609) 2026-06-24 11:17:45 +02:00
epenet 308fad166d Migrate media player entity attributes to StrEnum (#174605) 2026-06-24 11:16:33 +02:00
epenet 1305c2978c Migrate fan entity attributes to StrEnum (#174610) 2026-06-24 11:12:45 +02:00
epenet 955ad6db1b Migrate valve entity attributes to StrEnum (#174611) 2026-06-24 11:12:02 +02:00
Andreas Schneider 87dc013803 Use age-based filter for Matter BLE advertisement history replay (#173488) 2026-06-24 04:11:29 -05:00
Stefan Agner 1bb41cb2dd Use translated message when Matter Server add-on is not ready (#174529)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:02:08 +02:00
epenet 277af6c60b Migrate update entity attributes to StrEnum (#174608) 2026-06-24 10:57:58 +02:00
Raphael Hehl 69e18aa580 Source UniFi Protect light auto-shutoff duration from the public API (#174518) 2026-06-24 03:54:38 -05:00
Raphael Hehl 75852fc191 Add ufp_public_enabled_fn for public-API availability gating to UniFi Protect (#174544) 2026-06-24 03:53:49 -05:00
Mark Ruys a661b678a2 Add Sensoterra CODEOWNERs (#174431)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-24 10:53:36 +02:00
Raphael Hehl bd0951110d Drive the UniFi Protect doorbell ring event from the public events websocket (#174546) 2026-06-24 03:53:01 -05:00
epenet 899f904cf3 Migrate weather entity attributes to StrEnum (#174604) 2026-06-24 10:38:25 +02:00
epenet d2e7426aa5 Migrate image entity attributes to StrEnum (#174606) 2026-06-24 10:37:38 +02:00
epenet c0e02457bc Migrate lock entity attributes to StrEnum (#174607) 2026-06-24 10:37:11 +02:00
epenet e7562b50cf Migrate water heater entity attributes to StrEnum (#174603) 2026-06-24 10:32:38 +02:00
Thomas c36e4a03e0 Bump boschshcpy to 0.3.5 (#174550) 2026-06-24 10:16:45 +02:00
Nathan Spencer 71430af6ff Bump pylitterbot to 2025.5.0 (#174554) 2026-06-24 10:15:43 +02:00
Åke Strandberg 815cce5a0c Tweak aqvify entity names (#174597) 2026-06-24 10:08:15 +02:00
Robert Svensson 32929755eb Tighten the Axis unique ID in config flow (#172283) 2026-06-24 10:07:10 +02:00
Manu 88d4d1c879 Remove SCSGate integration (#174571) 2026-06-24 10:02:23 +02:00
Manu 51bd71d096 Remove Acer projector integration (#174579) 2026-06-24 09:58:09 +02:00
Thomas 1fcf9eb5b7 Add @mosandlt as codeowner for bosch_shc (#174563) 2026-06-24 09:57:19 +02:00
epenet 1917a007f8 Migrate event entity attributes to StrEnum (#174592) 2026-06-24 09:56:20 +02:00
Paul Bottein b095baa65a Bump Yoto quality scale to platinum (#174598) 2026-06-24 09:52:20 +02:00
Przemysław Szypowicz 2bd81c7351 Remove broken Ampio Smog integration (#173080) 2026-06-24 09:50:46 +02:00
Paul Bottein a576aef9a4 Add dynamic and stale device handling to Yoto (#173298)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-24 09:32:10 +02:00
Michael Hansen c2e780dfd2 Improve Wyoming satellite reconnect and tolerance of other satellites (#174460)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 09:22:03 +02:00
epenet 687064d5cc Migrate Ratio units to StrEnum (#172568) 2026-06-24 09:21:28 +02:00
epenet 0b801d74cd Migrate camera entity attributes to StrEnum (#174591) 2026-06-24 09:15:44 +02:00
epenet dbdcb1a91e Migrate alarm control panel entity attributes to StrEnum (#174590) 2026-06-24 09:06:16 +02:00
epenet f9bf7ab122 Migrate sensor entity attributes to StrEnum (#174595) 2026-06-24 09:05:32 +02:00
epenet d484f75c7b Migrate number entity attributes to StrEnum (#174593) 2026-06-24 09:04:22 +02:00
dependabot[bot] 5ba4d588b6 Bump actions/checkout from 6.0.3 to 7.0.0 (#174596)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 08:59:49 +02:00
epenet 42da67e7a9 Migrate light entity attributes to StrEnum (#174589) 2026-06-24 08:58:00 +02:00
Joost Lekkerkerker 7d08a7b898 Add new integration quality scale rules (#174555) 2026-06-24 08:53:46 +02:00
puddly 05016e46c8 Bump ZHA to 2.0.0 (#174586) 2026-06-24 08:52:44 +02:00
J. Nick Koston 58603326e3 Bump habluetooth to 6.19.1 (#174582) 2026-06-23 22:58:58 -05:00
Steve Rice 10fe3dc13f Add cloudhook support to switchbot_cloud webhook (#174566)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 21:50:08 -04:00
J. Nick Koston adf2f2854c Active scan govee_ble only when needed and widen the window to 30s (#174557) 2026-06-23 21:03:38 -04:00
A. Gideonse 7fd101005d Bump indevolt-api to 1.8.6 (#174573) 2026-06-24 03:03:09 +02:00
J. Nick Koston 40aa8dd617 Widen the INKBIRD IBS-TH2 active scan window to 30s (#174565) 2026-06-23 21:02:55 -04:00
Matthias Alphart fb283dfb93 Update knx-frontend to 2026.6.23.203726 (#174567) 2026-06-23 20:55:45 -04:00
J. Nick Koston 2084d52504 Bump habluetooth to 6.13.0 (#174568) 2026-06-23 20:55:34 -04:00
Fabian Munkes cb914495e7 Add username parameter to play media action in Music Assistant (#174486) 2026-06-24 01:04:44 +02:00
J. Nick Koston 97f2eecc57 Bump bluetooth-adapters to 2.4.0 (#174575) 2026-06-23 17:58:06 -05:00
J. Nick Koston 2d1b3f799d Bump dbus-fast to 5.0.22 (#174569) 2026-06-23 23:32:27 +02:00
J. Nick Koston f57418ed60 Bump govee-ble to 1.4.0 (#174553) 2026-06-23 20:27:33 +02:00
Abílio Costa 8d1bf68045 Enable mypy explicit-override check (#171853)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-06-23 18:20:08 +01:00
Raphael Hehl c5bfad9bfe Remove UniFi LED (unifiled) integration (#168232)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-06-23 17:45:31 +02:00
Abílio Costa 679b0ac2aa Add agent instruction to prevent divider comments (#174531) 2026-06-23 16:41:43 +01:00
epenet 22a583c83f Migrate climate entity attributes to StrEnum (#174528) 2026-06-23 16:48:38 +02:00
epenet d966f71832 Migrate select entity attributes to StrEnum (#174536) 2026-06-23 16:44:47 +02:00
Manu 08569420f6 Remove Eliqonline integration (#174538) 2026-06-23 17:10:21 +03:00
Ronald van der Meer c1bcbca520 Add filter remaining days sensor to Duco (#174316) 2026-06-23 15:16:42 +02:00
Simone Chemelli c73c647162 Improve docstring for async_get_entity_id() method (#174532) 2026-06-23 14:50:34 +02:00
Markus Tuominen b2f1c38b6f Bump ouman-eh-800-api to 1.0.0 (#174458) 2026-06-23 14:45:19 +02:00
Erik Montnemery e8824bedf5 Add additional sun triggers (#174485) 2026-06-23 14:23:29 +02:00
Ajinkya Gokhale 27b107f4a5 Update energieleser to silver quality scale (#174535) 2026-06-23 14:23:06 +02:00
Petro31 7536e8647f Fix entities listed in template blueprints (#171861)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-23 13:35:20 +02:00
Simone Chemelli 75c6058396 Add "Drop in" select to Alexa Devices (#174336) 2026-06-23 10:47:44 +02:00
Matthias Alphart 77533e5af5 Rename "Advanced options" in KNX strings (#174523) 2026-06-23 10:27:11 +02:00
LG-ThinQ-Integration 31a1e7c5e1 Bump thinqconnect 1.0.13 (#174510)
Co-authored-by: YunseonPark-LGE <yunseon.park@lge.com>
2026-06-23 09:55:33 +02:00
renovate[bot] 20f4e7306b Update pytest-unordered to 0.8.0 (#174515) 2026-06-23 09:15:35 +02:00
Mick Vleeshouwer 5c1ac08c92 Catch connection errors when executing Overkiz commands (#174453) 2026-06-23 08:51:36 +02:00
Franck Nijhof 5631c68069 Reword trigger descriptions for presence and detection entities (#174467) 2026-06-23 08:51:14 +02:00
Franck Nijhof 8648611278 Clarify the media player play media action name (#174480) 2026-06-23 08:49:04 +02:00
Franck Nijhof 971d15be1e Improve trigger wording for lawn mower and vacuum entities (#174477)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2026-06-23 08:48:45 +02:00
Åke Strandberg b6f2429ff3 Bump pyaqvify to 0.0.12 (#174516) 2026-06-23 08:35:13 +02:00
Jacob Hurwitz c676e2a806 Bump python-dropbox-api to 0.1.4 (#174512) 2026-06-23 07:22:21 +02:00
Robert Resch a27ea536db Enable aw check requirements on each deps PR (#174481) 2026-06-23 02:14:03 +02:00
Erwin Douna 22e25d9ce2 MELCloud Home exand sensor state behavior (#174495) 2026-06-23 00:44:14 +02:00
Nikhil Deepak 1cb5e31901 Pass keep_alive parameter to Ollama AI Task calls (#165410)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-06-23 00:16:56 +02:00
Thijs W. 4f414d0035 Fix duplicate-configuration edge-case in Frontier Silicon config flow (#172916) 2026-06-23 00:11:51 +02:00
Franck Nijhof 30b648ea6a Bump holidays to 0.99 (#174501) 2026-06-23 00:10:33 +02:00
fdebrus 18a259718f Vistapool: add reauthentication flow (#172825)
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-23 00:09:33 +02:00
fdebrus 45de0f4b4a Add select platform to Vistapool (#172547)
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-23 00:08:14 +02:00
Erwin Douna 940bf5bf09 Portainer fix type narrowing (#173040) 2026-06-23 00:07:34 +02:00
starkillerOG 07e78b6dbf Bump reolink-aio to 0.21.2 (#174497) 2026-06-23 00:06:47 +02:00
Franck Nijhof abcb677b57 Bump SQLAlchemy to 2.0.51 (#174499) 2026-06-22 23:59:00 +02:00
Ermanno Baschiera 7e638f9d0c Bump Helty Flow to silver quality scale (#173132)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:57:21 +02:00
Thomas D 8aca6115f4 Trigger location update on certain events for the Volvo integration (#172651)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-06-22 23:53:42 +02:00
Oscar Calvo dd688986f1 Add CCM15 swing control (#173793)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-06-22 23:51:53 +02:00
Branden Cash c519b7ba07 Populate hourly statistics in srp_energy (#167371)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Signed-off-by: Branden Cash <203336+ammmze@users.noreply.github.com>
2026-06-22 23:51:33 +02:00
Pete Sage 158595464a Cancel timers for Sonos on shutdown/reload (#172830) 2026-06-22 23:47:27 +02:00
Franck Nijhof f81aff0a69 Remove runtime_data dependency from SIA options flow (#174489) 2026-06-22 23:43:46 +02:00
Arie Catsman 9f90e6ec22 Bump pyenphase to v3.0.0 (#174496) 2026-06-23 00:41:23 +03:00
Simone Chemelli 31b311e17a Improve availability of notify enttities for Alexa Devices (#174220)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-06-22 23:37:27 +02:00
Manu 7e07f2ab87 Add notify entities to Notifications for Android TV / Fire TV (#169087) 2026-06-22 23:36:45 +02:00
Przemko92 312f4c8c35 Add new values for Compit climate (#174238)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-06-22 23:29:01 +02:00
Brett Adams ddeeba5f87 Bump Splunk to silver quality scale (#174236)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:20:16 +02:00
Przemko92 82a043bc47 Add new values for Compit sensor (#174240) 2026-06-22 23:17:32 +02:00
Alain 629b9a5f9b SwitchBot cloud fix webhook handling (#169141)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-06-22 23:16:22 +02:00
tacopiek 9400c2e40e Add energy_today sensor to LG ThinQ (#172983)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 23:16:07 +02:00
epenet 8a57bb0640 Refactor thermopro tests to avoid thermopro_sensor.async_setup_entry (#173880) 2026-06-22 23:14:39 +02:00
Chris ab8f1bf88e Add OpenEVSE button platform (#172964) 2026-06-22 23:14:27 +02:00
Erwin Douna b61ad539d9 MELCloud Home add frost and overheat protection (#174224) 2026-06-22 23:13:57 +02:00
Franck Nijhof 283dcee830 Fix Rachio calendar error when no events are scheduled (#173624) 2026-06-22 23:13:02 +02:00
Franck Nijhof e61a3ac684 Bump pyAtome to 0.1.2 (#173902) 2026-06-22 23:11:31 +02:00
Franck Nijhof 5595ba12fb Bump kiwiki-client to 0.1.2 (#173903)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-22 23:09:51 +02:00
Mick Vleeshouwer 1bfd4d1500 Handle ApplicationNotAllowedError in Overkiz cloud config flow (#174498) 2026-06-22 23:03:51 +02:00
Manu 249c3bb5dd Refactor sensor platform of Steam integration (#174415) 2026-06-22 22:59:42 +02:00
Ariel Ebersberger 982fe7a370 Use value comparison for value-based enums (#174494) 2026-06-22 22:44:26 +02:00
EnjoyingM 035e7e0a38 Wolflink Shared and multidevice hub support (#172795)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-06-22 22:40:14 +02:00
J. Nick Koston f6b0036ccc Bump zeroconf to 0.150.0 (#174484) 2026-06-22 22:29:17 +02:00
Denis Shulyaka b2a26afb14 Promote Anthropic IQS to Gold (#170268) 2026-06-22 22:20:39 +02:00
Mick Vleeshouwer 231470f4dc Configure RTS command duration on the Overkiz client (#174448) 2026-06-22 22:04:11 +02:00
Erwin Douna 8aeb5d63ab MELCloud Home add icon for holiday mode (#174459) 2026-06-22 22:02:10 +02:00
some-random-climber d613591463 Use service helper in Picnic (#174271) 2026-06-22 21:59:34 +02:00
Abílio Costa dc6269f52d Add override decorator to remaining homeassistant dir files (#174488) 2026-06-22 21:56:03 +02:00
some-random-climber 31690ac2d3 Use service helper in Volvo (#174267) 2026-06-22 21:54:54 +02:00
Tim Laing ffe008c6b2 Fix iCloud auth bug (#173816) 2026-06-22 21:52:47 +02:00
Raphael Hehl dc48c3e30f Fix UniFi Protect package detection via public events websocket (#173733) 2026-06-22 14:45:20 -05:00
Jonathan Swoboda 448fd04137 Convert epoch to datetime for ESPHome uptime sensor device class (#174223) 2026-06-22 21:30:31 +02:00
AlCalzone 41e7837bcf Set openSenseMap to bronze on the quality scale (#173864)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-06-22 21:29:59 +02:00
Mattie 951062fa82 Add SG Ready select entity to Qube heat pump (#170114)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-06-22 21:29:25 +02:00
Allen Porter f2ac5a4153 Support Roborock dynamic devices and simplify startup error handling (#173704) 2026-06-22 21:27:29 +02:00
Franck Nijhof 12832fe3cd Remove invalid state_class from System Monitor battery_empty sensor (#174487) 2026-06-22 21:09:46 +02:00
some-random-climber d35e58fdc7 Move service registration to async_setup in cloudflare (#174131) 2026-06-22 20:56:48 +02:00
smartcircuits 3570cd0ee3 Add reauthentication flow to WattWächter Plus (#174281)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 20:56:22 +02:00
Brett Adams 5a99cb4cca Fix Teslemetry rear seat heater entity availability (#174248)
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-22 20:54:55 +02:00
Abílio Costa d49a9c4780 Add override decorator to components S to T (#174483) 2026-06-22 20:52:27 +02:00
Abílio Costa c330a983e4 Add override decorator to components U to Z (#174482) 2026-06-22 20:51:13 +02:00
Legendberg e47f58362a Add per-day sleep schedule entities for Litter-Robot 5 (#173569)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2026-06-22 20:48:51 +02:00
3449 changed files with 94607 additions and 72506 deletions
+1
View File
@@ -53,3 +53,4 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- When validation guarantees a dict key exists, prefer direct key access (`data["key"]`) instead of `.get("key")` so contract violations are surfaced instead of silently masked.
- 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.
+6 -6
View File
@@ -38,7 +38,7 @@ jobs:
base_image_version: ${{ env.BASE_IMAGE_VERSION }}
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -102,7 +102,7 @@ jobs:
os: ubuntu-24.04-arm
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -245,7 +245,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -292,7 +292,7 @@ jobs:
contents: read
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -469,7 +469,7 @@ jobs:
if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -516,7 +516,7 @@ jobs:
HASSFEST_IMAGE_TAG: ghcr.io/home-assistant/hassfest:${{ needs.init.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -8,15 +8,11 @@ name: Check requirements (deterministic)
# yamllint disable-line rule:truthy
on:
# Auto-trigger on PRs that touch tracked requirement files is disabled
# for now while we iterate — testing the workflow_run handoff to the
# agentic stage is hard with an auto-trigger. Re-enable once the chain
# has been validated end-to-end.
# pull_request:
# types: [opened, synchronize, reopened]
# paths:
# - "**/requirements*.txt"
# - "homeassistant/package_constraints.txt"
pull_request:
types: [opened, synchronize, reopened]
paths:
- "requirements*.txt"
- "homeassistant/package_constraints.txt"
workflow_dispatch:
inputs:
pull_request_number:
@@ -40,7 +36,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Python
+4 -4
View File
@@ -31,7 +31,7 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -155,7 +155,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@@ -404,7 +404,7 @@ jobs:
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
} >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Create gh-aw temp directory
@@ -1236,7 +1236,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
+20 -20
View File
@@ -98,7 +98,7 @@ jobs:
skip_coverage: ${{ steps.info.outputs.skip_coverage }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Generate partial Python venv restore key
@@ -264,7 +264,7 @@ jobs:
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Register problem matchers
@@ -291,7 +291,7 @@ jobs:
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Run zizmor
@@ -318,7 +318,7 @@ jobs:
- script/hassfest/docker/Dockerfile
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Register hadolint problem matcher
@@ -341,7 +341,7 @@ jobs:
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
@@ -469,7 +469,7 @@ jobs:
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -512,7 +512,7 @@ jobs:
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Python
@@ -548,7 +548,7 @@ jobs:
&& github.event.inputs.audit-licenses-only != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Python
@@ -576,7 +576,7 @@ jobs:
&& github.event_name == 'pull_request'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Dependency review
@@ -603,7 +603,7 @@ jobs:
python-version: ${{ fromJson(needs.info.outputs.python_versions) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
@@ -654,7 +654,7 @@ jobs:
|| github.event.inputs.pylint-only == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Python
@@ -707,7 +707,7 @@ jobs:
&& (needs.info.outputs.tests_glob || needs.info.outputs.test_full_suite == 'true')
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Python
@@ -758,7 +758,7 @@ jobs:
|| github.event.inputs.mypy-only == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Python
@@ -825,7 +825,7 @@ jobs:
- base
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -889,7 +889,7 @@ jobs:
group: ${{ fromJson(needs.info.outputs.test_groups) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -1030,7 +1030,7 @@ jobs:
mariadb-group: ${{ fromJson(needs.info.outputs.mariadb_groups) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -1179,7 +1179,7 @@ jobs:
postgresql-group: ${{ fromJson(needs.info.outputs.postgresql_groups) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -1317,7 +1317,7 @@ jobs:
if: needs.info.outputs.skip_coverage != 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Download all coverage artifacts
@@ -1355,7 +1355,7 @@ jobs:
group: ${{ fromJson(needs.info.outputs.test_groups) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Install additional OS dependencies
@@ -1476,7 +1476,7 @@ jobs:
- pytest-partial
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Download all coverage artifacts
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
+3 -3
View File
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -116,7 +116,7 @@ jobs:
os: ubuntu-24.04-arm
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@@ -167,7 +167,7 @@ jobs:
os: ubuntu-24.04-arm
steps:
- name: Checkout the repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
-2
View File
@@ -43,7 +43,6 @@ homeassistant.components
homeassistant.components.abode.*
homeassistant.components.acaia.*
homeassistant.components.accuweather.*
homeassistant.components.acer_projector.*
homeassistant.components.acmeda.*
homeassistant.components.actiontec.*
homeassistant.components.actron_air.*
@@ -77,7 +76,6 @@ homeassistant.components.amberelectric.*
homeassistant.components.ambient_network.*
homeassistant.components.ambient_station.*
homeassistant.components.amcrest.*
homeassistant.components.ampio.*
homeassistant.components.analytics.*
homeassistant.components.analytics_insights.*
homeassistant.components.android_ip_webcam.*
+1
View File
@@ -42,3 +42,4 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- When validation guarantees a dict key exists, prefer direct key access (`data["key"]`) instead of `.get("key")` so contract violations are surfaced instead of silently masked.
- 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.
Generated
+6 -8
View File
@@ -230,7 +230,6 @@ CLAUDE.md @home-assistant/core
/tests/components/battery/ @home-assistant/core
/homeassistant/components/bayesian/ @HarvsG
/tests/components/bayesian/ @HarvsG
/homeassistant/components/beewi_smartclim/ @alemuro
/homeassistant/components/binary_sensor/ @home-assistant/core
/tests/components/binary_sensor/ @home-assistant/core
/homeassistant/components/bizkaibus/ @UgaitzEtxebarria
@@ -254,8 +253,8 @@ CLAUDE.md @home-assistant/core
/tests/components/bond/ @bdraco @prystupa @joshs85 @marciogranzotto
/homeassistant/components/bosch_alarm/ @mag1024 @sanjay900
/tests/components/bosch_alarm/ @mag1024 @sanjay900
/homeassistant/components/bosch_shc/ @tschamm
/tests/components/bosch_shc/ @tschamm
/homeassistant/components/bosch_shc/ @tschamm @mosandlt
/tests/components/bosch_shc/ @tschamm @mosandlt
/homeassistant/components/brands/ @home-assistant/core
/tests/components/brands/ @home-assistant/core
/homeassistant/components/braviatv/ @bieniu @Drafteed
@@ -1603,8 +1602,8 @@ CLAUDE.md @home-assistant/core
/tests/components/sensorpush/ @bdraco
/homeassistant/components/sensorpush_cloud/ @sstallion
/tests/components/sensorpush_cloud/ @sstallion
/homeassistant/components/sensoterra/ @markruys
/tests/components/sensoterra/ @markruys
/homeassistant/components/sensoterra/ @SanderBakkumCuriousInc @curious-florian @markruys
/tests/components/sensoterra/ @SanderBakkumCuriousInc @curious-florian @markruys
/homeassistant/components/sentry/ @dcramer @frenck
/tests/components/sentry/ @dcramer @frenck
/homeassistant/components/senz/ @milanmeu
@@ -1712,8 +1711,8 @@ CLAUDE.md @home-assistant/core
/tests/components/sql/ @gjohansson-ST @dougiteixeira
/homeassistant/components/squeezebox/ @rajlaud @pssc @peteS-UK
/tests/components/squeezebox/ @rajlaud @pssc @peteS-UK
/homeassistant/components/srp_energy/ @briglx
/tests/components/srp_energy/ @briglx
/homeassistant/components/srp_energy/ @briglx @ammmze
/tests/components/srp_energy/ @briglx @ammmze
/homeassistant/components/starline/ @anonym-tsk
/tests/components/starline/ @anonym-tsk
/homeassistant/components/statistics/ @ThomDietrich @gjohansson-ST
@@ -1900,7 +1899,6 @@ CLAUDE.md @home-assistant/core
/tests/components/unifi_direct/ @tofuSCHNITZEL
/homeassistant/components/unifi_discovery/ @RaHehl
/tests/components/unifi_discovery/ @RaHehl
/homeassistant/components/unifiled/ @florisvdk
/homeassistant/components/unifiprotect/ @RaHehl
/tests/components/unifiprotect/ @RaHehl
/homeassistant/components/upb/ @gwww
+3 -1
View File
@@ -6,7 +6,7 @@ from collections.abc import Mapping
from datetime import datetime, timedelta
from functools import partial
import time
from typing import Any, cast
from typing import Any, cast, override
import jwt
@@ -109,6 +109,7 @@ class AuthManagerFlowManager(
super().__init__(hass)
self.auth_manager = auth_manager
@override
async def async_create_flow(
self,
handler_key: tuple[str, str],
@@ -122,6 +123,7 @@ class AuthManagerFlowManager(
raise KeyError(f"Unknown auth provider {handler_key}")
return await auth_provider.async_login_flow(context)
@override
async def async_finish_flow(
self,
flow: FlowHandler[AuthFlowContext, AuthFlowResult, tuple[str, str]],
+1
View File
@@ -39,6 +39,7 @@ class _PyJWSWithLoadCache(PyJWS):
# We only ever have a global instance of this class
# so we do not have to worry about the LRU growing
# each time we create a new instance.
@override
def _load(self, jwt: str | bytes) -> tuple[bytes, bytes, dict, bytes]:
"""Load a JWS."""
return super()._load(jwt)
@@ -1,6 +1,6 @@
"""Example auth module."""
from typing import Any
from typing import Any, override
import voluptuous as vol
@@ -35,6 +35,7 @@ class InsecureExampleModule(MultiFactorAuthModule):
self._data = config["data"]
@property
@override
def input_schema(self) -> vol.Schema:
"""Validate login flow input data."""
return vol.Schema({vol.Required("pin"): str})
@@ -44,6 +45,7 @@ class InsecureExampleModule(MultiFactorAuthModule):
"""Validate async_setup_user input data."""
return vol.Schema({vol.Required("pin"): str})
@override
async def async_setup_flow(self, user_id: str) -> SetupFlow:
"""Return a data entry flow handler for setup module.
@@ -51,6 +53,7 @@ class InsecureExampleModule(MultiFactorAuthModule):
"""
return SetupFlow(self, self.setup_schema, user_id)
@override
async def async_setup_user(self, user_id: str, setup_data: Any) -> Any:
"""Set up user to use mfa module."""
# data shall has been validate in caller
@@ -64,6 +67,7 @@ class InsecureExampleModule(MultiFactorAuthModule):
self._data.append({"user_id": user_id, "pin": pin})
@override
async def async_depose_user(self, user_id: str) -> None:
"""Remove user from mfa module."""
found = None
@@ -74,10 +78,12 @@ class InsecureExampleModule(MultiFactorAuthModule):
if found:
self._data.remove(found)
@override
async def async_is_user_setup(self, user_id: str) -> bool:
"""Return whether user is setup."""
return any(data["user_id"] == user_id for data in self._data)
@override
async def async_validate(self, user_id: str, user_input: dict[str, Any]) -> bool:
"""Return True if validation passed."""
return any(
+8 -1
View File
@@ -5,7 +5,7 @@ Sending HOTP through notify service
import asyncio
import logging
from typing import Any, cast
from typing import Any, cast, override
import attr
import voluptuous as vol
@@ -107,6 +107,7 @@ class NotifyAuthModule(MultiFactorAuthModule):
self._init_lock = asyncio.Lock()
@property
@override
def input_schema(self) -> vol.Schema:
"""Validate login flow input data."""
return vol.Schema({vol.Required(INPUT_FIELD_CODE): str})
@@ -159,6 +160,7 @@ class NotifyAuthModule(MultiFactorAuthModule):
return sorted(unordered_services)
@override
async def async_setup_flow(self, user_id: str) -> NotifySetupFlow:
"""Return a data entry flow handler for setup module.
@@ -168,6 +170,7 @@ class NotifyAuthModule(MultiFactorAuthModule):
self, self.input_schema, user_id, self.aync_get_available_notify_services()
)
@override
async def async_setup_user(self, user_id: str, setup_data: Any) -> Any:
"""Set up auth module for user."""
if self._user_settings is None:
@@ -181,6 +184,7 @@ class NotifyAuthModule(MultiFactorAuthModule):
await self._async_save()
@override
async def async_depose_user(self, user_id: str) -> None:
"""Depose auth module for user."""
if self._user_settings is None:
@@ -190,6 +194,7 @@ class NotifyAuthModule(MultiFactorAuthModule):
if self._user_settings.pop(user_id, None):
await self._async_save()
@override
async def async_is_user_setup(self, user_id: str) -> bool:
"""Return whether user is setup."""
if self._user_settings is None:
@@ -198,6 +203,7 @@ class NotifyAuthModule(MultiFactorAuthModule):
return user_id in self._user_settings
@override
async def async_validate(self, user_id: str, user_input: dict[str, Any]) -> bool:
"""Return True if validation passed."""
if self._user_settings is None:
@@ -283,6 +289,7 @@ class NotifySetupFlow(SetupFlow[NotifyAuthModule]):
self._notify_service: str | None = None
self._target: str | None = None
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> FlowResult:
+8 -1
View File
@@ -2,7 +2,7 @@
import asyncio
from io import BytesIO
from typing import Any, cast
from typing import Any, cast, override
import voluptuous as vol
@@ -87,6 +87,7 @@ class TotpAuthModule(MultiFactorAuthModule):
self._init_lock = asyncio.Lock()
@property
@override
def input_schema(self) -> vol.Schema:
"""Validate login flow input data."""
return vol.Schema({vol.Required(INPUT_FIELD_CODE): str})
@@ -115,6 +116,7 @@ class TotpAuthModule(MultiFactorAuthModule):
self._users[user_id] = ota_secret # type: ignore[index]
return ota_secret
@override
async def async_setup_flow(self, user_id: str) -> TotpSetupFlow:
"""Return a data entry flow handler for setup module.
@@ -124,6 +126,7 @@ class TotpAuthModule(MultiFactorAuthModule):
assert user is not None
return TotpSetupFlow(self, self.input_schema, user)
@override
async def async_setup_user(self, user_id: str, setup_data: Any) -> str:
"""Set up auth module for user."""
if self._users is None:
@@ -136,6 +139,7 @@ class TotpAuthModule(MultiFactorAuthModule):
await self._async_save()
return result
@override
async def async_depose_user(self, user_id: str) -> None:
"""Depose auth module for user."""
if self._users is None:
@@ -144,6 +148,7 @@ class TotpAuthModule(MultiFactorAuthModule):
if self._users.pop(user_id, None): # type: ignore[union-attr]
await self._async_save()
@override
async def async_is_user_setup(self, user_id: str) -> bool:
"""Return whether user is setup."""
if self._users is None:
@@ -151,6 +156,7 @@ class TotpAuthModule(MultiFactorAuthModule):
return user_id in self._users # type: ignore[operator]
@override
async def async_validate(self, user_id: str, user_input: dict[str, Any]) -> bool:
"""Return True if validation passed."""
if self._users is None:
@@ -189,6 +195,7 @@ class TotpSetupFlow(SetupFlow[TotpAuthModule]):
super().__init__(auth_module, setup_schema, user.id)
self._user = user
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> FlowResult:
+6 -1
View File
@@ -1,7 +1,7 @@
"""Permissions for Home Assistant."""
from collections.abc import Callable, Iterable
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
import voluptuous as vol
@@ -68,14 +68,17 @@ class PolicyPermissions(AbstractPermissions):
self._policy = policy
self._perm_lookup = perm_lookup
@override
def access_all_entities(self, key: str) -> bool:
"""Check if we have a certain access to all entities."""
return test_all(self._policy.get(CAT_ENTITIES), key)
@override
def _entity_func(self) -> Callable[[str, str], bool]:
"""Return a function that can test entity access."""
return compile_entities(self._policy.get(CAT_ENTITIES), self._perm_lookup)
@override
def __eq__(self, other: object) -> bool:
"""Equals check."""
return isinstance(other, PolicyPermissions) and other._policy == self._policy
@@ -84,10 +87,12 @@ class PolicyPermissions(AbstractPermissions):
class _OwnerPermissions(AbstractPermissions):
"""Owner permissions."""
@override
def access_all_entities(self, key: str) -> bool:
"""Check if we have a certain access to all entities."""
return True
@override
def _entity_func(self) -> Callable[[str, str], bool]:
"""Return a function that can test entity access."""
return lambda entity_id, key: True
+5 -1
View File
@@ -4,7 +4,7 @@ import asyncio
from collections.abc import Mapping
import logging
import os
from typing import Any
from typing import Any, override
import voluptuous as vol
@@ -57,6 +57,7 @@ class CommandLineAuthProvider(AuthProvider):
super().__init__(*args, **kwargs)
self._user_meta: dict[str, dict[str, Any]] = {}
@override
async def async_login_flow(
self, context: AuthFlowContext | None
) -> CommandLineLoginFlow:
@@ -105,6 +106,7 @@ class CommandLineAuthProvider(AuthProvider):
meta[key] = value
self._user_meta[username] = meta
@override
async def async_get_or_create_credentials(
self, flow_result: Mapping[str, str]
) -> Credentials:
@@ -117,6 +119,7 @@ class CommandLineAuthProvider(AuthProvider):
# Create new credentials.
return self.async_create_credentials({"username": username})
@override
async def async_user_meta_for_credentials(
self, credentials: Credentials
) -> UserMeta:
@@ -136,6 +139,7 @@ class CommandLineAuthProvider(AuthProvider):
class CommandLineLoginFlow(LoginFlow[CommandLineAuthProvider]):
"""Handler for the login flow."""
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> AuthFlowResult:
@@ -4,7 +4,7 @@ import asyncio
import base64
from collections.abc import Mapping
import logging
from typing import Any, cast
from typing import Any, cast, override
import bcrypt
import voluptuous as vol
@@ -302,6 +302,7 @@ class HassAuthProvider(AuthProvider):
self.data: Data | None = None
self._init_lock = asyncio.Lock()
@override
async def async_initialize(self) -> None:
"""Initialize the auth provider."""
async with self._init_lock:
@@ -312,6 +313,7 @@ class HassAuthProvider(AuthProvider):
await data.async_load()
self.data = data
@override
async def async_login_flow(self, context: AuthFlowContext | None) -> HassLoginFlow:
"""Return a flow to login."""
return HassLoginFlow(self)
@@ -369,6 +371,7 @@ class HassAuthProvider(AuthProvider):
)
await self.data.async_save()
@override
async def async_get_or_create_credentials(
self, flow_result: Mapping[str, str]
) -> Credentials:
@@ -387,6 +390,7 @@ class HassAuthProvider(AuthProvider):
# Create new credentials.
return self.async_create_credentials({"username": username})
@override
async def async_user_meta_for_credentials(
self, credentials: Credentials
) -> UserMeta:
@@ -410,6 +414,7 @@ class HassAuthProvider(AuthProvider):
class HassLoginFlow(LoginFlow[HassAuthProvider]):
"""Handler for the login flow."""
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> AuthFlowResult:
@@ -2,6 +2,7 @@
from collections.abc import Mapping
import hmac
from typing import override
import voluptuous as vol
@@ -33,6 +34,7 @@ class InvalidAuthError(HomeAssistantError):
class ExampleAuthProvider(AuthProvider):
"""Example auth provider based on hardcoded usernames and passwords."""
@override
async def async_login_flow(
self, context: AuthFlowContext | None
) -> ExampleLoginFlow:
@@ -61,6 +63,7 @@ class ExampleAuthProvider(AuthProvider):
):
raise InvalidAuthError
@override
async def async_get_or_create_credentials(
self, flow_result: Mapping[str, str]
) -> Credentials:
@@ -74,6 +77,7 @@ class ExampleAuthProvider(AuthProvider):
# Create new credentials.
return self.async_create_credentials({"username": username})
@override
async def async_user_meta_for_credentials(
self, credentials: Credentials
) -> UserMeta:
@@ -95,6 +99,7 @@ class ExampleAuthProvider(AuthProvider):
class ExampleLoginFlow(LoginFlow[ExampleAuthProvider]):
"""Handler for the login flow."""
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> AuthFlowResult:
@@ -13,7 +13,7 @@ from ipaddress import (
ip_address,
ip_network,
)
from typing import Any, cast
from typing import Any, cast, override
import voluptuous as vol
@@ -98,10 +98,12 @@ class TrustedNetworksAuthProvider(AuthProvider):
]
@property
@override
def support_mfa(self) -> bool:
"""Trusted Networks auth provider does not support MFA."""
return False
@override
async def async_login_flow(
self, context: AuthFlowContext | None
) -> TrustedNetworksLoginFlow:
@@ -144,6 +146,7 @@ class TrustedNetworksAuthProvider(AuthProvider):
self.config[CONF_ALLOW_BYPASS_LOGIN],
)
@override
async def async_get_or_create_credentials(
self, flow_result: Mapping[str, str]
) -> Credentials:
@@ -172,6 +175,7 @@ class TrustedNetworksAuthProvider(AuthProvider):
# We only allow login as exist user
raise InvalidUserError
@override
async def async_user_meta_for_credentials(
self, credentials: Credentials
) -> UserMeta:
@@ -203,6 +207,7 @@ class TrustedNetworksAuthProvider(AuthProvider):
raise InvalidAuthError("Can't allow access from Home Assistant Cloud")
@callback
@override
def async_validate_refresh_token(
self, refresh_token: RefreshToken, remote_ip: str | None = None
) -> None:
@@ -230,6 +235,7 @@ class TrustedNetworksLoginFlow(LoginFlow[TrustedNetworksAuthProvider]):
self._ip_address = ip_addr
self._allow_bypass_login = allow_bypass_login
@override
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> AuthFlowResult:
+2 -1
View File
@@ -14,7 +14,7 @@ import platform
import sys
import threading
from time import monotonic
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, override
# Import cryptography early since import openssl is not thread-safe
# _frozen_importlib._DeadlockError: deadlock detected by
@@ -697,6 +697,7 @@ def _create_log_file(
class _RotatingFileHandlerWithoutShouldRollOver(RotatingFileHandler):
"""RotatingFileHandler that does not check if it should roll over on every log."""
@override
def shouldRollover(self, record: logging.LogRecord) -> bool:
"""Never roll over.
-1
View File
@@ -7,7 +7,6 @@
"unifi_access",
"unifi_direct",
"unifi_discovery",
"unifiled",
"unifiprotect"
]
}
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
No custom actions are defined.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
@@ -1 +0,0 @@
"""The acer_projector component."""
@@ -1,34 +0,0 @@
"""Use serial protocol of Acer projector to obtain state of the projector."""
from typing import Final
from homeassistant.const import STATE_OFF, STATE_ON
CONF_READ_TIMEOUT: Final = "timeout"
CONF_WRITE_TIMEOUT: Final = "write_timeout"
DEFAULT_NAME: Final = "Acer Projector"
DEFAULT_READ_TIMEOUT: Final = 1
DEFAULT_WRITE_TIMEOUT: Final = 1
ECO_MODE: Final = "ECO Mode"
ICON: Final = "mdi:projector"
INPUT_SOURCE: Final = "Input Source"
LAMP: Final = "Lamp"
LAMP_HOURS: Final = "Lamp Hours"
MODEL: Final = "Model"
# Commands known to the projector
CMD_DICT: Final[dict[str, str]] = {
LAMP: "* 0 Lamp ?\r",
LAMP_HOURS: "* 0 Lamp\r",
INPUT_SOURCE: "* 0 Src ?\r",
ECO_MODE: "* 0 IR 052\r",
MODEL: "* 0 IR 035\r",
STATE_ON: "* 0 IR 001\r",
STATE_OFF: "* 0 IR 002\r",
}
@@ -1,9 +0,0 @@
{
"domain": "acer_projector",
"name": "Acer Projector",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/acer_projector",
"iot_class": "local_polling",
"quality_scale": "legacy",
"requirements": ["serialx==1.8.2"]
}
@@ -1,155 +0,0 @@
"""Use serial protocol of Acer projector to obtain state of the projector."""
import logging
import re
from typing import Any, override
from serialx import Serial, SerialException
import voluptuous as vol
from homeassistant.components.switch import (
PLATFORM_SCHEMA as SWITCH_PLATFORM_SCHEMA,
SwitchEntity,
)
from homeassistant.const import (
CONF_FILENAME,
CONF_NAME,
STATE_OFF,
STATE_ON,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import (
CMD_DICT,
CONF_READ_TIMEOUT,
CONF_WRITE_TIMEOUT,
DEFAULT_NAME,
DEFAULT_READ_TIMEOUT,
DEFAULT_WRITE_TIMEOUT,
ECO_MODE,
ICON,
INPUT_SOURCE,
LAMP,
LAMP_HOURS,
)
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = SWITCH_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_FILENAME): cv.isdevice,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_READ_TIMEOUT, default=DEFAULT_READ_TIMEOUT): cv.positive_int,
vol.Optional(
CONF_WRITE_TIMEOUT, default=DEFAULT_WRITE_TIMEOUT
): cv.positive_int,
}
)
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Connect with serial port and return Acer Projector."""
serial_port = config[CONF_FILENAME]
name = config[CONF_NAME]
read_timeout = config[CONF_READ_TIMEOUT]
write_timeout = config[CONF_WRITE_TIMEOUT]
add_entities([AcerSwitch(serial_port, name, read_timeout, write_timeout)], True)
class AcerSwitch(SwitchEntity):
"""Represents an Acer Projector as a switch."""
_attr_icon = ICON
def __init__(
self,
serial_port: str,
name: str,
read_timeout: int,
write_timeout: int,
) -> None:
"""Init of the Acer projector."""
self._serial_port = serial_port
self._read_timeout = read_timeout
self._write_timeout = write_timeout
self._attr_name = name
self._attributes = {
LAMP_HOURS: STATE_UNKNOWN,
INPUT_SOURCE: STATE_UNKNOWN,
ECO_MODE: STATE_UNKNOWN,
}
def _write_read(self, msg: str) -> str:
"""Write to the projector and read the return."""
# Sometimes the projector won't answer for no reason or the projector
# was disconnected during runtime.
# This way the projector can be reconnected and will still work
try:
with Serial.from_url(
self._serial_port,
read_timeout=self._read_timeout,
write_timeout=self._write_timeout,
) as serial:
serial.write(msg.encode("utf-8"))
# Size is an experience value there is no real limit.
# AFAIK there is no limit and no end character so we will usually
# need to wait for timeout
return serial.read_until(size=20).decode("utf-8")
except (OSError, SerialException, TimeoutError) as exc:
raise HomeAssistantError(
f"Problem communicating with {self._serial_port}"
) from exc
def _write_read_format(self, msg: str) -> str:
"""Write msg, obtain answer and format output."""
# answers are formatted as ***\answer\r***
awns = self._write_read(msg)
if match := re.search(r"\r(.+)\r", awns):
return match.group(1)
return STATE_UNKNOWN
def update(self) -> None:
"""Get the latest state from the projector."""
awns = self._write_read_format(CMD_DICT[LAMP])
if awns == "Lamp 1":
self._attr_is_on = True
self._attr_available = True
elif awns == "Lamp 0":
self._attr_is_on = False
self._attr_available = True
else:
self._attr_available = False
for key in self._attributes:
if msg := CMD_DICT.get(key):
awns = self._write_read_format(msg)
self._attributes[key] = awns
self._attr_extra_state_attributes = self._attributes
@override
def turn_on(self, **kwargs: Any) -> None:
"""Turn the projector on."""
msg = CMD_DICT[STATE_ON]
self._write_read(msg)
self._attr_is_on = True
@override
def turn_off(self, **kwargs: Any) -> None:
"""Turn the projector off."""
msg = CMD_DICT[STATE_OFF]
self._write_read(msg)
self._attr_is_on = False
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: This integration does not have custom service actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: This integration does not subscribe to external events.
@@ -18,9 +18,15 @@ rules:
comment: Data descriptions missing
dependency-transparency: done
docs-actions: done
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: todo
docs-removal-instructions: todo
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: Entities do not explicitly subscribe to events.
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: This integration does not provide additional actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: This integration does not register any events.
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
@@ -14,9 +14,15 @@ rules:
docs-actions:
status: exempt
comment: Integration does not register custom actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: Integration does not subscribe to events.
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: Integration does not register custom actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: Integration does not use event subscriptions.
+1 -1
View File
@@ -8,5 +8,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "platinum",
"requirements": ["airos==0.6.8"]
"requirements": ["airos==0.6.9"]
}
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: airOS does not have actions
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: local_polling without events
@@ -10,9 +10,15 @@ rules:
docs-actions:
status: exempt
comment: Integration does not provide custom actions
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: Integration does not register any service actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: Integration does not subscribe to external events.
@@ -8,7 +8,7 @@ from propcache.api import cached_property
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
from homeassistant.const import ( # noqa: F401
ATTR_CODE,
ATTR_CODE_FORMAT,
SERVICE_ALARM_ARM_AWAY,
@@ -28,11 +28,12 @@ from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.hass_dict import HassKey
from .const import (
from .const import ( # noqa: F401
ATTR_CHANGED_BY,
ATTR_CODE_ARM_REQUIRED,
DOMAIN,
AlarmControlPanelEntityFeature,
AlarmControlPanelEntityStateAttribute,
AlarmControlPanelState,
CodeFormat,
)
@@ -303,9 +304,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
def state_attributes(self) -> dict[str, Any] | None:
"""Return the state attributes."""
return {
ATTR_CODE_FORMAT: self.code_format,
ATTR_CHANGED_BY: self.changed_by,
ATTR_CODE_ARM_REQUIRED: self.code_arm_required,
AlarmControlPanelEntityStateAttribute.CODE_FORMAT: self.code_format,
AlarmControlPanelEntityStateAttribute.CHANGED_BY: self.changed_by,
AlarmControlPanelEntityStateAttribute.CODE_ARM_REQUIRED: (
self.code_arm_required
),
}
@override
@@ -9,6 +9,14 @@ ATTR_CHANGED_BY: Final = "changed_by"
ATTR_CODE_ARM_REQUIRED: Final = "code_arm_required"
class AlarmControlPanelEntityStateAttribute(StrEnum):
"""State attributes for alarm control panel entities."""
CODE_FORMAT = "code_format"
CHANGED_BY = "changed_by"
CODE_ARM_REQUIRED = "code_arm_required"
class AlarmControlPanelState(StrEnum):
"""Alarm control panel entity states."""
@@ -16,6 +16,7 @@ PLATFORMS = [
Platform.EVENT,
Platform.MEDIA_PLAYER,
Platform.NOTIFY,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
Platform.TODO,
@@ -5,6 +5,16 @@
"default": "mdi:chat-processing"
}
},
"select": {
"dropin": {
"default": "mdi:account-multiple-plus",
"state": {
"all": "mdi:account-multiple-plus",
"home": "mdi:home-plus",
"off": "mdi:phone-off"
}
}
},
"sensor": {
"voc_index": {
"default": "mdi:molecule"
@@ -23,6 +23,7 @@ class AmazonNotifyEntityDescription(NotifyEntityDescription):
"""Alexa Devices notify entity description."""
is_supported: Callable[[AmazonDevice], bool] = lambda _device: True
is_available_fn: Callable[[AmazonDevice], bool] = lambda _device: True
method: Callable[[AmazonEchoApi, AmazonDevice, str], Awaitable[None]]
subkey: str
@@ -39,6 +40,9 @@ NOTIFY: Final = (
key="announce",
translation_key="announce",
subkey="AUDIO_PLAYER",
is_available_fn=lambda device: (
device.communication_settings.get("communications") != "OFF"
),
method=lambda api, device, message: api.call_alexa_announcement(
device, message
),
@@ -79,6 +83,14 @@ class AmazonNotifyEntity(AmazonEntity, NotifyEntity):
entity_description: AmazonNotifyEntityDescription
@property
@override
def available(self) -> bool:
"""Return if entity is available."""
return (
self.entity_description.is_available_fn(self.device) and super().available
)
@override
async def async_send_message(
self, message: str, title: str | None = None, **kwargs: Any
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: no actions
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: entities do not explicitly subscribe to events
@@ -0,0 +1,105 @@
"""Support for select entities."""
from dataclasses import dataclass
from typing import TYPE_CHECKING, Final, override
from aioamazondevices.structures import AmazonDropInStatus
from homeassistant.components.select import SelectEntity, SelectEntityDescription
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import AmazonConfigEntry, AmazonDevice, alexa_api_call
from .entity import AmazonEntity
PARALLEL_UPDATES = 1
@dataclass(frozen=True, kw_only=True)
class AmazonSelectEntityDescription(SelectEntityDescription):
"""Alexa Devices select entity description."""
method: str
SELECTS: Final = (
AmazonSelectEntityDescription(
key="dropin",
translation_key="dropin",
entity_category=EntityCategory.CONFIG,
method="set_dropin_status",
# API values: "All", "Home", "Off"
options=[status.value.lower() for status in AmazonDropInStatus],
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: AmazonConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up select entities based on a config entry."""
coordinator = entry.runtime_data
known_devices: set[str] = set()
def _check_device() -> None:
current_devices = set(coordinator.data)
new_devices = current_devices - known_devices
if new_devices:
known_devices.update(new_devices)
select_entities = [
AmazonSelectEntity(coordinator, serial_num, select_desc)
for select_desc in SELECTS
for serial_num in new_devices
if select_desc.key
in coordinator.data[serial_num].communication_settings
]
async_add_entities(select_entities)
_check_device()
entry.async_on_unload(coordinator.async_add_listener(_check_device))
class AmazonSelectEntity(AmazonEntity, SelectEntity):
"""Representation of a select entity for the default Alexa device."""
entity_description: AmazonSelectEntityDescription
@property
@override
def options(self) -> list[str]:
"""Return a list of available options."""
if TYPE_CHECKING:
assert self.entity_description.options is not None
return self.entity_description.options
@property
def _device(self) -> AmazonDevice:
"""Return the device."""
return self.coordinator.data[self._serial_num]
@override
async def async_added_to_hass(self) -> None:
"""Restore last known option."""
await super().async_added_to_hass()
self._attr_current_option = self._device.communication_settings[
self.entity_description.key
].lower()
@override
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
method = getattr(self.coordinator.api, self.entity_description.method)
if TYPE_CHECKING:
assert method is not None
async with alexa_api_call(self.coordinator):
await method(self.device, AmazonDropInStatus(option.capitalize()))
self._attr_current_option = option
self.async_write_ha_state()
@@ -78,6 +78,16 @@
"name": "Speak"
}
},
"select": {
"dropin": {
"name": "Drop In",
"state": {
"all": "Allow drop in from anyone",
"home": "Allow drop in from household members",
"off": "Don't allow drop in"
}
}
},
"sensor": {
"alarm": {
"name": "Next alarm"
@@ -140,6 +150,9 @@
"invalid_sound_value": {
"message": "Invalid sound {sound} specified"
},
"select_option_not_found": {
"message": "Selected option not found: {option}"
},
"unknown_exception": {
"message": "Unknown error occurred: {error}"
}
@@ -1,6 +1,6 @@
"""Platform for Alexa To-do integration."""
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, override
from aioamazondevices.structures import (
AmazonListInfo,
@@ -88,6 +88,7 @@ class AlexaToDoList(AmazonServiceEntity, TodoListEntity):
)
@property
@override
def todo_items(self) -> list[TodoItem]:
"""Return all to-do items in the list."""
@@ -104,6 +105,7 @@ class AlexaToDoList(AmazonServiceEntity, TodoListEntity):
for item in todo_items
]
@override
async def async_create_todo_item(self, item: TodoItem) -> None:
"""Add an item to the To-do list."""
_LOGGER.debug(
@@ -124,6 +126,7 @@ class AlexaToDoList(AmazonServiceEntity, TodoListEntity):
self._list.name,
)
@override
async def async_delete_todo_items(self, uids: list[str]) -> None:
"""Delete items from the to-do list."""
_LOGGER.debug("Called async_delete_todo_items for %s item(s)", len(uids))
@@ -150,6 +153,7 @@ class AlexaToDoList(AmazonServiceEntity, TodoListEntity):
existing_item.version,
)
@override
async def async_update_todo_item(self, item: TodoItem) -> None:
"""Update an item in the To-do list."""
list_items_lookup = self.coordinator.todo_list_items[self._list.id]
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
@@ -1 +0,0 @@
"""The Ampio component."""
@@ -1,105 +0,0 @@
"""Support for Ampio Air Quality data."""
import logging
from typing import Final, override
from asmog import AmpioSmog
import voluptuous as vol
from homeassistant.components.air_quality import (
PLATFORM_SCHEMA as AIR_QUALITY_PLATFORM_SCHEMA,
AirQualityEntity,
)
from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import Throttle
from .const import CONF_STATION_ID, SCAN_INTERVAL
_LOGGER: Final = logging.getLogger(__name__)
PLATFORM_SCHEMA: Final = AIR_QUALITY_PLATFORM_SCHEMA.extend(
{vol.Required(CONF_STATION_ID): cv.string, vol.Optional(CONF_NAME): cv.string}
)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the Ampio Smog air quality platform."""
name = config.get(CONF_NAME)
station_id = config[CONF_STATION_ID]
session = async_get_clientsession(hass)
api = AmpioSmogMapData(AmpioSmog(station_id, hass.loop, session))
await api.async_update()
if not api.api.data:
_LOGGER.error("Station %s is not available", station_id)
return
async_add_entities([AmpioSmogQuality(api, station_id, name)], True)
class AmpioSmogQuality(AirQualityEntity):
"""Implementation of an Ampio Smog air quality entity."""
_attr_attribution = "Data provided by Ampio"
def __init__(
self, api: AmpioSmogMapData, station_id: str, name: str | None
) -> None:
"""Initialize the air quality entity."""
self._ampio = api
self._station_id = station_id
self._name = name or api.api.name
@property
@override
def name(self) -> str:
"""Return the name of the air quality entity."""
return self._name
@property
@override
def unique_id(self) -> str:
"""Return unique_name."""
return f"ampio_smog_{self._station_id}" # pylint: disable=home-assistant-entity-unique-id-redundant-domain
@property
@override
def particulate_matter_2_5(self) -> str | None:
"""Return the particulate matter 2.5 level."""
return self._ampio.api.pm2_5 # type: ignore[no-any-return]
@property
@override
def particulate_matter_10(self) -> str | None:
"""Return the particulate matter 10 level."""
return self._ampio.api.pm10 # type: ignore[no-any-return]
async def async_update(self) -> None:
"""Get the latest data from the AmpioMap API."""
await self._ampio.async_update()
class AmpioSmogMapData:
"""Get the latest data and update the states."""
def __init__(self, api: AmpioSmog) -> None:
"""Initialize the data object."""
self.api = api
@Throttle(SCAN_INTERVAL)
async def async_update(self) -> None:
"""Get the latest data from AmpioMap."""
await self.api.get_data()
-7
View File
@@ -1,7 +0,0 @@
"""Constants for Ampio Air Quality platform."""
from datetime import timedelta
from typing import Final
CONF_STATION_ID: Final = "station_id"
SCAN_INTERVAL: Final = timedelta(minutes=10)
@@ -1,10 +0,0 @@
{
"domain": "ampio",
"name": "Ampio Smart Smog System",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/ampio",
"iot_class": "cloud_polling",
"loggers": ["asmog"],
"quality_scale": "legacy",
"requirements": ["asmog==0.0.6"]
}
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: todo
docs-installation-instructions: todo
docs-removal-instructions: todo
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
@@ -12,9 +12,15 @@ rules:
config-flow: done
dependency-transparency: done
docs-actions: done
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
No custom actions are defined.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
@@ -8,6 +8,6 @@
"documentation": "https://www.home-assistant.io/integrations/anthropic",
"integration_type": "service",
"iot_class": "cloud_polling",
"quality_scale": "silver",
"quality_scale": "gold",
"requirements": ["anthropic==0.108.0"]
}
@@ -4,10 +4,7 @@ rules:
status: exempt
comment: |
Integration has no actions.
appropriate-polling:
status: exempt
comment: |
Integration does not poll.
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage: done
@@ -17,9 +14,15 @@ rules:
status: exempt
comment: |
Integration has no actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
@@ -11,9 +11,15 @@ rules:
status: exempt
comment: |
The integration does not provide any actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
@@ -210,6 +210,7 @@ class AqvifyAggrDataCoordinator(
end_time = base_time.replace(minute=59).strftime(date_time_fmt)
return beg_time, end_time
@override
async def _async_update_data(self) -> dict[str, AqvifyHourAggregatedValues]:
"""Fetch device state."""
devices = self.config_entry.runtime_data.coordinator.data.devices
+7 -7
View File
@@ -1,23 +1,23 @@
{
"entity": {
"sensor": {
"available_volume": {
"default": "mdi:car-coolant-level"
},
"ground_water_level": {
"default": "mdi:arrow-collapse-down"
},
"in_flow": {
"default": "mdi:water-plus-outline"
},
"meter_value": {
"level_from_sensor": {
"default": "mdi:waves-arrow-up"
},
"level_from_top": {
"default": "mdi:waves"
},
"out_volume": {
"default": "mdi:water-pump"
},
"stored_volume": {
"default": "mdi:car-coolant-level"
},
"water_level": {
"default": "mdi:waves"
}
}
}
@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["pyaqvify"],
"quality_scale": "platinum",
"requirements": ["pyaqvify==0.0.11"]
"requirements": ["pyaqvify==0.0.12"]
}
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
The integration does not provide any actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
+7 -6
View File
@@ -46,8 +46,8 @@ class AqvifySensorAggrEntityDescription(SensorEntityDescription):
ENTITIES: tuple[AqvifySensorEntityDescription, ...] = (
AqvifySensorEntityDescription(
key="meter_value",
translation_key="meter_value",
key="level_from_sensor",
translation_key="level_from_sensor",
native_unit_of_measurement=UnitOfLength.METERS,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DISTANCE,
@@ -55,8 +55,8 @@ ENTITIES: tuple[AqvifySensorEntityDescription, ...] = (
value_fn=lambda value: value.meter_value,
),
AqvifySensorEntityDescription(
key="water_level",
translation_key="water_level",
key="level_from_top",
translation_key="level_from_top",
native_unit_of_measurement=UnitOfLength.METERS,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DISTANCE,
@@ -64,8 +64,8 @@ ENTITIES: tuple[AqvifySensorEntityDescription, ...] = (
value_fn=lambda value: value.water_level,
),
AqvifySensorEntityDescription(
key="stored_volume",
translation_key="stored_volume",
key="available_volume",
translation_key="available_volume",
native_unit_of_measurement=UnitOfVolume.LITERS,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.VOLUME_STORAGE,
@@ -171,6 +171,7 @@ class AqvifyAggrSensor(AqvifyAggrEntity, SensorEntity):
entity_description: AqvifySensorAggrEntityDescription
@property
@override
def native_value(self) -> StateType | datetime | None:
"""Return the state of the sensor."""
return self.entity_description.value_fn(self.coordinator.data[self.device_key])
+8 -8
View File
@@ -34,23 +34,23 @@
},
"entity": {
"sensor": {
"available_volume": {
"name": "Available volume"
},
"ground_water_level": {
"name": "Ground water level"
},
"in_flow": {
"name": "Inflow"
},
"meter_value": {
"name": "Meter value"
"level_from_sensor": {
"name": "Level from sensor"
},
"level_from_top": {
"name": "Level from top"
},
"out_volume": {
"name": "Outflow"
},
"stored_volume": {
"name": "Stored volume"
},
"water_level": {
"name": "Water level"
}
}
},
+1 -1
View File
@@ -6,5 +6,5 @@
"iot_class": "cloud_polling",
"loggers": ["pyatome"],
"quality_scale": "legacy",
"requirements": ["pyAtome==0.1.1"]
"requirements": ["pyAtome==0.1.2"]
}
@@ -14,9 +14,15 @@ rules:
status: exempt
comment: |
This integration does not provide additional actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
@@ -12,7 +12,7 @@ import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.components.blueprint import CONF_USE_BLUEPRINT
from homeassistant.const import (
from homeassistant.const import ( # noqa: F401
ATTR_AREA_ID,
ATTR_ENTITY_ID,
ATTR_FLOOR_ID,
@@ -58,7 +58,7 @@ from homeassistant.helpers.issue_registry import (
async_delete_issue,
)
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.script import (
from homeassistant.helpers.script import ( # noqa: F401
ATTR_CUR,
ATTR_MAX,
CONF_MAX,
@@ -91,6 +91,8 @@ from .const import (
DEFAULT_INITIAL_STATE,
DOMAIN,
LOGGER,
AutomationEntityCapabilityAttribute,
AutomationEntityStateAttribute,
)
from .helpers import async_get_blueprints
from .trace import trace_automation
@@ -318,7 +320,13 @@ class BaseAutomationEntity(ToggleEntity, ABC):
"""Base class for automation entities."""
_entity_component_unrecorded_attributes = frozenset(
(ATTR_LAST_TRIGGERED, ATTR_MODE, ATTR_CUR, ATTR_MAX, CONF_ID)
(
AutomationEntityStateAttribute.LAST_TRIGGERED,
AutomationEntityStateAttribute.MODE,
AutomationEntityStateAttribute.CUR,
AutomationEntityStateAttribute.MAX,
AutomationEntityCapabilityAttribute.ID,
)
)
raw_config: ConfigType | None
@@ -327,7 +335,7 @@ class BaseAutomationEntity(ToggleEntity, ABC):
def capability_attributes(self) -> dict[str, Any] | None:
"""Return capability attributes."""
if self.unique_id is not None:
return {CONF_ID: self.unique_id}
return {AutomationEntityCapabilityAttribute.ID: self.unique_id}
return None
@cached_property
@@ -507,13 +515,15 @@ class AutomationEntity(BaseAutomationEntity, RestoreEntity):
@override
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the entity state attributes."""
attrs = {
ATTR_LAST_TRIGGERED: self.action_script.last_triggered,
ATTR_MODE: self.action_script.script_mode,
ATTR_CUR: self.action_script.runs,
attrs: dict[str, Any] = {
AutomationEntityStateAttribute.LAST_TRIGGERED: (
self.action_script.last_triggered
),
AutomationEntityStateAttribute.MODE: self.action_script.script_mode,
AutomationEntityStateAttribute.CUR: self.action_script.runs,
}
if self.action_script.supports_max:
attrs[ATTR_MAX] = self.action_script.max_runs
attrs[AutomationEntityStateAttribute.MAX] = self.action_script.max_runs
return attrs
@property
@@ -1,10 +1,27 @@
"""Constants for the automation integration."""
from enum import StrEnum
import logging
CONF_TRIGGER_VARIABLES = "trigger_variables"
DOMAIN = "automation"
class AutomationEntityCapabilityAttribute(StrEnum):
"""Capability attributes for automation entities."""
ID = "id"
class AutomationEntityStateAttribute(StrEnum):
"""State attributes for automation entities."""
LAST_TRIGGERED = "last_triggered"
MODE = "mode"
CUR = "current"
MAX = "max"
CONF_HIDE_ENTITY = "hide_entity"
CONF_CONDITION_TYPE = "condition_type"
@@ -16,9 +16,15 @@ rules:
status: exempt
comment: |
Integration does not provide custom services.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
@@ -38,7 +38,7 @@
"data_description": {
"host": "The URL of your Autoskope API endpoint. Only change this if you use a white-label portal."
},
"name": "Advanced settings"
"name": "Additional settings"
}
},
"title": "Connect to Autoskope"
@@ -1 +0,0 @@
"""The avion component."""
-123
View File
@@ -1,123 +0,0 @@
"""Support for Avion dimmers."""
import importlib
import time
from typing import Any, override
import voluptuous as vol
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
PLATFORM_SCHEMA as LIGHT_PLATFORM_SCHEMA,
ColorMode,
LightEntity,
)
from homeassistant.const import (
CONF_API_KEY,
CONF_DEVICES,
CONF_ID,
CONF_NAME,
CONF_PASSWORD,
CONF_USERNAME,
)
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
DEVICE_SCHEMA = vol.Schema(
{
vol.Required(CONF_API_KEY): cv.string,
vol.Optional(CONF_ID): cv.positive_int,
vol.Optional(CONF_NAME): cv.string,
}
)
PLATFORM_SCHEMA = LIGHT_PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA},
vol.Optional(CONF_USERNAME): cv.string,
vol.Optional(CONF_PASSWORD): cv.string,
}
)
def setup_platform(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up an Avion switch."""
avion = importlib.import_module("avion")
lights = [
AvionLight(
avion.Avion(
mac=address,
passphrase=device_config[CONF_API_KEY],
name=device_config.get(CONF_NAME),
object_id=device_config.get(CONF_ID),
connect=False,
)
)
for address, device_config in config[CONF_DEVICES].items()
]
if CONF_USERNAME in config and CONF_PASSWORD in config:
lights.extend(
AvionLight(device)
for device in avion.get_devices(
config[CONF_USERNAME], config[CONF_PASSWORD]
)
)
add_entities(lights)
class AvionLight(LightEntity):
"""Representation of an Avion light."""
_attr_support_color_mode = ColorMode.BRIGHTNESS
_attr_support_color_modes = {ColorMode.BRIGHTNESS}
_attr_should_poll = False
_attr_assumed_state = True
_attr_is_on = True
def __init__(self, device):
"""Initialize the light."""
self._attr_name = device.name
self._attr_unique_id = device.mac
self._attr_brightness = 255
self._switch = device
def set_state(self, brightness):
"""Set the state of this lamp to the provided brightness."""
avion = importlib.import_module("avion")
# Bluetooth LE is unreliable, and the connection may drop at any
# time. Make an effort to re-establish the link.
initial = time.monotonic()
while True:
if time.monotonic() - initial >= 10:
return False
try:
self._switch.set_brightness(brightness)
break
except avion.AvionException:
self._switch.connect()
return True
@override
def turn_on(self, **kwargs: Any) -> None:
"""Turn the specified or all lights on."""
if (brightness := kwargs.get(ATTR_BRIGHTNESS)) is not None:
self._attr_brightness = brightness
self.set_state(self.brightness)
self._attr_is_on = True
@override
def turn_off(self, **kwargs: Any) -> None:
"""Turn the specified or all lights off."""
self.set_state(0)
self._attr_is_on = False
@@ -1,9 +0,0 @@
{
"domain": "avion",
"name": "Avi-on",
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/avion",
"iot_class": "assumed_state",
"quality_scale": "legacy",
"requirements": ["avion==0.10"]
}
@@ -28,9 +28,15 @@ rules:
comment: |
Dependency is not built in the CI
docs-actions: todo
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: No explicit event subscription
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: This integration does not have any custom actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: Entities of this integration does not explicitly subscribe to events.
+24 -20
View File
@@ -27,6 +27,7 @@ from homeassistant.const import (
)
from homeassistant.core import callback
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.selector import TextSelector, TextSelectorConfig
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from homeassistant.helpers.service_info.ssdp import (
ATTR_UPNP_FRIENDLY_NAME,
@@ -98,8 +99,11 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
errors["base"] = "cannot_connect"
else:
if (serial := self._get_serial_number(api)) is None:
return self.async_abort(reason="no_serial_number")
if not self.unique_id:
if (serial := self._get_formatted_serial(api)) is None:
return self.async_abort(reason="no_serial_number")
await self.async_set_unique_id(serial)
config = {
CONF_PROTOCOL: user_input[CONF_PROTOCOL],
CONF_HOST: user_input[CONF_HOST],
@@ -108,8 +112,6 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
CONF_PASSWORD: user_input[CONF_PASSWORD],
}
await self.async_set_unique_id(format_mac(serial))
if self.source == SOURCE_REAUTH:
self._abort_if_unique_id_mismatch()
return self.async_update_and_abort(
@@ -124,7 +126,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
self.config = config | {CONF_MODEL: api.vapix.product_number}
return await self._create_entry(serial)
return await self._create_entry()
data = self.discovery_schema or {
vol.Required(CONF_PROTOCOL): vol.In(PROTOCOL_CHOICES),
@@ -141,7 +143,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
errors=errors,
)
async def _create_entry(self, serial: str) -> ConfigFlowResult:
async def _create_entry(self) -> ConfigFlowResult:
"""Create entry for device.
Use the discovered device name when available.
@@ -149,7 +151,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
if (title_placeholders := self.context.get("title_placeholders")) is not None:
name = title_placeholders[CONF_NAME]
else:
name = f"{self.config[CONF_MODEL]} - {serial}"
name = f"{self.config[CONF_MODEL]} - {self.unique_id}"
self.config[CONF_NAME] = name
return self.async_create_entry(title=name, data=self.config)
@@ -196,7 +198,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
return await self._process_discovered_device(
{
CONF_HOST: discovery_info.ip,
CONF_MAC: format_mac(discovery_info.macaddress),
CONF_MAC: discovery_info.macaddress,
CONF_NAME: discovery_info.hostname,
CONF_PORT: 80,
}
@@ -211,7 +213,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
return await self._process_discovered_device(
{
CONF_HOST: url.hostname,
CONF_MAC: format_mac(discovery_info.upnp[ATTR_UPNP_SERIAL]),
CONF_MAC: discovery_info.upnp[ATTR_UPNP_SERIAL],
CONF_NAME: f"{discovery_info.upnp[ATTR_UPNP_FRIENDLY_NAME]}",
CONF_PORT: url.port,
}
@@ -225,7 +227,7 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
return await self._process_discovered_device(
{
CONF_HOST: discovery_info.host,
CONF_MAC: format_mac(discovery_info.properties["macaddress"]),
CONF_MAC: discovery_info.properties["macaddress"],
CONF_NAME: discovery_info.name.split(".", 1)[0],
CONF_PORT: discovery_info.port,
}
@@ -235,17 +237,17 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
self, discovery_info: dict[str, Any]
) -> ConfigFlowResult:
"""Prepare configuration for a discovered Axis device."""
if discovery_info[CONF_MAC][:8] not in AXIS_OUI:
serial = format_mac(discovery_info[CONF_MAC])
if serial[:8] not in AXIS_OUI:
return self.async_abort(reason="not_axis_device")
if is_link_local(ip_address(discovery_info[CONF_HOST])):
return self.async_abort(reason="link_local_address")
await self.async_set_unique_id(discovery_info[CONF_MAC])
self._abort_if_unique_id_configured(
updates={CONF_HOST: discovery_info[CONF_HOST]}, reload_on_update=False
)
if await self.async_set_unique_id(serial):
self._abort_if_unique_id_configured(
updates={CONF_HOST: discovery_info[CONF_HOST]}, reload_on_update=False
)
self.context.update(
{
@@ -259,7 +261,9 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
self.discovery_schema = {
vol.Required(CONF_PROTOCOL): vol.In(PROTOCOL_CHOICES),
vol.Required(CONF_HOST, default=discovery_info[CONF_HOST]): str,
vol.Required(CONF_HOST, default=discovery_info[CONF_HOST]): TextSelector(
TextSelectorConfig(read_only=True)
),
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
@@ -268,16 +272,16 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
return await self.async_step_user()
@staticmethod
def _get_serial_number(api: axis.AxisDevice) -> str | None:
def _get_formatted_serial(api: axis.AxisDevice) -> str | None:
"""Retrieve the device serial number from the Axis API.
Tries basic_device_info first, then property_handler. Returns None if not found.
"""
vapix = api.vapix
if vapix.basic_device_info.initialized:
return vapix.basic_device_info["0"].serial_number
return format_mac(vapix.basic_device_info["0"].serial_number)
if vapix.params.property_handler.initialized:
return vapix.params.property_handler["0"].system_serial_number
return format_mac(vapix.params.property_handler["0"].system_serial_number)
return None
@@ -16,9 +16,15 @@ rules:
status: exempt
comment: |
This integration does not have any custom actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
@@ -14,9 +14,15 @@ rules:
docs-actions:
status: exempt
comment: This integration does not have any custom actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: Entities of this integration do not explicitly subscribe to events.
@@ -1 +0,0 @@
"""The beewi_smartclim component."""
@@ -1,10 +0,0 @@
{
"domain": "beewi_smartclim",
"name": "BeeWi SmartClim BLE sensor",
"codeowners": ["@alemuro"],
"documentation": "https://www.home-assistant.io/integrations/beewi_smartclim",
"iot_class": "local_polling",
"loggers": ["beewi_smartclim"],
"quality_scale": "legacy",
"requirements": ["beewi-smartclim==0.0.10"]
}
@@ -1,84 +0,0 @@
"""Platform for beewi_smartclim integration."""
from beewi_smartclim import BeewiSmartClimPoller
import voluptuous as vol
from homeassistant.components.sensor import (
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
SensorDeviceClass,
SensorEntity,
)
from homeassistant.const import CONF_MAC, CONF_NAME, PERCENTAGE, UnitOfTemperature
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 values
DEFAULT_NAME = "BeeWi SmartClim"
# Sensor config
SENSOR_TYPES = [
[SensorDeviceClass.TEMPERATURE, "Temperature", UnitOfTemperature.CELSIUS],
[SensorDeviceClass.HUMIDITY, "Humidity", PERCENTAGE],
[SensorDeviceClass.BATTERY, "Battery", PERCENTAGE],
]
PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_MAC): 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 the beewi_smartclim platform."""
mac = config[CONF_MAC]
prefix = config[CONF_NAME]
poller = BeewiSmartClimPoller(mac)
sensors = []
for sensor_type in SENSOR_TYPES:
device = sensor_type[0]
name = sensor_type[1]
unit = sensor_type[2]
# `prefix` is the name configured by the user for the sensor, we're appending
# the device type at the end of the name (garden -> garden temperature)
if prefix:
name = f"{prefix} {name}"
sensors.append(BeewiSmartclimSensor(poller, name, mac, device, unit))
add_entities(sensors)
class BeewiSmartclimSensor(SensorEntity):
"""Representation of a Sensor."""
def __init__(self, poller, name, mac, device, unit):
"""Initialize the sensor."""
self._poller = poller
self._attr_name = name
self._device = device
self._attr_native_unit_of_measurement = unit
self._attr_device_class = self._device
self._attr_unique_id = f"{mac}_{device}"
def update(self) -> None:
"""Fetch new state data from the poller."""
self._poller.update_sensor()
self._attr_native_value = None
if self._device == SensorDeviceClass.TEMPERATURE:
self._attr_native_value = self._poller.get_temperature()
if self._device == SensorDeviceClass.HUMIDITY:
self._attr_native_value = self._poller.get_humidity()
if self._device == SensorDeviceClass.BATTERY:
self._attr_native_value = self._poller.get_battery()
@@ -17,10 +17,10 @@
"requirements": [
"bleak==3.0.2",
"bleak-retry-connector==4.6.1",
"bluetooth-adapters==2.3.0",
"bluetooth-adapters==2.4.0",
"bluetooth-auto-recovery==1.6.4",
"bluetooth-data-tools==1.29.18",
"dbus-fast==5.0.16",
"habluetooth==6.8.3"
"dbus-fast==5.0.22",
"habluetooth==6.23.1"
]
}
@@ -14,9 +14,15 @@ rules:
config-flow: done
dependency-transparency: done
docs-actions: done
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
@@ -2,13 +2,13 @@
"domain": "bosch_shc",
"name": "Bosch SHC",
"after_dependencies": ["zeroconf"],
"codeowners": ["@tschamm"],
"codeowners": ["@tschamm", "@mosandlt"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/bosch_shc",
"integration_type": "hub",
"iot_class": "local_push",
"loggers": ["boschshcpy"],
"requirements": ["boschshcpy==0.2.111"],
"requirements": ["boschshcpy==0.3.5"],
"zeroconf": [
{
"name": "bosch shc*",
@@ -10,9 +10,15 @@ rules:
config-flow: done
dependency-transparency: done
docs-actions: done
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: The integration registers no events
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: The integration does not register services.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
@@ -8,9 +8,15 @@ rules:
config-flow: done
dependency-transparency: done
docs-actions: done
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: |
+14 -7
View File
@@ -68,6 +68,7 @@ from .const import (
EVENT_UID,
LIST_EVENT_FIELDS,
CalendarEntityFeature,
CalendarEntityStateAttribute,
)
# mypy: disallow-any-generics
@@ -519,7 +520,9 @@ class CalendarEntity(Entity):
entity_description: CalendarEntityDescription
_entity_component_unrecorded_attributes = frozenset({"description"})
_entity_component_unrecorded_attributes = frozenset(
{CalendarEntityStateAttribute.DESCRIPTION}
)
_alarm_unsubs: list[CALLBACK_TYPE] | None = None
_event_listeners: (
@@ -573,12 +576,16 @@ class CalendarEntity(Entity):
return None
return {
"message": event.summary,
"all_day": event.all_day,
"start_time": event.start_datetime_local.strftime(DATE_STR_FORMAT),
"end_time": event.end_datetime_local.strftime(DATE_STR_FORMAT),
"location": event.location or "",
"description": event.description or "",
CalendarEntityStateAttribute.MESSAGE: event.summary,
CalendarEntityStateAttribute.ALL_DAY: event.all_day,
CalendarEntityStateAttribute.START_TIME: event.start_datetime_local.strftime(
DATE_STR_FORMAT
),
CalendarEntityStateAttribute.END_TIME: event.end_datetime_local.strftime(
DATE_STR_FORMAT
),
CalendarEntityStateAttribute.LOCATION: event.location or "",
CalendarEntityStateAttribute.DESCRIPTION: event.description or "",
}
@final
+12 -1
View File
@@ -1,6 +1,6 @@
"""Constants for calendar components."""
from enum import IntFlag
from enum import IntFlag, StrEnum
from typing import TYPE_CHECKING
from homeassistant.util.hass_dict import HassKey
@@ -14,6 +14,17 @@ DOMAIN = "calendar"
DATA_COMPONENT: HassKey[EntityComponent[CalendarEntity]] = HassKey(DOMAIN)
class CalendarEntityStateAttribute(StrEnum):
"""State attributes for calendar entities."""
MESSAGE = "message"
ALL_DAY = "all_day"
START_TIME = "start_time"
END_TIME = "end_time"
LOCATION = "location"
DESCRIPTION = "description"
class CalendarEntityFeature(IntFlag):
"""Supported features of the calendar entity."""
@@ -14,9 +14,15 @@ rules:
config-flow: done
dependency-transparency: done
docs-actions: done
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
+11 -6
View File
@@ -68,6 +68,7 @@ from .const import (
PREF_ORIENTATION,
PREF_PRELOAD_STREAM,
SERVICE_RECORD,
CameraEntityStateAttribute,
CameraState,
StreamType,
)
@@ -421,7 +422,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""The base class for camera entities."""
_entity_component_unrecorded_attributes = frozenset(
{"access_token", "entity_picture"}
{CameraEntityStateAttribute.ACCESS_TOKEN, "entity_picture"}
)
# Entity Properties
@@ -649,18 +650,22 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@final
@property
@override
def state_attributes(self) -> dict[str, str | None]:
def state_attributes(self) -> dict[str, str | bool | None]:
"""Return the camera state attributes."""
attrs = {"access_token": self.access_tokens[-1]}
attrs: dict[str, str | bool | None] = {
CameraEntityStateAttribute.ACCESS_TOKEN: self.access_tokens[-1]
}
if model := self.model:
attrs["model_name"] = model
attrs[CameraEntityStateAttribute.MODEL_NAME] = model
if brand := self.brand:
attrs["brand"] = brand
attrs[CameraEntityStateAttribute.BRAND] = brand
if motion_detection_enabled := self.motion_detection_enabled:
attrs["motion_detection"] = motion_detection_enabled
attrs[CameraEntityStateAttribute.MOTION_DETECTION] = (
motion_detection_enabled
)
return attrs
+9
View File
@@ -28,6 +28,15 @@ CAMERA_STREAM_SOURCE_TIMEOUT: Final = 10
CAMERA_IMAGE_TIMEOUT: Final = 10
class CameraEntityStateAttribute(StrEnum):
"""State attributes for camera entities."""
ACCESS_TOKEN = "access_token"
MODEL_NAME = "model_name"
BRAND = "brand"
MOTION_DETECTION = "motion_detection"
class CameraState(StrEnum):
"""Camera entity states."""
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: No custom actions/services.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
@@ -174,6 +174,13 @@ class CCM15Climate(CoordinatorEntity[CCM15Coordinator], ClimateEntity):
"""Set the fan mode."""
await self.coordinator.async_set_fan_mode(self._ac_index, self.data, fan_mode)
@override
async def async_set_swing_mode(self, swing_mode: str) -> None:
"""Set the swing mode."""
await self.coordinator.async_set_swing_mode(
self._ac_index, self.data, swing_mode
)
@override
async def async_turn_off(self) -> None:
"""Turn off."""
+10 -2
View File
@@ -4,10 +4,10 @@ import datetime
import logging
from typing import override
from ccm15 import CCM15Device, CCM15DeviceState, CCM15SlaveDevice
from ccm15 import CCM15Device, CCM15DeviceState, CCM15SlaveDevice, TriState
import httpx
from homeassistant.components.climate import HVACMode
from homeassistant.components.climate import SWING_ON, HVACMode
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.httpx_client import get_async_client
@@ -86,6 +86,14 @@ class CCM15Coordinator(DataUpdateCoordinator[CCM15DeviceState]):
data.fan_mode = CONST_FAN_CMD_MAP[fan_mode]
await self.async_set_state(ac_index, data)
async def async_set_swing_mode(
self, ac_index: int, data: CCM15SlaveDevice, swing_mode: str
) -> None:
"""Set the swing mode."""
_LOGGER.debug("Set Swing[%s]='%s'", ac_index, swing_mode)
data.desired_swing = TriState.ON if swing_mode == SWING_ON else TriState.OFF
await self.async_set_state(ac_index, data)
async def async_set_temperature(
self,
ac_index: int,
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: This integration does not provide actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
@@ -18,9 +18,15 @@ rules:
docs-actions:
status: exempt
comment: Integration does not register custom actions.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: Integration does not subscribe to events.
@@ -15,9 +15,15 @@ rules:
docs-actions:
status: exempt
comment: No custom actions are defined.
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: done
comment: |
@@ -12,9 +12,15 @@ rules:
docs-actions:
status: exempt
comment: There are no custom actions
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup:
status: exempt
comment: Entities do not explicitly subscribe to events
@@ -8,9 +8,15 @@ rules:
config-flow: done
dependency-transparency: done
docs-actions: done
docs-conditions:
status: exempt
comment: This integration does not have any conditions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-triggers:
status: exempt
comment: This integration does not have any triggers.
entity-event-setup: done
entity-unique-id: done
has-entity-name: done

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