Compare commits

..

780 Commits

Author SHA1 Message Date
farmio
2f15f5b4ec Move DPT translations into config_panel root key 2025-12-18 15:57:28 +01:00
farmio
5628a13e7d Add missing DPT sensor device classes 2025-12-18 15:57:28 +01:00
farmio
dce0ff0973 Add DPT name translations 2025-12-18 15:57:28 +01:00
farmio
09c9a51dcb add dpt selector translations 2025-12-18 15:57:28 +01:00
farmio
d661aa129a validate device_class, state_class and unit 2025-12-18 15:57:28 +01:00
farmio
27570233f9 sort in BE for test consistency 2025-12-18 15:57:28 +01:00
farmio
2632b6682b unit_o_m, device_class translations 2025-12-18 15:57:28 +01:00
Matthias Alphart
69a3dd8246 Update strings.json
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-12-18 15:57:27 +01:00
farmio
8a2dde73dd add tests 2025-12-18 15:57:27 +01:00
farmio
81d0c4638d Support KNX sensor entity configuration from UI 2025-12-18 15:57:27 +01:00
Matthias Alphart
3c865c6f41 Support KNX fan entity configuration from UI (#159167) 2025-12-18 15:54:55 +01:00
Raphael Hehl
3b32c4bcbf Remove custom device_class from unifiprotect doorbell_text select entity (#159366)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2025-12-18 15:51:16 +01:00
dependabot[bot]
fcdc1cfed9 Bump github/codeql-action from 4.31.8 to 4.31.9 (#159248)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-18 15:45:47 +01:00
wollew
0fd782c4ab Raise exception if velux integration setup fails because of connection erros (#159231) 2025-12-18 15:44:42 +01:00
adam-the-hero
bbcaf69973 Bump quality scale for watergate to silver (#155353) 2025-12-18 15:30:15 +01:00
Denis Shulyaka
f2b713acac Exclude gpt-4o model from extended caching (#159362) 2025-12-18 08:48:01 -05:00
LG-ThinQ-Integration
6c944d6b15 Adds a delay to the continuous control of the climate (#151177)
Co-authored-by: yunseon.park <yunseon.park@lge.com>
2025-12-18 14:46:11 +01:00
Raphael Hehl
4dd3abb16a Fix device classes in unifiprotect integration (#159281)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2025-12-18 14:43:44 +01:00
adam-the-hero
d2672b9ddf Introduce session inject to watergate integration (#159360) 2025-12-18 14:08:57 +01:00
Matthias Alphart
ff30492919 KNX unit tests: patch CEMIHandler at class level (#159317) 2025-12-18 14:02:02 +01:00
Duco Sebel
b5ccdf8165 Implement new battery charge modes in HomeWizard (#159107) 2025-12-18 14:01:37 +01:00
Robert Resch
b3c745cfa7 Bump go2rtc to 1.9.13 (#159043) 2025-12-18 14:00:50 +01:00
Robert Resch
67aeafa797 Add advanced section for generic camera config flow (#148430) 2025-12-18 13:30:25 +01:00
Michael
3d71b6de44 Add support for FRITZ! Smarthome routines (#158947) 2025-12-18 13:09:06 +01:00
Luke Lashley
5349045932 Add basic support for Q7 devices (#159274) 2025-12-18 12:30:20 +01:00
epenet
4960871c84 Revert name change in meteo_france (#159352) 2025-12-18 11:04:34 +01:00
epenet
af3861cd6b Rename attribute in Tuya climate wrapper (#159145) 2025-12-18 10:02:38 +01:00
epenet
f9a070e9b3 Use common options attribute in Tuya event wrapper (#159119) 2025-12-18 09:33:53 +01:00
epenet
fd503b2e33 Make VacuumEntityFeature.STATE conditional in Tuya vacuum (#159254) 2025-12-18 09:32:13 +01:00
epenet
e5a73fcf57 Disable blackbird integration (#157817) 2025-12-18 08:51:54 +01:00
Andre Lengwenus
6991e01489 Fix incorrect status updates for lcn (#159251) 2025-12-18 08:11:22 +01:00
Simone Chemelli
c8636ee6f3 Add missing strings for Shelly voltmeter sensor (#159332) 2025-12-18 08:05:46 +01:00
J. Nick Koston
52229dc5a8 Bump aioesphomeapi to 43.3.0 (#159141) 2025-12-17 20:22:38 -10:00
Andre Lengwenus
f013455843 Bump pypck to 0.9.8 (#159277) 2025-12-18 06:54:56 +01:00
Joost Lekkerkerker
cae5bca546 Add integration_type device to kostal_plenticore (#159288) 2025-12-17 21:00:27 +01:00
Joost Lekkerkerker
49299b06c6 Add integration_type device to kmtronic (#159286) 2025-12-17 20:58:58 +01:00
Paul Tarjan
8e39027ad5 Add guidance to not amend commits after review starts (#158804) 2025-12-17 20:58:43 +01:00
Joost Lekkerkerker
2a1ce2df61 Add integration_type service to kodi (#159287) 2025-12-17 20:57:47 +01:00
Joost Lekkerkerker
7a6d929150 Add integration_type device to kulersky (#159290) 2025-12-17 20:56:52 +01:00
Joost Lekkerkerker
6f4a112dbb Add integration_type hub to lacrosse_view (#159291) 2025-12-17 20:56:14 +01:00
Joost Lekkerkerker
2197b910fb Add integration_type device to landisgyr_heat_meter (#159293) 2025-12-17 20:55:11 +01:00
Joost Lekkerkerker
7e2a9cd7f9 Add integration_type hub to laundrify (#159295) 2025-12-17 20:54:20 +01:00
Joost Lekkerkerker
e7ed7a8ed2 Add integration_type hub to lcn (#159296) 2025-12-17 20:53:41 +01:00
Joost Lekkerkerker
9ba2d0defe Add integration_type device to leaone (#159297) 2025-12-17 20:52:35 +01:00
Joost Lekkerkerker
231300919c Add integration_type device to led_ble (#159298) 2025-12-17 20:51:45 +01:00
Joost Lekkerkerker
664c50586f Add integration_type device to lg_soundbar (#159299) 2025-12-17 20:51:00 +01:00
Joost Lekkerkerker
43b9ecfc2b Add integration_type device to lifx (#159302) 2025-12-17 20:48:33 +01:00
Joost Lekkerkerker
f1237ed52a Add integration_type hub to livisi (#159303) 2025-12-17 20:47:39 +01:00
Joost Lekkerkerker
ecf8f55cc4 Add integration_type device to loqed (#159305) 2025-12-17 20:45:23 +01:00
Joost Lekkerkerker
ff36693057 Add integration_type hub to lupusec (#159306) 2025-12-17 20:44:29 +01:00
Joost Lekkerkerker
005785997c Add integration_type hub to lutron (#159307) 2025-12-17 20:43:47 +01:00
Joost Lekkerkerker
9917b82b66 Add integration_type hub to lyric (#159309) 2025-12-17 20:42:30 +01:00
Joost Lekkerkerker
9c927406ac Add integration_type service to mailgun (#159310) 2025-12-17 20:41:43 +01:00
Joost Lekkerkerker
972d95602a Add integration_type hub to meater (#159311) 2025-12-17 20:41:10 +01:00
Joost Lekkerkerker
5e0549a18f Add integration_type device to medcom_ble (#159312) 2025-12-17 20:39:39 +01:00
Joost Lekkerkerker
bcbb159fb2 Add integration_type device to melnor (#159313) 2025-12-17 20:38:23 +01:00
Joost Lekkerkerker
0123ca656a Add integration_type hub to lg_thinq (#159300) 2025-12-17 20:34:25 +01:00
Joost Lekkerkerker
1f699c729c Add integration_type service to lastfm (#159294) 2025-12-17 20:33:49 +01:00
Joost Lekkerkerker
50c3fcfeba Add integration_type service to kraken (#159289) 2025-12-17 20:33:17 +01:00
Raphael Hehl
2af1e098cc Improve debug logging in UniFi Protect integration (#159318) 2025-12-17 20:31:33 +01:00
hanwg
c418d9750b Remove ALLOW_EXTRA from Telegram bot action schema (#158886) 2025-12-17 19:49:34 +01:00
Joost Lekkerkerker
e96d614076 Add integration_type service to meteo_france (#159315) 2025-12-17 19:19:14 +01:00
Abílio Costa
f0a5e0a023 Enable duplicated log file on supervised when env var is set (#158679) 2025-12-17 17:44:54 +00:00
Klaas Schoute
6ac6b86060 Set quality scale in Autarco manifest (#159263) 2025-12-17 16:17:19 +01:00
PaulCavill
3909171b1a Login exception reason (#159259) 2025-12-17 16:13:54 +01:00
Luke Lashley
769029505f Bump python-roborock to 3.18.0 (#159271) 2025-12-17 06:39:06 -08:00
Paul Tarjan
080ec3524b Fix flaky camera stream teardown (#158507)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-12-17 13:47:22 +01:00
Matthew Vance
48d671ad5f Update py-improv-ble-client to 2.0.1 (#159233) 2025-12-17 08:27:06 +01:00
alorente
7115db5d22 Change device class from PRESSURE to ATMOSPHERIC_PRESSURE (#159149) 2025-12-17 07:16:46 +01:00
Jordan Harvey
d0c8792e4b Improve Nintendo Switch parental controls exception handling (#159199) 2025-12-17 07:15:26 +01:00
Richard
84d7c37502 Bump mill-local to 0.5.0 (#159220) 2025-12-16 20:41:28 +01:00
Jordan Harvey
8a10638470 Add select platform to Nintendo Switch parental controls (#159217) 2025-12-16 19:06:43 +01:00
Abílio Costa
10dd53ffc2 Rename base trigger class and methods (#159213) 2025-12-16 18:01:37 +00:00
ryanjones-gentex
36aefce9e1 Store unique user configurations for HomeLink integration (#159111) 2025-12-16 17:14:49 +01:00
Raphael Hehl
fe34da19e2 Use typed HassKey for hass.data in unifiprotect (#158798)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-16 17:12:57 +01:00
Jordan Harvey
fe94dea1db Add missing tests for Nintendo parental controls code coverage (#159210)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-16 17:12:36 +01:00
Anthony Garera
3f57b46756 Add issue sensors to Overseerr integration (#158888) 2025-12-16 17:11:28 +01:00
Raphael Hehl
7e141533bb Improve config flow tests to verify error recovery (#158484)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2025-12-16 17:04:38 +01:00
Joost Lekkerkerker
391ccbafae Add integration_type service to ipma (#159179) 2025-12-16 17:04:29 +01:00
epenet
6af674e64e Use is over == comparison for ConfigEntryState in tests (#159212) 2025-12-16 16:51:39 +01:00
Manu
7b1653c77b Migrate friends to subentries in Xbox integration (#156101) 2025-12-16 16:22:28 +01:00
peteS-UK
c87dafa2e6 Create Squeezebox initial Quality Scale entry (#153993) 2025-12-16 15:56:03 +01:00
Abílio Costa
8375acf315 Add device_tracker home enter/leave triggers (#158083) 2025-12-16 14:50:56 +00:00
Paul Tarjan
4df5a41b57 Migrate Hikvision integration to config flow (#158279)
Co-authored-by: Kamil Breguła <mik-laj@users.noreply.github.com>
2025-12-16 15:44:23 +01:00
Niracler
5796b4c0d9 Enhance Sunricher DALI with update gateway IP from DHCP discovery (#157809)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-12-16 15:19:37 +01:00
Andrew Jackson
5f4f07803b Add a delay to switch statuses on Transmission (#157493) 2025-12-16 15:11:10 +01:00
Richard Polzer
a0a444e3c8 Bump ekey-bionyxpy to version 1.0.1 (#159196) 2025-12-16 14:30:58 +01:00
epenet
30cfe987ed Bump pyinsteon to 1.6.4 (#159067) 2025-12-16 14:29:06 +01:00
Siemon Geeroms
412ee0da05 Adds continuous play support to Plex integration (#158281) 2025-12-16 14:20:03 +01:00
Ludovic BOUÉ
d6b675138d Bump python-matter-server dependency to version 8.1.2 (#159198) 2025-12-16 14:07:34 +01:00
Joost Lekkerkerker
bde3cef17d Add integration_type service to imgw_pib (#159172) 2025-12-16 14:04:51 +01:00
starkillerOG
412ee30584 Do not check Reolink firmware at start (#158275) 2025-12-16 13:27:09 +01:00
Abílio Costa
7eecdc87fd Add lookup caching to get_x_for_target (#157888) 2025-12-16 12:17:58 +00:00
Jordan Harvey
9ba252d8e3 Bump pynintendoparental 2.1.3 (#159120)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-12-16 12:09:35 +00:00
johanzander
1709a9d255 Add services for managing Time-of-Use (TOU) schedule for Growatt integration (#154703)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-16 11:56:21 +01:00
Joost Lekkerkerker
bcf46f09a2 Add integration_type device to kegtron (#159187) 2025-12-16 11:51:57 +01:00
Joost Lekkerkerker
d4097a8686 Add integration_type device to keenetic_ndms2 (#159186) 2025-12-16 11:50:53 +01:00
Joost Lekkerkerker
2a92292e76 Add integration_type device to kaleidescape (#159185) 2025-12-16 11:49:26 +01:00
Joost Lekkerkerker
fe987a63d6 Add integration_type device to justnimbus (#159184) 2025-12-16 11:48:15 +01:00
Joost Lekkerkerker
91f3b991ba Add integration_type hub to izone (#159183) 2025-12-16 11:47:22 +01:00
Joost Lekkerkerker
46c6313068 Add integration_type service to israel_rail (#159181) 2025-12-16 11:46:33 +01:00
Joost Lekkerkerker
86e4a81934 Add integration_type service to ista_ecotrend (#159182) 2025-12-16 11:46:15 +01:00
Joost Lekkerkerker
234d6ae161 Add integration_type hub to insteon (#159176) 2025-12-16 11:45:06 +01:00
Joost Lekkerkerker
2ab203618e Add integration_type device to intellifire (#159177) 2025-12-16 11:44:06 +01:00
Joost Lekkerkerker
faae23ee1b Add integration_type device to iotawatt (#159178) 2025-12-16 11:42:59 +01:00
Joost Lekkerkerker
f6acd4f230 Add integration_type service to islamic_prayer_times (#159180) 2025-12-16 11:37:33 +01:00
Joost Lekkerkerker
71d36a6496 Add integration_type device to inkbird (#159175) 2025-12-16 11:36:56 +01:00
Joost Lekkerkerker
9fc014c6f4 Add integration_type hub to inels (#159174) 2025-12-16 11:35:58 +01:00
Joost Lekkerkerker
537f93872c Add integration_type service to imap (#159171) 2025-12-16 11:32:48 +01:00
Joost Lekkerkerker
06a55175a8 Add integration_type device to ialarm (#159166) 2025-12-16 11:31:44 +01:00
Joost Lekkerkerker
5f37016baa Add integration_type hub to icloud (#159169) 2025-12-16 11:30:44 +01:00
Joost Lekkerkerker
1af884293f Add integration_type hub to igloohome (#159170) 2025-12-16 11:30:23 +01:00
Joost Lekkerkerker
ba73ab38e8 Add integration_type hub to iaqualink (#159168) 2025-12-16 11:29:41 +01:00
Artur Pragacz
2d33a720f7 Modernise condition checker in helper (#159159) 2025-12-16 10:46:10 +01:00
epenet
dbfdaf6a2e Use is over == comparison for FlowResultType in flow tests (#159158) 2025-12-16 09:48:45 +01:00
Artur Pragacz
278cb4d3ae Add integration type to sun (#159146) 2025-12-16 09:38:50 +01:00
Przemko92
1c6f8b7e54 Update compit-inext-api to 0.3.4 (#158821)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-12-16 09:15:00 +01:00
epenet
731f5078a6 Fix actron_air config_flow test (#159157) 2025-12-16 09:10:26 +01:00
Federico Imberti
9863d3484d Prevent empty aliases in registries (#156061)
Co-authored-by: J. Diego Rodríguez Royo <jdrr1998@hotmail.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-12-15 22:28:53 +01:00
Åke Strandberg
f85a684e31 Handle missing Miele status codes gracefully (#159124) 2025-12-15 19:58:02 +00:00
inventor7777
e292a67692 Increase maximum screensaver time for Fully Kiosk (#159122) 2025-12-15 20:34:14 +01:00
vexofp
c82d159c14 Add enum options for Octoprint status sensor (#157213) 2025-12-15 20:24:19 +01:00
Joost Lekkerkerker
d890387d3d Add integration_type hub to hive (#159126) 2025-12-15 20:21:10 +01:00
Joost Lekkerkerker
d996d7b113 Add integration_type service to hko (#159127) 2025-12-15 20:20:23 +01:00
Joost Lekkerkerker
d28a4598d5 Add integration_type device to hlk_sw16 (#159128) 2025-12-15 20:19:46 +01:00
Joost Lekkerkerker
229f7c4f37 Add integration_type hub to homematicip_cloud (#159129) 2025-12-15 20:18:27 +01:00
Joost Lekkerkerker
9f2138aa18 Add integration_type hub to homeworks (#159130) 2025-12-15 20:17:49 +01:00
Joost Lekkerkerker
7506ff826c Add integration_type hub to honeywell (#159131) 2025-12-15 20:17:09 +01:00
Joost Lekkerkerker
317a3ed044 Add integration_type device to huawei_lte (#159132) 2025-12-15 20:16:19 +01:00
Frederic Mariën
d7801881e9 Add Risco set_time service (#139015) 2025-12-15 20:14:49 +01:00
Joost Lekkerkerker
a4bbdafd55 Add integration_type hub to hunterdouglas_powerview (#159134) 2025-12-15 20:12:32 +01:00
Joost Lekkerkerker
97673f22cb Add integration_type device to husqvarna_automower_ble (#159135) 2025-12-15 20:11:38 +01:00
Joost Lekkerkerker
d63cdafad2 Add integration_type device to huum (#159136) 2025-12-15 20:10:58 +01:00
Joost Lekkerkerker
50f47a7397 Add integration_type service to hvv_departures (#159137) 2025-12-15 20:09:15 +01:00
Kurt Chrisford
123d573274 Actron Air Integration: Add reauthentication flow (#158246)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-15 20:08:57 +01:00
Joost Lekkerkerker
64ccde6709 Add integration_type hub to hydrawise (#159138) 2025-12-15 20:08:38 +01:00
Pete Sage
c69ef7e1f6 Sonos fix media player join to avoid race condition (#159106) 2025-12-15 20:04:55 +01:00
Davide
d51cca3325 Fix Philips TV channel logos not displaying in media browser (#158975) 2025-12-15 20:04:41 +01:00
Allen Porter
2679ac3f5e Add support for dynamic nest devices and remove stale devices (#159060)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-15 19:58:25 +01:00
Retha Runolfsson
47f476af32 Remove the restriction that Bluetooth login to the Switchbot account is only possible in active mode (#157154) 2025-12-15 19:55:43 +01:00
Pete Sage
ca3d03131e Bump soco to 0.30.13 for Sonos (#159123) 2025-12-15 19:50:39 +01:00
Josef Zweck
a3f3586b02 Add option to enable offline mode to lamarzocco (#159094)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-15 19:22:50 +01:00
Abílio Costa
0ced960d1d Add button pressed trigger (#158745) 2025-12-15 18:02:30 +00:00
Michael
78f1b434b3 Add update became available trigger (#158984) 2025-12-15 18:31:51 +01:00
Joost Lekkerkerker
563fa8f958 Add integration_type device to enphase_envoy (#159006)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-15 18:11:45 +01:00
RSDynamics
8de6f04829 Change Lektrico lifetime_energy sensor to float (#158880) 2025-12-15 17:42:42 +01:00
epenet
f74128de49 Drop supports_action in Tuya alarm_control_panel wrapper (#159118) 2025-12-15 17:41:47 +01:00
epenet
25e1cc42eb Make valid options an attribute of Tuya device wrapper (#159109) 2025-12-15 17:27:51 +01:00
epenet
245d57be1a Make min/max/step attributes of Tuya device wrapper (#159116) 2025-12-15 17:27:13 +01:00
Joost Lekkerkerker
c09c016299 Add integration_type service to apcupsd (#158844)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-12-15 17:27:01 +01:00
Joost Lekkerkerker
d9283ad4cd Add integration_type service to autarco (#158854)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-15 17:23:24 +01:00
Joost Lekkerkerker
11f319c79c Add integration_type device to fjaraskupan (#159017)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-15 17:23:12 +01:00
Joost Lekkerkerker
cc5d98fe8b Add integration_type device to coolmaster (#158925)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-15 17:22:52 +01:00
Andrew Jackson
3e1f9de0de Add new Mealie meal plan types to calendar and services (#158987) 2025-12-15 17:18:59 +01:00
Joost Lekkerkerker
34212c6e65 Add integration_type service to duke_energy (#158934)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-15 17:18:09 +01:00
Joost Lekkerkerker
10f02d040f Add integration_type device to directv (#158929)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-15 17:03:53 +01:00
Pete Sage
1114ce8509 Sonos increase wait for groups timeout (#159108) 2025-12-15 16:57:11 +01:00
Denis Shulyaka
8687a7b306 Add GPT-5.2 support (#158783) 2025-12-15 16:39:01 +01:00
Abílio Costa
d325f677df Deprecate TargetSelectorData in favor of TargetSelection (#158734)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-15 14:45:35 +00:00
Petro31
f786ec18a9 Add common template test framework to vacuum platform (#157846) 2025-12-15 15:22:56 +01:00
Kamil Breguła
ef0add1d6c Set station name as device name in GIOS (#155762)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-15 14:55:48 +01:00
hanwg
46f56c60f2 Remove deprecated yaml import from Telegram bot (#158520) 2025-12-15 12:57:41 +01:00
dependabot[bot]
060b258921 Bump actions/upload-artifact from 5.0.0 to 6.0.0 (#159066)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-15 12:54:19 +01:00
Aidan Timson
8eb3e63d9d Update systembridgeconnector to 5.2.4, fix media source (#158917) 2025-12-15 12:47:34 +01:00
Joost Lekkerkerker
6c96acda82 Add integration_type device to harmony (#159091) 2025-12-15 12:34:20 +01:00
Jan Čermák
c82b179e03 Bump Docker base image to 2025.12.0 (#159077) 2025-12-15 12:29:16 +01:00
Joost Lekkerkerker
1e1265c99c Add integration_type device to goodwe (#159083) 2025-12-15 12:25:20 +01:00
Joost Lekkerkerker
22e975f911 Add integration_type device to govee_ble (#159087) 2025-12-15 12:23:55 +01:00
Joost Lekkerkerker
b1e3f8d4f4 Add integration_type service to google_tasks (#159085) 2025-12-15 12:23:35 +01:00
Joost Lekkerkerker
95fe573620 Add integration_type hub to growatt_server (#159088) 2025-12-15 12:23:09 +01:00
Joost Lekkerkerker
d29d82cf20 Add integration_type service to google_photos (#159084) 2025-12-15 12:22:12 +01:00
Joost Lekkerkerker
23459d69c9 Add integration_type hub to hanna (#159090) 2025-12-15 12:21:26 +01:00
Joost Lekkerkerker
5209f4d296 Add integration_type service to here_travel_time (#159092) 2025-12-15 12:20:55 +01:00
Joost Lekkerkerker
39c5983571 Add integration_type service to google_travel_time (#159086) 2025-12-15 12:20:30 +01:00
Robert Resch
b5015faffe Revert "Pin pycares to 4.11.0" (#159076) 2025-12-15 12:17:49 +01:00
hanwg
16d3707d13 Add subentry description for Telegram bot (#158761) 2025-12-15 11:50:44 +01:00
TimL
9bb6d740e0 Bump pysmlight to v0.2.13 (#159075)
Co-authored-by: Tim Lunn <tim@feathertop.org>
2025-12-15 11:46:32 +01:00
mithomas
f640795de1 Bump aiodns to 3.6.1 (#159073) 2025-12-15 11:12:10 +01:00
Robert Resch
c1b512d50a Bump uv to 0.9.17 (#159044)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-12-15 11:10:50 +01:00
Raphael Hehl
0ba43b22a9 Improve entity names for unifiprotect sensors (#158803)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2025-12-15 10:43:09 +01:00
Kai Winter
b11b790958 Increase http timeout in wolflink (#158912) 2025-12-15 10:26:51 +01:00
dependabot[bot]
81fb769233 Bump dessant/lock-threads from 5.0.1 to 6.0.0 (#159065)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 10:13:30 +01:00
dependabot[bot]
c5ac806832 Bump actions/download-artifact from 6.0.0 to 7.0.0 (#159064)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 10:07:11 +01:00
dependabot[bot]
256baf5097 Bump actions/cache from 5.0.0 to 5.0.1 (#159063)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 10:06:31 +01:00
Paul Tarjan
cccefddb72 Set default RTSP transport protocol to TCP in Generic Camera (#159061) 2025-12-15 10:06:09 +01:00
dependabot[bot]
85294b1d96 Bump github/codeql-action from 4.31.7 to 4.31.8 (#159062)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 10:05:18 +01:00
Allen Porter
ffcde8dd74 Fix slow event state updates for remote calendar (#159058) 2025-12-15 10:02:24 +01:00
Joost Lekkerkerker
13b50b355e Add integration_type hub to elkm1 (#159001) 2025-12-15 09:58:25 +01:00
Joost Lekkerkerker
7a2592173b Add integration_type service to emoncms (#159003) 2025-12-15 09:57:53 +01:00
Joost Lekkerkerker
d3b36d0081 Add integration_type device to emonitor (#159004) 2025-12-15 09:57:20 +01:00
Joost Lekkerkerker
5bea0d57ec Add integration_type device to epson (#159008) 2025-12-15 09:49:19 +01:00
Joost Lekkerkerker
903c73b5dd Add integration_type device to escea (#159009) 2025-12-15 09:48:47 +01:00
Joost Lekkerkerker
603c664c9b Add integration_type device to evil_genius_labs (#159010) 2025-12-15 09:48:07 +01:00
epenet
43d0d582ef Cleanup deprecated hassio constants and functions (#158802) 2025-12-15 09:45:38 +01:00
Joost Lekkerkerker
c12d2ec0bd Add integration_type service to faa_delays (#159011) 2025-12-15 09:44:37 +01:00
Joost Lekkerkerker
bfedcef9b9 Add integration_type service to fing (#159012) 2025-12-15 09:44:00 +01:00
Joost Lekkerkerker
d41d93c44e Add integration_type service to fireservicerota (#159014) 2025-12-15 09:41:13 +01:00
Joost Lekkerkerker
11cc8beac1 Add integration_type service to fivem (#159016) 2025-12-15 09:40:48 +01:00
Joost Lekkerkerker
0f827403c5 Add integration_type hub to flipr (#159018) 2025-12-15 09:39:11 +01:00
Joost Lekkerkerker
4827a603e5 Add integration_type hub to flo (#159019) 2025-12-15 09:38:18 +01:00
Joost Lekkerkerker
e3f3861d4e Add integration_type hub to flume (#159020) 2025-12-15 09:37:48 +01:00
Joost Lekkerkerker
60df4433ca Add integration_type device to foscam (#159021) 2025-12-15 09:37:18 +01:00
Josef Zweck
46b6557348 Fix CI by patching correctly in ekeybionyx test (#159070) 2025-12-15 09:29:39 +01:00
Brett Adams
639e736c66 Bump teslemetry-stream to 0.8.2 (#159056) 2025-12-15 09:27:52 +01:00
Ludovic BOUÉ
8a198401a7 Add IKEA ALPSTUGA air quality monitor Matter fixture (#158986) 2025-12-15 09:23:13 +01:00
Jordan Harvey
c917dfeed9 Update pyanglianwater requirement to version 3.1.0 (#159046) 2025-12-15 06:59:16 +01:00
Allen Porter
25155de30c Bump ical to 12.1.2 (#158965) 2025-12-14 21:18:05 -08:00
Richard Polzer
6a927c37be Improve auth handling and test coverage for ekeybionyx (#159057) 2025-12-14 21:17:00 -08:00
Brett Adams
9b41bb09a7 Bump Tesla Fleet to 1.3.0 (#159048) 2025-12-14 18:34:11 -08:00
Xidorn Quan
e58fc6976d Bump aioasuswrt to 1.5.4 (#159038) 2025-12-15 01:04:42 +01:00
Kevin Fronczak
9def627a57 Bump blinkpy to 0.25.2 (#159049) 2025-12-15 00:57:21 +01:00
Paul Tarjan
9b56759c1e Add 'task:' label to exception handler log messages (#158674) 2025-12-14 23:55:13 +01:00
Joost Lekkerkerker
c3f743cafd Add integration_type hub to fujitsu_fglair (#159026) 2025-12-14 23:51:59 +01:00
Joost Lekkerkerker
160c495ddc Add integration_type hub to freedompro (#159024) 2025-12-14 23:50:05 +01:00
Ludovic BOUÉ
9a1cd8545d Add Ikea scroll wheel Matter fixture (#159037) 2025-12-14 23:49:55 +01:00
Joost Lekkerkerker
fa81e6cd04 Add integration_type device to frontier_silicon (#159025) 2025-12-14 23:49:38 +01:00
Joost Lekkerkerker
746f4ef1e2 Add integration_type service to glances (#159033) 2025-12-14 23:49:31 +01:00
Joost Lekkerkerker
0149de6ba6 Add integration_type service to anglian_water (#158839) 2025-12-14 21:16:52 +01:00
Joost Lekkerkerker
1df2f18e0a Add integration_type service to environment_canada (#159007) 2025-12-14 21:12:54 +01:00
Joost Lekkerkerker
5800824893 Add integration_type service to firefly_iii (#159013) 2025-12-14 21:12:23 +01:00
Joost Lekkerkerker
aac07b6b4b Add integration_type device to fully_kiosk (#159027) 2025-12-14 21:10:29 +01:00
Joost Lekkerkerker
2448ce1970 Add integration_type service to garages_amsterdam (#159028) 2025-12-14 21:08:34 +01:00
Joost Lekkerkerker
7b40e3b8a7 Add integration_type service to geocaching (#159030) 2025-12-14 21:07:31 +01:00
Joost Lekkerkerker
29d06cfcc9 Add integration_type service to github (#159032) 2025-12-14 19:29:44 +01:00
Joost Lekkerkerker
365d168ddd Add integration_type device to gardena_bluetooth (#159029) 2025-12-14 19:20:54 +01:00
Joost Lekkerkerker
234191336e Add integration_type service to fitbit (#159015) 2025-12-14 10:19:34 -08:00
Joost Lekkerkerker
ba57b72658 Add integration_type service to elvia (#159002) 2025-12-14 19:09:12 +01:00
Allen Porter
bb08b315b8 Add exception handling for rate limited or unauthorized MQTT requests (#158997) 2025-12-14 18:45:12 +01:00
Petro31
50621df244 Update unnecessary error logging of unknown and unavailable source states from mold indicator (#158979) 2025-12-14 16:52:38 +01:00
Jan Bouwhuis
2db7b5c99f Assume cover or valve is always "running" in google assistant when the state is assumed or the position is reported to allow it to be be stopped (#158919) 2025-12-14 10:24:40 -05:00
Michael
78af3acf35 Bump pyfritzhome to 0.6.18 (#158877) 2025-12-14 08:12:25 +01:00
Joost Lekkerkerker
b72f04d44e Add integration_type hub to electrasmart (#158942) 2025-12-14 07:17:50 +01:00
Joost Lekkerkerker
35f287e330 Add integration_type hub to ekeybionyx (#158941) 2025-12-14 07:16:40 +01:00
Joost Lekkerkerker
0a55f83b46 Add integration_type hub to econet (#158940) 2025-12-14 07:14:44 +01:00
Joost Lekkerkerker
5030d0ba90 Add integration_type device to ecoforest (#158939) 2025-12-14 07:13:44 +01:00
Joost Lekkerkerker
f582f06ee4 Add integration_type service to eafm (#158937) 2025-12-14 06:59:46 +01:00
Joost Lekkerkerker
662bada5d8 Add integration_type hub to duotecno (#158936) 2025-12-14 06:59:13 +01:00
Joost Lekkerkerker
3ca338dd25 Add integration_type device to dunehd (#158935) 2025-12-14 06:58:03 +01:00
Joost Lekkerkerker
9337a0e71b Add integration_type device to droplet (#158933) 2025-12-14 06:56:08 +01:00
Joost Lekkerkerker
ccbb00197d Add integration_type hub to drop_connect (#158932) 2025-12-14 06:55:22 +01:00
Joost Lekkerkerker
0f59c17e61 Add integration_type service to dexcom (#158928) 2025-12-14 06:42:34 +01:00
Joost Lekkerkerker
6253ade3e2 Add integration_type service to datadog (#158927) 2025-12-14 06:40:48 +01:00
Joost Lekkerkerker
e5890378a1 Add integration_type device to daikin (#158926) 2025-12-14 06:40:29 +01:00
Joost Lekkerkerker
b8ab0bcadf Add integration_type service to dnsip (#158930) 2025-12-13 16:25:51 -06:00
Joost Lekkerkerker
19cb827577 Add integration_type device to doorbird (#158931) 2025-12-13 16:23:37 -06:00
Joost Lekkerkerker
03676d7e5a Add integration_type hub to ecobee (#158938) 2025-12-13 16:23:15 -06:00
Magnus
13f3b49b96 Bump aioasuswrt 1.5.3 (#158882) 2025-12-13 22:43:21 +01:00
Allen Porter
90c8c56a06 Suppress roborock failures under some unavailability threshold (#158673)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 22:30:21 +01:00
Josef Zweck
afb9e18a7d Add brew by weight controls to lamarzocco (#158169) 2025-12-13 22:28:11 +01:00
Andrew Jackson
2c2934065f Bump aiomealie to 1.1.1 and statically define mealplan entry types (#158907) 2025-12-13 22:26:31 +01:00
mettolen
0bead67df9 Add device uptime to Airobot integration (#158516) 2025-12-13 22:20:52 +01:00
James Cole
2895849203 Update strings for Firefly III integration (#158911) 2025-12-13 22:20:30 +01:00
David Recordon
b2400708ac Add myself as a maintainer for Control4 (#158948) 2025-12-13 22:15:35 +01:00
Anthony Garera
0bed9c20b3 Bump python-overseerr to 0.8.0 (#158924) 2025-12-13 19:31:21 +01:00
Brett Adams
d3fb7a7b87 Bump tesla-fleet-api to 1.2.7 (#158904) 2025-12-13 15:02:19 +01:00
Bouwe Westerdijk
60dcca4143 Show Plugwise configuration-link on gateway only (#158094) 2025-12-13 11:38:23 +01:00
Paul Tarjan
01f498f239 Clarify previous state in total_increasing warning message (#158805) 2025-12-13 11:15:37 +01:00
Andre Lengwenus
15055b8e8e Fix race condition in LCN climate and cover entites (#158894) 2025-12-13 11:12:20 +01:00
Bouwe Westerdijk
6826619e12 Revert adding entity_category to Plugwise thermostat schedule select (#158901) 2025-12-13 11:08:17 +01:00
Joost Lekkerkerker
b50a8e04a8 Add integration_type hub to airtouch5 (#158834)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:47:27 +01:00
Joost Lekkerkerker
c6c67c5357 Add integration_type hub to blue_current (#158863) 2025-12-13 10:46:12 +01:00
Joost Lekkerkerker
c82803d1e2 Add integration_type hub to agent_dvr (#158829)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:45:09 +01:00
Joost Lekkerkerker
732b30f181 Add integration_type hub to airzone (#158835)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:44:05 +01:00
Joost Lekkerkerker
0e2e57a657 Add integration_type device to android_ip_webcam (#158838)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:42:39 +01:00
Joost Lekkerkerker
f00b0080a9 Add integration_type device to advantage_air (#158826)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:39:58 +01:00
Joost Lekkerkerker
ad970c1234 Add integration_type hub to cert_expiry (#158897) 2025-12-13 10:39:14 +01:00
Joost Lekkerkerker
02ec56bffa Add integration_type device to ccm15 (#158896)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-13 10:37:36 +01:00
Joost Lekkerkerker
8388c290bf Add integration_type hub to canary (#158895) 2025-12-13 09:41:01 +01:00
Joost Lekkerkerker
576ee99faf Add integration_type hub to control4 (#158900) 2025-12-13 09:36:36 +01:00
Joost Lekkerkerker
8a3534c345 Add integration_type service to coinbase (#158899) 2025-12-13 09:31:54 +01:00
Joost Lekkerkerker
e1e91c5568 Add integration_type service to cloudflare (#158898) 2025-12-13 09:31:25 +01:00
epenet
1e09bddb1d Cleanup deprecated alias in core (#158799) 2025-12-13 09:29:15 +01:00
Joost Lekkerkerker
90e4340595 Add integration_type hub to brunt (#158870)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-12-13 09:04:05 +01:00
Joost Lekkerkerker
120b17349c Add integration_type service to aussie_broadband (#158853) 2025-12-13 08:56:35 +01:00
Joost Lekkerkerker
8a26961304 Add integration_type service to aurora (#158852) 2025-12-13 08:56:01 +01:00
Joost Lekkerkerker
407b675080 Add integration_type device to atag (#158850) 2025-12-13 08:55:35 +01:00
Joost Lekkerkerker
274844271b Add integration_type hub to aseko_pool_live (#158849) 2025-12-13 08:54:46 +01:00
Joost Lekkerkerker
f11e4e7bda Add integration_type hub to aosmith (#158843) 2025-12-13 08:52:45 +01:00
Joost Lekkerkerker
96f8c39c6f Add integration_type device to anthemav (#158841) 2025-12-13 08:51:25 +01:00
Joost Lekkerkerker
77b79fef8d Add integration_type hub to anova (#158840) 2025-12-13 08:50:24 +01:00
mkmer
a0d2f285f3 blink: Remove mkmer as codeowner (#158884) 2025-12-13 08:45:13 +01:00
Joost Lekkerkerker
3aef05d1ec Add integration_type hub to airzone_cloud (#158836) 2025-12-13 08:43:57 +01:00
Joost Lekkerkerker
510e391ee4 Add integration_type device to airtouch4 (#158833) 2025-12-13 08:41:17 +01:00
Joost Lekkerkerker
54adfdd694 Add integration_type device to bluesound (#158865) 2025-12-13 08:38:48 +01:00
Joost Lekkerkerker
d45f920b4a Add integration_type service to amberelectric (#158837) 2025-12-13 08:37:16 +01:00
Joost Lekkerkerker
3080ef9a4a Add integration_type device to airthings_ble (#158832) 2025-12-13 08:36:06 +01:00
Joost Lekkerkerker
51cebb52f3 Add integration_type hub to airthings (#158831) 2025-12-13 08:34:28 +01:00
Joost Lekkerkerker
7b0d4c47b7 Add integration_type service to airnow (#158830) 2025-12-13 08:33:53 +01:00
Joost Lekkerkerker
a660ab3f97 Add integration_type service to aftership (#158828) 2025-12-13 08:32:31 +01:00
Joost Lekkerkerker
dd8fc16788 Add integration_type service to aemet (#158827) 2025-12-13 08:32:01 +01:00
Joost Lekkerkerker
2b0fab0468 Add integration_type service to brottsplatskartan (#158869) 2025-12-13 08:30:59 +01:00
Joost Lekkerkerker
3bb88ed433 Add integration_type hub to bosch_shc (#158868) 2025-12-13 08:30:04 +01:00
Joost Lekkerkerker
984385cd98 Add integration_type service to buienradar (#158871) 2025-12-13 08:27:55 +01:00
Joost Lekkerkerker
09de108676 Add integration_type service to caldav (#158872) 2025-12-13 08:26:40 +01:00
Joost Lekkerkerker
ebc7581718 Add integration_type hub to bmw_connected_drive (#158866) 2025-12-13 08:26:16 +01:00
Joost Lekkerkerker
e55162812d Add integration_type hub to blink (#158862) 2025-12-13 08:22:22 +01:00
Joost Lekkerkerker
aa6ccaa024 Add integration_type device to blebox (#158860) 2025-12-13 08:21:25 +01:00
Joost Lekkerkerker
e1b009a6de Add integration_type device to balboa (#158859) 2025-12-13 08:20:12 +01:00
Joost Lekkerkerker
91ddc525b0 Add integration_type service to azure_event_hub (#158857) 2025-12-13 08:18:56 +01:00
Joost Lekkerkerker
d7d7954ac2 Add integration_type service to azure_devops (#158856) 2025-12-13 08:18:35 +01:00
Joost Lekkerkerker
e87c260df7 Add integration_type service to azure_data_explorer (#158855) 2025-12-13 08:18:13 +01:00
Joost Lekkerkerker
5185c6cd68 Add integration_type hub to arve (#158848) 2025-12-13 08:17:37 +01:00
Joost Lekkerkerker
7599c918e2 Add integration_type hub to august (#158851) 2025-12-12 23:00:06 +01:00
Joost Lekkerkerker
fa7e22ec91 Add integration_type device to arcam_fmj (#158846) 2025-12-12 22:59:47 +01:00
Joost Lekkerkerker
606519e51b Add integration_type device to baf (#158858) 2025-12-12 22:59:28 +01:00
Joost Lekkerkerker
8e39e010f7 Add integration_type device to bluemaestro (#158864) 2025-12-12 22:59:13 +01:00
Joost Lekkerkerker
dc01cf49a0 Add integration_type hub to bond (#158867) 2025-12-12 22:58:57 +01:00
Joost Lekkerkerker
1f3ad382f1 Set Denon AVR integration type to device (#158815) 2025-12-12 20:33:56 +01:00
Joost Lekkerkerker
2595c7dcb2 Set Actron Air integration type to hub (#158816) 2025-12-12 20:33:25 +01:00
Kamil Breguła
d445b320de Accept URLs in WLED Host input (#157793)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
2025-12-12 18:55:03 +01:00
epenet
7b6df1a8a0 Cleanup deprecated typing helpers (#158806) 2025-12-12 17:04:41 +01:00
Manu
2a151dcd19 Add tests for discovery to Xbox integration (#158808) 2025-12-12 17:02:32 +01:00
epenet
adbab150af Move blue_current services to separate module (#158389) 2025-12-12 16:50:34 +01:00
Allen Porter
d20edf7928 Improve Roborock exception logging behavior for Zeo/Dyad devices (#158465)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-12 16:43:45 +01:00
Ludovic BOUÉ
7d6d37fe76 Fix Matter Door Lock Operating Mode select entity (#158468) 2025-12-12 16:30:48 +01:00
Ludovic BOUÉ
228e0453a7 Add Matter Thermostat remote sensing status (#157650) 2025-12-12 16:26:27 +01:00
Raphael Hehl
1da31c0530 Move icons to icons.json for unifiprotect (#158800)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2025-12-12 16:23:11 +01:00
Joost Lekkerkerker
41ad15e577 Bump pySmartThings to 3.5.1 (#158795) 2025-12-12 15:45:08 +01:00
Markus Jacobsen
421af881fe Add video source reporting to Bang & Olufsen (#158675) 2025-12-12 15:40:12 +01:00
Klaas Schoute
715a484f7e Add AutarcoSensorBase class for Autarco sensors (#158691)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-12 15:39:18 +01:00
Samuel Xiao
0a789f51b8 Switchbot Cloud: Fixed binary sensors didn't update automatically (#158434)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-12 15:39:08 +01:00
epenet
fa25d45123 Remove incorrect bring test (#158797) 2025-12-12 15:32:27 +01:00
johanzander
6d255b2521 Add state_class to Growatt power and energy sensors (#158705)
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-12 15:27:47 +01:00
peteS-UK
5ffb39f064 Trap for missing UUID in config_flow for Squeezebox (#158721)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-12 15:15:04 +01:00
Zoltán Farkasdi
d642109436 Netatmo NOCamera on/off fix (#158741) 2025-12-12 14:57:57 +01:00
Heindrich Paul
10f6d8d14f Add diagnostics support for Nederlandse Spoorwegen integration (#158722)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-12 14:36:35 +01:00
Thomas55555
a94678cb06 Bump google air quality api to 2.0.2 (#158742) 2025-12-12 14:35:07 +01:00
Manu
0d8d466003 Increase Xbox update interval to 15 seconds and refactor title data handling (#158780) 2025-12-12 14:04:25 +01:00
Marc Mueller
8ddf3e1734 Update pytest warnings filter (#158790) 2025-12-12 13:54:00 +01:00
cdutr
d88047a750 Migrate Blink component to use hardware_id instead of device_id (#158765) 2025-12-12 13:49:19 +01:00
Jordan Harvey
61c7ac81d6 Bump pynintendoparental to 2.1.1 (#158779) 2025-12-12 13:45:48 +01:00
Josef Zweck
bbe07bddb0 Bump pylamarzocco to 2.2.4 (#158774) 2025-12-12 13:45:14 +01:00
Denis Shulyaka
a3afc2beb1 Bump openai to 2.11.0 (#158785) 2025-12-12 13:40:43 +01:00
epenet
374cd93d3d Replace Tuya remap methods with helper class (#158718) 2025-12-12 13:37:29 +01:00
Maciej Bieniek
6e99411084 Add get_kvs_value and set_kvs_value actions for Shelly RPC devices (#157349) 2025-12-12 13:15:25 +01:00
dependabot[bot]
41d5415c86 Bump actions/cache from 4.3.0 to 5.0.0 (#158771) 2025-12-12 10:39:58 +01:00
Allen Porter
052d56f358 Bump ical to 12.1.1 (#158770) 2025-12-12 08:34:22 +01:00
Abílio Costa
0a676b5812 Remove alarm panel test from text tests (#158743) 2025-12-12 02:18:40 +01:00
Michael
1f4cf67daa Add turned off and turned on triggers to switch platform (#158688)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-11 22:02:22 +00:00
Maikel Punie
bb4ec229ce Add Velbus VLP file loading (#154883) 2025-12-11 22:53:01 +01:00
ndrwrbgs
ff62b460d5 Update advanced_options display text for MQTT (#158728) 2025-12-11 22:16:35 +01:00
Willem-Jan van Rootselaar
9b48e92940 Bump python-bsblan to 3.1.4 (#158725)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-12-11 18:04:57 +00:00
Magnus
c03b9d1f87 Bump aioasuswrt to 1.5.2 (#158727) 2025-12-11 17:31:46 +00:00
Koknico
3f30df203c Add support for AtlanticDomesticHotWaterProductionV2_CETHI_V4_IOComponent to Overkiz (#157872) 2025-12-11 13:10:37 +01:00
epenet
7fe0d96c88 Remove unnecessary wrapper base method in Tuya (#158708) 2025-12-11 10:13:24 +01:00
Joost Lekkerkerker
cdc2192bba Clean up Homelink tests (#158685)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-11 09:10:02 +01:00
Matt Zimmerman
74b1c1f6fd Bump python-smarttub to 0.0.46 (#158702) 2025-12-11 09:07:46 +01:00
J. Nick Koston
69c7a7b0ab Pin pycares to 4.11.0 (#158695) 2025-12-10 22:00:59 -05:00
Norbert Rittel
ef302215cc Add two common config flow strings in energyid (#158680) 2025-12-10 21:23:18 +01:00
Norbert Rittel
6378f5f02a Use common reauth_successful string in rituals_perfume_genie (#158684) 2025-12-10 21:14:25 +01:00
Bouwe Westerdijk
79245195cd Bump Plugwise to v1.11.0 (#158067) 2025-12-10 21:03:27 +01:00
epenet
d0e33a6e04 Use process_raw_value in Tuya JsonTypeInformation (#158517) 2025-12-10 20:13:16 +01:00
Joost Lekkerkerker
f55fc788db Cleanup homelink config flow (#158479) 2025-12-10 18:39:44 +01:00
epenet
6152e0fa27 Split action and state wrapper in Tuya alarm control panel (#158532) 2025-12-10 18:37:04 +01:00
Matthias Alphart
f1a89741c0 Add counter for KNX DataSecure undecodable telegrams (#157844) 2025-12-10 17:59:00 +01:00
Paul Tarjan
7629c9f280 Fix flaky test_stream_source timeout in generic camera tests (#158506) 2025-12-10 17:49:27 +01:00
Abílio Costa
6b8650c6d9 Remove uneeded check in whirlpool oven temperature sensors (#157997) 2025-12-10 17:45:34 +01:00
Paul Tarjan
48f186368a Fix flaky playstation_network test_image_platform test (#158296) 2025-12-10 17:07:58 +01:00
Allen Porter
d65baac8d4 Bump python-roborock to 3.12.2 (#158572) 2025-12-10 17:05:57 +01:00
Paul Tarjan
d57801407b Fix flaky test_calls_not_allowed by using thread-safe event signaling (#158504) 2025-12-10 16:38:34 +01:00
Markus Jacobsen
4495a76557 Change Bang & Olufsen "stopped" state translation (#158534) 2025-12-10 16:37:48 +01:00
Paul Tarjan
99dfb93ac0 Fix flaky test_rename_entity_collision test (#158297) 2025-12-10 16:36:52 +01:00
Abílio Costa
7c7c0aad25 Rename trigger helper state checkers (#158537)
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-10 14:56:56 +00:00
Andrew Jackson
5992898340 Add measurement state class to ohme sensors (#158541) 2025-12-10 15:35:24 +01:00
Sander Blomvågnes
4f2ff9a4f4 Add codeowner and integration_type to Entur manifest (#158543) 2025-12-10 15:26:24 +01:00
bestycame
a8a135c2ca Bump hanna-cloud to version 0.0.7 (#158536)
Co-authored-by: Olivier d'Otreppe <odotreppe@abbove.com>
2025-12-10 14:50:05 +01:00
epenet
43e241ee39 Use process_raw_value in Tuya RawTypeInformation (#158521) 2025-12-10 10:44:49 +01:00
Kinachi249
6af7052b9d Bump PyCync to 0.5.0 (#158509) 2025-12-10 08:15:47 +01:00
dependabot[bot]
c0aa35ff6d Bump codecov/codecov-action from 5.5.1 to 5.5.2 (#158515) 2025-12-10 07:27:42 +01:00
Ludovic BOUÉ
2c7763e350 Fix Matter epoch timestamp sensors (#157600) 2025-12-10 07:13:21 +01:00
Joost Lekkerkerker
95e344ea44 Cleanup homelink (#158477) 2025-12-10 01:09:23 +01:00
Yevhenii Vaskivskyi
7ed8613411 Bump asusrouter to 1.21.3 (#158492) 2025-12-09 22:56:11 +01:00
Anton Dalgren
4ac0567ccc Add AirPatrol integration (#149247)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-09 21:26:21 +01:00
epenet
bc031e7a81 Improve Tuya HVACMode handling (#158042)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-09 21:17:57 +01:00
Raphael Hehl
ad1ba629c5 Fix typos in UniFi Protect integration (#158478) 2025-12-09 21:13:38 +01:00
Paul Tarjan
0c2cb460cb Fix flaky laundrify coordinator test (#158460) 2025-12-09 21:12:55 +01:00
Kira
5388740c83 Bump blinkpy to 0.25.1 (#158135)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-09 20:53:59 +01:00
Raphael Hehl
2a54d4c3a9 Add model_id to NVR device info in UniFi Protect (#158481)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2025-12-09 20:18:04 +01:00
Steve Easley
2008972215 Bump kaleidescape requirement version to v1.0.2 (#158068) 2025-12-09 20:12:55 +01:00
Jordan Harvey
39004bd0a2 Update pynintendoparental to 2.1.0 (#158487) 2025-12-09 20:11:28 +01:00
Michael Hansen
bb847ce3ff Bump pysilero-vad to 3.0.1 (#158486) 2025-12-09 13:08:34 -06:00
Denis Shulyaka
05920a9c73 Bump openai to 2.9.0 (#158476) 2025-12-09 12:30:57 -05:00
epenet
61499a5ad4 Fix Tuya BitmapTypeInformation parsing (#158474) 2025-12-09 17:39:54 +01:00
epenet
0076aafa6e Move color_extractor services to separate module (#158341) 2025-12-09 17:32:03 +01:00
epenet
c50f4d6d2d Add Tuya local_strategy to Tuya diagnostic (#158450) 2025-12-09 17:29:11 +01:00
hanwg
68036099a2 Remove timeout parameter for Telegram bot actions (#155198)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-12-09 16:53:02 +01:00
Willem-Jan van Rootselaar
180053fe98 Bump python-bsblan to v3.1.3 (#157626) 2025-12-09 15:33:04 +01:00
Thomas D
280c25cb85 Enable volvo engine status for all engine types (#158437) 2025-12-09 14:35:02 +01:00
Petro31
4064b6d28c Sort weather platform keys (#158106)
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-12-09 14:29:01 +01:00
epenet
ff25809a3e Do not unregister google_mail services (#158431) 2025-12-09 13:52:37 +01:00
mettolen
245f47c7fb Add diagnostics to Airobot integration (#158247) 2025-12-09 12:55:37 +01:00
Andre Lengwenus
86135a19d1 Bump pypck to 0.9.7 (#158089) 2025-12-09 12:43:21 +01:00
Simon Lamon
2e038250a9 Bump doorbirdpy to v3.0.11 (#151178)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-12-09 10:37:18 +01:00
Jordan Harvey
88c7c6fc8a Update pynintendoparental to 2.0.0 (#158285) 2025-12-09 08:47:52 +01:00
epenet
d691862d0d Bump heatmiserV3 to 2.0.4 (#158304) 2025-12-09 08:47:16 +01:00
Allen Porter
cceaff7bc6 Fix roborock off peak electricity timer (#158292) 2025-12-09 08:24:04 +01:00
Michael Hansen
079c6daa63 Replace microVAD with Silero VAD (ggml) (#158282) 2025-12-08 20:02:14 -06:00
Michel D'Astous
b120ae827f Fix webhook exception when empty json data is sent (#158254) 2025-12-08 23:44:59 +01:00
Tsvi Mostovicz
c1227aaf1f Jewish Calendar coordinator (#152434) 2025-12-08 22:41:58 +01:00
Artur Pragacz
c0365dfe99 Query state after turn on in Onkyo (#158093) 2025-12-08 21:49:00 +01:00
Petro31
02aa3fc906 Fix legacy template entity_id field in migration (#158105)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-12-08 21:07:40 +01:00
Marcel van der Veldt
42e55491cc Skip check for onboarding done in Music Assistant integration (#158270) 2025-12-08 20:28:02 +01:00
Harvey
33e09c4967 Bump HueBLE to 2.1.0 (#158197) 2025-12-08 19:55:55 +01:00
Petro31
6f5507670f Fix multiple top-level support for template integration (#158244) 2025-12-08 19:19:52 +01:00
Paul Bottein
765be3f047 Update frontend to 20251203.2 (#158259) 2025-12-08 18:08:03 +01:00
omrishiv
12bc9e9f68 fix Lutron Caseta smart away subscription (#158082)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2025-12-08 17:45:36 +01:00
Nic Eggert
2617c4a453 Add eGauge integration (#155279)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-08 17:33:11 +01:00
andreimoraru
0e6d9ecbdc Bump yt-dlp to 2025.12.08 (#158253) 2025-12-08 16:45:23 +01:00
Sander Blomvågnes
5cdbbe999d Move Entur constants to separate module (#158256) 2025-12-08 16:40:54 +01:00
Yevhenii Vaskivskyi
5ca61386f8 Bump asusrouter to 1.21.1 (#158192) 2025-12-08 16:16:25 +01:00
Thomas55555
6d6ee866a6 Remove stale fixture in Google Air Quality (#158235) 2025-12-08 15:44:05 +01:00
Paul Bottein
eeb2b2febc Be more specific about winter mode in the description (#158230)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-12-08 15:28:17 +01:00
Thomas55555
a6c7bd76eb Bump google air quality api to 2.0.0 (#158234) 2025-12-08 13:41:28 +01:00
epenet
470f5a2396 Validate action translation placeholders (#158225)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2025-12-08 13:40:53 +01:00
Marc Mueller
d934fd974d Update Python to 3.13.11 and 3.14.2 in CI (#158238) 2025-12-08 13:30:45 +01:00
epenet
edc81b706d Fix teslemetry service description placeholders (#158240) 2025-12-08 12:50:23 +01:00
epenet
03aaebe718 Fix zwave_js service description placeholders (#158236) 2025-12-08 12:47:35 +01:00
epenet
98d61aa5b2 Fix yeelight service description placeholders (#158239) 2025-12-08 12:46:33 +01:00
Jan Bouwhuis
fe5d411856 Fix description placeholders for system_bridge (#158232) 2025-12-08 12:26:46 +01:00
Petar Petrov
efa5a773eb Add query params handling for requests in Supervisor API (#157832)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-12-08 12:09:59 +01:00
Jordan Harvey
32399de5f1 Bump pyanglianwater to 3.0.0 (#158121) 2025-12-08 11:01:41 +01:00
Klaas Schoute
a1ad28c066 Update powerfox to v2.0.0 (#158223) 2025-12-08 10:59:59 +01:00
Álvaro Fernández Rojas
6faccf4327 Update aioairzone to v1.0.4 (#158208)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2025-12-08 10:28:26 +01:00
Allen Porter
2ac15ab67d Ensure Roborock disconnects mqtt on unload/stop (#158144)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-08 09:36:34 +01:00
dependabot[bot]
d599bb9553 Bump github/codeql-action from 4.31.6 to 4.31.7 (#158218)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-08 08:51:12 +01:00
Klaas Schoute
92ee37017d Update energyzero to v4.0.1 (#158211) 2025-12-08 08:50:14 +01:00
Allen Porter
adf698d570 Bump python-roborock to 3.10.10 (#158212) 2025-12-07 20:06:50 -08:00
taltenbach
6ce9a13816 Add Roborock attach/detach mop status translations (#158184) 2025-12-07 16:33:24 -08:00
epenet
9cb9efeb88 Make Tuya find_dpcode a class method (#158028) 2025-12-07 22:32:14 +01:00
J. Nick Koston
ca31134caa Keep persistent BLE connection during Shelly WiFi provisioning (#158145)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-07 14:28:53 -06:00
Raphael Hehl
769578dc51 UnifiProtect: Create NVR device before loading platforms to fix via_device references (#158191) 2025-12-07 21:22:39 +01:00
Thomas55555
9dcabfe804 Use SectionConfig in Google Air Quality (#158188) 2025-12-07 19:49:44 +01:00
Åke Strandberg
dc6c23a58c Add program id codes for Miele WQ1000 (#158175) 2025-12-07 19:34:35 +01:00
Álvaro Fernández Rojas
6ec7efc2b8 Update aioairzone to v1.0.3 (#158181) 2025-12-07 11:54:04 -06:00
Åke Strandberg
97e5b7954e Bump pymiele dependency to 0.6.1 (#158177) 2025-12-07 18:35:28 +01:00
J. Nick Koston
25505752b7 Use "Output" for Shelly RPC switch sub-device names (#158139) 2025-12-07 11:33:00 -06:00
J. Nick Koston
95a347dcf8 Bump aioshelly to 13.23.0 (#158183) 2025-12-07 11:18:53 -06:00
Manu
8c0f3014f7 Fix secure URLs for promotional game media in Xbox integration (#158162) 2025-12-07 11:34:18 +01:00
Josef Zweck
bb3cd3ebd3 Bump pylamarzocco to 2.2.3 (#158104) 2025-12-07 08:57:02 +01:00
Philip Cheong
319d6711c4 Add support for LockStatus.JAMMED to yalexs_ble (#157551)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-12-06 13:17:09 -06:00
J. Nick Koston
ea3f76c315 Bump yalexs-ble to 3.2.2 (#158124) 2025-12-06 12:16:52 -06:00
Shay Levy
b892cc1cad Revert "Remove Shelly redundant device entry check for sleepy devices" (#158108) 2025-12-06 19:40:05 +02:00
Marc Mueller
3046c7afd8 Fix shelly RuntimeWarnings in tests (#158101) 2025-12-06 11:34:24 -06:00
Raphael Hehl
73dc81034e Implement reconfiguration flow for UniFi Protect integration (#157532)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-12-06 11:32:43 -06:00
Jesse Hills
f306cde3b6 Add response support to esphome custom actions (#157393)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-12-06 09:48:20 -06:00
Kevin Stillhammer
38c5e483a8 add entity_picture to fressnapf_tracker (#158099) 2025-12-06 13:28:53 +01:00
Michael
ce14544ec1 Add packet loss sensor to Ping integration (#158081) 2025-12-06 10:57:31 +01:00
mettolen
87b9c3193e Add sensor entities to Airobot integration (#157938) 2025-12-06 07:57:03 +01:00
Adam Goode
061c38d2a7 Make unifi LEDs EntityCategory.CONFIG (#158088) 2025-12-06 07:51:09 +01:00
Allen Porter
e1720be5a4 Update roborock quality scale (#158024) 2025-12-05 22:52:38 +01:00
Paul Bottein
2d13a92496 Update frontend to 20251203.1 (#158069) 2025-12-05 21:25:01 +01:00
Artur Pragacz
b06bffa815 Add ai_task to core files (#158058) 2025-12-05 21:14:49 +01:00
Joost Lekkerkerker
b8f4b9515b Prevent entsoe from loading (#158036)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-12-05 21:08:57 +01:00
Petro31
3c10e9f1c0 Fix inverted kelvin issue (#158054) 2025-12-05 19:12:12 +00:00
Artur Pragacz
2dec3befcd Assign hass in Condition init (#158062) 2025-12-05 19:04:11 +00:00
J. Nick Koston
7d065bf314 Bump aiodns to 3.6.0 (#158063) 2025-12-05 20:00:09 +01:00
Raphael Hehl
3315680d0b Bump uiprotect to 7.33.2 (#158057) 2025-12-05 19:43:44 +01:00
Markus Jacobsen
ce48c89a26 Fix button event entity creation in Bang & Olufsen (#157982)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
2025-12-05 18:39:58 +00:00
Jan Bouwhuis
f67a926f56 Move lametric URLs out of strings.json (#158051) 2025-12-05 19:35:07 +01:00
Paulus Schoutsen
e0a9d305b2 Use multiple selector for validation in AI task (#158056) 2025-12-05 18:51:18 +01:00
Jan Bouwhuis
4ff141d35e Move example image path out of translatable strings (#158053) 2025-12-05 18:05:09 +01:00
Artur Pragacz
f12a43b2b7 Mark reauthentication in music assistant quality scale (#158055) 2025-12-05 18:02:16 +01:00
Paul Tarjan
35e6f504a3 Fix doorbird duplicate unique ID generation (#158013)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2025-12-05 10:17:43 -06:00
Denis Shulyaka
1f68809cf9 Replace deprecated preview image model (#158048) 2025-12-05 07:55:05 -08:00
Paul Bottein
66bddebca1 Add subscribe preview feature endpoint to labs (#157976) 2025-12-05 16:36:56 +01:00
TheJulianJES
2280d779a8 Change ZHA strings for incorrect adapter state (#158021)
Co-authored-by: Norbert Rittel <norbert@rittel.de>
2025-12-05 16:35:34 +01:00
Maciej Bieniek
ebc608845c Do not create restart button for sleeping gen2+ Shelly devices (#158047) 2025-12-05 16:33:11 +01:00
Max Michels
5d13a41926 Move telegram-bot URLs out of strings.json (#155130)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
Co-authored-by: jbouwh <jan@jbsoft.nl>
2025-12-05 16:33:01 +01:00
Quentin Ulmer
630b40fbba Fix Rituals Perfume Genie (#151537)
Co-authored-by: Joostlek <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-05 16:16:51 +01:00
Manu
7fd440c4a0 Add coordinator to Duck DNS integration (#158041) 2025-12-05 15:49:48 +01:00
Petro31
2a116a2a11 Fix missing template key in deprecation repair (#158033) 2025-12-05 15:30:39 +01:00
David Bonnes
f189e3b5ca Bump evohome-async to 1.0.6 (#158005) 2025-12-05 13:27:38 +01:00
wollew
4cd460351d Add Squeezebox binary sensors for player alarm status (#154491)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-12-05 11:43:19 +01:00
Johnny Willemsen
afea571c2c Enhance migration logging for home_connect (#158027) 2025-12-05 11:30:10 +01:00
Manu
e4aadd675e Add reconfigure flow to Duck DNS (#157948) 2025-12-05 10:19:05 +01:00
Abílio Costa
a47255c233 Bump whirlpool-sixth-sense to 1.0.3 (#157996) 2025-12-05 08:27:31 +01:00
hanwg
c1e7492743 Improve action descriptions for Telegram bot (#158022) 2025-12-05 08:26:42 +01:00
Kevin Stillhammer
63e8cf582f Set PARALLEL_UPDATES in fressnapf_tracker (#158008) 2025-12-05 08:21:48 +01:00
Allen Porter
73f23168a2 Bump python-roborock to 3.10.2 (#158020) 2025-12-05 08:20:41 +01:00
Mark Adkins
20d8176515 SharkIQ dep upgrade v1.5.0 (#158015) 2025-12-04 22:00:47 -05:00
Ezra Freedman
c9351a022e Add HassStopMoving intent for covers and valves (#155267)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2025-12-04 22:23:49 +01:00
epenet
4e8a31a4e2 Improve Tuya data validation (#157968)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-04 22:20:05 +01:00
johanzander
2beb551db3 Replace bare Exception with specific exceptions in Growatt (#157790) 2025-12-04 20:44:41 +01:00
Markus Jacobsen
90cea0325f Bump mozart_api to 5.3.1.108.0 (#157983) 2025-12-04 19:29:35 +00:00
dontinelli
f5dd9d83ac Bump solarlog_cli to 0.6.1 (#157845) 2025-12-04 19:24:33 +00:00
Alsatian67
e0484ba1ff Improve dev error message for YAML platform setup missing method (#155505)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-04 20:06:37 +01:00
Kevin Stillhammer
62f758f695 mark quality_scale rules done for fressnapf_tracker (#157990) 2025-12-04 20:03:53 +01:00
Abílio Costa
20d2115122 Bump oralb-ble to 1.0.2 (#157992) 2025-12-04 18:51:16 +01:00
Jan Bouwhuis
2bed7afe0e Move out example URL and IP of strings.json for reolink (#157970) 2025-12-04 18:37:30 +01:00
Petro31
2eeac5f9c9 Update template deprecation to be more explicit (#157965) 2025-12-04 18:34:01 +01:00
Abílio Costa
a35af9097b Remove uneeded async_setup_component from trigger/condition tests (#157873) 2025-12-04 17:21:30 +00:00
Luke Lashley
710b7c2b41 Bump python-Roborock to 3.10.0 (#157980) 2025-12-04 17:26:41 +01:00
Abílio Costa
c058810461 Cache flattened service descriptions in websocket api (#157510)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-12-04 16:05:49 +00:00
Kevin Stillhammer
0ccfd77fef add switch platform to fressnapf_tracker (#157971) 2025-12-04 16:44:53 +01:00
epenet
4805b33a27 Fix unit parsing in Tuya climate entities (#157964) 2025-12-04 16:17:39 +01:00
Hem Bhagat
c333036959 Move translatable URL out of strings.json for ntfy integration (#155859) 2025-12-04 16:17:16 +01:00
Petro31
002eed24f1 Fix template migration errors (#157949) 2025-12-04 16:16:58 +01:00
Jan Bouwhuis
9a9f8271b3 Move pilight URL out of strings.json (#157967) 2025-12-04 16:02:28 +01:00
Paulus Schoutsen
855d7c6e16 Extract WebRTC integration (#157648) 2025-12-04 09:44:24 -05:00
Jordan Harvey
837de55ce6 Set account number as required for Anglian Water config entry (#157939) 2025-12-04 15:39:52 +01:00
epenet
81ed259c59 Move Tuya type information classes to separate module (#157958)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-04 15:34:04 +01:00
Manu
5f00452c96 Convert image URLs to secure URLs in Xbox integration (#157945) 2025-12-04 15:12:33 +01:00
Jan Bouwhuis
06a44de3fb Move Yeelight URLs out of translatable strings for action descriptions (#157957) 2025-12-04 15:10:50 +01:00
Jan Bouwhuis
11b4d75cfb Move out zwave_js api docs url from strings.json (#157959) 2025-12-04 15:10:26 +01:00
David Rapan
845c9ee05f Fix Starlink's ever updating uptime (#155574)
Signed-off-by: David Rapan <david@rapan.cz>
2025-12-04 14:44:23 +01:00
Jordan Harvey
dedf6b1223 Add pyanglianwater to Anglian Water loggers (#157947) 2025-12-04 13:55:24 +01:00
Felipe Santos
c1b631d049 Remove Intellicode extension from devcontainer (#157894) 2025-12-04 13:43:01 +01:00
milanhin
6cc645bc6c Remove deprecation warning of step_id in ConfigFlow class (#157925) 2025-12-04 13:41:52 +01:00
Jan Bouwhuis
f10866395d Move out URL of Xiaomy_aquara from strings.json (#157937)
Co-authored-by: Michelle "MishManners®™" Duke <36594527+mishmanners@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-04 12:59:55 +01:00
Jan Bouwhuis
df68448b27 Move translatable URL from rainmachine push_weather_data action description (#157941)
Co-authored-by: Michelle "MishManners®™" Duke <36594527+mishmanners@users.noreply.github.com>
Co-authored-by: Josef Zweck <josef@zweck.dev>
2025-12-04 12:59:15 +01:00
Abílio Costa
bf7b96622c Correct websocket commands test name (#157870) 2025-12-04 11:14:34 +00:00
Kevin Stillhammer
53c644ac5b add light platform to fressnapf_tracker (#157865) 2025-12-04 11:09:53 +01:00
starkillerOG
5e9107e52b Bump reolink_aio to 0.17.1 (#157929) 2025-12-04 10:58:25 +01:00
Jan Bouwhuis
ca9ea267c7 Move teslemetry time-of-use URL out of strings.json (#157874) 2025-12-04 10:34:51 +01:00
ryanjones-gentex
f1bfe2f11e Add HomeLink integration (#136460)
Co-authored-by: Nicholas Aelick <niaexa@syntronic.com>
2025-12-04 10:32:02 +01:00
Franck Nijhof
34cc6036b9 Merge branch 'master' into dev 2025-12-04 09:12:55 +00:00
cdnninja
2facfbadaa Add VeSync type hints and returns (#157900)
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2025-12-04 09:19:48 +01:00
cdnninja
1b1dface35 Fix VeSync binary sensor discovery (#157898) 2025-12-04 09:14:35 +01:00
TheJulianJES
3c0cfd5e0c Display error when forming new ZHA network fails (#157863) 2025-12-03 22:37:52 -05:00
Luke Lashley
69f66ffef4 Correctly pass MopParserConfig for Roborock (#157891) 2025-12-03 17:35:51 -08:00
Norbert Rittel
d2c3543b6c Consistently use "Labs" as name in kitchen_sink (#157875) 2025-12-04 00:19:24 +01:00
Franck Nijhof
ca4a2d441e 2025.12.0 (#157330) 2025-12-03 19:06:27 +01:00
Joost Lekkerkerker
f42fe9cee3 Add button to reset hood filter to SmartThings (#157847) 2025-12-03 18:38:23 +01:00
Kevin Stillhammer
b67873f40c bump fressnapftracker to 0.2.0 (#157868) 2025-12-03 18:29:20 +01:00
Markus Jacobsen
ecc08fce0f Reduce naming verbosity in Bang & Olufsen (#157825) 2025-12-03 17:46:18 +01:00
Ludovic BOUÉ
375f536b15 Add Matter DoorPositionSensor open/closed count sensors (#155809) 2025-12-03 17:35:47 +01:00
Kevin Stillhammer
5cff813eac remove deep_sleep binary_sensor from fressnapf_tracker (#157857) 2025-12-03 17:28:59 +01:00
Thomas55555
9129665c64 Fix strings in Google Air Quality (#157862) 2025-12-03 17:26:21 +01:00
Robert Resch
f4e11da1a6 Add retry logic to docker.io image push step (#157859) 2025-12-03 16:53:45 +01:00
Bram Kragten
e0238b5ab2 Update frontend to 20251203.0 (#157851) 2025-12-03 10:40:05 -05:00
Luke Lashley
352f3813e2 Bump Roborock to 3.9.3 (#157852) 2025-12-03 16:37:59 +01:00
Manu
b1399a5541 Prevent startup blocking when a friend’s trophy summary is private on PlayStation Network (#157597)
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
2025-12-03 16:29:51 +01:00
J. Nick Koston
316cddec86 Bump bleak to 2.0.0 (#157766) 2025-12-03 15:25:42 +00:00
starkillerOG
2f71aec26f Bump reolink_aio to 0.17.0 (#157850) 2025-12-03 16:22:02 +01:00
Joost Lekkerkerker
aa72b76ee7 Add cooktop fixture to SmartThings (#157842) 2025-12-03 15:36:29 +01:00
Erik Montnemery
e009898107 Remove template config entry from source device (#157814)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-12-03 15:24:48 +01:00
Artur Pragacz
ceb13e70b9 Add integration type to wake_on_lan (#157726)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-03 15:14:57 +01:00
Robert Resch
498a80ac7f Bump deebot-client to 17.0.0 (#157836) 2025-12-03 15:12:31 +01:00
Artur Pragacz
1a60c46d67 Bump aioonkyo to 0.4.0 (#157838) 2025-12-03 14:46:52 +01:00
Matthias Alphart
62fba5ca20 Update xknx to 3.12.0 (#157835) 2025-12-03 14:40:40 +01:00
victorigualada
b54cde795c Bump hass-nabucasa from 1.6.2 to 1.7.0 (#157834) 2025-12-03 14:37:45 +01:00
victorigualada
0f456373bf Allow non strict response_format structures for Cloud LLM generation (#157822) 2025-12-03 14:31:09 +01:00
IAmStiven
a5042027b8 Add support for new ElevenLabs model Scribe v2 (#156961) 2025-12-03 14:29:25 +01:00
Franck Nijhof
b15b5ba95c Add final learn more and feedback links for purpose-specific triggers and conditions preview feature (#157830) 2025-12-03 13:14:37 +01:00
Robert Resch
cd6e72798e Prioritize default stun port over alternative (#157829) 2025-12-03 13:14:28 +01:00
Kamil Breguła
739157e59f Simplify availability property in WLED (#157800)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
2025-12-03 13:00:21 +01:00
torben-iometer
267aa1af42 bump iometer to v0.3.0 (#157826) 2025-12-03 12:47:05 +01:00
Michael
7328b61a69 Add integration_type to Oralb (#157828) 2025-12-03 12:46:50 +01:00
Allen Porter
203f2fb364 Bump google-nest-sdm to 9.1.2 (#157812)
Co-authored-by: Josef Zweck <josef@zweck.dev>
Co-authored-by: Robert Resch <robert@resch.dev>
2025-12-03 11:23:00 +01:00
Josef Zweck
b956c17ce4 Mark nordpool as service integration_type (#157810) 2025-12-03 11:22:42 +01:00
Marc Mueller
5163dc0567 Fix ping TypeError when killing the process (#157794) 2025-12-03 11:22:14 +01:00
Allen Porter
31a0478717 Bump python-roborock to 3.9.2 (#157815)
Co-authored-by: Robert Resch <robert@resch.dev>
2025-12-03 10:56:56 +01:00
dependabot[bot]
24da3f0db8 Bump actions/checkout from 6.0.0 to 6.0.1 (#157806)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-03 10:38:45 +01:00
dependabot[bot]
786922fc5d Bump actions/stale from 10.1.0 to 10.1.1 (#157807)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-03 10:36:44 +01:00
Erik Montnemery
c2f8b6986b Pin Python point release used in CI (#157819) 2025-12-03 10:26:15 +01:00
hanwg
0a0832671f Fix bug in group notify entities when title is missing (#157171)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-12-03 09:44:01 +01:00
Aidan Timson
7b353d7ad4 Add levoit virtual integration (#157618) 2025-12-03 09:38:01 +01:00
epenet
99de73a729 Update SFR Box unit of measurement (#157813) 2025-12-03 08:46:59 +01:00
Joost Lekkerkerker
1995fbd252 Make occupancy trigger check occupancy instead of presence (#157791) 2025-12-03 08:15:31 +01:00
Kamil Breguła
315ea9dc76 Update release URL in WLED (#157801) 2025-12-03 05:55:03 +01:00
Josef Zweck
639a96f8cb La Marzocco add Bluetooth offline mode (#157011) 2025-12-03 05:53:27 +01:00
Stefan Agner
b6786c5a42 Add storage link to low disk space repair issue (#157786)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2025-12-02 22:14:24 -05:00
Kamil Breguła
6f6e9b8057 Add quality scale for WLED (#155482)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
2025-12-03 01:19:02 +01:00
johanzander
e0c687e415 Remove extra logging in Growatt (#157788) 2025-12-03 00:38:03 +01:00
Abestanis
982362110c Allow to configure KNX time, date & datetime entities via UI (#157603) 2025-12-02 23:45:43 +01:00
Lukas
90dc3a8fdf Pooldose: add number platform (#157787) 2025-12-02 23:31:44 +01:00
J. Nick Koston
5112742b71 Bump habluetooth to 5.8.0 (#157771) 2025-12-02 15:55:37 -06:00
johanzander
8899bc01bd Add bronze quality scale to Growatt Server integration (#154649)
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-12-02 22:36:51 +01:00
Joris Pelgröm
ed8f9105ff Bump letpot to 0.6.4 (#157781) 2025-12-02 22:12:41 +01:00
Michael Hansen
185de98f5e Bump hassil to 3.5.0 (#157780) 2025-12-02 22:11:00 +01:00
Paulus Schoutsen
e857abb43f Allow fetching the Cloud ICE servers (#157774) 2025-12-02 16:02:30 -05:00
Joost Lekkerkerker
5b1829f3a1 Add hood filter usage entity to SmartThings (#157775) 2025-12-02 21:45:17 +01:00
Kamil Breguła
520156a33a Handle unsupported version in WLED (#157778)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
2025-12-02 21:20:36 +01:00
Kevin Stillhammer
e3b5342b76 use sentence casing in binary_sensor for fressnapf_tracker (#157772) 2025-12-02 21:06:18 +01:00
Josef Zweck
951b19e80c Add integration_type for tedee (#157776) 2025-12-02 21:04:51 +01:00
Sab44
e2351ecec2 Fix orphaned devices not being removed during integration startup (#155900) 2025-12-02 21:03:37 +01:00
Joost Lekkerkerker
d75e5498c6 Add health concern entities to SmartThings (#157773) 2025-12-02 21:00:50 +01:00
puddly
2dd58dbe39 Fix ZHA network formation (#157769) 2025-12-02 14:59:55 -05:00
Joost Lekkerkerker
4ef17799db Add snapshot test to Vivotek (#157767) 2025-12-02 20:47:02 +01:00
Joost Lekkerkerker
9373378350 Add fixture for hood to SmartThings (#157770) 2025-12-02 20:46:06 +01:00
Marcel van der Veldt
18833a194b Let AuthenticationRequired also trigger the reauth flow in MusicAssistant (#157580) 2025-12-02 14:22:40 -05:00
Kevin Stillhammer
2631c77bee add platform binary_sensor to fressnapf_tracker (#157753) 2025-12-02 20:05:34 +01:00
Kevin McCormack
c67247bf32 Add config flow for Vivotek integration (#154801)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-12-02 19:47:22 +01:00
Joost Lekkerkerker
18b5ffd365 Add SmartThings walloven fixtures (#157748) 2025-12-02 19:32:28 +01:00
Bram Kragten
c4e3a4d65e Update frontend to 20251202.0 (#157755)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-12-02 17:50:13 +01:00
victorigualada
84d2686517 Don't register Home Assistant Cloud LLM platforms if not logged in (#157630) 2025-12-02 17:47:08 +01:00
Michael Hansen
ae8980ce5b Bump intents to 2025.12.2 (#157758) 2025-12-02 17:43:21 +01:00
epenet
b2d4c9ecb4 Add exception translation to SFR box (#157756) 2025-12-02 17:42:16 +01:00
Matthias Alphart
f5b046ee7d Add integration_type for Fronius (#157760) 2025-12-02 17:31:05 +01:00
epenet
55c5fb7374 Migrate Tuya climate (swing) to use wrapper class (#157646) 2025-12-02 17:24:36 +01:00
Erik Montnemery
5d78cd328a Remove explicit templating of velbus service data (#157749) 2025-12-02 17:00:10 +01:00
epenet
bc36578ada Add mac address to SFR Box device registry entries (#157752) 2025-12-02 16:52:09 +01:00
Erik Montnemery
e63242e465 Add occupancy binary sensor triggers (#157631) 2025-12-02 16:37:02 +01:00
epenet
e84c09745d Bump SFR box IQS to silver (#157754) 2025-12-02 16:32:38 +01:00
Julian Meier
f07991d0ba Add boot and energy sensor to MyStrom Switch (#155132) 2025-12-02 15:42:04 +01:00
epenet
872fef1f6f Add reconfigure flow to SFR Box (#157711) 2025-12-02 15:35:25 +01:00
Kevin Stillhammer
c866dc973c Add sensor platform to fressnapf_tracker (#157658)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-02 15:33:57 +01:00
Zoltán Farkasdi
e2acf30637 Add Netatmo outdoor camera test (#156740) 2025-12-02 15:01:47 +01:00
epenet
29631a2c5a Cleanup SFR Box sensors (#157708) 2025-12-02 14:52:52 +01:00
Heindrich Paul
1d31e6d0ea Create more sensors for Nederlandse Spoorwegen (#154466)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
Co-authored-by: Joostlek <joostlek@outlook.com>
2025-12-02 14:39:05 +01:00
Artur Pragacz
8109d9a39c Add integration type to music_assistant (#157725)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 13:38:08 +01:00
Artur Pragacz
e1abd451b8 Add integration type to google (#157729)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 13:37:37 +01:00
Robert Resch
2c72cd94f2 Create the go2rtc unix socket inside a temporary folder (#157742) 2025-12-02 13:35:39 +01:00
Franck Nijhof
3bccb4b89c Rename preview feature to purpose-specific triggers and conditions (#157717) 2025-12-02 13:34:52 +01:00
Artur Pragacz
6d4fb30630 Add integration type to tplink (#157735)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 13:24:21 +01:00
Artur Pragacz
c04411f1bc Add integration type to dlna_dmr (#157733)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 12:48:59 +01:00
Artur Pragacz
753ea023de Add integration type to ibeacon (#157734)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 12:48:33 +01:00
Artur Pragacz
1ca1cf59eb Add integration type to ring (#157738)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 12:44:09 +01:00
Artur Pragacz
5b01bb1a29 Add integration type to broadlink (#157739)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 12:43:19 +01:00
Artur Pragacz
15c89d24eb Add integration type to xiaomi_ble (#157740)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 12:42:21 +01:00
Artur Pragacz
b26b2347e6 Add integration type to roborock (#157737)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 12:42:10 +01:00
Artur Pragacz
7d54103c09 Add integration type to speedtestdotnet (#157727)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 12:27:25 +01:00
Artur Pragacz
c705a1dc4b Add integration type to rest (#157728)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 12:26:02 +01:00
Artur Pragacz
998bd23446 Add integration type to webostv (#157736)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 12:25:37 +01:00
Artur Pragacz
3a1a58d6ad Add integration type to ping (#157730)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 12:23:19 +01:00
Artur Pragacz
f9219dd841 Add integration type to dlna_dms (#157723)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 12:04:17 +01:00
Artur Pragacz
402ed7e0f3 Add integration type to met (#157720)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 11:51:51 +01:00
epenet
7a1a5df89e Use _async_send_commands in Tuya base entity (#157716) 2025-12-02 11:50:07 +01:00
Artur Pragacz
df558fc1e7 Add integration type to google_translate (#157718)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 11:47:30 +01:00
Erik Montnemery
ec66407ef1 Improve helpers.condition.async_subscribe_platform_events (#157710) 2025-12-02 11:32:14 +01:00
Paulus Schoutsen
6b99234a43 Add integration_type to SwitchBot Bluetooth manifest (#157675)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 11:31:04 +01:00
Erik Montnemery
393be71009 Improve trigger descriptions (#157643)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-12-02 11:08:39 +01:00
epenet
12bc1687ec Use _async_send_commands in Tuya vacuum (#157704) 2025-12-02 11:01:51 +01:00
epenet
c59b322c0a Use _async_send_commands in Tuya light (#157703) 2025-12-02 11:01:38 +01:00
Arjan
e00266463d Meteo France: add new mapping "Brouillard dense givrant" (#157627) 2025-12-02 10:55:51 +01:00
dependabot[bot]
cbc8a33553 Bump github/codeql-action from 4.31.5 to 4.31.6 (#157700)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-02 10:52:13 +01:00
Paulus Schoutsen
28582f75d4 Add integration_type to Ecowitt manifest (#157666) 2025-12-02 10:49:58 +01:00
J. Diego Rodríguez Royo
39cccd212d Bump aiohomeconnect to version 0.24.0 (#157670) 2025-12-02 10:46:37 +01:00
Brett Adams
329ea33337 Add integration_type to Teslemetry manifest (#157677) 2025-12-02 10:45:41 +01:00
Brett Adams
521733c420 Revert integration type in Tessie (#157713) 2025-12-02 10:45:21 +01:00
Brett Adams
33e9f9a0ff Add integration_type to Tesla Fleet manifest (#157679) 2025-12-02 10:44:49 +01:00
Erik Montnemery
5fda2bccbe Improve helpers.trigger.async_subscribe_platform_events (#157709) 2025-12-02 10:37:19 +01:00
Åke Strandberg
ae75332656 Add program id:s and phases to new Miele WQ1000 (#157660) 2025-12-02 09:25:47 +01:00
Paulus Schoutsen
b171785f96 Add integration_type to SmartThings manifest (#157673) 2025-12-02 09:17:49 +01:00
Paulus Schoutsen
ff3d6783c6 Add integration_type to Konnected.io manifest (#157681) 2025-12-02 09:15:18 +01:00
cdnninja
b1e579bea0 Bump pyvesync to 3.3.3 (#157697) 2025-12-02 09:14:41 +01:00
Jan Bouwhuis
87241ea051 Add read support for MQTT config entry version to 2.1 (#157623) 2025-12-02 08:02:06 +01:00
dependabot[bot]
a871ec0bdf Bump home-assistant/wheels from 2025.11.0 to 2025.12.0 (#157699) 2025-12-02 07:41:44 +01:00
Copilot
b8829b645a Add labs_updated event to subscription allowlist (#157552)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: balloob <1444314+balloob@users.noreply.github.com>
2025-12-02 07:35:29 +01:00
Paulus Schoutsen
5b056a83d4 Add integration_type to Motionblinds manifest (#157682)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 07:15:11 +01:00
Paulus Schoutsen
02a70123c1 Add integration_type to HomeWizard Energy manifest (#157680)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 00:04:08 -05:00
Paulus Schoutsen
5f6d2f537a Add integration_type to Tessie manifest (#157676)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 23:50:35 -05:00
Paulus Schoutsen
5e04e9f04d Add integration_type to Home Connect manifest (#157668)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 23:49:39 -05:00
Paulus Schoutsen
56515ad7b5 Add integration_type to Sonos manifest (#157674)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 05:03:53 +01:00
Paulus Schoutsen
a1fe2bf4fa Add integration_type to HomeKit Device manifest (#157671)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 05:02:59 +01:00
Paulus Schoutsen
b8fa8efd91 Add integration_type to Apple TV manifest (#157664)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-02 05:01:56 +01:00
Jesse Hills
03557b5ef2 Bump aioesphomeapi to 42.10.0 (#157678) 2025-12-01 20:59:35 -05:00
Paulus Schoutsen
dafec8ce58 Add integration_type to Reolink manifest (#157672)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 20:58:51 -05:00
Paulus Schoutsen
6ff3f74347 Add integration_type to Nuki Bridge manifest (#157683)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-01 20:58:03 -05:00
karwosts
ddd8cf7fde Fix a bad script error message (#157654) 2025-12-01 15:19:30 -05:00
TheJulianJES
1356eea52f Set Matter integration type to "hub" (#157657) 2025-12-01 15:18:55 -05:00
TheJulianJES
6188e0e39b Set ZHA integration type to "hub" (#157656) 2025-12-01 15:18:49 -05:00
Aidan Timson
699fa1617d Default area icons for new instances (#157619) 2025-12-01 20:02:38 +01:00
Erik Montnemery
449f0fa5a5 Bump floor registry to version 1.3 and sort floors (#157614)
Co-authored-by: Franck Nijhof <git@frenck.dev>
2025-12-01 19:54:43 +01:00
andreimoraru
2e008d2bb7 bump yt-dlp to 2025.11.12 (#157645) 2025-12-01 19:44:54 +01:00
Petro31
05dec2619d Ensure platform template does not appear in repair (#157486) 2025-12-01 19:38:49 +01:00
Paul Bottein
25a6778ba8 Fix user store not loaded on restart (#157616) 2025-12-01 19:37:27 +01:00
epenet
f564b8cb44 Remove unnecessary instanciating in Tuya find_dpcode (#157473) 2025-12-01 19:37:06 +01:00
Erik Montnemery
ce6bfdebfc Improve typing of floor registry events (#157624) 2025-12-01 19:36:50 +01:00
Bram Kragten
f00a944ac1 Update frontend to 20251201.0 (#157638) 2025-12-01 19:28:18 +01:00
Petro31
3073a99ce6 Reload config entry templates when labs flag automation.new_triggers_conditions is set (#157637)
Co-authored-by: Erik Montnemery <erik@montnemery.com>
2025-12-01 19:27:30 +01:00
Erik Montnemery
8b04ce1328 Bump area registry to version 1.9 and sort areas (#157634) 2025-12-01 19:26:17 +01:00
Andrew Jackson
39f76787ab Allow multiline post in Mastodon (#157647) 2025-12-01 18:35:59 +01:00
puddly
e8acced335 Disable owning integrations for the entire firmware interaction process (#157082) 2025-12-01 18:18:32 +01:00
Åke Strandberg
758a30eebc Add code mappings for Miele WQ1000 (#157642) 2025-12-01 17:28:02 +01:00
epenet
faf94bea24 Use read_wrapper entity helper in Tuya (#157632) 2025-12-01 17:00:08 +01:00
epenet
ff91c57228 Adjust Tuya wrapper to return a command list (#157622) 2025-12-01 16:59:26 +01:00
epenet
3d2b506997 Rename Tuya method (#157640) 2025-12-01 16:56:46 +01:00
Kevin Stillhammer
d3c1c28605 Add integration fressnapf_tracker (#157480)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-01 16:48:30 +01:00
mettolen
d4e1f7741d Add sensor entities to Saunum integration (#157342)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-12-01 16:47:00 +01:00
mettolen
e713632eed Add reauth flow to Airobot integration (#157501) 2025-12-01 16:28:10 +01:00
Maciej Bieniek
060ad35ddc Bump aioshelly to version 13.22.0 (#157629) 2025-12-01 15:47:53 +01:00
Erik Montnemery
6c5dba40cd Remove cover triggers (#157621) 2025-12-01 14:09:29 +01:00
Erik Montnemery
a04d595424 Remove description_configured from condition and trigger translations (#157620) 2025-12-01 12:57:07 +01:00
Lukas
fe85eaf2a2 Pooldose: Add sensors for water meter (#157382) 2025-12-01 11:43:50 +01:00
Raphael Hehl
3551c4b01f Fix UniFi Protect G6 Instant speaker volume control (#157549) 2025-12-01 11:38:34 +01:00
Shay Levy
e7edd51a65 Refactor Shelly number platform to use upstream set_thermostat_state (#157527)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-01 12:37:12 +02:00
Raphael Hehl
0c4f2326ef Use public API for UniFi Protect light brightness control (#157550) 2025-12-01 11:32:03 +01:00
dependabot[bot]
81f4456d7c Bump actions/ai-inference from 2.0.3 to 2.0.4 (#157608)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-01 11:04:04 +01:00
Maciej Bieniek
2b608bf15c Remove name from Shelly RGBCCT sensors (#157492) 2025-12-01 10:54:19 +01:00
karwosts
972ed4b27f Finish removal of sensor.sun_solar_rising (#157606) 2025-12-01 09:26:54 +01:00
LG-ThinQ-Integration
23c167da1b Bump thinqconnect to 1.0.9 (#157607) 2025-12-01 09:26:01 +01:00
Jan Bouwhuis
34d6938171 Fix subentry ID is not updated when renaming the entity ID (#157498) 2025-12-01 07:55:13 +01:00
Kevin Stillhammer
4bb8590076 Revert "Force httpx client to use IPv4 for waze_travel_time" (#157596) 2025-12-01 06:35:26 +01:00
Norbert Rittel
5e0923b60d Fix spelling of "to set up" in hue_ble (#157593) 2025-12-01 06:33:18 +01:00
Norbert Rittel
ad48f3c634 Fix spelling of "to log in" in anglian_water (#157594) 2025-12-01 06:32:54 +01:00
cdnninja
2bdd6854eb Bump pyvesync to 3.3.2 (#157605) 2025-11-30 23:41:46 -05:00
Lukas
0bf906911c pooldose bump to api 0.8.1 (#157591) 2025-11-30 23:49:40 +01:00
Ludovic BOUÉ
874d6f5613 Add Matter fixture for Eufy vacuum Omni E28 (#157590) 2025-11-30 21:47:31 +01:00
Raphael Hehl
43ba10eebd Add missing translations for UniFi Protect integration (#157570) 2025-11-30 17:05:05 +01:00
Sanjay Govind
64bed19805 Bump bosch-alarm-mode2 to v0.4.10 (#157564) 2025-11-30 16:02:43 +01:00
Shay Levy
6357067f0f Rename Shelly SENSORS to BLOCK_SENSORS to match naming in other platforms (#157553) 2025-11-30 12:48:35 +02:00
Thomas55555
e328ba4045 Bump google air quality api to 1.1.3 (#157555) 2025-11-30 07:17:36 +01:00
Allen Porter
332dbddce6 Bump google-nest-sdm to 9.1.1 (#157562) 2025-11-29 23:19:44 -05:00
J. Nick Koston
82d935a819 Bump aioesphomeapi to 42.9.0 (#157558) 2025-11-29 18:04:55 -06:00
Raphael Hehl
4b84998c0c Fix UFPConfigEntry type consistency in unifiprotect (#157548) 2025-11-29 17:07:44 -06:00
Raphael Hehl
e10c1ebcf6 Fix UniFi Protect RTSP repair warnings when globally disabled (#157516) 2025-11-29 22:53:34 +02:00
Raphael Hehl
0174bad182 Add PARALLEL_UPDATES to UniFi Protect platforms (#157504) 2025-11-29 19:48:43 +01:00
Allen Porter
d5be623684 Bump python-roborock to 3.8.4 (#157538) 2025-11-29 20:34:27 +02:00
Raphael Hehl
d006b044c8 Bump uiprotect to 7.31.0 (#157543) 2025-11-29 20:33:09 +02:00
Jan Bouwhuis
fdd9571623 Fix MQTT entity cannot be renamed (#157540) 2025-11-29 19:29:54 +01:00
Shay Levy
4f4c5152b9 Refactor Shelly setup to use async_setup_entry_block for block entities (#157517) 2025-11-29 18:08:12 +02:00
Denis Shulyaka
b031a082cd Bump anthropic to 0.75.0 (#157491) 2025-11-29 14:35:30 +01:00
Shay Levy
a1132195fd Refactor Shelly RPC event platform to use base class (#157499) 2025-11-29 13:09:32 +02:00
Jordan Harvey
708b3dc8b2 Disable cookie quotes for Anglian Water (#157518) 2025-11-29 11:52:55 +01:00
J. Nick Koston
8ae0216135 Bump ESPHome stable BLE version to 2025.11.0 (#157511) 2025-11-29 03:40:22 -06:00
David Woodhouse
1472281cd5 Clarify percentage_command_topic and percentage_state_topic for MQTT fan (#157460)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2025-11-29 02:47:34 -06:00
Allen Porter
ceaa71d198 Bump python-roborock to 3.8.3 (#157512) 2025-11-29 09:34:22 +01:00
Arie Catsman
7f0d0c555a Bump pyenphase to 2.4.2 (#157500) 2025-11-28 21:58:57 +01:00
steaura
3b94b2491a Update bootstrap.py for grammar in slow startup error log (#157458) 2025-11-28 19:34:30 +01:00
Sebastian Schneider
8c8708d5bc Support UniFi LED control for devices without RGB (#156812) 2025-11-28 17:33:15 +01:00
Maciej Bieniek
ca35102138 Remove name for Shelly gas valve (gen1) entity (#157490) 2025-11-28 15:26:17 +01:00
Maciej Bieniek
1a1b50ef1a Add missing string for Shelly away mode switch (#157488) 2025-11-28 16:07:40 +02:00
epenet
5a4d51e57a Mark config-flow-test-coverage as done in SFR Box IQS (#157485) 2025-11-28 12:46:01 +01:00
epenet
9e1bc637e2 Improve diagnostics tests in SFR Box API (#157483) 2025-11-28 11:58:33 +01:00
Joakim Plate
ab879c07ca Add logbook support for args same as params for zha (#154997) 2025-11-28 11:15:49 +01:00
Hem Bhagat
488c97531e Move translatable URLs out of strings.json for opentherm_gw integration (#157437) 2025-11-28 10:45:45 +01:00
epenet
3b52c5df79 Use snapshot_platform helper in SFR Box tests (#157481) 2025-11-28 10:44:39 +01:00
Shay Levy
7f4b56104d Update Shelly utils coverage to 100% (#157478) 2025-11-28 11:32:41 +02:00
Åke Strandberg
ab8135ba1a Add loggers to senz manifest (#157479) 2025-11-28 10:19:28 +01:00
epenet
a88599bc09 Improve tests in SFR Box (#157444)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-28 10:10:38 +01:00
Manuel Stahl
45034279c8 Update pystiebeleltron to 0.2.5 (#157450) 2025-11-28 09:48:51 +01:00
Artur Pragacz
9f3dae6254 Add tools in default agent also in fallback pipeline (#157441) 2025-11-28 09:47:52 +01:00
epenet
ef36d7b1e5 Fix blocking call in Tuya initialisation (#157477) 2025-11-28 09:45:28 +01:00
dependabot[bot]
e5346ba017 Bump home-assistant/builder from 2025.09.0 to 2025.11.0 (#157468)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-28 09:30:37 +01:00
dependabot[bot]
68d41d2a48 Bump docker/metadata-action from 5.9.0 to 5.10.0 (#157467)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-28 09:30:22 +01:00
dependabot[bot]
00a882c20a Bump actions/ai-inference from 2.0.2 to 2.0.3 (#157466)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-28 09:28:59 +01:00
Jordan Harvey
44a6772947 Fix Anglian Water sensor setup (#157457) 2025-11-28 07:25:04 +01:00
cdnninja
f874ba1355 Move device_info to attribute in vesync (#157462) 2025-11-28 07:20:50 +01:00
Allen Porter
4fc125c49a Improve Nest error message wording in test before setup (#157465) 2025-11-28 07:19:54 +01:00
Artur Pragacz
8c59196e19 Provide log info for discovered flows in logger (#157454) 2025-11-28 02:13:10 +01:00
Shay Levy
326f7f0559 Add coverage to Shelly utils (#157455) 2025-11-28 00:29:47 +02:00
ElectricSteve
11afda8c22 bump: youtubeaio to 2.1.1 (#157452) 2025-11-27 22:42:57 +01:00
StaleLoafOfBread
f1ee0e4ac9 Add support for gallons per day as a unit of volume flow rate (#157394) 2025-11-27 20:42:16 +01:00
Joakim Plate
5f522e5afa Fix cancel propagation in update coordinator and config entry (#153504) 2025-11-27 19:48:45 +01:00
Thomas55555
4f6624d0aa Fix strings in Google Air Quality (#157297) 2025-11-27 19:26:33 +01:00
epenet
70990645a7 Mark config-flow as done in SFR Box IQS (#157439) 2025-11-27 19:14:13 +01:00
Andrew Jackson
2f7d74ff62 Add icons to transmission entities (#157436) 2025-11-27 18:38:32 +01:00
epenet
885667832b Add initial IQS to sfr_box (#155419) 2025-11-27 18:36:51 +01:00
Petro31
4646929987 Avoid custom template platform deprecations (#157415) 2025-11-27 18:06:29 +01:00
Petro31
010aea952c Reload templates when labs flag automation.new_triggers_conditions is set (#157368) 2025-11-27 18:05:33 +01:00
Bram Kragten
563678dc47 Update frontend to 20251127.0 (#157431) 2025-11-27 18:05:18 +01:00
epenet
a48f01f213 Raise UpdateFailed if API returns None in sfr_box (#157434) 2025-11-27 18:01:56 +01:00
Andrew Jackson
08b758b0d2 Add device info and parallel_updates to Transmission (#157423) 2025-11-27 17:37:27 +01:00
Allen Porter
4306fbea52 Fix regression in roborock image entity naming (#157432) 2025-11-27 17:36:18 +01:00
Robert Resch
6f4c479f8f Use same cosign version in build workflow (#157365) 2025-11-27 17:13:04 +01:00
Shay Levy
1d9c06264e Fix Shelly support for button5 trigger (#157422) 2025-11-27 16:38:45 +01:00
epenet
d045ecaf13 Add parallel_updates to SFR Box (#157426) 2025-11-27 16:04:25 +01:00
Markus Jacobsen
f7c41e694c Add media content id attribute to Bang & Olufsen (#156597) 2025-11-27 15:53:43 +01:00
Kamil Breguła
9ee7ed5cdb Fix MAC address mix-ups between WLED devices (#155491)
Co-authored-by: mik-laj <12058428+mik-laj@users.noreply.github.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2025-11-27 15:10:32 +01:00
Denis Shulyaka
83c4e2abc9 Fix Anthropic init with incorrect model (#157421) 2025-11-27 14:16:46 +01:00
Erik Montnemery
a7dbf551a3 Add climate started_cooling and started_drying triggers (#156945) 2025-11-27 12:41:08 +01:00
Petro31
0b2bb9f6bf Modernize template binary sensor (#157279) 2025-11-27 12:28:16 +01:00
tan-lawrence
0769163b67 Use "medium" instead of "med" for the medium fan mode in Coolmaster (#157253) 2025-11-27 12:27:49 +01:00
Robert Resch
2bb51e1146 Reduce Devcontainer docker layers (#157412) 2025-11-27 12:27:18 +01:00
Paulus Schoutsen
d2248d282c Default conversation agent to store tool calls in chat log (#157377) 2025-11-27 12:27:03 +01:00
Jan Čermák
8fe79a88ca Fix state classes of Ecowitt rain sensors (#157409) 2025-11-27 12:24:28 +01:00
Jaap Pieroen
7a328539b2 Bugfix: Essent remove average gas price today (#157317) 2025-11-27 12:24:07 +01:00
abelyliu
ec69efee4d Fix parsing of Tuya electricity RAW values (#157039) 2025-11-27 12:23:33 +01:00
Shay Levy
dbcde549d4 Update Shelly coordinator coverage to 100% (#157380) 2025-11-27 12:22:19 +01:00
Michael
988355e138 Add tests for the switch platform to the AdGuard Home integration (#157105) 2025-11-27 12:21:23 +01:00
victorigualada
7711eac607 Return early when setting cloud ai_task and conversation and not logged in to cloud (#157402) 2025-11-27 12:20:42 +01:00
Denis Shulyaka
32fe53cceb Add anthropic model to the device info (#157413) 2025-11-27 12:16:05 +01:00
Andrew Jackson
3a65d3c0dc Add tests to Transmission (#157355) 2025-11-27 12:15:10 +01:00
epenet
7fe26223ac Bump renault-api to 0.5.1 (#157411) 2025-11-27 12:06:57 +01:00
victorigualada
7e8496afb2 Bump hass-nabucasa from 1.6.1 to 1.6.2 (#157405) 2025-11-27 11:40:50 +01:00
Paulus Schoutsen
2ec5190243 Install requirements_test_all in dev (#157392) 2025-11-27 10:30:50 +01:00
Erik Montnemery
a706db8fdb Minor polish of cover trigger tests (#157397) 2025-11-27 09:57:03 +01:00
starkillerOG
a00923c48b Bump reolink-aio to 0.16.6 (#157399) 2025-11-27 09:53:25 +01:00
Sarah Seidman
7480d59f0f Normalize input for Droplet pairing code (#157361) 2025-11-27 08:36:30 +01:00
Erik Montnemery
4c8d9ed401 Adjust type hints in sensor group (#157373) 2025-11-27 08:34:16 +01:00
Lukas
eef10c59db Pooldose bump api 0.8.0 (new) (#157381) 2025-11-27 08:33:32 +01:00
dependabot[bot]
a1a1f8dd77 Bump docker/metadata-action from 5.5.1 to 5.9.0 (#157395) 2025-11-27 07:26:58 +01:00
dependabot[bot]
c75a5c5151 Bump docker/setup-buildx-action from 3.5.0 to 3.11.1 (#157396) 2025-11-27 07:25:16 +01:00
Allen Porter
cdaaa2bd8f Update fitbit to use new asyncio client library for device list (#157308)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-27 00:23:49 -05:00
Allen Porter
bd84dac8fb Update roborock test typing (#157370) 2025-11-27 00:21:48 -05:00
Allen Porter
42cbeca5b0 Remove old roborock map storage (#157379) 2025-11-27 00:21:04 -05:00
Allen Porter
ad0a498d10 Bump python-roborock to 3.8.1 (#157376) 2025-11-26 16:12:19 -08:00
Jan Bouwhuis
973405822b Move translatable URL out of strings.json for knx integration (#155244) 2025-11-26 23:09:59 +01:00
Franck Nijhof
b883d2f519 Bump version to 2026.1.0dev0 2025-11-26 17:15:29 +00:00
1196 changed files with 55840 additions and 12221 deletions

View File

@@ -13,6 +13,7 @@ core: &core
# Our base platforms, that are used by other integrations # Our base platforms, that are used by other integrations
base_platforms: &base_platforms base_platforms: &base_platforms
- homeassistant/components/ai_task/**
- homeassistant/components/air_quality/** - homeassistant/components/air_quality/**
- homeassistant/components/alarm_control_panel/** - homeassistant/components/alarm_control_panel/**
- homeassistant/components/assist_satellite/** - homeassistant/components/assist_satellite/**

View File

@@ -27,7 +27,6 @@
"charliermarsh.ruff", "charliermarsh.ruff",
"ms-python.pylint", "ms-python.pylint",
"ms-python.vscode-pylance", "ms-python.vscode-pylance",
"visualstudioexptteam.vscodeintellicode",
"redhat.vscode-yaml", "redhat.vscode-yaml",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"GitHub.vscode-pull-request-github", "GitHub.vscode-pull-request-github",

View File

@@ -51,6 +51,9 @@ rules:
- **Missing imports** - We use static analysis tooling to catch that - **Missing imports** - We use static analysis tooling to catch that
- **Code formatting** - We have ruff as a formatting tool that will catch those if needed (unless specifically instructed otherwise in these instructions) - **Code formatting** - We have ruff as a formatting tool that will catch those if needed (unless specifically instructed otherwise in these instructions)
**Git commit practices during review:**
- **Do NOT amend, squash, or rebase commits after review has started** - Reviewers need to see what changed since their last review
## Python Requirements ## Python Requirements
- **Compatibility**: Python 3.13+ - **Compatibility**: Python 3.13+

View File

@@ -15,7 +15,7 @@ env:
UV_HTTP_TIMEOUT: 60 UV_HTTP_TIMEOUT: 60
UV_SYSTEM_PYTHON: "true" UV_SYSTEM_PYTHON: "true"
# Base image version from https://github.com/home-assistant/docker # Base image version from https://github.com/home-assistant/docker
BASE_IMAGE_VERSION: "2025.11.3" BASE_IMAGE_VERSION: "2025.12.0"
ARCHITECTURES: '["amd64", "aarch64"]' ARCHITECTURES: '["amd64", "aarch64"]'
jobs: jobs:
@@ -30,7 +30,7 @@ jobs:
architectures: ${{ env.ARCHITECTURES }} architectures: ${{ env.ARCHITECTURES }}
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
@@ -70,7 +70,7 @@ jobs:
run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T - run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T -
- name: Upload translations - name: Upload translations
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with: with:
name: translations name: translations
path: translations.tar.gz path: translations.tar.gz
@@ -96,7 +96,7 @@ jobs:
os: ubuntu-24.04-arm os: ubuntu-24.04-arm
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Download nightly wheels of frontend - name: Download nightly wheels of frontend
if: needs.init.outputs.channel == 'dev' if: needs.init.outputs.channel == 'dev'
@@ -169,7 +169,7 @@ jobs:
fi fi
- name: Download translations - name: Download translations
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with: with:
name: translations name: translations
@@ -190,7 +190,8 @@ jobs:
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Install Cosign - &install_cosign
name: Install Cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
with: with:
cosign-release: "v2.5.3" cosign-release: "v2.5.3"
@@ -272,7 +273,7 @@ jobs:
- green - green
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set build additional args - name: Set build additional args
run: | run: |
@@ -294,7 +295,7 @@ jobs:
# home-assistant/builder doesn't support sha pinning # home-assistant/builder doesn't support sha pinning
- name: Build base image - name: Build base image
uses: home-assistant/builder@2025.09.0 uses: home-assistant/builder@2025.11.0
with: with:
args: | args: |
$BUILD_ARGS \ $BUILD_ARGS \
@@ -310,7 +311,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Initialize git - name: Initialize git
uses: home-assistant/actions/helpers/git-init@master uses: home-assistant/actions/helpers/git-init@master
@@ -353,10 +354,7 @@ jobs:
matrix: matrix:
registry: ["ghcr.io/home-assistant", "docker.io/homeassistant"] registry: ["ghcr.io/home-assistant", "docker.io/homeassistant"]
steps: steps:
- name: Install Cosign - *install_cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
with:
cosign-release: "v2.2.3"
- name: Login to DockerHub - name: Login to DockerHub
if: matrix.registry == 'docker.io/homeassistant' if: matrix.registry == 'docker.io/homeassistant'
@@ -393,7 +391,7 @@ jobs:
# 2025.12.0.dev202511250240 -> tags: 2025.12.0.dev202511250240, dev # 2025.12.0.dev202511250240 -> tags: 2025.12.0.dev202511250240, dev
- name: Generate Docker metadata - name: Generate Docker metadata
id: meta id: meta
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with: with:
images: ${{ matrix.registry }}/home-assistant images: ${{ matrix.registry }}/home-assistant
sep-tags: "," sep-tags: ","
@@ -407,7 +405,7 @@ jobs:
type=semver,pattern={{major}}.{{minor}},value=${{ needs.init.outputs.version }},enable=${{ !contains(needs.init.outputs.version, 'd') && !contains(needs.init.outputs.version, 'b') }} type=semver,pattern={{major}}.{{minor}},value=${{ needs.init.outputs.version }},enable=${{ !contains(needs.init.outputs.version, 'd') && !contains(needs.init.outputs.version, 'b') }}
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 # v3.7.1 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.7.1
- name: Copy architecture images to DockerHub - name: Copy architecture images to DockerHub
if: matrix.registry == 'docker.io/homeassistant' if: matrix.registry == 'docker.io/homeassistant'
@@ -476,7 +474,7 @@ jobs:
if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true' if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
@@ -484,7 +482,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Download translations - name: Download translations
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with: with:
name: translations name: translations
@@ -521,7 +519,7 @@ jobs:
HASSFEST_IMAGE_TAG: ghcr.io/home-assistant/hassfest:${{ needs.init.outputs.version }} HASSFEST_IMAGE_TAG: ghcr.io/home-assistant/hassfest:${{ needs.init.outputs.version }}
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0

View File

@@ -40,9 +40,9 @@ env:
CACHE_VERSION: 2 CACHE_VERSION: 2
UV_CACHE_VERSION: 1 UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 1 MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2025.12" HA_SHORT_VERSION: "2026.1"
DEFAULT_PYTHON: "3.13.9" DEFAULT_PYTHON: "3.13.11"
ALL_PYTHON_VERSIONS: "['3.13.9', '3.14.0']" ALL_PYTHON_VERSIONS: "['3.13.11', '3.14.2']"
# 10.3 is the oldest supported version # 10.3 is the oldest supported version
# - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022) # - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022)
# 10.6 is the current long-term-support # 10.6 is the current long-term-support
@@ -99,7 +99,7 @@ jobs:
steps: steps:
- &checkout - &checkout
name: Check out code from GitHub name: Check out code from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Generate partial Python venv restore key - name: Generate partial Python venv restore key
id: generate_python_cache_key id: generate_python_cache_key
run: | run: |
@@ -263,7 +263,7 @@ jobs:
check-latest: true check-latest: true
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: &actions-cache actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 uses: &actions-cache actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
with: with:
path: venv path: venv
key: &key-pre-commit-venv >- key: &key-pre-commit-venv >-
@@ -304,7 +304,7 @@ jobs:
- &cache-restore-pre-commit-venv - &cache-restore-pre-commit-venv
name: Restore base Python virtual environment name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: &actions-cache-restore actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 uses: &actions-cache-restore actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
with: with:
path: venv path: venv
fail-on-cache-miss: true fail-on-cache-miss: true
@@ -511,7 +511,7 @@ jobs:
fi fi
- name: Save apt cache - name: Save apt cache
if: steps.cache-apt-check.outputs.cache-hit != 'true' if: steps.cache-apt-check.outputs.cache-hit != 'true'
uses: &actions-cache-save actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 uses: &actions-cache-save actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
with: with:
path: *path-apt-cache path: *path-apt-cache
key: *key-apt-cache key: *key-apt-cache
@@ -534,7 +534,7 @@ jobs:
python --version python --version
uv pip freeze >> pip_freeze.txt uv pip freeze >> pip_freeze.txt
- name: Upload pip_freeze artifact - name: Upload pip_freeze artifact
uses: &actions-upload-artifact actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 uses: &actions-upload-artifact actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with: with:
name: pip-freeze-${{ matrix.python-version }} name: pip-freeze-${{ matrix.python-version }}
path: pip_freeze.txt path: pip_freeze.txt
@@ -864,7 +864,7 @@ jobs:
run: | run: |
echo "::add-matcher::.github/workflows/matchers/pytest-slow.json" echo "::add-matcher::.github/workflows/matchers/pytest-slow.json"
- name: Download pytest_buckets - name: Download pytest_buckets
uses: &actions-download-artifact actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 uses: &actions-download-artifact actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with: with:
name: pytest_buckets name: pytest_buckets
- &compile-english-translations - &compile-english-translations
@@ -1188,7 +1188,7 @@ jobs:
pattern: coverage-* pattern: coverage-*
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'true' if: needs.info.outputs.test_full_suite == 'true'
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
with: with:
fail_ci_if_error: true fail_ci_if_error: true
flags: full-suite flags: full-suite
@@ -1313,7 +1313,7 @@ jobs:
pattern: coverage-* pattern: coverage-*
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'false' if: needs.info.outputs.test_full_suite == 'false'
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
with: with:
fail_ci_if_error: true fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -21,14 +21,14 @@ jobs:
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
with: with:
languages: python languages: python
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
with: with:
category: "/language:python" category: "/language:python"

View File

@@ -231,7 +231,7 @@ jobs:
- name: Detect duplicates using AI - name: Detect duplicates using AI
id: ai_detection id: ai_detection
if: steps.extract.outputs.should_continue == 'true' && steps.fetch_similar.outputs.has_similar == 'true' if: steps.extract.outputs.should_continue == 'true' && steps.fetch_similar.outputs.has_similar == 'true'
uses: actions/ai-inference@5022b33bc1431add9b2831934daf8147a2ad9331 # v2.0.2 uses: actions/ai-inference@334892bb203895caaed82ec52d23c1ed9385151e # v2.0.4
with: with:
model: openai/gpt-4o model: openai/gpt-4o
system-prompt: | system-prompt: |

View File

@@ -57,7 +57,7 @@ jobs:
- name: Detect language using AI - name: Detect language using AI
id: ai_language_detection id: ai_language_detection
if: steps.detect_language.outputs.should_continue == 'true' if: steps.detect_language.outputs.should_continue == 'true'
uses: actions/ai-inference@5022b33bc1431add9b2831934daf8147a2ad9331 # v2.0.2 uses: actions/ai-inference@334892bb203895caaed82ec52d23c1ed9385151e # v2.0.4
with: with:
model: openai/gpt-4o-mini model: openai/gpt-4o-mini
system-prompt: | system-prompt: |

View File

@@ -10,7 +10,7 @@ jobs:
if: github.repository_owner == 'home-assistant' if: github.repository_owner == 'home-assistant'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1 - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0
with: with:
github-token: ${{ github.token }} github-token: ${{ github.token }}
issue-inactive-days: "30" issue-inactive-days: "30"

View File

@@ -17,7 +17,7 @@ jobs:
# - No PRs marked as no-stale # - No PRs marked as no-stale
# - No issues (-1) # - No issues (-1)
- name: 60 days stale PRs policy - name: 60 days stale PRs policy
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 60 days-before-stale: 60
@@ -57,7 +57,7 @@ jobs:
# - No issues marked as no-stale or help-wanted # - No issues marked as no-stale or help-wanted
# - No PRs (-1) # - No PRs (-1)
- name: 90 days stale issues - name: 90 days stale issues
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with: with:
repo-token: ${{ steps.token.outputs.token }} repo-token: ${{ steps.token.outputs.token }}
days-before-stale: 90 days-before-stale: 90
@@ -87,7 +87,7 @@ jobs:
# - No Issues marked as no-stale or help-wanted # - No Issues marked as no-stale or help-wanted
# - No PRs (-1) # - No PRs (-1)
- name: Needs more information stale issues policy - name: Needs more information stale issues policy
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with: with:
repo-token: ${{ steps.token.outputs.token }} repo-token: ${{ steps.token.outputs.token }}
only-labels: "needs-more-information" only-labels: "needs-more-information"

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0

View File

@@ -31,7 +31,7 @@ jobs:
steps: steps:
- &checkout - &checkout
name: Checkout the repository name: Checkout the repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python id: python
@@ -74,7 +74,7 @@ jobs:
) > .env_file ) > .env_file
- name: Upload env_file - name: Upload env_file
uses: &actions-upload-artifact actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 uses: &actions-upload-artifact actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with: with:
name: env_file name: env_file
path: ./.env_file path: ./.env_file
@@ -119,7 +119,7 @@ jobs:
- &download-env-file - &download-env-file
name: Download env_file name: Download env_file
uses: &actions-download-artifact actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 uses: &actions-download-artifact actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with: with:
name: env_file name: env_file
@@ -136,7 +136,7 @@ jobs:
sed -i "/uv/d" requirements_diff.txt sed -i "/uv/d" requirements_diff.txt
- name: Build wheels - name: Build wheels
uses: &home-assistant-wheels home-assistant/wheels@6066c17a2a4aafcf7bdfeae01717f63adfcdba98 # 2025.11.0 uses: &home-assistant-wheels home-assistant/wheels@e5742a69d69f0e274e2689c998900c7d19652c21 # 2025.12.0
with: with:
abi: ${{ matrix.abi }} abi: ${{ matrix.abi }}
tag: musllinux_1_2 tag: musllinux_1_2

22
CODEOWNERS generated
View File

@@ -73,6 +73,8 @@ build.json @home-assistant/supervisor
/tests/components/airobot/ @mettolen /tests/components/airobot/ @mettolen
/homeassistant/components/airos/ @CoMPaTech /homeassistant/components/airos/ @CoMPaTech
/tests/components/airos/ @CoMPaTech /tests/components/airos/ @CoMPaTech
/homeassistant/components/airpatrol/ @antondalgren
/tests/components/airpatrol/ @antondalgren
/homeassistant/components/airq/ @Sibgatulin @dl2080 /homeassistant/components/airq/ @Sibgatulin @dl2080
/tests/components/airq/ @Sibgatulin @dl2080 /tests/components/airq/ @Sibgatulin @dl2080
/homeassistant/components/airthings/ @danielhiversen @LaStrada /homeassistant/components/airthings/ @danielhiversen @LaStrada
@@ -218,8 +220,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/bizkaibus/ @UgaitzEtxebarria /homeassistant/components/bizkaibus/ @UgaitzEtxebarria
/homeassistant/components/blebox/ @bbx-a @swistakm /homeassistant/components/blebox/ @bbx-a @swistakm
/tests/components/blebox/ @bbx-a @swistakm /tests/components/blebox/ @bbx-a @swistakm
/homeassistant/components/blink/ @fronzbot @mkmer /homeassistant/components/blink/ @fronzbot
/tests/components/blink/ @fronzbot @mkmer /tests/components/blink/ @fronzbot
/homeassistant/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23 /homeassistant/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23
/tests/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23 /tests/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23
/homeassistant/components/bluemaestro/ @bdraco /homeassistant/components/bluemaestro/ @bdraco
@@ -306,8 +308,8 @@ build.json @home-assistant/supervisor
/tests/components/config/ @home-assistant/core /tests/components/config/ @home-assistant/core
/homeassistant/components/configurator/ @home-assistant/core /homeassistant/components/configurator/ @home-assistant/core
/tests/components/configurator/ @home-assistant/core /tests/components/configurator/ @home-assistant/core
/homeassistant/components/control4/ @lawtancool /homeassistant/components/control4/ @lawtancool @davidrecordon
/tests/components/control4/ @lawtancool /tests/components/control4/ @lawtancool @davidrecordon
/homeassistant/components/conversation/ @home-assistant/core @synesthesiam @arturpragacz /homeassistant/components/conversation/ @home-assistant/core @synesthesiam @arturpragacz
/tests/components/conversation/ @home-assistant/core @synesthesiam @arturpragacz /tests/components/conversation/ @home-assistant/core @synesthesiam @arturpragacz
/homeassistant/components/cookidoo/ @miaucl /homeassistant/components/cookidoo/ @miaucl
@@ -418,6 +420,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/efergy/ @tkdrob /homeassistant/components/efergy/ @tkdrob
/tests/components/efergy/ @tkdrob /tests/components/efergy/ @tkdrob
/homeassistant/components/egardia/ @jeroenterheerdt /homeassistant/components/egardia/ @jeroenterheerdt
/homeassistant/components/egauge/ @neggert
/tests/components/egauge/ @neggert
/homeassistant/components/eheimdigital/ @autinerd /homeassistant/components/eheimdigital/ @autinerd
/tests/components/eheimdigital/ @autinerd /tests/components/eheimdigital/ @autinerd
/homeassistant/components/ekeybionyx/ @richardpolzer /homeassistant/components/ekeybionyx/ @richardpolzer
@@ -460,7 +464,7 @@ build.json @home-assistant/supervisor
/tests/components/enigma2/ @autinerd /tests/components/enigma2/ @autinerd
/homeassistant/components/enphase_envoy/ @bdraco @cgarwood @catsmanac /homeassistant/components/enphase_envoy/ @bdraco @cgarwood @catsmanac
/tests/components/enphase_envoy/ @bdraco @cgarwood @catsmanac /tests/components/enphase_envoy/ @bdraco @cgarwood @catsmanac
/homeassistant/components/entur_public_transport/ @hfurubotten /homeassistant/components/entur_public_transport/ @hfurubotten @SanderBlom
/homeassistant/components/environment_canada/ @gwww @michaeldavie /homeassistant/components/environment_canada/ @gwww @michaeldavie
/tests/components/environment_canada/ @gwww @michaeldavie /tests/components/environment_canada/ @gwww @michaeldavie
/homeassistant/components/ephember/ @ttroy50 @roberty99 /homeassistant/components/ephember/ @ttroy50 @roberty99
@@ -539,6 +543,8 @@ build.json @home-assistant/supervisor
/tests/components/freebox/ @hacf-fr @Quentame /tests/components/freebox/ @hacf-fr @Quentame
/homeassistant/components/freedompro/ @stefano055415 /homeassistant/components/freedompro/ @stefano055415
/tests/components/freedompro/ @stefano055415 /tests/components/freedompro/ @stefano055415
/homeassistant/components/fressnapf_tracker/ @eifinger
/tests/components/fressnapf_tracker/ @eifinger
/homeassistant/components/fritz/ @AaronDavidSchneider @chemelli74 @mib1185 /homeassistant/components/fritz/ @AaronDavidSchneider @chemelli74 @mib1185
/tests/components/fritz/ @AaronDavidSchneider @chemelli74 @mib1185 /tests/components/fritz/ @AaronDavidSchneider @chemelli74 @mib1185
/homeassistant/components/fritzbox/ @mib1185 @flabbamann /homeassistant/components/fritzbox/ @mib1185 @flabbamann
@@ -569,6 +575,8 @@ build.json @home-assistant/supervisor
/tests/components/generic_hygrostat/ @Shulyaka /tests/components/generic_hygrostat/ @Shulyaka
/homeassistant/components/geniushub/ @manzanotti /homeassistant/components/geniushub/ @manzanotti
/tests/components/geniushub/ @manzanotti /tests/components/geniushub/ @manzanotti
/homeassistant/components/gentex_homelink/ @niaexa @ryanjones-gentex
/tests/components/gentex_homelink/ @niaexa @ryanjones-gentex
/homeassistant/components/geo_json_events/ @exxamalte /homeassistant/components/geo_json_events/ @exxamalte
/tests/components/geo_json_events/ @exxamalte /tests/components/geo_json_events/ @exxamalte
/homeassistant/components/geo_location/ @home-assistant/core /homeassistant/components/geo_location/ @home-assistant/core
@@ -657,6 +665,7 @@ build.json @home-assistant/supervisor
/homeassistant/components/here_travel_time/ @eifinger /homeassistant/components/here_travel_time/ @eifinger
/tests/components/here_travel_time/ @eifinger /tests/components/here_travel_time/ @eifinger
/homeassistant/components/hikvision/ @mezz64 /homeassistant/components/hikvision/ @mezz64
/tests/components/hikvision/ @mezz64
/homeassistant/components/hikvisioncam/ @fbradyirl /homeassistant/components/hikvisioncam/ @fbradyirl
/homeassistant/components/hisense_aehw4a1/ @bannhead /homeassistant/components/hisense_aehw4a1/ @bannhead
/tests/components/hisense_aehw4a1/ @bannhead /tests/components/hisense_aehw4a1/ @bannhead
@@ -1761,6 +1770,7 @@ build.json @home-assistant/supervisor
/homeassistant/components/vilfo/ @ManneW /homeassistant/components/vilfo/ @ManneW
/tests/components/vilfo/ @ManneW /tests/components/vilfo/ @ManneW
/homeassistant/components/vivotek/ @HarlemSquirrel /homeassistant/components/vivotek/ @HarlemSquirrel
/tests/components/vivotek/ @HarlemSquirrel
/homeassistant/components/vizio/ @raman325 /homeassistant/components/vizio/ @raman325
/tests/components/vizio/ @raman325 /tests/components/vizio/ @raman325
/homeassistant/components/vlc_telnet/ @rodripf @MartinHjelmare /homeassistant/components/vlc_telnet/ @rodripf @MartinHjelmare
@@ -1800,6 +1810,8 @@ build.json @home-assistant/supervisor
/tests/components/weatherflow_cloud/ @jeeftor /tests/components/weatherflow_cloud/ @jeeftor
/homeassistant/components/weatherkit/ @tjhorner /homeassistant/components/weatherkit/ @tjhorner
/tests/components/weatherkit/ @tjhorner /tests/components/weatherkit/ @tjhorner
/homeassistant/components/web_rtc/ @home-assistant/core
/tests/components/web_rtc/ @home-assistant/core
/homeassistant/components/webdav/ @jpbede /homeassistant/components/webdav/ @jpbede
/tests/components/webdav/ @jpbede /tests/components/webdav/ @jpbede
/homeassistant/components/webhook/ @home-assistant/core /homeassistant/components/webhook/ @home-assistant/core

4
Dockerfile generated
View File

@@ -24,13 +24,13 @@ ENV \
COPY rootfs / COPY rootfs /
# Add go2rtc binary # Add go2rtc binary
COPY --from=ghcr.io/alexxit/go2rtc@sha256:baef0aa19d759fcfd31607b34ce8eaf039d496282bba57731e6ae326896d7640 /usr/local/bin/go2rtc /bin/go2rtc COPY --from=ghcr.io/alexxit/go2rtc@sha256:f394f6329f5389a4c9a7fc54b09fdec9621bbb78bf7a672b973440bbdfb02241 /usr/local/bin/go2rtc /bin/go2rtc
RUN \ RUN \
# Verify go2rtc can be executed # Verify go2rtc can be executed
go2rtc --version \ go2rtc --version \
# Install uv # Install uv
&& pip3 install uv==0.9.6 && pip3 install uv==0.9.17
WORKDIR /usr/src WORKDIR /usr/src

View File

@@ -35,25 +35,22 @@ COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
USER vscode USER vscode
COPY .python-version ./
RUN uv python install
ENV VIRTUAL_ENV="/home/vscode/.local/ha-venv" ENV VIRTUAL_ENV="/home/vscode/.local/ha-venv"
RUN uv venv $VIRTUAL_ENV RUN --mount=type=bind,source=.python-version,target=.python-version \
uv python install \
&& uv venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH" ENV PATH="$VIRTUAL_ENV/bin:$PATH"
WORKDIR /tmp
# Setup hass-release # Setup hass-release
RUN git clone --depth 1 https://github.com/home-assistant/hass-release ~/hass-release \ RUN git clone --depth 1 https://github.com/home-assistant/hass-release ~/hass-release \
&& uv pip install -e ~/hass-release/ && uv pip install -e ~/hass-release/
# Install Python dependencies from requirements # Install Python dependencies from requirements
COPY requirements.txt ./ RUN --mount=type=bind,source=requirements.txt,target=requirements.txt \
COPY homeassistant/package_constraints.txt homeassistant/package_constraints.txt --mount=type=bind,source=homeassistant/package_constraints.txt,target=homeassistant/package_constraints.txt \
RUN uv pip install -r requirements.txt --mount=type=bind,source=requirements_test.txt,target=requirements_test.txt \
COPY requirements_test.txt requirements_test_pre_commit.txt ./ --mount=type=bind,source=requirements_test_pre_commit.txt,target=requirements_test_pre_commit.txt \
RUN uv pip install -r requirements_test.txt uv pip install -r requirements.txt -r requirements_test.txt
WORKDIR /workspaces WORKDIR /workspaces

View File

@@ -624,13 +624,16 @@ async def async_enable_logging(
if log_file is None: if log_file is None:
default_log_path = hass.config.path(ERROR_LOG_FILENAME) default_log_path = hass.config.path(ERROR_LOG_FILENAME)
if "SUPERVISOR" in os.environ: if "SUPERVISOR" in os.environ and "HA_DUPLICATE_LOG_FILE" not in os.environ:
_LOGGER.info("Running in Supervisor, not logging to file")
# Rename the default log file if it exists, since previous versions created # Rename the default log file if it exists, since previous versions created
# it even on Supervisor # it even on Supervisor
if os.path.isfile(default_log_path): def rename_old_file() -> None:
with contextlib.suppress(OSError): """Rename old log file in executor."""
os.rename(default_log_path, f"{default_log_path}.old") if os.path.isfile(default_log_path):
with contextlib.suppress(OSError):
os.rename(default_log_path, f"{default_log_path}.old")
await hass.async_add_executor_job(rename_old_file)
err_log_path = None err_log_path = None
else: else:
err_log_path = default_log_path err_log_path = default_log_path
@@ -1000,7 +1003,7 @@ class _WatchPendingSetups:
# We log every LOG_SLOW_STARTUP_INTERVAL until all integrations are done # We log every LOG_SLOW_STARTUP_INTERVAL until all integrations are done
# once we take over LOG_SLOW_STARTUP_INTERVAL (60s) to start up # once we take over LOG_SLOW_STARTUP_INTERVAL (60s) to start up
_LOGGER.warning( _LOGGER.warning(
"Waiting on integrations to complete setup: %s", "Waiting for integrations to complete setup: %s",
self._setup_started, self._setup_started,
) )

View File

@@ -9,8 +9,9 @@ from actron_neo_api import (
from homeassistant.const import CONF_API_TOKEN, Platform from homeassistant.const import CONF_API_TOKEN, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from .const import _LOGGER from .const import _LOGGER, DOMAIN
from .coordinator import ( from .coordinator import (
ActronAirConfigEntry, ActronAirConfigEntry,
ActronAirRuntimeData, ActronAirRuntimeData,
@@ -29,12 +30,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) ->
try: try:
systems = await api.get_ac_systems() systems = await api.get_ac_systems()
await api.update_status() await api.update_status()
except ActronAirAuthError: except ActronAirAuthError as err:
_LOGGER.error("Authentication error while setting up Actron Air integration") raise ConfigEntryAuthFailed(
raise translation_domain=DOMAIN,
translation_key="auth_error",
) from err
except ActronAirAPIError as err: except ActronAirAPIError as err:
_LOGGER.error("API error while setting up Actron Air integration: %s", err) raise ConfigEntryNotReady from err
raise
system_coordinators: dict[str, ActronAirSystemCoordinator] = {} system_coordinators: dict[str, ActronAirSystemCoordinator] = {}
for system in systems: for system in systems:

View File

@@ -1,11 +1,12 @@
"""Setup config flow for Actron Air integration.""" """Setup config flow for Actron Air integration."""
import asyncio import asyncio
from collections.abc import Mapping
from typing import Any from typing import Any
from actron_neo_api import ActronAirAPI, ActronAirAuthError from actron_neo_api import ActronAirAPI, ActronAirAuthError
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_API_TOKEN from homeassistant.const import CONF_API_TOKEN
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
@@ -95,8 +96,16 @@ class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
unique_id = str(user_data["id"]) unique_id = str(user_data["id"])
await self.async_set_unique_id(unique_id) await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
# Check if this is a reauth flow
if self.source == SOURCE_REAUTH:
self._abort_if_unique_id_mismatch(reason="wrong_account")
return self.async_update_reload_and_abort(
self._get_reauth_entry(),
data_updates={CONF_API_TOKEN: self._api.refresh_token_value},
)
self._abort_if_unique_id_configured()
return self.async_create_entry( return self.async_create_entry(
title=user_data["email"], title=user_data["email"],
data={CONF_API_TOKEN: self._api.refresh_token_value}, data={CONF_API_TOKEN: self._api.refresh_token_value},
@@ -114,6 +123,21 @@ class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
del self.login_task del self.login_task
return await self.async_step_user() return await self.async_step_user()
async def async_step_reauth(
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
"""Handle reauthentication request."""
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Confirm reauth dialog."""
if user_input is not None:
return await self.async_step_user()
return self.async_show_form(step_id="reauth_confirm")
async def async_step_connection_error( async def async_step_connection_error(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult: ) -> ConfigFlowResult:

View File

@@ -5,16 +5,23 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from actron_neo_api import ActronAirACSystem, ActronAirAPI, ActronAirStatus from actron_neo_api import (
ActronAirACSystem,
ActronAirAPI,
ActronAirAuthError,
ActronAirStatus,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from .const import _LOGGER from .const import _LOGGER, DOMAIN
STALE_DEVICE_TIMEOUT = timedelta(hours=24) SCAN_INTERVAL = timedelta(seconds=30)
STALE_DEVICE_TIMEOUT = timedelta(minutes=5)
ERROR_NO_SYSTEMS_FOUND = "no_systems_found" ERROR_NO_SYSTEMS_FOUND = "no_systems_found"
ERROR_UNKNOWN = "unknown_error" ERROR_UNKNOWN = "unknown_error"
@@ -29,9 +36,6 @@ class ActronAirRuntimeData:
type ActronAirConfigEntry = ConfigEntry[ActronAirRuntimeData] type ActronAirConfigEntry = ConfigEntry[ActronAirRuntimeData]
AUTH_ERROR_THRESHOLD = 3
SCAN_INTERVAL = timedelta(seconds=30)
class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirACSystem]): class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirACSystem]):
"""System coordinator for Actron Air integration.""" """System coordinator for Actron Air integration."""
@@ -59,7 +63,14 @@ class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirACSystem]):
async def _async_update_data(self) -> ActronAirStatus: async def _async_update_data(self) -> ActronAirStatus:
"""Fetch updates and merge incremental changes into the full state.""" """Fetch updates and merge incremental changes into the full state."""
await self.api.update_status() try:
await self.api.update_status()
except ActronAirAuthError as err:
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="auth_error",
) from err
self.status = self.api.state_manager.get_status(self.serial_number) self.status = self.api.state_manager.get_status(self.serial_number)
self.last_seen = dt_util.utcnow() self.last_seen = dt_util.utcnow()
return self.status return self.status

View File

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

View File

@@ -36,7 +36,7 @@ rules:
integration-owner: done integration-owner: done
log-when-unavailable: done log-when-unavailable: done
parallel-updates: done parallel-updates: done
reauthentication-flow: todo reauthentication-flow: done
test-coverage: todo test-coverage: todo
# Gold # Gold

View File

@@ -2,10 +2,12 @@
"config": { "config": {
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]", "already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
"oauth2_error": "Failed to start OAuth2 flow" "oauth2_error": "Failed to start authentication flow",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"wrong_account": "You must reauthenticate with the same Actron Air account that was originally configured."
}, },
"error": { "error": {
"oauth2_error": "Failed to start OAuth2 flow. Please try again later." "oauth2_error": "Failed to start authentication flow. Please try again later."
}, },
"progress": { "progress": {
"wait_for_authorization": "To authenticate, open the following URL and login at Actron Air:\n{verification_uri}\nIf the code is not automatically copied, paste the following code to authorize the integration:\n\n```{user_code}```\n\n\nThe login attempt will time out after {expires_minutes} minutes." "wait_for_authorization": "To authenticate, open the following URL and login at Actron Air:\n{verification_uri}\nIf the code is not automatically copied, paste the following code to authorize the integration:\n\n```{user_code}```\n\n\nThe login attempt will time out after {expires_minutes} minutes."
@@ -16,14 +18,23 @@
"description": "Failed to connect to Actron Air. Please check your internet connection and try again.", "description": "Failed to connect to Actron Air. Please check your internet connection and try again.",
"title": "Connection error" "title": "Connection error"
}, },
"reauth_confirm": {
"description": "Your Actron Air authentication has expired. Select continue to reauthenticate with your Actron Air account. You will be prompted to log in again to restore the connection.",
"title": "Authentication expired"
},
"timeout": { "timeout": {
"data": {}, "data": {},
"description": "The authorization process timed out. Please try again.", "description": "The authentication process timed out. Please try again.",
"title": "Authorization timeout" "title": "Authentication timeout"
}, },
"user": { "user": {
"title": "Actron Air OAuth2 Authorization" "title": "Actron Air Authentication"
} }
} }
},
"exceptions": {
"auth_error": {
"message": "Authentication failed, please reauthenticate"
}
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -101,8 +101,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
vol.Schema({str: STRUCTURE_FIELD_SCHEMA}), vol.Schema({str: STRUCTURE_FIELD_SCHEMA}),
_validate_structure_fields, _validate_structure_fields,
), ),
vol.Optional(ATTR_ATTACHMENTS): vol.All( vol.Optional(ATTR_ATTACHMENTS): selector.MediaSelector(
cv.ensure_list, [selector.MediaSelector({"accept": ["*/*"]})] {"accept": ["*/*"], "multiple": True}
), ),
} }
), ),
@@ -118,8 +118,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
vol.Required(ATTR_TASK_NAME): cv.string, vol.Required(ATTR_TASK_NAME): cv.string,
vol.Optional(ATTR_ENTITY_ID): cv.entity_id, vol.Optional(ATTR_ENTITY_ID): cv.entity_id,
vol.Required(ATTR_INSTRUCTIONS): cv.string, vol.Required(ATTR_INSTRUCTIONS): cv.string,
vol.Optional(ATTR_ATTACHMENTS): vol.All( vol.Optional(ATTR_ATTACHMENTS): selector.MediaSelector(
cv.ensure_list, [selector.MediaSelector({"accept": ["*/*"]})] {"accept": ["*/*"], "multiple": True}
), ),
} }
), ),

View File

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

View File

@@ -7,7 +7,7 @@ from homeassistant.core import HomeAssistant
from .coordinator import AirobotConfigEntry, AirobotDataUpdateCoordinator from .coordinator import AirobotConfigEntry, AirobotDataUpdateCoordinator
PLATFORMS: list[Platform] = [Platform.CLIMATE] PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.SENSOR]
async def async_setup_entry(hass: HomeAssistant, entry: AirobotConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: AirobotConfigEntry) -> bool:

View File

@@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Mapping
from dataclasses import dataclass from dataclasses import dataclass
import logging import logging
from typing import Any from typing import Any
@@ -174,6 +175,56 @@ class AirobotConfigFlow(BaseConfigFlow, domain=DOMAIN):
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
) )
async def async_step_reauth(
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
"""Handle reauthentication upon an API authentication error."""
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Confirm reauthentication dialog."""
errors: dict[str, str] = {}
reauth_entry = self._get_reauth_entry()
if user_input is not None:
# Combine existing data with new password
data = {
CONF_HOST: reauth_entry.data[CONF_HOST],
CONF_USERNAME: reauth_entry.data[CONF_USERNAME],
CONF_PASSWORD: user_input[CONF_PASSWORD],
}
try:
await validate_input(self.hass, data)
except CannotConnect:
errors["base"] = "cannot_connect"
except InvalidAuth:
errors["base"] = "invalid_auth"
except Exception:
_LOGGER.exception("Unexpected exception")
errors["base"] = "unknown"
else:
return self.async_update_reload_and_abort(
reauth_entry,
data_updates={CONF_PASSWORD: user_input[CONF_PASSWORD]},
)
return self.async_show_form(
step_id="reauth_confirm",
data_schema=vol.Schema(
{
vol.Required(CONF_PASSWORD): str,
}
),
description_placeholders={
"username": reauth_entry.data[CONF_USERNAME],
"host": reauth_entry.data[CONF_HOST],
},
errors=errors,
)
class CannotConnect(HomeAssistantError): class CannotConnect(HomeAssistantError):
"""Error to indicate we cannot connect.""" """Error to indicate we cannot connect."""

View File

@@ -11,6 +11,7 @@ from pyairobotrest.exceptions import AirobotAuthError, AirobotConnectionError
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@@ -53,7 +54,15 @@ class AirobotDataUpdateCoordinator(DataUpdateCoordinator[AirobotData]):
try: try:
status = await self.client.get_statuses() status = await self.client.get_statuses()
settings = await self.client.get_settings() settings = await self.client.get_settings()
except (AirobotAuthError, AirobotConnectionError) as err: except AirobotAuthError as err:
raise UpdateFailed(f"Failed to communicate with device: {err}") from err raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="authentication_failed",
) from err
except AirobotConnectionError as err:
raise UpdateFailed(
translation_domain=DOMAIN,
translation_key="connection_failed",
) from err
return AirobotData(status=status, settings=settings) return AirobotData(status=status, settings=settings)

View File

@@ -0,0 +1,38 @@
"""Diagnostics support for Airobot."""
from __future__ import annotations
from dataclasses import asdict
from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from .coordinator import AirobotConfigEntry
TO_REDACT_CONFIG = [CONF_HOST, CONF_MAC, CONF_PASSWORD, CONF_USERNAME]
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: AirobotConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
coordinator = entry.runtime_data
# Build device capabilities info
device_capabilities = None
if coordinator.data:
device_capabilities = {
"has_floor_sensor": coordinator.data.status.has_floor_sensor,
"has_co2_sensor": coordinator.data.status.has_co2_sensor,
"hw_version": coordinator.data.status.hw_version,
"fw_version": coordinator.data.status.fw_version,
}
return {
"entry_data": async_redact_data(entry.data, TO_REDACT_CONFIG),
"device_capabilities": device_capabilities,
"status": asdict(coordinator.data.status) if coordinator.data else None,
"settings": asdict(coordinator.data.settings) if coordinator.data else None,
}

View File

@@ -12,6 +12,6 @@
"integration_type": "device", "integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["pyairobotrest"], "loggers": ["pyairobotrest"],
"quality_scale": "bronze", "quality_scale": "silver",
"requirements": ["pyairobotrest==0.1.0"] "requirements": ["pyairobotrest==0.1.0"]
} }

View File

@@ -34,17 +34,17 @@ rules:
integration-owner: done integration-owner: done
log-when-unavailable: done log-when-unavailable: done
parallel-updates: done parallel-updates: done
reauthentication-flow: todo reauthentication-flow: done
test-coverage: done test-coverage: done
# Gold # Gold
devices: done devices: done
diagnostics: todo diagnostics: done
discovery-update-info: done discovery-update-info: done
discovery: done discovery: done
docs-data-update: done docs-data-update: done
docs-examples: todo docs-examples: todo
docs-known-limitations: todo docs-known-limitations: done
docs-supported-devices: done docs-supported-devices: done
docs-supported-functions: done docs-supported-functions: done
docs-troubleshooting: done docs-troubleshooting: done
@@ -54,8 +54,8 @@ rules:
comment: Single device integration, no dynamic device discovery needed. comment: Single device integration, no dynamic device discovery needed.
entity-category: done entity-category: done
entity-device-class: done entity-device-class: done
entity-disabled-by-default: todo entity-disabled-by-default: done
entity-translations: todo entity-translations: done
exception-translations: done exception-translations: done
icon-translations: todo icon-translations: todo
reconfiguration-flow: todo reconfiguration-flow: todo

View File

@@ -0,0 +1,150 @@
"""Sensor platform for Airobot thermostat."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime, timedelta
from pyairobotrest.models import ThermostatStatus
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE,
EntityCategory,
UnitOfTemperature,
UnitOfTime,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.util.dt import utcnow
from homeassistant.util.variance import ignore_variance
from . import AirobotConfigEntry
from .entity import AirobotEntity
PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True)
class AirobotSensorEntityDescription(SensorEntityDescription):
"""Describes Airobot sensor entity."""
value_fn: Callable[[ThermostatStatus], StateType | datetime]
supported_fn: Callable[[ThermostatStatus], bool] = lambda _: True
uptime_to_stable_datetime = ignore_variance(
lambda value: utcnow().replace(microsecond=0) - timedelta(seconds=value),
timedelta(minutes=2),
)
SENSOR_TYPES: tuple[AirobotSensorEntityDescription, ...] = (
AirobotSensorEntityDescription(
key="air_temperature",
translation_key="air_temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.temp_air,
),
AirobotSensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.hum_air,
),
AirobotSensorEntityDescription(
key="floor_temperature",
translation_key="floor_temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.temp_floor,
supported_fn=lambda status: status.has_floor_sensor,
),
AirobotSensorEntityDescription(
key="co2",
device_class=SensorDeviceClass.CO2,
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.co2,
supported_fn=lambda status: status.has_co2_sensor,
),
AirobotSensorEntityDescription(
key="air_quality_index",
device_class=SensorDeviceClass.AQI,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda status: status.aqi,
supported_fn=lambda status: status.has_co2_sensor,
),
AirobotSensorEntityDescription(
key="heating_uptime",
translation_key="heating_uptime",
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
suggested_unit_of_measurement=UnitOfTime.HOURS,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda status: status.heating_uptime,
entity_registry_enabled_default=False,
),
AirobotSensorEntityDescription(
key="errors",
translation_key="errors",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda status: status.errors,
),
AirobotSensorEntityDescription(
key="device_uptime",
translation_key="device_uptime",
device_class=SensorDeviceClass.TIMESTAMP,
entity_category=EntityCategory.DIAGNOSTIC,
value_fn=lambda status: uptime_to_stable_datetime(status.device_uptime),
entity_registry_enabled_default=False,
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: AirobotConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Airobot sensor platform."""
coordinator = entry.runtime_data
async_add_entities(
AirobotSensor(coordinator, description)
for description in SENSOR_TYPES
if description.supported_fn(coordinator.data.status)
)
class AirobotSensor(AirobotEntity, SensorEntity):
"""Representation of an Airobot sensor."""
entity_description: AirobotSensorEntityDescription
def __init__(
self,
coordinator,
description: AirobotSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = f"{coordinator.data.status.device_id}_{description.key}"
@property
def native_value(self) -> StateType | datetime:
"""Return the state of the sensor."""
return self.entity_description.value_fn(self.coordinator.data.status)

View File

@@ -1,7 +1,8 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]" "already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
}, },
"error": { "error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
@@ -14,15 +15,24 @@
"password": "[%key:common::config_flow::data::password%]" "password": "[%key:common::config_flow::data::password%]"
}, },
"data_description": { "data_description": {
"password": "The thermostat password." "password": "[%key:component::airobot::config::step::user::data_description::password%]"
}, },
"description": "Airobot thermostat {device_id} discovered at {host}. Enter the password to complete setup. Find the password in the thermostat settings menu under Connectivity → Mobile app." "description": "Airobot thermostat {device_id} discovered at {host}. Enter the password to complete setup. Find the password in the thermostat settings menu under Connectivity → Mobile app."
}, },
"reauth_confirm": {
"data": {
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"password": "[%key:component::airobot::config::step::user::data_description::password%]"
},
"description": "The authentication for Airobot thermostat at {host} (Device ID: {username}) has expired. Please enter the password to reauthenticate. Find the password in the thermostat settings menu under Connectivity → Mobile app."
},
"user": { "user": {
"data": { "data": {
"host": "[%key:common::config_flow::data::host%]", "host": "[%key:common::config_flow::data::host%]",
"password": "[%key:common::config_flow::data::password%]", "password": "[%key:common::config_flow::data::password%]",
"username": "[%key:common::config_flow::data::username%]" "username": "Device ID"
}, },
"data_description": { "data_description": {
"host": "The hostname or IP address of your Airobot thermostat.", "host": "The hostname or IP address of your Airobot thermostat.",
@@ -33,7 +43,32 @@
} }
} }
}, },
"entity": {
"sensor": {
"air_temperature": {
"name": "Air temperature"
},
"device_uptime": {
"name": "Device uptime"
},
"errors": {
"name": "Error count"
},
"floor_temperature": {
"name": "Floor temperature"
},
"heating_uptime": {
"name": "Heating uptime"
}
}
},
"exceptions": { "exceptions": {
"authentication_failed": {
"message": "Authentication failed, please reauthenticate."
},
"connection_failed": {
"message": "Failed to communicate with device."
},
"set_preset_mode_failed": { "set_preset_mode_failed": {
"message": "Failed to set preset mode to {preset_mode}." "message": "Failed to set preset mode to {preset_mode}."
}, },

View File

@@ -0,0 +1,24 @@
"""The AirPatrol integration."""
from __future__ import annotations
from homeassistant.core import HomeAssistant
from .const import PLATFORMS
from .coordinator import AirPatrolConfigEntry, AirPatrolDataUpdateCoordinator
async def async_setup_entry(hass: HomeAssistant, entry: AirPatrolConfigEntry) -> bool:
"""Set up AirPatrol from a config entry."""
coordinator = AirPatrolDataUpdateCoordinator(hass, entry)
await coordinator.async_config_entry_first_refresh()
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: AirPatrolConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@@ -0,0 +1,208 @@
"""Climate platform for AirPatrol integration."""
from __future__ import annotations
from typing import Any
from homeassistant.components.climate import (
FAN_AUTO,
FAN_HIGH,
FAN_LOW,
SWING_OFF,
SWING_ON,
ClimateEntity,
ClimateEntityFeature,
HVACMode,
)
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AirPatrolConfigEntry
from .coordinator import AirPatrolDataUpdateCoordinator
from .entity import AirPatrolEntity
PARALLEL_UPDATES = 0
AP_TO_HA_HVAC_MODES = {
"heat": HVACMode.HEAT,
"cool": HVACMode.COOL,
"off": HVACMode.OFF,
}
HA_TO_AP_HVAC_MODES = {value: key for key, value in AP_TO_HA_HVAC_MODES.items()}
AP_TO_HA_FAN_MODES = {
"min": FAN_LOW,
"max": FAN_HIGH,
"auto": FAN_AUTO,
}
HA_TO_AP_FAN_MODES = {value: key for key, value in AP_TO_HA_FAN_MODES.items()}
AP_TO_HA_SWING_MODES = {
"on": SWING_ON,
"off": SWING_OFF,
}
HA_TO_AP_SWING_MODES = {value: key for key, value in AP_TO_HA_SWING_MODES.items()}
async def async_setup_entry(
hass: HomeAssistant,
config_entry: AirPatrolConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up AirPatrol climate entities."""
coordinator = config_entry.runtime_data
units = coordinator.data
async_add_entities(
AirPatrolClimate(coordinator, unit_id)
for unit_id, unit in units.items()
if "climate" in unit
)
class AirPatrolClimate(AirPatrolEntity, ClimateEntity):
"""AirPatrol climate entity."""
_attr_name = None
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.SWING_MODE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.COOL, HVACMode.OFF]
_attr_fan_modes = [FAN_LOW, FAN_HIGH, FAN_AUTO]
_attr_swing_modes = [SWING_ON, SWING_OFF]
_attr_min_temp = 16.0
_attr_max_temp = 30.0
def __init__(
self,
coordinator: AirPatrolDataUpdateCoordinator,
unit_id: str,
) -> None:
"""Initialize the climate entity."""
super().__init__(coordinator, unit_id)
self._attr_unique_id = f"{coordinator.config_entry.unique_id}-{unit_id}"
@property
def climate_data(self) -> dict[str, Any]:
"""Return the climate data."""
return self.device_data.get("climate") or {}
@property
def params(self) -> dict[str, Any]:
"""Return the current parameters for the climate entity."""
return self.climate_data.get("ParametersData") or {}
@property
def available(self) -> bool:
"""Return if entity is available."""
return super().available and bool(self.climate_data)
@property
def current_humidity(self) -> float | None:
"""Return the current humidity."""
if humidity := self.climate_data.get("RoomHumidity"):
return float(humidity)
return None
@property
def current_temperature(self) -> float | None:
"""Return the current temperature."""
if temp := self.climate_data.get("RoomTemp"):
return float(temp)
return None
@property
def target_temperature(self) -> float | None:
"""Return the target temperature."""
if temp := self.params.get("PumpTemp"):
return float(temp)
return None
@property
def hvac_mode(self) -> HVACMode | None:
"""Return the current HVAC mode."""
pump_power = self.params.get("PumpPower")
pump_mode = self.params.get("PumpMode")
if pump_power and pump_power == "on" and pump_mode:
return AP_TO_HA_HVAC_MODES.get(pump_mode)
return HVACMode.OFF
@property
def fan_mode(self) -> str | None:
"""Return the current fan mode."""
fan_speed = self.params.get("FanSpeed")
if fan_speed:
return AP_TO_HA_FAN_MODES.get(fan_speed)
return None
@property
def swing_mode(self) -> str | None:
"""Return the current swing mode."""
swing = self.params.get("Swing")
if swing:
return AP_TO_HA_SWING_MODES.get(swing)
return None
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
params = self.params.copy()
if ATTR_TEMPERATURE in kwargs:
temp = kwargs[ATTR_TEMPERATURE]
params["PumpTemp"] = f"{temp:.3f}"
await self._async_set_params(params)
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target hvac mode."""
params = self.params.copy()
if hvac_mode == HVACMode.OFF:
params["PumpPower"] = "off"
else:
params["PumpPower"] = "on"
params["PumpMode"] = HA_TO_AP_HVAC_MODES.get(hvac_mode)
await self._async_set_params(params)
async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set new target fan mode."""
params = self.params.copy()
params["FanSpeed"] = HA_TO_AP_FAN_MODES.get(fan_mode)
await self._async_set_params(params)
async def async_set_swing_mode(self, swing_mode: str) -> None:
"""Set new target swing mode."""
params = self.params.copy()
params["Swing"] = HA_TO_AP_SWING_MODES.get(swing_mode)
await self._async_set_params(params)
async def async_turn_on(self) -> None:
"""Turn the entity on."""
params = self.params.copy()
if mode := AP_TO_HA_HVAC_MODES.get(params["PumpMode"]):
await self.async_set_hvac_mode(mode)
async def async_turn_off(self) -> None:
"""Turn the entity off."""
await self.async_set_hvac_mode(HVACMode.OFF)
async def _async_set_params(self, params: dict[str, Any]) -> None:
"""Set the unit to dry mode."""
new_climate_data = self.climate_data.copy()
new_climate_data["ParametersData"] = params
await self.coordinator.api.set_unit_climate_data(
self._unit_id, new_climate_data
)
await self.coordinator.async_request_refresh()

View File

@@ -0,0 +1,111 @@
"""Config flow for the AirPatrol integration."""
from __future__ import annotations
from collections.abc import Mapping
from typing import Any
from airpatrol.api import AirPatrolAPI, AirPatrolAuthenticationError, AirPatrolError
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_EMAIL, CONF_PASSWORD
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.selector import (
TextSelector,
TextSelectorConfig,
TextSelectorType,
)
from .const import DOMAIN
DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_EMAIL): TextSelector(
TextSelectorConfig(
type=TextSelectorType.EMAIL,
autocomplete="email",
)
),
vol.Required(CONF_PASSWORD): TextSelector(
TextSelectorConfig(
type=TextSelectorType.PASSWORD,
autocomplete="current-password",
)
),
}
)
async def validate_api(
hass: HomeAssistant, user_input: dict[str, str]
) -> tuple[str | None, str | None, dict[str, str]]:
"""Validate the API connection."""
errors: dict[str, str] = {}
session = async_get_clientsession(hass)
access_token = None
unique_id = None
try:
api = await AirPatrolAPI.authenticate(
session, user_input[CONF_EMAIL], user_input[CONF_PASSWORD]
)
except AirPatrolAuthenticationError:
errors["base"] = "invalid_auth"
except AirPatrolError:
errors["base"] = "cannot_connect"
else:
access_token = api.get_access_token()
unique_id = api.get_unique_id()
return (access_token, unique_id, errors)
class AirPatrolConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for AirPatrol."""
VERSION = 1
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
errors: dict[str, str] = {}
if user_input is not None:
access_token, unique_id, errors = await validate_api(self.hass, user_input)
if access_token and unique_id:
user_input[CONF_ACCESS_TOKEN] = access_token
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=user_input[CONF_EMAIL], data=user_input
)
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors
)
async def async_step_reauth(
self, user_input: Mapping[str, Any]
) -> ConfigFlowResult:
"""Handle reauthentication with new credentials."""
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reauthentication confirmation."""
errors: dict[str, str] = {}
if user_input:
access_token, unique_id, errors = await validate_api(self.hass, user_input)
if access_token and unique_id:
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_mismatch()
user_input[CONF_ACCESS_TOKEN] = access_token
return self.async_update_reload_and_abort(
self._get_reauth_entry(), data_updates=user_input
)
return self.async_show_form(
step_id="reauth_confirm", data_schema=DATA_SCHEMA, errors=errors
)

View File

@@ -0,0 +1,16 @@
"""Constants for the AirPatrol integration."""
from datetime import timedelta
import logging
from airpatrol.api import AirPatrolAuthenticationError, AirPatrolError
from homeassistant.const import Platform
DOMAIN = "airpatrol"
LOGGER = logging.getLogger(__package__)
PLATFORMS = [Platform.CLIMATE]
SCAN_INTERVAL = timedelta(minutes=1)
AIRPATROL_ERRORS = (AirPatrolAuthenticationError, AirPatrolError)

View File

@@ -0,0 +1,100 @@
"""Data update coordinator for AirPatrol."""
from __future__ import annotations
from typing import Any
from airpatrol.api import AirPatrolAPI, AirPatrolAuthenticationError, AirPatrolError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_EMAIL, CONF_PASSWORD
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN, LOGGER, SCAN_INTERVAL
type AirPatrolConfigEntry = ConfigEntry[AirPatrolDataUpdateCoordinator]
class AirPatrolDataUpdateCoordinator(DataUpdateCoordinator[dict[str, dict[str, Any]]]):
"""Class to manage fetching AirPatrol data."""
config_entry: AirPatrolConfigEntry
api: AirPatrolAPI
def __init__(self, hass: HomeAssistant, config_entry: AirPatrolConfigEntry) -> None:
"""Initialize."""
super().__init__(
hass,
LOGGER,
name=f"{DOMAIN.capitalize()} {config_entry.title}",
update_interval=SCAN_INTERVAL,
config_entry=config_entry,
)
async def _async_setup(self) -> None:
try:
await self._setup_client()
except AirPatrolError as api_err:
raise UpdateFailed(
f"Error communicating with AirPatrol API: {api_err}"
) from api_err
async def _async_update_data(self) -> dict[str, dict[str, Any]]:
"""Update unit data from AirPatrol API."""
return {unit_data["unit_id"]: unit_data for unit_data in await self._get_data()}
async def _get_data(self, retry: bool = False) -> list[dict[str, Any]]:
"""Fetch data from API."""
try:
return await self.api.get_data()
except AirPatrolAuthenticationError as auth_err:
if retry:
raise ConfigEntryAuthFailed(
"Authentication with AirPatrol failed"
) from auth_err
await self._update_token()
return await self._get_data(retry=True)
except AirPatrolError as err:
raise UpdateFailed(
f"Error communicating with AirPatrol API: {err}"
) from err
async def _update_token(self) -> None:
"""Refresh the AirPatrol API client and update the access token."""
session = async_get_clientsession(self.hass)
try:
self.api = await AirPatrolAPI.authenticate(
session,
self.config_entry.data[CONF_EMAIL],
self.config_entry.data[CONF_PASSWORD],
)
except AirPatrolAuthenticationError as auth_err:
raise ConfigEntryAuthFailed(
"Authentication with AirPatrol failed"
) from auth_err
self.hass.config_entries.async_update_entry(
self.config_entry,
data={
**self.config_entry.data,
CONF_ACCESS_TOKEN: self.api.get_access_token(),
},
)
async def _setup_client(self) -> None:
"""Set up the AirPatrol API client from stored access_token."""
session = async_get_clientsession(self.hass)
api = AirPatrolAPI(
session,
self.config_entry.data[CONF_ACCESS_TOKEN],
self.config_entry.unique_id,
)
try:
await api.get_data()
except AirPatrolAuthenticationError:
await self._update_token()
self.api = api

View File

@@ -0,0 +1,44 @@
"""Base entity for AirPatrol integration."""
from __future__ import annotations
from typing import Any
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import AirPatrolDataUpdateCoordinator
class AirPatrolEntity(CoordinatorEntity[AirPatrolDataUpdateCoordinator]):
"""Base entity for AirPatrol devices."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: AirPatrolDataUpdateCoordinator,
unit_id: str,
) -> None:
"""Initialize the AirPatrol entity."""
super().__init__(coordinator)
self._unit_id = unit_id
device = coordinator.data[unit_id]
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, unit_id)},
name=device["name"],
manufacturer=device["manufacturer"],
model=device["model"],
serial_number=device["hwid"],
)
@property
def device_data(self) -> dict[str, Any]:
"""Return the device data."""
return self.coordinator.data[self._unit_id]
@property
def available(self) -> bool:
"""Return if entity is available."""
return super().available and self._unit_id in self.coordinator.data

View File

@@ -0,0 +1,11 @@
{
"domain": "airpatrol",
"name": "AirPatrol",
"codeowners": ["@antondalgren"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airpatrol",
"integration_type": "device",
"iot_class": "cloud_polling",
"quality_scale": "bronze",
"requirements": ["airpatrol==0.1.0"]
}

View File

@@ -0,0 +1,65 @@
rules:
# Bronze
action-setup: done
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: Integration does not provide custom actions
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup:
status: exempt
comment: |
Entities doesn't subscribe to events.
entity-unique-id: done
has-entity-name: done
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry: done
# Silver
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters: done
docs-installation-parameters: done
entity-unavailable: done
integration-owner: done
log-when-unavailable: todo
parallel-updates: done
reauthentication-flow: done
test-coverage: done
# Gold
devices: done
diagnostics: todo
discovery-update-info: todo
discovery: todo
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo
docs-supported-devices: todo
docs-supported-functions: todo
docs-troubleshooting: todo
docs-use-cases: todo
dynamic-devices: todo
entity-category: done
entity-device-class: done
entity-disabled-by-default: todo
entity-translations: done
exception-translations: todo
icon-translations: todo
reconfiguration-flow: todo
repair-issues: todo
stale-devices: todo
# Platinum
async-dependency: todo
inject-websession: todo
strict-typing: todo

View File

@@ -0,0 +1,38 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"unique_id_mismatch": "Login credentials do not match the configured account"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"step": {
"reauth_confirm": {
"data": {
"email": "[%key:common::config_flow::data::email%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"email": "[%key:component::airpatrol::config::step::user::data_description::email%]",
"password": "[%key:component::airpatrol::config::step::user::data_description::password%]"
},
"description": "Reauthenticate with AirPatrol"
},
"user": {
"data": {
"email": "[%key:common::config_flow::data::email%]",
"password": "[%key:common::config_flow::data::password%]"
},
"data_description": {
"email": "Your AirPatrol email address",
"password": "Your AirPatrol password"
},
"description": "Connect to AirPatrol"
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,10 +4,10 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity import get_supported_features from homeassistant.helpers.entity import get_supported_features
from homeassistant.helpers.trigger import ( from homeassistant.helpers.trigger import (
EntityStateTriggerBase, EntityTargetStateTriggerBase,
Trigger, Trigger,
make_conditional_entity_state_trigger, make_entity_target_state_trigger,
make_entity_state_trigger, make_entity_transition_trigger,
) )
from .const import DOMAIN, AlarmControlPanelEntityFeature, AlarmControlPanelState from .const import DOMAIN, AlarmControlPanelEntityFeature, AlarmControlPanelState
@@ -21,7 +21,7 @@ def supports_feature(hass: HomeAssistant, entity_id: str, features: int) -> bool
return False return False
class EntityStateTriggerRequiredFeatures(EntityStateTriggerBase): class EntityStateTriggerRequiredFeatures(EntityTargetStateTriggerBase):
"""Trigger for entity state changes.""" """Trigger for entity state changes."""
_required_features: int _required_features: int
@@ -38,7 +38,7 @@ class EntityStateTriggerRequiredFeatures(EntityStateTriggerBase):
def make_entity_state_trigger_required_features( def make_entity_state_trigger_required_features(
domain: str, to_state: str, required_features: int domain: str, to_state: str, required_features: int
) -> type[EntityStateTriggerBase]: ) -> type[EntityTargetStateTriggerBase]:
"""Create an entity state trigger class.""" """Create an entity state trigger class."""
class CustomTrigger(EntityStateTriggerRequiredFeatures): class CustomTrigger(EntityStateTriggerRequiredFeatures):
@@ -52,7 +52,7 @@ def make_entity_state_trigger_required_features(
TRIGGERS: dict[str, type[Trigger]] = { TRIGGERS: dict[str, type[Trigger]] = {
"armed": make_conditional_entity_state_trigger( "armed": make_entity_transition_trigger(
DOMAIN, DOMAIN,
from_states={ from_states={
AlarmControlPanelState.ARMING, AlarmControlPanelState.ARMING,
@@ -89,8 +89,12 @@ TRIGGERS: dict[str, type[Trigger]] = {
AlarmControlPanelState.ARMED_VACATION, AlarmControlPanelState.ARMED_VACATION,
AlarmControlPanelEntityFeature.ARM_VACATION, AlarmControlPanelEntityFeature.ARM_VACATION,
), ),
"disarmed": make_entity_state_trigger(DOMAIN, AlarmControlPanelState.DISARMED), "disarmed": make_entity_target_state_trigger(
"triggered": make_entity_state_trigger(DOMAIN, AlarmControlPanelState.TRIGGERED), DOMAIN, AlarmControlPanelState.DISARMED
),
"triggered": make_entity_target_state_trigger(
DOMAIN, AlarmControlPanelState.TRIGGERED
),
} }

View File

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

View File

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

View File

@@ -39,7 +39,6 @@ async def async_setup_entry(
cookie_jar=CookieJar(quote_cookie=False), cookie_jar=CookieJar(quote_cookie=False),
), ),
refresh_token=entry.data[CONF_ACCESS_TOKEN], refresh_token=entry.data[CONF_ACCESS_TOKEN],
account_number=entry.data[CONF_ACCOUNT_NUMBER],
) )
try: try:
await auth.send_refresh_request() await auth.send_refresh_request()
@@ -49,7 +48,7 @@ async def async_setup_entry(
_aw = AnglianWater(authenticator=auth) _aw = AnglianWater(authenticator=auth)
try: try:
await _aw.validate_smart_meter() await _aw.validate_smart_meter(entry.data[CONF_ACCOUNT_NUMBER])
except SmartMeterUnavailableError as err: except SmartMeterUnavailableError as err:
raise ConfigEntryError( raise ConfigEntryError(
translation_domain=DOMAIN, translation_key="smart_meter_unavailable" translation_domain=DOMAIN, translation_key="smart_meter_unavailable"

View File

@@ -7,7 +7,7 @@ from typing import Any
from aiohttp import CookieJar from aiohttp import CookieJar
from pyanglianwater import AnglianWater from pyanglianwater import AnglianWater
from pyanglianwater.auth import BaseAuth, MSOB2CAuth from pyanglianwater.auth import MSOB2CAuth
from pyanglianwater.exceptions import ( from pyanglianwater.exceptions import (
InvalidAccountIdError, InvalidAccountIdError,
SelfAssertedError, SelfAssertedError,
@@ -35,7 +35,9 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
) )
async def validate_credentials(auth: MSOB2CAuth) -> str | MSOB2CAuth: async def validate_credentials(
auth: MSOB2CAuth, account_number: str
) -> str | MSOB2CAuth:
"""Validate the provided credentials.""" """Validate the provided credentials."""
try: try:
await auth.send_login_request() await auth.send_login_request()
@@ -46,7 +48,7 @@ async def validate_credentials(auth: MSOB2CAuth) -> str | MSOB2CAuth:
return "unknown" return "unknown"
_aw = AnglianWater(authenticator=auth) _aw = AnglianWater(authenticator=auth)
try: try:
await _aw.validate_smart_meter() await _aw.validate_smart_meter(account_number)
except (InvalidAccountIdError, SmartMeterUnavailableError): except (InvalidAccountIdError, SmartMeterUnavailableError):
return "smart_meter_unavailable" return "smart_meter_unavailable"
return auth return auth
@@ -69,10 +71,12 @@ class AnglianWaterConfigFlow(ConfigFlow, domain=DOMAIN):
self.hass, self.hass,
cookie_jar=CookieJar(quote_cookie=False), cookie_jar=CookieJar(quote_cookie=False),
), ),
account_number=user_input[CONF_ACCOUNT_NUMBER], ),
) user_input[CONF_ACCOUNT_NUMBER],
) )
if isinstance(validation_response, BaseAuth): if isinstance(validation_response, str):
errors["base"] = validation_response
else:
await self.async_set_unique_id(user_input[CONF_ACCOUNT_NUMBER]) await self.async_set_unique_id(user_input[CONF_ACCOUNT_NUMBER])
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()
return self.async_create_entry( return self.async_create_entry(
@@ -82,7 +86,6 @@ class AnglianWaterConfigFlow(ConfigFlow, domain=DOMAIN):
CONF_ACCESS_TOKEN: validation_response.refresh_token, CONF_ACCESS_TOKEN: validation_response.refresh_token,
}, },
) )
errors["base"] = validation_response
return self.async_show_form( return self.async_show_form(
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors

View File

@@ -12,7 +12,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN from .const import CONF_ACCOUNT_NUMBER, DOMAIN
type AnglianWaterConfigEntry = ConfigEntry[AnglianWaterUpdateCoordinator] type AnglianWaterConfigEntry = ConfigEntry[AnglianWaterUpdateCoordinator]
@@ -44,6 +44,6 @@ class AnglianWaterUpdateCoordinator(DataUpdateCoordinator[None]):
async def _async_update_data(self) -> None: async def _async_update_data(self) -> None:
"""Update data from Anglian Water's API.""" """Update data from Anglian Water's API."""
try: try:
return await self.api.update() return await self.api.update(self.config_entry.data[CONF_ACCOUNT_NUMBER])
except (ExpiredAccessTokenError, UnknownEndpointError) as err: except (ExpiredAccessTokenError, UnknownEndpointError) as err:
raise UpdateFailed from err raise UpdateFailed from err

View File

@@ -4,8 +4,9 @@
"codeowners": ["@pantherale0"], "codeowners": ["@pantherale0"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/anglian_water", "documentation": "https://www.home-assistant.io/integrations/anglian_water",
"integration_type": "service",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["pyanglianwater"], "loggers": ["pyanglianwater"],
"quality_scale": "bronze", "quality_scale": "bronze",
"requirements": ["pyanglianwater==2.1.0"] "requirements": ["pyanglianwater==3.1.0"]
} }

View File

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

View File

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

View File

@@ -421,6 +421,8 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
) )
if short_form.search(model_alias): if short_form.search(model_alias):
model_alias += "-0" model_alias += "-0"
if model_alias.endswith(("haiku", "opus", "sonnet")):
model_alias += "-latest"
model_options.append( model_options.append(
SelectOptionDict( SelectOptionDict(
label=model_info.display_name, label=model_info.display_name,

View File

@@ -583,7 +583,7 @@ class AnthropicBaseLLMEntity(Entity):
identifiers={(DOMAIN, subentry.subentry_id)}, identifiers={(DOMAIN, subentry.subentry_id)},
name=subentry.title, name=subentry.title,
manufacturer="Anthropic", manufacturer="Anthropic",
model="Claude", model=subentry.data.get(CONF_CHAT_MODEL, DEFAULT[CONF_CHAT_MODEL]),
entry_type=dr.DeviceEntryType.SERVICE, entry_type=dr.DeviceEntryType.SERVICE,
) )

View File

@@ -8,5 +8,5 @@
"documentation": "https://www.home-assistant.io/integrations/anthropic", "documentation": "https://www.home-assistant.io/integrations/anthropic",
"integration_type": "service", "integration_type": "service",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"requirements": ["anthropic==0.73.0"] "requirements": ["anthropic==0.75.0"]
} }

View File

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

View File

@@ -4,6 +4,7 @@
"codeowners": ["@yuxincs"], "codeowners": ["@yuxincs"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/apcupsd", "documentation": "https://www.home-assistant.io/integrations/apcupsd",
"integration_type": "device",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["apcaccess"], "loggers": ["apcaccess"],
"quality_scale": "platinum", "quality_scale": "platinum",

View File

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

View File

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

View File

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

View File

@@ -3,8 +3,9 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import dataclass from dataclasses import dataclass
import logging import logging
import math
from pymicro_vad import MicroVad from pysilero_vad import SileroVoiceActivityDetector
from pyspeex_noise import AudioProcessor from pyspeex_noise import AudioProcessor
from .const import BYTES_PER_CHUNK from .const import BYTES_PER_CHUNK
@@ -42,8 +43,8 @@ class AudioEnhancer(ABC):
"""Enhance chunk of PCM audio @ 16Khz with 16-bit mono samples.""" """Enhance chunk of PCM audio @ 16Khz with 16-bit mono samples."""
class MicroVadSpeexEnhancer(AudioEnhancer): class SileroVadSpeexEnhancer(AudioEnhancer):
"""Audio enhancer that runs microVAD and speex.""" """Audio enhancer that runs Silero VAD and speex."""
def __init__( def __init__(
self, auto_gain: int, noise_suppression: int, is_vad_enabled: bool self, auto_gain: int, noise_suppression: int, is_vad_enabled: bool
@@ -69,21 +70,49 @@ class MicroVadSpeexEnhancer(AudioEnhancer):
self.noise_suppression, self.noise_suppression,
) )
self.vad: MicroVad | None = None self.vad: SileroVoiceActivityDetector | None = None
# We get 10ms chunks but Silero works on 32ms chunks, so we have to
# buffer audio. The previous speech probability is used until enough
# audio has been buffered.
self._vad_buffer: bytearray | None = None
self._vad_buffer_chunks = 0
self._vad_buffer_chunk_idx = 0
self._last_speech_probability: float | None = None
if self.is_vad_enabled: if self.is_vad_enabled:
self.vad = MicroVad() self.vad = SileroVoiceActivityDetector()
_LOGGER.debug("Initialized microVAD")
# VAD buffer is a multiple of 10ms, but Silero VAD needs 32ms.
self._vad_buffer_chunks = int(
math.ceil(self.vad.chunk_bytes() / BYTES_PER_CHUNK)
)
self._vad_leftover_bytes = self.vad.chunk_bytes() - BYTES_PER_CHUNK
self._vad_buffer = bytearray(self.vad.chunk_bytes())
_LOGGER.debug("Initialized Silero VAD")
def enhance_chunk(self, audio: bytes, timestamp_ms: int) -> EnhancedAudioChunk: def enhance_chunk(self, audio: bytes, timestamp_ms: int) -> EnhancedAudioChunk:
"""Enhance 10ms chunk of PCM audio @ 16Khz with 16-bit mono samples.""" """Enhance 10ms chunk of PCM audio @ 16Khz with 16-bit mono samples."""
speech_probability: float | None = None
assert len(audio) == BYTES_PER_CHUNK assert len(audio) == BYTES_PER_CHUNK
if self.vad is not None: if self.vad is not None:
# Run VAD # Run VAD
speech_probability = self.vad.Process10ms(audio) assert self._vad_buffer is not None
start_idx = self._vad_buffer_chunk_idx * BYTES_PER_CHUNK
self._vad_buffer[start_idx : start_idx + BYTES_PER_CHUNK] = audio
self._vad_buffer_chunk_idx += 1
if self._vad_buffer_chunk_idx >= self._vad_buffer_chunks:
# We have enough data to run Silero VAD (32 ms)
self._last_speech_probability = self.vad.process_chunk(
self._vad_buffer[: self.vad.chunk_bytes()]
)
# Copy leftover audio that wasn't processed to start
self._vad_buffer[: self._vad_leftover_bytes] = self._vad_buffer[
-self._vad_leftover_bytes :
]
self._vad_buffer_chunk_idx = 0
if self.audio_processor is not None: if self.audio_processor is not None:
# Run noise suppression and auto gain # Run noise suppression and auto gain
@@ -92,5 +121,5 @@ class MicroVadSpeexEnhancer(AudioEnhancer):
return EnhancedAudioChunk( return EnhancedAudioChunk(
audio=audio, audio=audio,
timestamp_ms=timestamp_ms, timestamp_ms=timestamp_ms,
speech_probability=speech_probability, speech_probability=self._last_speech_probability,
) )

View File

@@ -8,5 +8,5 @@
"integration_type": "system", "integration_type": "system",
"iot_class": "local_push", "iot_class": "local_push",
"quality_scale": "internal", "quality_scale": "internal",
"requirements": ["pymicro-vad==1.0.1", "pyspeex-noise==1.0.2"] "requirements": ["pysilero-vad==3.0.1", "pyspeex-noise==1.0.2"]
} }

View File

@@ -55,7 +55,7 @@ from homeassistant.util import (
from homeassistant.util.hass_dict import HassKey from homeassistant.util.hass_dict import HassKey
from homeassistant.util.limited_size_dict import LimitedSizeDict from homeassistant.util.limited_size_dict import LimitedSizeDict
from .audio_enhancer import AudioEnhancer, EnhancedAudioChunk, MicroVadSpeexEnhancer from .audio_enhancer import AudioEnhancer, EnhancedAudioChunk, SileroVadSpeexEnhancer
from .const import ( from .const import (
ACKNOWLEDGE_PATH, ACKNOWLEDGE_PATH,
BYTES_PER_CHUNK, BYTES_PER_CHUNK,
@@ -633,7 +633,7 @@ class PipelineRun:
# Initialize with audio settings # Initialize with audio settings
if self.audio_settings.needs_processor and (self.audio_enhancer is None): if self.audio_settings.needs_processor and (self.audio_enhancer is None):
# Default audio enhancer # Default audio enhancer
self.audio_enhancer = MicroVadSpeexEnhancer( self.audio_enhancer = SileroVadSpeexEnhancer(
self.audio_settings.auto_gain_dbfs, self.audio_settings.auto_gain_dbfs,
self.audio_settings.noise_suppression_level, self.audio_settings.noise_suppression_level,
self.audio_settings.is_vad_enabled, self.audio_settings.is_vad_enabled,

View File

@@ -1,16 +1,22 @@
"""Provides triggers for assist satellites.""" """Provides triggers for assist satellites."""
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.trigger import Trigger, make_entity_state_trigger from homeassistant.helpers.trigger import Trigger, make_entity_target_state_trigger
from .const import DOMAIN from .const import DOMAIN
from .entity import AssistSatelliteState from .entity import AssistSatelliteState
TRIGGERS: dict[str, type[Trigger]] = { TRIGGERS: dict[str, type[Trigger]] = {
"idle": make_entity_state_trigger(DOMAIN, AssistSatelliteState.IDLE), "idle": make_entity_target_state_trigger(DOMAIN, AssistSatelliteState.IDLE),
"listening": make_entity_state_trigger(DOMAIN, AssistSatelliteState.LISTENING), "listening": make_entity_target_state_trigger(
"processing": make_entity_state_trigger(DOMAIN, AssistSatelliteState.PROCESSING), DOMAIN, AssistSatelliteState.LISTENING
"responding": make_entity_state_trigger(DOMAIN, AssistSatelliteState.RESPONDING), ),
"processing": make_entity_target_state_trigger(
DOMAIN, AssistSatelliteState.PROCESSING
),
"responding": make_entity_target_state_trigger(
DOMAIN, AssistSatelliteState.RESPONDING
),
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,6 +4,8 @@
"codeowners": ["@klaasnicolaas"], "codeowners": ["@klaasnicolaas"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/autarco", "documentation": "https://www.home-assistant.io/integrations/autarco",
"integration_type": "hub",
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"quality_scale": "silver",
"requirements": ["autarco==3.2.0"] "requirements": ["autarco==3.2.0"]
} }

View File

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

View File

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

View File

@@ -125,13 +125,17 @@ _EXPERIMENTAL_TRIGGER_PLATFORMS = {
"alarm_control_panel", "alarm_control_panel",
"assist_satellite", "assist_satellite",
"binary_sensor", "binary_sensor",
"button",
"climate", "climate",
"cover", "cover",
"device_tracker",
"fan", "fan",
"lawn_mower", "lawn_mower",
"light", "light",
"media_player", "media_player",
"switch",
"text", "text",
"update",
"vacuum", "vacuum",
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,29 +21,29 @@ from homeassistant.helpers import device_registry as dr
from homeassistant.util.ssl import get_default_context from homeassistant.util.ssl import get_default_context
from .const import DOMAIN from .const import DOMAIN
from .websocket import BangOlufsenWebsocket from .websocket import BeoWebsocket
@dataclass @dataclass
class BangOlufsenData: class BeoData:
"""Dataclass for API client and WebSocket client.""" """Dataclass for API client and WebSocket client."""
websocket: BangOlufsenWebsocket websocket: BeoWebsocket
client: MozartClient client: MozartClient
type BangOlufsenConfigEntry = ConfigEntry[BangOlufsenData] type BeoConfigEntry = ConfigEntry[BeoData]
PLATFORMS = [Platform.EVENT, Platform.MEDIA_PLAYER] PLATFORMS = [Platform.EVENT, Platform.MEDIA_PLAYER]
async def async_setup_entry(hass: HomeAssistant, entry: BangOlufsenConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: BeoConfigEntry) -> bool:
"""Set up from a config entry.""" """Set up from a config entry."""
# Remove casts to str # Remove casts to str
assert entry.unique_id assert entry.unique_id
# Create device now as BangOlufsenWebsocket needs a device for debug logging, firing events etc. # Create device now as BeoWebsocket needs a device for debug logging, firing events etc.
device_registry = dr.async_get(hass) device_registry = dr.async_get(hass)
device_registry.async_get_or_create( device_registry.async_get_or_create(
config_entry_id=entry.entry_id, config_entry_id=entry.entry_id,
@@ -68,10 +68,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: BangOlufsenConfigEntry)
await client.close_api_client() await client.close_api_client()
raise ConfigEntryNotReady(f"Unable to connect to {entry.title}") from error raise ConfigEntryNotReady(f"Unable to connect to {entry.title}") from error
websocket = BangOlufsenWebsocket(hass, entry, client) websocket = BeoWebsocket(hass, entry, client)
# Add the websocket and API client # Add the websocket and API client
entry.runtime_data = BangOlufsenData(websocket, client) entry.runtime_data = BeoData(websocket, client)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
@@ -82,9 +82,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: BangOlufsenConfigEntry)
return True return True
async def async_unload_entry( async def async_unload_entry(hass: HomeAssistant, entry: BeoConfigEntry) -> bool:
hass: HomeAssistant, entry: BangOlufsenConfigEntry
) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
# Close the API client and WebSocket notification listener # Close the API client and WebSocket notification listener
entry.runtime_data.client.disconnect_notifications() entry.runtime_data.client.disconnect_notifications()

View File

@@ -47,7 +47,7 @@ _exception_map = {
} }
class BangOlufsenConfigFlowHandler(ConfigFlow, domain=DOMAIN): class BeoConfigFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a config flow.""" """Handle a config flow."""
_beolink_jid = "" _beolink_jid = ""

View File

@@ -14,21 +14,26 @@ from homeassistant.components.media_player import (
) )
class BangOlufsenSource: class BeoSource:
"""Class used for associating device source ids with friendly names. May not include all sources.""" """Class used for associating device source ids with friendly names. May not include all sources."""
DEEZER: Final[Source] = Source(name="Deezer", id="deezer")
LINE_IN: Final[Source] = Source(name="Line-In", id="lineIn") LINE_IN: Final[Source] = Source(name="Line-In", id="lineIn")
NET_RADIO: Final[Source] = Source(name="B&O Radio", id="netRadio")
SPDIF: Final[Source] = Source(name="Optical", id="spdif") SPDIF: Final[Source] = Source(name="Optical", id="spdif")
TIDAL: Final[Source] = Source(name="Tidal", id="tidal")
TV: Final[Source] = Source(name="TV", id="tv")
UNKNOWN: Final[Source] = Source(name="Unknown Source", id="unknown")
URI_STREAMER: Final[Source] = Source(name="Audio Streamer", id="uriStreamer") URI_STREAMER: Final[Source] = Source(name="Audio Streamer", id="uriStreamer")
BANG_OLUFSEN_STATES: dict[str, MediaPlayerState] = { BEO_STATES: dict[str, MediaPlayerState] = {
# Dict used for translating device states to Home Assistant states. # Dict used for translating device states to Home Assistant states.
"started": MediaPlayerState.PLAYING, "started": MediaPlayerState.PLAYING,
"buffering": MediaPlayerState.PLAYING, "buffering": MediaPlayerState.PLAYING,
"idle": MediaPlayerState.IDLE, "idle": MediaPlayerState.IDLE,
"paused": MediaPlayerState.PAUSED, "paused": MediaPlayerState.PAUSED,
"stopped": MediaPlayerState.PAUSED, "stopped": MediaPlayerState.IDLE,
"ended": MediaPlayerState.PAUSED, "ended": MediaPlayerState.PAUSED,
"error": MediaPlayerState.IDLE, "error": MediaPlayerState.IDLE,
# A device's initial state is "unknown" and should be treated as "idle" # A device's initial state is "unknown" and should be treated as "idle"
@@ -36,30 +41,31 @@ BANG_OLUFSEN_STATES: dict[str, MediaPlayerState] = {
} }
# Dict used for translating Home Assistant settings to device repeat settings. # Dict used for translating Home Assistant settings to device repeat settings.
BANG_OLUFSEN_REPEAT_FROM_HA: dict[RepeatMode, str] = { BEO_REPEAT_FROM_HA: dict[RepeatMode, str] = {
RepeatMode.ALL: "all", RepeatMode.ALL: "all",
RepeatMode.ONE: "track", RepeatMode.ONE: "track",
RepeatMode.OFF: "none", RepeatMode.OFF: "none",
} }
# Dict used for translating device repeat settings to Home Assistant settings. # Dict used for translating device repeat settings to Home Assistant settings.
BANG_OLUFSEN_REPEAT_TO_HA: dict[str, RepeatMode] = { BEO_REPEAT_TO_HA: dict[str, RepeatMode] = {
value: key for key, value in BANG_OLUFSEN_REPEAT_FROM_HA.items() value: key for key, value in BEO_REPEAT_FROM_HA.items()
} }
# Media types for play_media # Media types for play_media
class BangOlufsenMediaType(StrEnum): class BeoMediaType(StrEnum):
"""Bang & Olufsen specific media types.""" """Bang & Olufsen specific media types."""
FAVOURITE = "favourite"
DEEZER = "deezer" DEEZER = "deezer"
FAVOURITE = "favourite"
OVERLAY_TTS = "overlay_tts"
RADIO = "radio" RADIO = "radio"
TIDAL = "tidal" TIDAL = "tidal"
TTS = "provider" TTS = "provider"
OVERLAY_TTS = "overlay_tts" TV = "tv"
class BangOlufsenModel(StrEnum): class BeoModel(StrEnum):
"""Enum for compatible model names.""" """Enum for compatible model names."""
# Mozart devices # Mozart devices
@@ -78,8 +84,18 @@ class BangOlufsenModel(StrEnum):
BEOREMOTE_ONE = "Beoremote One" BEOREMOTE_ONE = "Beoremote One"
class BeoAttribute(StrEnum):
"""Enum for extra_state_attribute keys."""
BEOLINK = "beolink"
BEOLINK_PEERS = "peers"
BEOLINK_SELF = "self"
BEOLINK_LEADER = "leader"
BEOLINK_LISTENERS = "listeners"
# Physical "buttons" on devices # Physical "buttons" on devices
class BangOlufsenButtons(StrEnum): class BeoButtons(StrEnum):
"""Enum for device buttons.""" """Enum for device buttons."""
BLUETOOTH = "Bluetooth" BLUETOOTH = "Bluetooth"
@@ -126,7 +142,7 @@ class WebsocketNotification(StrEnum):
DOMAIN: Final[str] = "bang_olufsen" DOMAIN: Final[str] = "bang_olufsen"
# Default values for configuration. # Default values for configuration.
DEFAULT_MODEL: Final[str] = BangOlufsenModel.BEOSOUND_BALANCE DEFAULT_MODEL: Final[str] = BeoModel.BEOSOUND_BALANCE
# Configuration. # Configuration.
CONF_SERIAL_NUMBER: Final = "serial_number" CONF_SERIAL_NUMBER: Final = "serial_number"
@@ -134,7 +150,7 @@ CONF_BEOLINK_JID: Final = "jid"
# Models to choose from in manual configuration. # Models to choose from in manual configuration.
SELECTABLE_MODELS: list[str] = [ SELECTABLE_MODELS: list[str] = [
model.value for model in BangOlufsenModel if model != BangOlufsenModel.BEOREMOTE_ONE model.value for model in BeoModel if model != BeoModel.BEOREMOTE_ONE
] ]
MANUFACTURER: Final[str] = "Bang & Olufsen" MANUFACTURER: Final[str] = "Bang & Olufsen"
@@ -146,15 +162,15 @@ ATTR_ITEM_NUMBER: Final[str] = "in"
ATTR_FRIENDLY_NAME: Final[str] = "fn" ATTR_FRIENDLY_NAME: Final[str] = "fn"
# Power states. # Power states.
BANG_OLUFSEN_ON: Final[str] = "on" BEO_ON: Final[str] = "on"
VALID_MEDIA_TYPES: Final[tuple] = ( VALID_MEDIA_TYPES: Final[tuple] = (
BangOlufsenMediaType.FAVOURITE, BeoMediaType.FAVOURITE,
BangOlufsenMediaType.DEEZER, BeoMediaType.DEEZER,
BangOlufsenMediaType.RADIO, BeoMediaType.RADIO,
BangOlufsenMediaType.TTS, BeoMediaType.TTS,
BangOlufsenMediaType.TIDAL, BeoMediaType.TIDAL,
BangOlufsenMediaType.OVERLAY_TTS, BeoMediaType.OVERLAY_TTS,
MediaType.MUSIC, MediaType.MUSIC,
MediaType.URL, MediaType.URL,
MediaType.CHANNEL, MediaType.CHANNEL,
@@ -232,7 +248,7 @@ FALLBACK_SOURCES: Final[SourceArray] = SourceArray(
) )
# Device events # Device events
BANG_OLUFSEN_WEBSOCKET_EVENT: Final[str] = f"{DOMAIN}_websocket_event" BEO_WEBSOCKET_EVENT: Final[str] = f"{DOMAIN}_websocket_event"
# Dict used to translate native Bang & Olufsen event names to string.json compatible ones # Dict used to translate native Bang & Olufsen event names to string.json compatible ones
EVENT_TRANSLATION_MAP: dict[str, str] = { EVENT_TRANSLATION_MAP: dict[str, str] = {
@@ -249,7 +265,7 @@ EVENT_TRANSLATION_MAP: dict[str, str] = {
CONNECTION_STATUS: Final[str] = "CONNECTION_STATUS" CONNECTION_STATUS: Final[str] = "CONNECTION_STATUS"
DEVICE_BUTTONS: Final[list[str]] = [x.value for x in BangOlufsenButtons] DEVICE_BUTTONS: Final[list[str]] = [x.value for x in BeoButtons]
DEVICE_BUTTON_EVENTS: Final[list[str]] = [ DEVICE_BUTTON_EVENTS: Final[list[str]] = [

View File

@@ -10,13 +10,13 @@ from homeassistant.const import CONF_MODEL
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from . import BangOlufsenConfigEntry from . import BeoConfigEntry
from .const import DOMAIN from .const import DOMAIN
from .util import get_device_buttons from .util import get_device_buttons
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: BangOlufsenConfigEntry hass: HomeAssistant, config_entry: BeoConfigEntry
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Return diagnostics for a config entry.""" """Return diagnostics for a config entry."""

View File

@@ -24,8 +24,8 @@ from homeassistant.helpers.entity import Entity
from .const import DOMAIN from .const import DOMAIN
class BangOlufsenBase: class BeoBase:
"""Base class for BangOlufsen Home Assistant objects.""" """Base class for Bang & Olufsen Home Assistant objects."""
def __init__(self, entry: ConfigEntry, client: MozartClient) -> None: def __init__(self, entry: ConfigEntry, client: MozartClient) -> None:
"""Initialize the object.""" """Initialize the object."""
@@ -51,8 +51,8 @@ class BangOlufsenBase:
) )
class BangOlufsenEntity(Entity, BangOlufsenBase): class BeoEntity(Entity, BeoBase):
"""Base Entity for BangOlufsen entities.""" """Base Entity for Bang & Olufsen entities."""
_attr_has_entity_name = True _attr_has_entity_name = True
_attr_should_poll = False _attr_should_poll = False

View File

@@ -14,7 +14,7 @@ from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import BangOlufsenConfigEntry from . import BeoConfigEntry
from .const import ( from .const import (
BEO_REMOTE_CONTROL_KEYS, BEO_REMOTE_CONTROL_KEYS,
BEO_REMOTE_KEY_EVENTS, BEO_REMOTE_KEY_EVENTS,
@@ -25,10 +25,10 @@ from .const import (
DEVICE_BUTTON_EVENTS, DEVICE_BUTTON_EVENTS,
DOMAIN, DOMAIN,
MANUFACTURER, MANUFACTURER,
BangOlufsenModel, BeoModel,
WebsocketNotification, WebsocketNotification,
) )
from .entity import BangOlufsenEntity from .entity import BeoEntity
from .util import get_device_buttons, get_remotes from .util import get_device_buttons, get_remotes
PARALLEL_UPDATES = 0 PARALLEL_UPDATES = 0
@@ -36,14 +36,14 @@ PARALLEL_UPDATES = 0
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: BangOlufsenConfigEntry, config_entry: BeoConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up Event entities from config entry.""" """Set up Event entities from config entry."""
entities: list[BangOlufsenEvent] = [] entities: list[BeoEvent] = []
async_add_entities( async_add_entities(
BangOlufsenButtonEvent(config_entry, button_type) BeoButtonEvent(config_entry, button_type)
for button_type in get_device_buttons(config_entry.data[CONF_MODEL]) for button_type in get_device_buttons(config_entry.data[CONF_MODEL])
) )
@@ -54,7 +54,7 @@ async def async_setup_entry(
# Add Light keys # Add Light keys
entities.extend( entities.extend(
[ [
BangOlufsenRemoteKeyEvent( BeoRemoteKeyEvent(
config_entry, config_entry,
remote, remote,
f"{BEO_REMOTE_SUBMENU_LIGHT}/{key_type}", f"{BEO_REMOTE_SUBMENU_LIGHT}/{key_type}",
@@ -66,7 +66,7 @@ async def async_setup_entry(
# Add Control keys # Add Control keys
entities.extend( entities.extend(
[ [
BangOlufsenRemoteKeyEvent( BeoRemoteKeyEvent(
config_entry, config_entry,
remote, remote,
f"{BEO_REMOTE_SUBMENU_CONTROL}/{key_type}", f"{BEO_REMOTE_SUBMENU_CONTROL}/{key_type}",
@@ -84,10 +84,9 @@ async def async_setup_entry(
config_entry.entry_id config_entry.entry_id
) )
for device in devices: for device in devices:
if ( if device.model == BeoModel.BEOREMOTE_ONE and device.serial_number not in {
device.model == BangOlufsenModel.BEOREMOTE_ONE remote.serial_number for remote in remotes
and device.serial_number not in {remote.serial_number for remote in remotes} }:
):
device_registry.async_update_device( device_registry.async_update_device(
device.id, remove_config_entry_id=config_entry.entry_id device.id, remove_config_entry_id=config_entry.entry_id
) )
@@ -95,13 +94,13 @@ async def async_setup_entry(
async_add_entities(new_entities=entities) async_add_entities(new_entities=entities)
class BangOlufsenEvent(BangOlufsenEntity, EventEntity): class BeoEvent(BeoEntity, EventEntity):
"""Base Event class.""" """Base Event class."""
_attr_device_class = EventDeviceClass.BUTTON _attr_device_class = EventDeviceClass.BUTTON
_attr_entity_registry_enabled_default = False _attr_entity_registry_enabled_default = False
def __init__(self, config_entry: BangOlufsenConfigEntry) -> None: def __init__(self, config_entry: BeoConfigEntry) -> None:
"""Initialize Event.""" """Initialize Event."""
super().__init__(config_entry, config_entry.runtime_data.client) super().__init__(config_entry, config_entry.runtime_data.client)
@@ -112,12 +111,12 @@ class BangOlufsenEvent(BangOlufsenEntity, EventEntity):
self.async_write_ha_state() self.async_write_ha_state()
class BangOlufsenButtonEvent(BangOlufsenEvent): class BeoButtonEvent(BeoEvent):
"""Event class for Button events.""" """Event class for Button events."""
_attr_event_types = DEVICE_BUTTON_EVENTS _attr_event_types = DEVICE_BUTTON_EVENTS
def __init__(self, config_entry: BangOlufsenConfigEntry, button_type: str) -> None: def __init__(self, config_entry: BeoConfigEntry, button_type: str) -> None:
"""Initialize Button.""" """Initialize Button."""
super().__init__(config_entry) super().__init__(config_entry)
@@ -146,14 +145,14 @@ class BangOlufsenButtonEvent(BangOlufsenEvent):
) )
class BangOlufsenRemoteKeyEvent(BangOlufsenEvent): class BeoRemoteKeyEvent(BeoEvent):
"""Event class for Beoremote One key events.""" """Event class for Beoremote One key events."""
_attr_event_types = BEO_REMOTE_KEY_EVENTS _attr_event_types = BEO_REMOTE_KEY_EVENTS
def __init__( def __init__(
self, self,
config_entry: BangOlufsenConfigEntry, config_entry: BeoConfigEntry,
remote: PairedRemote, remote: PairedRemote,
key_type: str, key_type: str,
) -> None: ) -> None:
@@ -166,8 +165,8 @@ class BangOlufsenRemoteKeyEvent(BangOlufsenEvent):
self._attr_unique_id = f"{remote.serial_number}_{self._unique_id}_{key_type}" self._attr_unique_id = f"{remote.serial_number}_{self._unique_id}_{key_type}"
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{remote.serial_number}_{self._unique_id}")}, identifiers={(DOMAIN, f"{remote.serial_number}_{self._unique_id}")},
name=f"{BangOlufsenModel.BEOREMOTE_ONE}-{remote.serial_number}-{self._unique_id}", name=f"{BeoModel.BEOREMOTE_ONE}-{remote.serial_number}-{self._unique_id}",
model=BangOlufsenModel.BEOREMOTE_ONE, model=BeoModel.BEOREMOTE_ONE,
serial_number=remote.serial_number, serial_number=remote.serial_number,
sw_version=remote.app_version, sw_version=remote.app_version,
manufacturer=MANUFACTURER, manufacturer=MANUFACTURER,

View File

@@ -6,6 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/bang_olufsen", "documentation": "https://www.home-assistant.io/integrations/bang_olufsen",
"integration_type": "device", "integration_type": "device",
"iot_class": "local_push", "iot_class": "local_push",
"requirements": ["mozart-api==5.1.0.247.1"], "requirements": ["mozart-api==5.3.1.108.0"],
"zeroconf": ["_bangolufsen._tcp.local."] "zeroconf": ["_bangolufsen._tcp.local."]
} }

View File

@@ -69,11 +69,11 @@ from homeassistant.helpers.entity_platform import (
) )
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from . import BangOlufsenConfigEntry from . import BeoConfigEntry
from .const import ( from .const import (
BANG_OLUFSEN_REPEAT_FROM_HA, BEO_REPEAT_FROM_HA,
BANG_OLUFSEN_REPEAT_TO_HA, BEO_REPEAT_TO_HA,
BANG_OLUFSEN_STATES, BEO_STATES,
BEOLINK_JOIN_SOURCES, BEOLINK_JOIN_SOURCES,
BEOLINK_JOIN_SOURCES_TO_UPPER, BEOLINK_JOIN_SOURCES_TO_UPPER,
CONF_BEOLINK_JID, CONF_BEOLINK_JID,
@@ -82,11 +82,12 @@ from .const import (
FALLBACK_SOURCES, FALLBACK_SOURCES,
MANUFACTURER, MANUFACTURER,
VALID_MEDIA_TYPES, VALID_MEDIA_TYPES,
BangOlufsenMediaType, BeoAttribute,
BangOlufsenSource, BeoMediaType,
BeoSource,
WebsocketNotification, WebsocketNotification,
) )
from .entity import BangOlufsenEntity from .entity import BeoEntity
from .util import get_serial_number_from_jid from .util import get_serial_number_from_jid
PARALLEL_UPDATES = 0 PARALLEL_UPDATES = 0
@@ -95,7 +96,7 @@ SCAN_INTERVAL = timedelta(seconds=30)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
BANG_OLUFSEN_FEATURES = ( BEO_FEATURES = (
MediaPlayerEntityFeature.BROWSE_MEDIA MediaPlayerEntityFeature.BROWSE_MEDIA
| MediaPlayerEntityFeature.CLEAR_PLAYLIST | MediaPlayerEntityFeature.CLEAR_PLAYLIST
| MediaPlayerEntityFeature.GROUPING | MediaPlayerEntityFeature.GROUPING
@@ -118,15 +119,13 @@ BANG_OLUFSEN_FEATURES = (
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: BangOlufsenConfigEntry, config_entry: BeoConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback, async_add_entities: AddConfigEntryEntitiesCallback,
) -> None: ) -> None:
"""Set up a Media Player entity from config entry.""" """Set up a Media Player entity from config entry."""
# Add MediaPlayer entity # Add MediaPlayer entity
async_add_entities( async_add_entities(
new_entities=[ new_entities=[BeoMediaPlayer(config_entry, config_entry.runtime_data.client)],
BangOlufsenMediaPlayer(config_entry, config_entry.runtime_data.client)
],
update_before_add=True, update_before_add=True,
) )
@@ -186,7 +185,7 @@ async def async_setup_entry(
) )
class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity): class BeoMediaPlayer(BeoEntity, MediaPlayerEntity):
"""Representation of a media player.""" """Representation of a media player."""
_attr_name = None _attr_name = None
@@ -219,12 +218,14 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
self._sources: dict[str, str] = {} self._sources: dict[str, str] = {}
self._state: str = MediaPlayerState.IDLE self._state: str = MediaPlayerState.IDLE
self._video_sources: dict[str, str] = {} self._video_sources: dict[str, str] = {}
self._video_source_id_map: dict[str, str] = {}
self._sound_modes: dict[str, int] = {} self._sound_modes: dict[str, int] = {}
# Beolink compatible sources # Beolink compatible sources
self._beolink_sources: dict[str, bool] = {} self._beolink_sources: dict[str, bool] = {}
self._remote_leader: BeolinkLeader | None = None self._remote_leader: BeolinkLeader | None = None
# Extra state attributes for showing Beolink: peer(s), listener(s), leader and self # Extra state attributes:
# Beolink: peer(s), listener(s), leader and self
self._beolink_attributes: dict[str, dict[str, dict[str, str]]] = {} self._beolink_attributes: dict[str, dict[str, dict[str, str]]] = {}
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
@@ -286,7 +287,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
queue_settings = await self._client.get_settings_queue(_request_timeout=5) queue_settings = await self._client.get_settings_queue(_request_timeout=5)
if queue_settings.repeat is not None: if queue_settings.repeat is not None:
self._attr_repeat = BANG_OLUFSEN_REPEAT_TO_HA[queue_settings.repeat] self._attr_repeat = BEO_REPEAT_TO_HA[queue_settings.repeat]
if queue_settings.shuffle is not None: if queue_settings.shuffle is not None:
self._attr_shuffle = queue_settings.shuffle self._attr_shuffle = queue_settings.shuffle
@@ -355,6 +356,9 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
and menu_item.label != "TV" and menu_item.label != "TV"
): ):
self._video_sources[key] = menu_item.label self._video_sources[key] = menu_item.label
self._video_source_id_map[
menu_item.content.content_uri.removeprefix("tv://")
] = menu_item.label
# Combine the source dicts # Combine the source dicts
self._sources = self._audio_sources | self._video_sources self._sources = self._audio_sources | self._video_sources
@@ -406,8 +410,8 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
# Check if source is line-in or optical and progress should be updated # Check if source is line-in or optical and progress should be updated
if self._source_change.id in ( if self._source_change.id in (
BangOlufsenSource.LINE_IN.id, BeoSource.LINE_IN.id,
BangOlufsenSource.SPDIF.id, BeoSource.SPDIF.id,
): ):
self._playback_progress = PlaybackProgress(progress=0) self._playback_progress = PlaybackProgress(progress=0)
@@ -436,7 +440,10 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
await self._async_update_beolink() await self._async_update_beolink()
async def _async_update_beolink(self) -> None: async def _async_update_beolink(self) -> None:
"""Update the current Beolink leader, listeners, peers and self.""" """Update the current Beolink leader, listeners, peers and self.
Updates Home Assistant state.
"""
self._beolink_attributes = {} self._beolink_attributes = {}
@@ -445,18 +452,22 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
# Add Beolink self # Add Beolink self
self._beolink_attributes = { self._beolink_attributes = {
"beolink": {"self": {self.device_entry.name: self._beolink_jid}} BeoAttribute.BEOLINK: {
BeoAttribute.BEOLINK_SELF: {self.device_entry.name: self._beolink_jid}
}
} }
# Add Beolink peers # Add Beolink peers
peers = await self._client.get_beolink_peers() peers = await self._client.get_beolink_peers()
if len(peers) > 0: if len(peers) > 0:
self._beolink_attributes["beolink"]["peers"] = {} self._beolink_attributes[BeoAttribute.BEOLINK][
BeoAttribute.BEOLINK_PEERS
] = {}
for peer in peers: for peer in peers:
self._beolink_attributes["beolink"]["peers"][peer.friendly_name] = ( self._beolink_attributes[BeoAttribute.BEOLINK][
peer.jid BeoAttribute.BEOLINK_PEERS
) ][peer.friendly_name] = peer.jid
# Add Beolink listeners / leader # Add Beolink listeners / leader
self._remote_leader = self._playback_metadata.remote_leader self._remote_leader = self._playback_metadata.remote_leader
@@ -477,7 +488,9 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
# Add self # Add self
group_members.append(self.entity_id) group_members.append(self.entity_id)
self._beolink_attributes["beolink"]["leader"] = { self._beolink_attributes[BeoAttribute.BEOLINK][
BeoAttribute.BEOLINK_LEADER
] = {
self._remote_leader.friendly_name: self._remote_leader.jid, self._remote_leader.friendly_name: self._remote_leader.jid,
} }
@@ -514,9 +527,9 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
beolink_listener.jid beolink_listener.jid
) )
break break
self._beolink_attributes["beolink"]["listeners"] = ( self._beolink_attributes[BeoAttribute.BEOLINK][
beolink_listeners_attribute BeoAttribute.BEOLINK_LISTENERS
) ] = beolink_listeners_attribute
self._attr_group_members = group_members self._attr_group_members = group_members
@@ -574,7 +587,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
for sound_mode in sound_modes: for sound_mode in sound_modes:
label = f"{sound_mode.name} ({sound_mode.id})" label = f"{sound_mode.name} ({sound_mode.id})"
self._sound_modes[label] = sound_mode.id self._sound_modes[label] = cast(int, sound_mode.id)
if sound_mode.id == active_sound_mode.id: if sound_mode.id == active_sound_mode.id:
self._attr_sound_mode = label self._attr_sound_mode = label
@@ -587,7 +600,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
@property @property
def supported_features(self) -> MediaPlayerEntityFeature: def supported_features(self) -> MediaPlayerEntityFeature:
"""Flag media player features that are supported.""" """Flag media player features that are supported."""
features = BANG_OLUFSEN_FEATURES features = BEO_FEATURES
# Add seeking if supported by the current source # Add seeking if supported by the current source
if self._source_change.is_seekable is True: if self._source_change.is_seekable is True:
@@ -598,7 +611,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
@property @property
def state(self) -> MediaPlayerState: def state(self) -> MediaPlayerState:
"""Return the current state of the media player.""" """Return the current state of the media player."""
return BANG_OLUFSEN_STATES[self._state] return BEO_STATES[self._state]
@property @property
def volume_level(self) -> float | None: def volume_level(self) -> float | None:
@@ -615,11 +628,19 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
return None return None
@property @property
def media_content_type(self) -> str: def media_content_type(self) -> MediaType | str | None:
"""Return the current media type.""" """Return the current media type."""
# Hard to determine content type content_type = {
if self._source_change.id == BangOlufsenSource.URI_STREAMER.id: BeoSource.DEEZER.id: BeoMediaType.DEEZER,
return MediaType.URL BeoSource.NET_RADIO.id: BeoMediaType.RADIO,
BeoSource.TIDAL.id: BeoMediaType.TIDAL,
BeoSource.TV.id: BeoMediaType.TV,
BeoSource.URI_STREAMER.id: MediaType.URL,
}
# Hard to determine content type.
if self._source_change.id in content_type:
return content_type[self._source_change.id]
return MediaType.MUSIC return MediaType.MUSIC
@property @property
@@ -632,6 +653,11 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
"""Return the current playback progress.""" """Return the current playback progress."""
return self._playback_progress.progress return self._playback_progress.progress
@property
def media_content_id(self) -> str | None:
"""Return internal ID of Deezer, Tidal and radio stations."""
return self._playback_metadata.source_internal_id
@property @property
def media_image_url(self) -> str | None: def media_image_url(self) -> str | None:
"""Return URL of the currently playing music.""" """Return URL of the currently playing music."""
@@ -669,7 +695,11 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
@property @property
def source(self) -> str | None: def source(self) -> str | None:
"""Return the current audio source.""" """Return the current audio/video source."""
# Associate TV content ID with a video source
if self.media_content_id in self._video_source_id_map:
return self._video_source_id_map[self.media_content_id]
return self._source_change.name return self._source_change.name
@property @property
@@ -740,9 +770,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
async def async_set_repeat(self, repeat: RepeatMode) -> None: async def async_set_repeat(self, repeat: RepeatMode) -> None:
"""Set playback queues to repeat.""" """Set playback queues to repeat."""
await self._client.set_settings_queue( await self._client.set_settings_queue(
play_queue_settings=PlayQueueSettings( play_queue_settings=PlayQueueSettings(repeat=BEO_REPEAT_FROM_HA[repeat])
repeat=BANG_OLUFSEN_REPEAT_FROM_HA[repeat]
)
) )
async def async_set_shuffle(self, shuffle: bool) -> None: async def async_set_shuffle(self, shuffle: bool) -> None:
@@ -846,7 +874,7 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
self._volume.level.level + offset_volume, 100 self._volume.level.level + offset_volume, 100
) )
if media_type == BangOlufsenMediaType.OVERLAY_TTS: if media_type == BeoMediaType.OVERLAY_TTS:
# Bang & Olufsen cloud TTS # Bang & Olufsen cloud TTS
overlay_play_request.text_to_speech = ( overlay_play_request.text_to_speech = (
OverlayPlayRequestTextToSpeechTextToSpeech( OverlayPlayRequestTextToSpeechTextToSpeech(
@@ -863,14 +891,14 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
# The "provider" media_type may not be suitable for overlay all the time. # The "provider" media_type may not be suitable for overlay all the time.
# Use it for now. # Use it for now.
elif media_type == BangOlufsenMediaType.TTS: elif media_type == BeoMediaType.TTS:
await self._client.post_overlay_play( await self._client.post_overlay_play(
overlay_play_request=OverlayPlayRequest( overlay_play_request=OverlayPlayRequest(
uri=Uri(location=media_id), uri=Uri(location=media_id),
) )
) )
elif media_type == BangOlufsenMediaType.RADIO: elif media_type == BeoMediaType.RADIO:
await self._client.run_provided_scene( await self._client.run_provided_scene(
scene_properties=SceneProperties( scene_properties=SceneProperties(
action_list=[ action_list=[
@@ -882,13 +910,13 @@ class BangOlufsenMediaPlayer(BangOlufsenEntity, MediaPlayerEntity):
) )
) )
elif media_type == BangOlufsenMediaType.FAVOURITE: elif media_type == BeoMediaType.FAVOURITE:
await self._client.activate_preset(id=int(media_id)) await self._client.activate_preset(id=int(media_id))
elif media_type in (BangOlufsenMediaType.DEEZER, BangOlufsenMediaType.TIDAL): elif media_type in (BeoMediaType.DEEZER, BeoMediaType.TIDAL):
try: try:
# Play Deezer flow. # Play Deezer flow.
if media_id == "flow" and media_type == BangOlufsenMediaType.DEEZER: if media_id == "flow" and media_type == BeoMediaType.DEEZER:
deezer_id = None deezer_id = None
if "id" in kwargs[ATTR_MEDIA_EXTRA]: if "id" in kwargs[ATTR_MEDIA_EXTRA]:

View File

@@ -11,7 +11,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.device_registry import DeviceEntry
from .const import DEVICE_BUTTONS, DOMAIN, BangOlufsenButtons, BangOlufsenModel from .const import DEVICE_BUTTONS, DOMAIN, BeoButtons, BeoModel
def get_device(hass: HomeAssistant, unique_id: str) -> DeviceEntry: def get_device(hass: HomeAssistant, unique_id: str) -> DeviceEntry:
@@ -40,16 +40,27 @@ async def get_remotes(client: MozartClient) -> list[PairedRemote]:
] ]
def get_device_buttons(model: BangOlufsenModel) -> list[str]: def get_device_buttons(model: BeoModel) -> list[str]:
"""Get supported buttons for a given model.""" """Get supported buttons for a given model."""
# Beoconnect Core does not have any buttons
if model == BeoModel.BEOCONNECT_CORE:
return []
buttons = DEVICE_BUTTONS.copy() buttons = DEVICE_BUTTONS.copy()
# Beosound Premiere does not have a bluetooth button # Models that don't have a microphone button
if model == BangOlufsenModel.BEOSOUND_PREMIERE: if model in (
buttons.remove(BangOlufsenButtons.BLUETOOTH) BeoModel.BEOSOUND_A5,
BeoModel.BEOSOUND_A9,
BeoModel.BEOSOUND_PREMIERE,
):
buttons.remove(BeoButtons.MICROPHONE)
# Beoconnect Core does not have any buttons # Models that don't have a Bluetooth button
elif model == BangOlufsenModel.BEOCONNECT_CORE: if model in (
buttons = [] BeoModel.BEOSOUND_A9,
BeoModel.BEOSOUND_PREMIERE,
):
buttons.remove(BeoButtons.BLUETOOTH)
return buttons return buttons

View File

@@ -27,20 +27,20 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.util.enum import try_parse_enum from homeassistant.util.enum import try_parse_enum
from .const import ( from .const import (
BANG_OLUFSEN_WEBSOCKET_EVENT, BEO_WEBSOCKET_EVENT,
CONNECTION_STATUS, CONNECTION_STATUS,
DOMAIN, DOMAIN,
EVENT_TRANSLATION_MAP, EVENT_TRANSLATION_MAP,
BangOlufsenModel, BeoModel,
WebsocketNotification, WebsocketNotification,
) )
from .entity import BangOlufsenBase from .entity import BeoBase
from .util import get_device, get_remotes from .util import get_device, get_remotes
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
class BangOlufsenWebsocket(BangOlufsenBase): class BeoWebsocket(BeoBase):
"""The WebSocket listeners.""" """The WebSocket listeners."""
def __init__( def __init__(
@@ -48,7 +48,7 @@ class BangOlufsenWebsocket(BangOlufsenBase):
) -> None: ) -> None:
"""Initialize the WebSocket listeners.""" """Initialize the WebSocket listeners."""
BangOlufsenBase.__init__(self, entry, client) BeoBase.__init__(self, entry, client)
self.hass = hass self.hass = hass
self._device = get_device(hass, self._unique_id) self._device = get_device(hass, self._unique_id)
@@ -178,7 +178,7 @@ class BangOlufsenWebsocket(BangOlufsenBase):
self.entry.entry_id self.entry.entry_id
) )
if device.serial_number is not None if device.serial_number is not None
and device.model == BangOlufsenModel.BEOREMOTE_ONE and device.model == BeoModel.BEOREMOTE_ONE
] ]
# Get paired remotes from device # Get paired remotes from device
remote_serial_numbers = [ remote_serial_numbers = [
@@ -274,4 +274,4 @@ class BangOlufsenWebsocket(BangOlufsenBase):
} }
_LOGGER.debug("%s", debug_notification) _LOGGER.debug("%s", debug_notification)
self.hass.bus.async_fire(BANG_OLUFSEN_WEBSOCKET_EVENT, debug_notification) self.hass.bus.async_fire(BEO_WEBSOCKET_EVENT, debug_notification)

View File

@@ -4,7 +4,7 @@ from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity import get_device_class from homeassistant.helpers.entity import get_device_class
from homeassistant.helpers.trigger import EntityStateTriggerBase, Trigger from homeassistant.helpers.trigger import EntityTargetStateTriggerBase, Trigger
from homeassistant.helpers.typing import UNDEFINED, UndefinedType from homeassistant.helpers.typing import UNDEFINED, UndefinedType
from . import DOMAIN, BinarySensorDeviceClass from . import DOMAIN, BinarySensorDeviceClass
@@ -20,7 +20,7 @@ def get_device_class_or_undefined(
return UNDEFINED return UNDEFINED
class BinarySensorOnOffTrigger(EntityStateTriggerBase): class BinarySensorOnOffTrigger(EntityTargetStateTriggerBase):
"""Class for binary sensor on/off triggers.""" """Class for binary sensor on/off triggers."""
_device_class: BinarySensorDeviceClass | None _device_class: BinarySensorDeviceClass | None

View File

@@ -2,6 +2,7 @@
"domain": "blackbird", "domain": "blackbird",
"name": "Monoprice Blackbird Matrix Switch", "name": "Monoprice Blackbird Matrix Switch",
"codeowners": [], "codeowners": [],
"disabled": "This integration is disabled because it references pyserial-asyncio, which does blocking I/O in the asyncio loop and is not maintained.",
"documentation": "https://www.home-assistant.io/integrations/blackbird", "documentation": "https://www.home-assistant.io/integrations/blackbird",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["pyblackbird"], "loggers": ["pyblackbird"],

View File

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

View File

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

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