Compare commits

...

612 Commits

Author SHA1 Message Date
Mike Degatano c86d7052c0 Fixes from feedback 2026-05-26 21:06:53 +00:00
Mike Degatano 07965e468b Fix prek errors 2026-05-26 20:24:10 +00:00
Mike Degatano 5972dc182b Fix config entry from feedback
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 20:24:07 +00:00
Mike Degatano 7ad535841a Hassfest changes for config flow 2026-05-26 20:24:07 +00:00
Mike Degatano d9fae7fecf Migrate analytics integration to config entry setup
- Add config_flow.py with a minimal system config flow
- Split async_setup (lightweight: YAML config, labs feature, discovery
  flow, websocket/HTTP registration) from async_setup_entry (heavy:
  Analytics init, load, scheduling, listeners)
- Add async_unload_entry that cancels scheduled analytics tasks
- Thread snapshots_url from YAML through hass.data so it reaches
  async_setup_entry without persisting to config entry data, keeping
  the option as a hidden developer-only YAML setting for now
- Catch HassioNotReadyError from Analytics.load and raise
  ConfigEntryNotReady so setup is retried when Supervisor is not yet
  ready
- Register websocket commands and HTTP view in async_setup so they
  survive entry reload; guard both handlers with ERR_NOT_FOUND when
  the entry is not loaded
- Replace async_listen_once(EVENT_HOMEASSISTANT_STARTED) with
  async_at_started so the schedule starts immediately on reload when
  HA is already running
- Add cancel_scheduled() to Analytics class
- Update stale comments in Analytics.load and send_analytics
- Add supervisor_not_ready exception translation key
- Add tests for: ConfigEntryNotReady on supervisor failure, schedule
  fires and sends analytics, unload stops the schedule, websocket
  error when entry not loaded, snapshots_url routes to custom URL

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-26 20:24:07 +00:00
Ronald van der Meer 2f3f91ec82 Require Duco Connectivity API 2.1 for new setups (#170766) 2026-05-26 22:21:39 +02:00
Markus Adrario f6e8394771 Homeegrams (#170932)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 22:09:42 +02:00
Will Pike 27b0ba1a25 Bump python-ecobee-api to 0.4.0 and handle MFA in ecobee config flow (#172101)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 22:07:35 +02:00
Samuel Xiao 1070226acf Switchbot Cloud: Debug make_device_data function too complex issue 0521 (#171688)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-26 22:06:08 +02:00
Simone Chemelli e8d7df7770 Add history events for Alexa Devices (#170905) 2026-05-26 22:05:20 +02:00
G Johansson 31f87b3a8a Remove name from workday (#169210)
Co-authored-by: Copilot <copilot@github.com>
2026-05-26 22:00:52 +02:00
Heikki Henriksen 81efe6ddbf Bump pyprusalink to 3.0.0 (#170480)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:59:58 +02:00
Tomasz Dylewski af53865b2a Speed sensor in paj_gps (#171755)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-26 21:50:09 +02:00
Franck Nijhof beeb8aca4c Merge branch 'master' into dev 2026-05-26 19:46:47 +00:00
Marko Todorić b4063aaac9 Refactor SFTP Storage integration to replace duplicate constants (#171730) 2026-05-26 21:45:22 +02:00
Michael Bisbjerg 7087cb2046 Fix Loqed webhook cleanup across setup retries (#162453)
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-26 21:43:12 +02:00
J. Nick Koston 0044c43f3a Fix flaky test_overflow_queue in history websocket tests (#171766) 2026-05-26 21:41:57 +02:00
Raphael Hehl 0bb6113bfd Migrate more UniFi Protect entities to public API (#171785)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-05-26 21:41:25 +02:00
Florent Thoumie b6fa89c032 iaqualink: complete test coverage, bump to silver (#168268) 2026-05-26 21:40:54 +02:00
Manu 6a18e05bda Make service response optional for Habitica integration (#171818) 2026-05-26 21:37:55 +02:00
Nolan Gilley b312bd010b bump python-join-api to 0.1.1 (#171802) 2026-05-26 21:31:49 +02:00
Karl Beecken 3487eaf8c5 Bump teltasync to 0.3.1, add strict typing (#169665)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-26 21:31:29 +02:00
Manu 9db7b3d012 Change selector and add translations in System Bridge send_keypress action (#171860) 2026-05-26 21:30:51 +02:00
Ingo Fischer 23ecc311fd Add BLE proxy support to matter integration (#171384)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:26:32 +02:00
Ashton 3355581bbf Add disk_life_time to hassio system health info (#171770)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 21:16:01 +02:00
starkillerOG 995707160f Implement entity available for battery cameras (#171838) 2026-05-26 21:14:31 +02:00
starkillerOG b3199bac88 Do not wake Reolink battery camera for privacy mode check (#171842) 2026-05-26 21:13:32 +02:00
Christian Lackas 97de25d55a homematicip_cloud: migrate simple binary sensors to entity descriptions (#171825)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-26 21:12:06 +02:00
yoxcu 16ef7f967e Fix automatic stop calling in continous move in onvif (#163173)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Boris Obmoroshev <bobmoroshev@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-26 20:06:54 +01:00
Christian Lackas 4864a4125e Add per-button event entities for HomematicIP key-press devices (#171065) 2026-05-26 21:06:34 +02:00
Matthew Gibson e2b71cee1f Fix exception translation placeholder mismatches in PTDevices integration (#171750) 2026-05-26 21:06:02 +02:00
Christian Lackas 5bd92d47a9 Map ViCare hvac_action to compressor phase for cooling support (#171945) 2026-05-26 21:03:27 +02:00
Michael 7f0133e2ce Remove deprecated yaml import in vivotek (#172279) 2026-05-26 21:02:49 +02:00
G Johansson ba1ed66f7a Bump holidays to 0.97 (#172088) 2026-05-26 20:20:08 +02:00
Simone Chemelli 2bc91e7a3e Filter unsupported soundbar devices for SamsungTV (#172126) 2026-05-26 20:15:43 +02:00
Mattias Arrelid 1c3a080506 Remove stale ONVIF asyncio.CancelledError workaround for anyio #374 (#172139) 2026-05-26 20:12:33 +02:00
Max Michels 5ecbfea028 Add missing exception translation key in local_file (#172271) 2026-05-26 20:06:38 +02:00
Jan-Philipp Benecke 9b67a24d92 Allow multiple headers in response in REST command (#165613) 2026-05-26 19:53:32 +02:00
Robert Resch 4bd829a6a8 Replace archived tibdex/github-app-token with actions/create-github-app-token (#172269) 2026-05-26 19:40:38 +02:00
Jordan Harvey 6feeba1f4f Add get_color service for RGB extraction from images (#167403)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-26 19:39:22 +02:00
Paul Bottein 71b849cb58 Add Yoto integration (#171207)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 18:35:28 +02:00
Michael Barrett ba5855f5d2 Added gift members sensor to Ghost integration (#171441) 2026-05-26 18:20:27 +02:00
Alex Romanov 4b04006302 Add test fixture for Tuya smart kettle (dft4ebatvon3ha5s) (#172260)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
2026-05-26 17:57:31 +02:00
1einmal1 430e03f299 Ignore DS1420 devices in onewire (#172132) 2026-05-26 16:59:41 +02:00
J. Nick Koston 7a2422013c Bump bthome-ble to 3.23.2 and add support for light level, settings revision, and command events (#172216) 2026-05-26 16:58:18 +02:00
Ian c906dc3d0c Fix invalid schema for HassStartTimer in OpenRouter extension (#172153) 2026-05-26 16:51:11 +02:00
Matt f2fa25d449 Fix Netatmo select AttributeError when webhook schedule_id not in cache (#171914)
Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
2026-05-26 16:50:17 +02:00
Yardian Support 0426f9beb6 Bump yardian to v133 (#170982)
Co-authored-by: Robert Resch <robert@resch.dev>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-26 16:48:39 +02:00
J. Nick Koston b6f0ca13f9 Bump inkbird-ble to 1.4.4 (#172266) 2026-05-26 16:45:17 +02:00
Michael 83e8f4991c Add SecureOn password support to Wake On Lan (#172167) 2026-05-26 16:43:14 +02:00
Max Michels 3b38208e07 Remove positional message strings when translation_key is set in tesla_fleet (#172267) 2026-05-26 16:39:06 +02:00
Arsène Reymond 1a15f925a0 Add entity_picture_local on universal media player (#164872)
Co-authored-by: Copilot <copilot@github.com>
2026-05-26 16:38:34 +02:00
Vincent Knoop Pathuis 10d944eab7 Migrate landisgyr_heat_meter to ultraheat-api 0.6.0 (serialx) (#172186)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 16:37:32 +02:00
Sören 1f873927aa Add Avea device info (#171624) 2026-05-26 16:27:07 +02:00
Chrystyan A Pulido fe071ff66b Add tests for states_in_range and int_states_in_range (#164548)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-26 16:19:56 +02:00
Joost Lekkerkerker e4b79d4f3d Don't use async_setup in vesync tests (#172257) 2026-05-26 16:17:37 +02:00
dontinelli f6d4d0289e Fix timeout increase for longtime coordinator for solarlog (#170564)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 16:11:14 +02:00
Mattie 3089f3cc06 Bump python-qube-heatpump to 1.11.0 (#172261) 2026-05-26 16:06:57 +02:00
Max Michels e24f35473c Replace duplicate constants in kiosker with homeassistant.const imports (#172263) 2026-05-26 16:05:07 +02:00
Petro31 1da605230d Move device_tracker entity classes out of device_tracker.config_entry (#171857) 2026-05-26 16:04:34 +02:00
Joost Lekkerkerker fd572d83b7 Use async_setup_component in emulated_kasa (#172256) 2026-05-26 16:03:42 +02:00
bkobus-bbx 305d4429ec Resolve cover device class from blebox unified cover type (#171174)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-26 16:03:28 +02:00
Simone Chemelli b95a3f5b2d Bump aioamazondevices to 13.8.0 (#172251) 2026-05-26 16:03:07 +02:00
Joost Lekkerkerker 4e986b181b Lower update interval for zinvolt (#171851) 2026-05-26 16:02:37 +02:00
Joost Lekkerkerker 65c074af9a Add Copper water meter sensors to SmartThings (#171848) 2026-05-26 16:01:40 +02:00
Markus Tuominen 58eae0b815 Add climate platform to Ouman EH-800 (#172163) 2026-05-26 16:01:27 +02:00
Markus Tuominen c201c62b3d Add select platform to Ouman EH-800 (#170496) 2026-05-26 15:43:19 +02:00
bkobus-bbx 8b9b21c006 Add update platform to Blebox integration (#172148) 2026-05-26 15:32:32 +02:00
Duco Sebel b9c00dd82b Generate repair when predictive mode is enabled while cloud communication is disabled in HomeWizard (#171850)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-26 15:28:38 +02:00
bkobus-bbx 910b87b847 Add support for 180-degree tilt in BleBox shutter covers (#172237) 2026-05-26 15:19:43 +02:00
Markus Tuominen e37459c16b Add the number platform to the Ouman EH-800 integration (#172134) 2026-05-26 16:16:12 +03:00
Nathan Osman c347afe28d Add video_count sensor to YouTube integration (#171999) 2026-05-26 15:12:50 +02:00
bkobus-bbx c8270fcb91 Add tilt-only mode support for BleBox cover entities (#172235) 2026-05-26 15:10:26 +02:00
Joost Lekkerkerker ed399a6d14 Remove internal test for ps4 (#172258) 2026-05-26 15:07:29 +02:00
Jan Bouwhuis afa01d3d8c Improve docstring and comment in mqtt code (#172246) 2026-05-26 14:36:26 +02:00
Jan Bouwhuis ba03aaa2fa Add subentry support for MQTT date, datetime and time entity platforms (#171396) 2026-05-26 14:26:30 +02:00
Duco Sebel 33f3640f66 Add HomeWizard battery group power sensor (#172248) 2026-05-26 14:25:19 +02:00
Erik Montnemery 46fc47bcdf Add explicit tests of trigger helper extract_xxx functions (#172238) 2026-05-26 14:15:07 +02:00
Markus Tuominen 71ec3c31fa Add valve platform to Ouman EH-800 (#172149) 2026-05-26 12:22:37 +02:00
Duco Sebel 2d54070cab Add HomeWizard battery group target power sensor (#172243) 2026-05-26 11:50:51 +02:00
Duco Sebel 67e4f04f09 Add serial_number to HomeWizard device registry entries (#172233) 2026-05-26 11:25:09 +02:00
Erik Montnemery 78db1e3407 Deprecate the FlowHandler show_advanced_options property (#171754)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-05-26 10:25:41 +02:00
Erik Montnemery 2368a3614d Remove support for advanced mode from schema config flow (#172117)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-26 09:50:55 +02:00
Erik Montnemery 5053392cf2 Add in_zones property to mobile_app device tracker (#171814) 2026-05-26 08:51:47 +02:00
Max Michels 6ec11460ed Replace duplicate constants in bluetooth with homeassistant.const imports (#172079) 2026-05-26 08:38:23 +02:00
Paul Tarjan 975e30c048 Remove unreachable Hikvision Shelter Alarm binary sensor (#172152) 2026-05-26 08:30:23 +02:00
J. Nick Koston 7655cb0fc6 Fix blocking time_zone validation in config/core/update websocket command (#172227) 2026-05-26 08:28:34 +02:00
Åke Strandberg 7566839e9d Add missing Miele program phase codes (#172144) 2026-05-26 08:28:31 +02:00
Manu 7db5e82f58 Use non-reloading entry update methods in config flow of ntfy integration (#172222) 2026-05-26 08:25:37 +02:00
Manu 7e67c53417 Use non-reloading entry update method in config flow of PlayStation Netwwork integration (#172223) 2026-05-26 08:25:20 +02:00
Manu 89fb856302 Use non-reloading entry update method in config flow of Xbox integration (#172224) 2026-05-26 08:25:07 +02:00
Manu a2fbd2b1ea Migrate EDL21 to use SerialPortSelector (#172220) 2026-05-26 08:23:34 +02:00
Manu 231ed34133 Bump pysml to 0.1.7 (#172217) 2026-05-26 08:23:05 +02:00
Manu 6cff433b2e Remove artificial throttling of push updates in EDL21 integration (#172213) 2026-05-26 08:22:57 +02:00
Joakim Plate eca83fb7b1 Switch to async_setup in coordinator for gardena setup (#172198)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 08:22:35 +02:00
A. Gideonse 2c5adaec5c Bump indevolt-api to 1.8.2 (#172201) 2026-05-26 08:21:56 +02:00
Manu 5d75f1c33b Use non-reloading entry update method in config flow of Habitica integration (#172225) 2026-05-26 08:20:48 +02:00
Manu d628d2314e Fix wrong integration type classification of EDL21 (#172230) 2026-05-26 08:19:46 +02:00
J. Nick Koston a9547ec349 Bump inkbird-ble to 1.4.3 (#172211) 2026-05-25 22:16:07 -05:00
J. Nick Koston 2ec637df84 Bump dbus-fast to 5.0.14 (#172215) 2026-05-25 22:15:54 -05:00
J. Nick Koston 4f50ee5675 Fix ONVIF camera_address using uninitialized inner device attribute (#172219) 2026-05-25 22:15:41 -05:00
J. Nick Koston 0faf96b983 Bump onvif-zeep-async to 4.1.0 (#172212) 2026-05-25 22:15:27 -05:00
Allen Porter c3dacbc601 Bump ical to 13.2.5 (#172214) 2026-05-25 19:23:46 -07:00
renovate[bot] 2659484000 Update uv to 0.11.15 (#172208) 2026-05-25 20:18:52 -05:00
J. Nick Koston 6830ca75f5 Bump aiodiscover to 3.2.4 (#172203) 2026-05-25 20:17:54 -05:00
J. Nick Koston 38b4184dc3 Bump inkbird-ble to 1.4.0 (#172199) 2026-05-25 19:04:41 -05:00
A. Gideonse cfde7975d8 Add main MOS temp to Indevolt (#171476) 2026-05-26 00:42:59 +02:00
J. Nick Koston d7ab696a4c Bump ESPHome stable BLE version to 2026.5.1 (#172196) 2026-05-25 17:00:30 -05:00
J. Nick Koston 7f7dad7f71 Bump bluetooth-adapters to 2.3.0 (#172165) 2026-05-26 00:52:38 +03:00
J. Nick Koston 0ed21dbed7 Bump icmplib to 3.0.4 (#172189) 2026-05-25 23:15:08 +02:00
J. Nick Koston d2b37ee28b Bump aiohttp-asyncmdnsresolver to 0.2.0 (#172188) 2026-05-25 23:03:12 +02:00
J. Nick Koston b82c95e77f Bump ulid-transform to 2.2.9 (#172190) 2026-05-25 15:56:22 -05:00
J. Nick Koston baa61982a1 Bump dbus-fast to 5.0.11 (#172191) 2026-05-25 15:56:09 -05:00
Robert Svensson 8ff6de788d Local helper for Axis serial number (#172172) 2026-05-25 22:24:47 +02:00
J. Nick Koston 640f82642a Bump habluetooth to 6.7.4 (#172162) 2026-05-25 22:15:11 +02:00
Robert Svensson 64ed269f9c Bump to aiounifi v91 (#172175) 2026-05-25 22:04:46 +02:00
Bouwe Westerdijk 2b58ef96eb Refactor set HVAC mode for Plugwise (#172121) 2026-05-25 21:56:44 +02:00
J. Nick Koston 74ca79ac28 Extend INKBIRD active scan duration to cover slower broadcasters (#172171) 2026-05-25 14:55:27 -05:00
Pete Sage afb27bc165 bump soco to 0.31.1 for Sonos (#172168) 2026-05-25 22:53:32 +03:00
J. Nick Koston 0cbf27f44f Restore sensorpro sensor entity data across reloads (#172182) 2026-05-25 14:52:21 -05:00
J. Nick Koston a5ceafa544 Restore tilt_ble sensor entity data across reloads (#172184) 2026-05-25 22:51:56 +03:00
J. Nick Koston cd4d669231 Restore thermobeacon sensor entity data across reloads (#172183) 2026-05-25 22:51:03 +03:00
J. Nick Koston cc411d06b5 Restore victron_ble sensor entity data across reloads (#172185) 2026-05-25 14:50:18 -05:00
J. Nick Koston 1329f12d37 Restore aranet sensor entity data across reloads (#172173) 2026-05-25 22:50:07 +03:00
J. Nick Koston 3899f5347b Restore sensirion_ble sensor entity data across reloads (#172181) 2026-05-25 14:49:59 -05:00
J. Nick Koston cf02cfaa7c Restore rapt_ble sensor entity data across reloads (#172179) 2026-05-25 14:49:43 -05:00
J. Nick Koston e77c16ea1b Restore bluemaestro sensor entity data across reloads (#172174) 2026-05-25 14:49:26 -05:00
J. Nick Koston f1e2f94ee0 Restore moat sensor entity data across reloads (#172177) 2026-05-25 14:49:07 -05:00
J. Nick Koston 3516883b0a Restore ruuvitag_ble sensor entity data across reloads (#172180) 2026-05-25 14:48:15 -05:00
J. Nick Koston c8b70b1a38 Restore kegtron sensor entity data across reloads (#172176) 2026-05-25 14:47:55 -05:00
J. Nick Koston 946625e281 Restore mopeka sensor entity data across reloads (#172178) 2026-05-25 14:47:09 -05:00
J. Nick Koston f4b7840d5c Bump aiodhcpwatcher to 1.2.7 (#172161) 2026-05-25 22:43:14 +03:00
Michael 060f447e4a Fix swallowed exceptions in homeassistant action handlers (#170922)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-25 21:02:49 +02:00
Joost Lekkerkerker d5bae0a2cf Add pylint rule for checking async_setup calls in tests (#171890) 2026-05-25 20:56:44 +02:00
Abílio Costa f9bef804b1 Add infrared receiver support to ESPHome (#171789)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-25 19:42:34 +01:00
Erik Montnemery 6de03f4ed6 Add state attribute in_zones to BaseScannerEntity (#171832) 2026-05-25 18:28:31 +02:00
J. Nick Koston e7f3e5637f Bump aioshelly to 13.26.1 (#172160) 2026-05-25 10:31:21 -05:00
Paul Bottein 80cefc74ec Update rf-protocols to 4.0.0 (#172131) 2026-05-25 17:17:53 +02:00
J. Nick Koston 2f33b4b7f9 Bump aioharmony to 1.0.8 (#172116) 2026-05-25 17:16:47 +02:00
J. Nick Koston cf52a7a509 Bump bluetooth-adapters to 2.2.0 (#172120) 2026-05-25 10:10:54 -05:00
Mattias Arrelid f5835f849a Update anyio to 4.13.0 (#172138) 2026-05-25 09:36:53 -05:00
J. Nick Koston ec5210dca8 Bump led-ble to 1.1.11 (#172154) 2026-05-25 09:35:11 -05:00
Michael 422ea1a9b1 Bump wakeonlan to 3.3.0 (#172150) 2026-05-25 16:13:38 +02:00
Artur Pragacz b6f69f6b99 Clean up should_expose in google assistant (#171937) 2026-05-25 13:48:49 +02:00
Tom a2a3819241 Extract ProxmoxVE TOKEN_ID from full token string (#172129) 2026-05-25 12:57:05 +02:00
Erwin Douna 3ce33b0ac6 Proxmox fix duplicate const (#171352) 2026-05-25 12:56:19 +02:00
bkobus-bbx e507a97d8b Bump blebox_uniapi to v2.5.4 (#172130) 2026-05-25 12:44:25 +02:00
Erik Montnemery 5801fdad14 Add property in_zones to TrackerEntity (#171765) 2026-05-25 12:22:45 +02:00
johanzander 2f4abd6a25 growatt_server: implement dynamic-devices and stale-devices Gold rules (#166081)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-25 11:37:25 +02:00
Erik Montnemery 1c045ab715 Add negative test to WS API test test_test_condition (#171427) 2026-05-25 11:26:14 +02:00
Simone Chemelli d4ca541a96 Cleanup tests for Waze Travel Time (#172122) 2026-05-25 11:23:34 +02:00
rlrghb a07a9dc6c8 Add Lichess sensors for Ultra Bullet, Correspondence, and variant perfs (#172098)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:22:51 +02:00
Mick Vleeshouwer 6d60b3a23a Add tests for Overkiz switch platform (#171901) 2026-05-25 11:19:58 +02:00
Will Pike 37bb895b91 Bump python-ecobee-api to 0.4.0 (#172108) 2026-05-25 11:19:45 +02:00
Onero-testdev f87dc917a6 Add support for SwitchBot Weather Station (#170571)
Co-authored-by: Fan Kai <fankai@onero.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-25 11:14:42 +02:00
Marc Hörsken 71a15c188e Bump pywmspro to 0.3.5 to avoid freeze of WMS WebControl pro (#172096) 2026-05-25 11:14:15 +02:00
Simone Chemelli 003ecdb867 Handle connection error in Waze Travel Time (#172086) 2026-05-25 11:13:37 +02:00
A. Gideonse ec7e5e5a75 Switch DHCP discovery to zeroconf for Indevolt (#172093) 2026-05-25 11:13:00 +02:00
Tomer 7587f062e1 Bump victron-mqtt to 2026.5.4 (#170876) 2026-05-25 11:09:53 +02:00
J. Nick Koston 11970144e4 Bump bluetooth-auto-recovery to 1.6.4 (#172114) 2026-05-25 10:50:13 +02:00
Paulus Schoutsen 70750a6d79 Guard AppleTV will only send valid URLs to AirPlay (#172103) 2026-05-25 10:48:17 +02:00
dependabot[bot] a53437315f Bump codecov/codecov-action from 6.0.0 to 6.0.1 (#172124)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-25 10:41:52 +02:00
Markus Adrario 5c73ad0310 Homee: exception-translations (#171995) 2026-05-25 09:13:38 +02:00
J. Nick Koston 4a04a271ec Bump cached-ipaddress to 1.1.1 (#172110) 2026-05-25 08:40:05 +02:00
J. Nick Koston 52c27bdea5 Bump inkbird-ble to 1.2.3 (#172113) 2026-05-25 08:39:42 +02:00
J. Nick Koston 6fdc52c002 Bump dbus-fast to 5.0.9 (#172118) 2026-05-25 08:39:19 +02:00
J. Nick Koston e560bbc103 Bump aiodhcpwatcher to 1.2.6 (#172105) 2026-05-24 23:10:11 -05:00
J. Nick Koston b8c573685f Trigger active scan when picking an idasen_desk device in the config flow (#172068) 2026-05-24 23:58:11 -04:00
J. Nick Koston 3764b70b90 Bump bleak, habluetooth, and bleak-retry-connector for BlueZ backend fix (#172094) 2026-05-24 23:57:29 -04:00
Paulus Schoutsen 5d2de6f82b Prefer local file access for streaming in AppleTV (#172102) 2026-05-24 22:56:35 -04:00
fdebrus 64d17f44fa Add aquarite integration (#168051)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-25 00:48:48 +02:00
Sebastian Lövdahl 6f67d44cfe Fix swallowed exceptions in vallox action handlers (#170839) 2026-05-25 00:42:13 +02:00
Robert Svensson def3befb0e Use discovered Axis name for config entry title and device name (#171894) 2026-05-25 00:35:56 +02:00
renovate[bot] 05716ae196 Update infrared-protocols to 5.6.0 (#171916) 2026-05-25 00:29:13 +02:00
rlrghb c0a864297f Update aiolichess to 1.3.0 (#172082)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 00:26:04 +02:00
J. Nick Koston 04bb84cd03 Add AUTO bluetooth scanner mode to Shelly (#172008) 2026-05-24 14:53:54 -05:00
J. Nick Koston cb55accc3b Use latest service info for INKBIRD fallback poll recency check (#172041) 2026-05-24 14:43:58 -05:00
Erwin Douna d21c227804 SMA refactor validate input (#171956) 2026-05-24 19:59:31 +02:00
Cyrill Raccaud 1ebccd9fa2 Update cookidoo API requirement to version 0.17.2 (#171793) 2026-05-24 19:57:44 +02:00
rlrghb cfbd0f3217 Add puzzles to Lichess integration (#171987)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-24 19:55:07 +02:00
Kamil Breguła 4afb7c0997 Use explicit translation keys in WLED number entities (#171984)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-24 19:49:48 +02:00
Allen Porter 105caccc51 Add shared rainbird device lock and increase calendar timeout (#172002) 2026-05-24 19:38:07 +02:00
J. Nick Koston 6419551117 Trigger active scan when picking an inkbird device in the config flow (#172048) 2026-05-24 19:30:42 +02:00
J. Nick Koston 585bd6616a Trigger active scan when picking a switchbot device in the config flow (#172046) 2026-05-24 19:30:33 +02:00
Max Michels b8dd97cf21 Replace duplicate constants in google_generative_ai_conversation with homeassistant.const imports (#172050) 2026-05-24 19:29:24 +02:00
J. Nick Koston 68fc4aed78 Trigger active scan when picking a govee_ble device in the config flow (#172051) 2026-05-24 19:29:03 +02:00
Max Michels 7dbb259625 Replace duplicate constants in aws_s3 with homeassistant.const imports (#172055) 2026-05-24 19:27:40 +02:00
J. Nick Koston 057eac7fb6 Trigger active scan when picking a rapt_ble device in the config flow (#172054) 2026-05-24 19:27:07 +02:00
Max Michels 31c9cdf742 Replace duplicate constants in xthings_cloud with homeassistant.const imports (#172076) 2026-05-24 19:25:39 +02:00
J. Nick Koston 3147104132 Trigger active scan when picking a togrill device in the config flow (#172072) 2026-05-24 19:22:52 +02:00
Max Michels d6d0f37b52 Replace duplicate constants in clicksend_tts with homeassistant.const imports (#172058) 2026-05-24 19:21:39 +02:00
G Johansson 75e48745a8 Remove useless test from trafikverket_camera (#172059) 2026-05-24 19:21:29 +02:00
J. Nick Koston 533417778c Trigger active scan when picking a sensirion_ble device in the config flow (#172056) 2026-05-24 19:21:21 +02:00
J. Nick Koston e49fd4ebbd Trigger active scan when picking a victron_ble device in the config flow (#172057) 2026-05-24 19:21:18 +02:00
Max Michels 8412b029b1 Replace duplicate constants in cloudflare_r2 with homeassistant.const imports (#172060) 2026-05-24 19:21:08 +02:00
J. Nick Koston c65de7521f Trigger active scan when picking a tilt_ble device in the config flow (#172053) 2026-05-24 19:20:48 +02:00
J. Nick Koston 752c17917e Trigger active scan when picking a ruuvitag_ble device in the config flow (#172062) 2026-05-24 19:19:58 +02:00
Max Michels f643c7ddc6 Replace duplicate constants in intent_script with homeassistant.const imports (#172066) 2026-05-24 19:19:29 +02:00
J. Nick Koston 6f5d4cf991 Trigger active scan when picking a ld2410_ble device in the config flow (#172061) 2026-05-24 19:18:55 +02:00
Max Michels b52466fed1 Replace duplicate constants in linux_battery with homeassistant.const imports (#172070) 2026-05-24 19:18:42 +02:00
J. Nick Koston 189534e32b Trigger active scan when picking a eufylife_ble device in the config flow (#172067) 2026-05-24 19:18:30 +02:00
J. Nick Koston 684ae23b18 Trigger active scan when picking a thermopro device in the config flow (#172052) 2026-05-24 19:17:35 +02:00
J. Nick Koston f4d2f65602 Trigger active scan when picking a qingping device in the config flow (#172071) 2026-05-24 19:17:05 +02:00
J. Nick Koston 65879ff37b Trigger active scan when picking a xiaomi_ble device in the config flow (#172074) 2026-05-24 19:16:57 +02:00
J. Nick Koston d902104bee Trigger active scan when picking a keymitt_ble device in the config flow (#172075) 2026-05-24 19:16:43 +02:00
J. Nick Koston 7bad27c412 Trigger active scan when picking a snooz device in the config flow (#172073) 2026-05-24 19:16:28 +02:00
Max Michels 74a7102cf6 Replace duplicate constants in altruist with homeassistant.const imports (#172078) 2026-05-24 19:14:36 +02:00
Max Michels e88fb03388 Replace duplicate constants in husqvarna_automower with homeassistant.const imports (#172064) 2026-05-24 19:13:21 +02:00
G Johansson 92ce5ed75a Group sequential executor calls in yale_smart_alarm (#172065) 2026-05-24 18:21:27 +02:00
SeifEddineMezned 466e28eae2 worldclock: Remove name field from config flow (#169000)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-24 18:20:58 +02:00
J. Nick Koston d53a8c7df9 Bump aioshelly to 13.26.0 (#172049) 2026-05-24 17:53:17 +02:00
renovate[bot] 8fe26bdf59 Update fnv-hash-fast to 2.0.3 (#172003) 2026-05-24 10:22:02 -05:00
Max Michels f75d56c096 Remove unused duplicate constants in google_travel_time with homeassistant.const imports (#172045) 2026-05-24 17:01:45 +02:00
Max Michels 7e5942ae51 Remove unused duplicate constants in aurora_abb_powerone with homeassistant.const imports (#172047) 2026-05-24 17:01:22 +02:00
renovate[bot] f9ef9963e6 Update ulid-transform to 2.2.1 (#172004) 2026-05-24 10:00:58 -05:00
J. Nick Koston cea718528f Bump aioesphomeapi to 45.2.2 (#172043) 2026-05-24 16:57:09 +02:00
Max Michels 9c38868bbe Replace duplicate constants in victron_remote_monitoring with homeassistant.const imports (#172044) 2026-05-24 16:51:25 +02:00
J. Nick Koston 62da1c34fb Expose async_request_active_scan via the bluetooth API (#172010) 2026-05-24 10:44:49 -04:00
J. Nick Koston 2d3a3bf4fc Allow bluetooth coordinators to request an active scan cadence (#172015) 2026-05-24 10:43:52 -04:00
J. Nick Koston daa60c6d55 Bump habluetooth to 6.7.2 (#172042) 2026-05-24 09:40:31 -05:00
J. Nick Koston d45730aa02 Bump inkbird-ble to 1.2.2 (#172040) 2026-05-24 16:28:40 +02:00
Ron 05ef944766 Group sequential async_add_executor_job calls in fireservicerota (#171474) 2026-05-24 16:20:10 +02:00
J. Nick Koston a51daf48c7 Bump bluetooth-data-tools to 1.29.18 (#172017) 2026-05-24 15:35:44 +02:00
Simone Chemelli 6a789d5af7 Bump aiovodafone to 3.3.0 (#172036) 2026-05-24 15:07:47 +02:00
Glenn Waters ad4e218b69 UPB integration: bump lib to 0.7.2 (#171975) 2026-05-24 15:06:42 +02:00
Max Michels 55f576c784 Remove unused duplicate constants in tuya with homeassistant.const imports (#171971) 2026-05-24 12:12:11 +02:00
Max Michels c7c3988b11 Replace duplicate constants in color_extractor with homeassistant.const imports (#172032) 2026-05-24 12:10:36 +02:00
Max Michels ac825ca36d Replace duplicate constants in iperf3 with homeassistant.const imports (#171972) 2026-05-24 12:07:17 +02:00
Allen Porter 0b439a25e1 Bump ical to 13.2.4 (#172001) 2026-05-24 12:03:21 +02:00
Maciej Bieniek 0385e81010 Import ATTR_MODEL from homeassistant.const in BraviaTV (#171983) 2026-05-24 12:01:54 +02:00
Max Michels 699fed7a3a Replace duplicate constants in fujitsu_fglair with homeassistant.const imports (#172029) 2026-05-24 12:00:34 +02:00
Max Michels 0eefc8f327 Replace duplicate constants in drop_connect with homeassistant.const imports (#172031) 2026-05-24 11:59:32 +02:00
Max Michels 6888d203eb Replace duplicate constants in fish_audio with homeassistant.const imports (#172030) 2026-05-24 11:58:47 +02:00
Max Michels 359949adc2 Replace duplicate constants in growatt_server with homeassistant.const imports (#172027) 2026-05-24 11:34:00 +02:00
Maciej Bieniek afe7d0cbbf Bump gios to 7.1.0 (#171962) 2026-05-24 10:45:03 +02:00
Max Michels 2f7b3cb7d9 Replace duplicate constants in hegel with homeassistant.const imports (#171974) 2026-05-24 10:43:43 +02:00
Max Michels c49ed549db Replace duplicate constants in energyid with homeassistant.const imports (#171976) 2026-05-24 10:43:16 +02:00
Joakim Sørensen 3016198644 Add devcontainer-lock.json file (#171982) 2026-05-24 10:38:04 +02:00
Max Michels f0c9156cdb Replace duplicate constants in version with homeassistant.const imports (#171970) 2026-05-24 10:32:46 +02:00
Max Michels f091871aa5 Replace duplicate constants in zeroconf with homeassistant.const imports (#171968) 2026-05-24 10:31:54 +02:00
Max Michels d25207180a Replace duplicate constants in elevenlabs with homeassistant.const imports (#171977) 2026-05-24 10:31:28 +02:00
Max Michels 9ce047b9be Replace duplicate constants in tplink_omada with homeassistant.const imports (#171978) 2026-05-24 10:30:58 +02:00
Nick Kuiper 488c04fc5b Remove myself as code owner from blue_current integration (#171998) 2026-05-24 10:29:59 +02:00
Allen Porter 7598fdb8cb Fix exception translation placeholder mismatch in google_photos (#172012) 2026-05-24 10:29:28 +02:00
Penny Wood 49e22072c9 Bump python-izone to 1.2.10 (#172021) 2026-05-24 10:29:04 +02:00
J. Nick Koston c056242390 Bump habluetooth to 6.7.1 (#172000) 2026-05-24 08:52:21 +02:00
J. Nick Koston 9cbb14bbde Bump inkbird-ble to 1.1.2 (#172011) 2026-05-24 08:41:11 +02:00
Allen Porter 6634c4ce78 Replace duplicate constant ATTR_ELEVATION in fitbit (#172018) 2026-05-24 08:40:32 +02:00
Allen Porter ae1355666b Remove positional message strings from roborock exceptions (#172016) 2026-05-23 22:14:12 -07:00
Allen Porter 2d0d202b80 Fix exception translation placeholder mismatch in roborock (#172014) 2026-05-23 22:14:02 -07:00
skye-harris 9fd48344f8 Reorder device location context towards the end of the Assist LLM instructions (#165136) 2026-05-23 20:51:17 -07:00
J. Nick Koston 7b4ed59861 Change default ESPHome bluetooth proxy scanning mode to Auto (#171996) 2026-05-23 18:21:37 -05:00
J. Nick Koston fb8f82542e Use AlarmControlPanelEntityFeature from aioesphomeapi in esphome (#171961) 2026-05-23 19:08:52 -04:00
Robert Svensson af5583ba76 Axis bump to v72 (#171967) 2026-05-23 19:06:47 -04:00
J. Nick Koston 2a943369d5 Change default Bluetooth scanning mode to Auto (#171985) 2026-05-23 17:44:19 -05:00
J. Nick Koston 29425fd0ac Bump bleak-esphome to 3.9.1 (#171994) 2026-05-23 17:25:35 -05:00
Markus Adrario 271111fe75 Homee: Update quality-scale for current state. (#171981) 2026-05-23 22:17:02 +02:00
J. Nick Koston 37e9bdd36f Wire scan_interval and scan_duration into bluetooth.async_register_callback (#171806) 2026-05-23 15:34:31 -04:00
J. Nick Koston e1d1bdd377 Bump aioesphomeapi to 45.2.0 (#171986) 2026-05-23 14:34:01 -05:00
J. Nick Koston b3a60de487 Bump habluetooth to 6.5.0 (#171966) 2026-05-23 14:33:42 -05:00
Michael 0cb7ea5584 Improve switch definitions in FRITZ!Box Tools (#171862) 2026-05-23 21:19:22 +02:00
Max Michels 7bc7694e14 Replace duplicate constants in ios with homeassistant.const imports (#171973) 2026-05-23 19:40:31 +02:00
Max Michels c45c949080 Replace duplicate constants in wiz with homeassistant.const imports (#171969) 2026-05-23 19:01:03 +02:00
SeifEddineMezned ec4f64172b Fix grammar and clarity in homekit_controller/strings.json (#169625) 2026-05-23 17:52:39 +02:00
Max Michels f88b7bcdf6 Replace duplicate constants in olama with homeassistant.const imports (#171949)
Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me>
2026-05-23 17:03:44 +02:00
Max Michels 05009871aa Replace duplicate constants in numato with homeassistant.const imports (#171950) 2026-05-23 16:57:46 +02:00
Max Michels 4aa7323af2 Replace duplicate constants in nmbs with homeassistant.const imports (#171951) 2026-05-23 16:57:07 +02:00
Max Michels bcacf3a73c Remove unused duplicate constants in nice_go with homeassistant.const imports (#171952) 2026-05-23 16:56:16 +02:00
Maciej Bieniek 96a6babaef Remove Shelly temperature and humidity sensors with error (#170900) 2026-05-23 14:02:32 +02:00
Max Michels e856271a5a Replace duplicate constants in motioneye with homeassistant.const imports (#171954)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-05-23 12:05:02 +02:00
Max Michels add023ed74 Replace duplicate constants in openerz with homeassistant.const imports (#171946) 2026-05-23 11:58:51 +02:00
Max Michels 8d456cb24f Replace duplicate constants osoenergy with homeassistant.const imports (#171944) 2026-05-23 11:52:54 +02:00
Max Michels 5ebd95eb34 Replace duplicate constants in netatmo with homeassistant.const imports (#171953) 2026-05-23 11:47:58 +02:00
Max Michels 228d7189c3 Replace duplicate constants in profiler with homeassistant.const imports (#171943) 2026-05-23 11:01:02 +02:00
Max Michels a8e141a48a Replace duplicate constants in rainmachine with homeassistant.const imports (#171942)
Co-authored-by: Jan-Philipp Benecke <jan-philipp@bnck.me>
2026-05-23 10:59:53 +02:00
Max Michels d42d52a0f7 Replace duplicate constants in onkyo with homeassistant.const imports (#171947) 2026-05-23 10:57:48 +02:00
Mick Vleeshouwer cee0fe071d Fix tilt-only DynamicPergola covers in Overkiz (#171898) 2026-05-23 10:52:16 +02:00
Josh Gustafson e3593c3076 Arcam reconfig flow (#171767) 2026-05-23 10:24:28 +02:00
Martin Hjelmare 5498de07ff Remove legacy Konnected integration (#171896) 2026-05-23 10:19:35 +02:00
J. Nick Koston ac3f973d7d Bump aioesphomeapi to 45.1.0 (#171935) 2026-05-23 09:47:15 +02:00
J. Nick Koston 6b8a2a4032 Bump bleak-esphome to 3.8.1 (#171936) 2026-05-23 09:46:49 +02:00
Artur Pragacz 74e40af4bb Remove CLOUD_NEVER_EXPOSED_ENTITIES (#171933) 2026-05-23 00:26:45 -04:00
J. Nick Koston 833e15d6f2 Bump habluetooth to 6.4.0 (#171918) 2026-05-23 00:10:50 -04:00
Matt ee56fd1eb0 Fix two HEOS bugs: host set construction and missing error decorator (#171913) 2026-05-22 18:42:43 -05:00
Felipe Santos e6528bae8a Add missing translation for connection failure on OpenRGB (#171892) 2026-05-22 21:59:39 +02:00
Joost Lekkerkerker a17eb65498 Refactor labs websocket API tests to use async_setup_component (#171891) 2026-05-22 21:53:52 +02:00
Joost Lekkerkerker 912a839d66 Don't call migrate entry in generic thermostat tests directly (#171887) 2026-05-22 21:44:10 +02:00
Martin Hjelmare 4306863729 Fix homekit test_reload flaky test (#171878) 2026-05-22 14:33:27 -05:00
Martin Hjelmare ba2f66e751 Remove not needed default force_update in flo (#171854) 2026-05-22 20:15:00 +02:00
Franck Nijhof 0723d8d83f 2026.5.4 (#171859) 2026-05-22 19:26:21 +02:00
Manu 94581d8ab6 Move service registration in System Bridge integration to async_setup (#171761)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-22 18:43:22 +02:00
Ingo Fischer 7d6ec7fc58 Bump matter-python-client to 0.7.1 (#171764)
Co-authored-by: TheJulianJES <TheJulianJES@users.noreply.github.com>
2026-05-22 17:34:20 +01:00
Jan Bouwhuis f49de3548e Add MQTT message expiry interval option (#171143) 2026-05-22 18:27:22 +02:00
Franck Nijhof 73c9edd3e8 Ran gen_requirements_all 2026-05-22 16:18:20 +00:00
Franck Nijhof 18f30bd97b Bump version to 2026.5.4 2026-05-22 16:06:32 +00:00
Manu eae6e79b61 Fix dead link in System Bridge service action (#171855) 2026-05-22 16:04:27 +00:00
Franck Nijhof 5bb42801d9 Fix Hue device trigger crash for devices removed from bridge (#171844) 2026-05-22 16:04:25 +00:00
Franck Nijhof 98271265d3 Fix OpenHome config flow crash when UDN is a list (#171841) 2026-05-22 16:04:23 +00:00
Franck Nijhof 92d20477bc Register Insteon modem device before platform setup (#171839) 2026-05-22 16:04:21 +00:00
Franck Nijhof 9352a0057e Fix invalid MDI icon references (#171831)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 16:04:19 +00:00
Franck Nijhof 5fb874277a Fix Lutron Caseta battery sensor crash on unsupported devices (#171829) 2026-05-22 16:04:17 +00:00
Franck Nijhof d65f605398 Fix ZBT-2 hardware page crash when entry data is missing VID (#171828) 2026-05-22 16:04:16 +00:00
Simone Chemelli 7e5b448f70 Add missing exception translation keys in alexa_devices (#171749) 2026-05-22 16:04:14 +00:00
Simone Chemelli ef5da5ef36 Fix exception translation placeholder mismatches in comelit (#171748) 2026-05-22 16:04:11 +00:00
epenet 410f00c4ed Bump renault-api to 0.5.10 (#171692) 2026-05-22 15:58:41 +00:00
Kamil Breguła 33c205dc04 Bump wled to 0.23.0 and remove backoff exception (#171622) 2026-05-22 15:58:39 +00:00
dontinelli 267b3e279d Fix update error message key in solarlog (#171611) 2026-05-22 15:58:37 +00:00
Maciej Bieniek 9c1cd8093d Fix media_image_hash and validate the MIME type in the Shelly media player (#171585) 2026-05-22 15:58:35 +00:00
Josef Zweck 201c0c2470 Fix string ref for tedee (#171548) 2026-05-22 15:58:33 +00:00
Franck Nijhof 281d6e0e8b Fix Wyoming satellite crash when TTS is not configured (#171513) 2026-05-22 15:58:31 +00:00
Franck Nijhof 88746534a4 Fix PowerView cover crash when shade position is unavailable (#171471) 2026-05-22 15:58:29 +00:00
Franck Nijhof 135f91c3c5 Fix habitica ignoring zero values for interval and streak (#171468) 2026-05-22 15:58:27 +00:00
Franck Nijhof 49d8dc88d9 Fix SmartThings crash when timestamp attribute is None (#171467) 2026-05-22 15:58:25 +00:00
epenet a7a2c1eb02 Bump renault-api to 0.5.9 (#171428) 2026-05-22 15:58:23 +00:00
J. Nick Koston 6596f956d2 Bump aiodns to 4.0.4 (#171420)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: frenck <195327+frenck@users.noreply.github.com>
2026-05-22 15:57:27 +00:00
TheJulianJES 9d8859833b Fix ZHA blocking minor version downgrades (#171319) 2026-05-22 15:48:41 +00:00
Aidan Timson 65a4c10660 Bump aiolyric to 2.1.1, Update OAuth URL for lyric (#171181) 2026-05-22 15:48:39 +00:00
Åke Strandberg 1737b50558 Add missing Miele Dishwasher codes (#171175) 2026-05-22 15:48:37 +00:00
Luke Lashley 614c7006f6 Bump python-roborock to 5.12.0 (#171112)
Co-authored-by: Robert Resch <robert@resch.dev>
2026-05-22 15:46:06 +00:00
Jonathan Segev 8c901cc405 Bump aiolyric to 2.1.0 (#171007)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
2026-05-22 15:46:04 +00:00
Franck Nijhof 5d0fdfd38b Apply web search citation stripping for GPT-5.x models in OpenAI conversation (#170956) 2026-05-22 15:46:02 +00:00
Manu 49ab42d3a2 Fix dead link in System Bridge service action (#171855) 2026-05-22 17:00:30 +02:00
Franck Nijhof 383f6142f0 Fix ZBT-2 hardware page crash when entry data is missing VID (#171828) 2026-05-22 16:58:01 +02:00
Kamil Breguła 2f120cf604 Fix rgb_color passed as RGBColor NamedTuple instead of plain tuple to light entity turn_on (#171795)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 16:56:34 +02:00
Franck Nijhof 37288849b3 Register Insteon modem device before platform setup (#171839) 2026-05-22 10:23:47 -04:00
zhangluofeng aa8659f507 Add xthings cloud lock (#171176)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-22 15:54:37 +02:00
DeerMaximum 40c0d79d1d Replaced duplicate constant with homeassistant.const in NINA (#171852) 2026-05-22 15:41:04 +02:00
Franck Nijhof bef8632d78 Fix OpenHome config flow crash when UDN is a list (#171841) 2026-05-22 15:23:42 +02:00
Duco Sebel f00decfaa3 Use uptime device class for HomeWizard uptime sensor (#171830) 2026-05-22 15:23:09 +02:00
Manu 42e7add026 Add selector options translations to System Bridge integration (#171771) 2026-05-22 15:22:22 +02:00
Franck Nijhof 263aa3f16e Fix Hue device trigger crash for devices removed from bridge (#171844) 2026-05-22 15:18:00 +02:00
mhuiskes 03b364dcf0 Refactor zeversolar tests: use fixtures, patch at use site, add unique_id (#171697) 2026-05-22 14:58:56 +02:00
Duco Sebel 3b1aaf39af Bumb python homewizard energy 10.1.0 (#171826) 2026-05-22 14:51:58 +02:00
Franck Nijhof b82ba43fa4 Add pylint checker for invalid MDI icon references (#171824)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 13:45:40 +02:00
starkillerOG d81ef5593c Bump reolink_aio to 0.20.0: Reolink battery camera support (#171836) 2026-05-22 12:59:33 +02:00
Manu 5c5e50f024 Fix platform unloading in System Bridge integration (#171822) 2026-05-22 12:56:03 +02:00
Lex Postma e796d9c467 Update strings.json to align with HomeWizard app (#171740)
Co-authored-by: Duco Sebel <74970928+DCSBL@users.noreply.github.com>
2026-05-22 12:54:58 +02:00
Karl Beecken 342f23526f Remove empty requirements_test_all.txt (#171530 follow-up) (#171834) 2026-05-22 12:38:58 +02:00
Erik Montnemery 814ec697cf Remove advanced mode from hue service actions (#171442) 2026-05-22 11:45:33 +02:00
Erik Montnemery 120f1446d4 Rename advanced section to additional options in telegram_bot service actions (#171460) 2026-05-22 11:44:05 +02:00
Franck Nijhof 170af75b7d Fix Lutron Caseta battery sensor crash on unsupported devices (#171829) 2026-05-22 11:37:05 +02:00
Ariel Ebersberger 5432d29489 Use is/is not for same-enum identity comparisons (tests) (#171689) 2026-05-22 11:32:27 +02:00
Franck Nijhof 8098f4f6bc Fix invalid MDI icon references (#171831)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 11:25:28 +02:00
Simone Chemelli 6a70077687 Fix exception translation placeholder mismatches in comelit (#171748) 2026-05-22 11:17:17 +02:00
Max Michels 5dbb0464ba Replace duplicate constants with homeassistant.const imports (#171815) 2026-05-22 11:10:21 +02:00
dependabot[bot] 1df165ea02 Bump j178/prek-action from 2.0.3 to 2.0.4 (#171812)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 10:39:43 +02:00
Manu 62542eb911 Replace duplicate constants with homeassistant.const imports in xiaomi_miio (#171823) 2026-05-22 10:39:09 +02:00
Max Michels a842cac34c Replace duplicate constants with homeassistant.const imports (#171817) 2026-05-22 10:38:06 +02:00
Simone Chemelli 2460f688e3 Add missing exception translation keys in alexa_devices (#171749) 2026-05-22 10:34:00 +02:00
Simone Chemelli a868ea443c Fix hardcoded exception strings in uptimerobot (#171744) 2026-05-22 10:33:07 +02:00
Franck Nijhof 1d8565483b Apply web search citation stripping for GPT-5.x models in OpenAI conversation (#170956) 2026-05-22 10:31:10 +02:00
dependabot[bot] 1ef3301253 Bump github/codeql-action from 4.35.4 to 4.35.5 (#171813)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 09:47:16 +02:00
Manu 525952f016 Add entity translations to System Bridge integration (#171807) 2026-05-22 09:00:54 +02:00
Shay Levy 3257275c5a Fix LG webOS TV hardcoded exception strings (#171777) 2026-05-22 08:28:19 +02:00
Max Michels cb54fd4921 Replace duplicate constants with homeassistant.const imports (#171809) 2026-05-22 07:57:08 +02:00
Max Michels b391fc61ea Replace duplicate constants with homeassistant.const imports (#171808) 2026-05-22 07:56:29 +02:00
J. Nick Koston fcd4e4939c Bump habluetooth to 6.2.0 (#171800) 2026-05-21 23:08:17 -05:00
J. Nick Koston deb8b5da05 Parallelize pytest --collect-only in split_tests.py (#171772) 2026-05-21 22:58:01 -04:00
g4bri3lDev c7754a6ce9 Bump py-opendisplay to 7.2.3 (#171775) 2026-05-21 22:52:36 -04:00
J. Nick Koston 242724bd50 Bump aiodiscover to 3.2.3 (#171803) 2026-05-21 22:51:54 -04:00
Max Michels 42454563db Replace duplicate constants with homeassistant.const imports (#171790)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-21 22:51:34 -04:00
J. Nick Koston bf03d0c216 Bump dbus-fast to 5.0.3 (#171595) 2026-05-21 21:11:35 -05:00
Max Michels 568107e06b Replace duplicate constants with homeassistant.const imports (#171784) 2026-05-22 01:33:48 +03:00
Jens Timmerman 7da44428b6 Bump guntamatic to v1.9.0 (#171631) 2026-05-21 22:55:29 +01:00
Max Michels 0a27f31949 Replace duplicate constants with homeassistant.const imports (#171781) 2026-05-21 22:53:07 +01:00
Erwin Douna 905b868c82 Add recreate services to Portainer (#167225)
Co-authored-by: G Johansson <goran.johansson@shiftit.se>
2026-05-21 22:52:07 +01:00
Max Michels 3187289913 Replace duplicate constants with homeassistant.const imports (#171776) 2026-05-22 00:18:54 +03:00
Max Michels 87cecd4a44 Replace duplicate constants with homeassistant.const imports (#171778) 2026-05-22 00:18:23 +03:00
Robert Svensson fed38b0e38 Replace duplicate ATTR_LOCKED constant with homeassistant.const import in deconz (#171779) 2026-05-22 00:17:22 +03:00
Raphael Hehl 6a36d1260b Bump uiprotect to 10.5.0 (#171768)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-05-21 15:42:31 -05:00
Raphael Hehl 49fc1b413d Bump pydantic to 2.13.4 (#171763)
Co-authored-by: RaHehl <rahehl@users.noreply.github.com>
2026-05-21 14:42:06 -05:00
Abílio Costa bffb0417cc Instruct agents to run prek after doing changes (#171757) 2026-05-21 20:16:26 +01:00
G Johansson 8b8c687fc3 Remove not needed exception handling in dnsip (#171758) 2026-05-21 20:58:32 +02:00
Lukas e3dd6b5fc5 Fix hardcoded exception strings in pooldose integration (#171652)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 20:36:17 +02:00
Ariel Ebersberger 94d620438b Use is/is not for same-enum identity comparisons (source) (#171591) 2026-05-21 19:30:55 +02:00
Erik Montnemery 8867b792dc Remove use of advanced mode from the zha integration (#171753) 2026-05-21 19:26:24 +02:00
G Johansson 97967abfeb Fix missing string in smhi (#171756) 2026-05-21 19:25:03 +02:00
mhuiskes af8fea272d Declare Bronze quality scale for Zeversolar integration (#170410) 2026-05-21 19:12:54 +02:00
Simone Chemelli 2db0eed570 Fix hardcoded exception strings in samsungtv (#171745) 2026-05-21 18:59:37 +02:00
Erwin Douna ded1628c20 Downloader add missing data description (#171727) 2026-05-21 18:59:24 +02:00
Petro31 a02e54f332 Update documentation link to point to each domain/platform (#171734)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 18:47:39 +02:00
Erik Montnemery 1858649bc7 Improve tests of trigger variables (#171742) 2026-05-21 17:55:41 +02:00
Leonardo Merza 109e09c3ec Add fan minimum on time number entity to ecobee (#171419)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 17:53:02 +02:00
Manu ad139b259b Add notify entity to System Bridge integration (#171736)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-21 17:49:25 +02:00
Scott Giminiani a1a76874fd Fix name of config flow form field (#171741) 2026-05-21 17:33:53 +02:00
Ariel Ebersberger e7bd56325b Use is for IntentResponseType identity check in conversation (#171699)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: arturpragacz <49985303+arturpragacz@users.noreply.github.com>
2026-05-21 17:31:14 +02:00
J. Nick Koston ef2ef0c8ba Bump zeroconf to 0.149.16 (#171737) 2026-05-21 17:28:26 +02:00
Max Michels a8381e923a Replace duplicate constants with homeassistant.const imports (#171675) 2026-05-21 17:13:31 +02:00
Max Michels b7adba559b Replace duplicate constants with homeassistant.const imports (#171677) 2026-05-21 17:13:20 +02:00
Matthias Alphart 5cf6dceb04 Normalize empty string to None in knx config flow (#171693) 2026-05-21 17:13:11 +02:00
Paul Bottein 975bcc5431 Reorganize Freebox entity categories (#171480) 2026-05-21 16:55:27 +02:00
Michael Barrett f24a44e81f Update aioghost to 0.4.16 (#171690) 2026-05-21 16:53:45 +02:00
Phil-Rad 43c91843cd Remove unreachable import config flow path from cert_expiry (#171733) 2026-05-21 16:50:49 +02:00
Chris dbce1d328a Bump python-openevse-http to 0.3.4 (#171621) 2026-05-21 16:46:54 +02:00
Petro31 d294b04b79 Add EntityComponent to device_tracker (#171507) 2026-05-21 16:10:20 +02:00
Markus Tuominen 8b0e9060b3 Set _attr_has_entity_name on tplink_omada OmadaClientScannerEntity (#171680) 2026-05-21 16:41:37 +03:00
MoonDevLT 39066b6e3a Fix missing exceptions translation key missing_device_info in lunatone (#171569) 2026-05-21 14:59:48 +02:00
Max Michels a23a9b350b Replace duplicate constants with homeassistant.const imports (#171701) 2026-05-21 14:57:58 +02:00
chiro79 fdaa807ca8 Switch to aiopvpc-ng (#171025) 2026-05-21 14:54:23 +02:00
A. Gideonse f290dcc03f Update Indevolt integration quality scale to platinum (#170320) 2026-05-21 14:53:06 +02:00
Franck Nijhof c9ed57bc56 2026.5.3 (#171185) 2026-05-19 11:49:12 +02:00
bkobus-bbx 0e0901993d Fix blebox light temperature scaling (#170573) 2026-05-19 08:47:23 +00:00
Franck Nijhof 54aba11091 Bump version to 2026.5.3 2026-05-19 08:39:50 +00:00
Mick Vleeshouwer dc9116a7a7 Fix tilt and position support for VenetianBlind covers in Overkiz (#170974) 2026-05-19 08:39:38 +00:00
Mick Vleeshouwer 1e90882918 Fix is_closed state and position for DynamicPergola covers in Overkiz (#170983) 2026-05-19 08:37:54 +00:00
puddly e8295e14b1 Fix ZHA config entries using a URI without a port (#171164) 2026-05-19 08:35:43 +00:00
Mick Vleeshouwer 7ebaaf129a Fix controls for UpDownGarageDoor4T and additional 4T covers in Overkiz (#171144) 2026-05-19 08:35:00 +00:00
Michael ee734dede6 Bump aioimmich to 0.14.1 (#171138) 2026-05-19 08:33:58 +00:00
Franck Nijhof ebc582c813 Return media_content_id as string in forked_daapd (#171059) 2026-05-19 08:33:56 +00:00
James Nimmo 311e5a9bd2 Bump pyIntesishome to 1.8.8 (#171041) 2026-05-19 08:33:54 +00:00
Franck Nijhof cd6c3c878b Fix WeatherFlow websocket crash when data payload is None (#171037) 2026-05-19 08:33:52 +00:00
Franck Nijhof 51589ec2ff Add stop command to Overkiz pergola horizontal awning covers (#171034) 2026-05-19 08:33:50 +00:00
Franck Nijhof 8e1a04dc82 Fix Verisure alarm crash when cloud rejects arm/disarm command (#171024) 2026-05-19 08:33:48 +00:00
Mick Vleeshouwer 6b15f9a2ec Add additional overrides to cover entity in Overkiz (#171019) 2026-05-19 08:33:46 +00:00
Franck Nijhof 8d66752556 Fix shorthand template conditions in choose blocks crashing all automations (#171018) 2026-05-19 08:33:44 +00:00
Franck Nijhof 266767e37d Handle Daikin connection errors gracefully in coordinator (#171017) 2026-05-19 08:33:42 +00:00
Franck Nijhof d39775ac34 Fix manual alarm panel crash on restore with invalid state (#171016) 2026-05-19 08:33:40 +00:00
Franck Nijhof a314f7bf64 Fix Control4 climate crash when humidity is 'Undefined' (#171015) 2026-05-19 08:33:38 +00:00
Franck Nijhof 37478d33eb Fix SleepIQ timer units: seconds should be minutes for core climate and foot warmer (#171013) 2026-05-19 08:33:36 +00:00
Franck Nijhof 5a76f3bd19 Fix Growatt mix device IndexError when chart data is empty (#171012) 2026-05-19 08:33:34 +00:00
Franck Nijhof 17e105083e Fix threshold preview crash when hysteresis is not provided (#171009) 2026-05-19 08:33:32 +00:00
Franck Nijhof db8589b2bc Fix time trigger crash when using entity_id dict format without offset (#171006)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-19 08:33:30 +00:00
Franck Nijhof 771b016f33 Fix Netatmo valve KeyError when hvac_action state is unavailable in Overkiz (#171004) 2026-05-19 08:33:28 +00:00
Franck Nijhof 0bc0745e8c Use asyncio.get_running_loop() in emulated_hue UPnP responder (#171000)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:31:43 +00:00
Franck Nijhof ea084797d3 Load template extensions by class to prevent import deadlock (#170995)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:31:41 +00:00
Franck Nijhof 2456753caf Prevent Google Assistant entity sync from blocking startup (#170991)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:31:39 +00:00
Mick Vleeshouwer 070de13c14 Fix controls for OpenCloseGate4T (rts:GateOpenerRTS4TComponent) in Overkiz (#170987) 2026-05-19 08:30:30 +00:00
Mick Vleeshouwer 5e45f37ee6 Fix is_closed state for DiscretePositionableGarageDoor in Overkiz (#170981) 2026-05-19 08:25:10 +00:00
Franck Nijhof 4a96880f51 Reduce GoodWe connect retries to avoid blocking startup (#170964)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:20:26 +00:00
Franck Nijhof 228ac01124 Use correct state_class for utility meters with device classes that don't support total_increasing (#170962)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:20:24 +00:00
Franck Nijhof d366027e6b Fix utility meter next_reset shifting forward on entity rename (#170957)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 08:20:22 +00:00
puddly 2f35ad2a8a Disable USB discovery for teleinfo (#170933) 2026-05-19 08:20:20 +00:00
Mick Vleeshouwer 95cc9aed64 Fix is_closed state for SlidingDiscreteGateWithPedestrianPosition covers in Overkiz (#170913) 2026-05-19 08:19:03 +00:00
Franck Nijhof 37d6449a49 Populate uid and recurrence_id in CalDAV calendar events (#170910) 2026-05-19 08:14:10 +00:00
J. Nick Koston 249b5435d9 Bump aiodns to 4.0.3 (#170865)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-19 08:07:21 +00:00
bkobus-bbx 3293ebcea5 Fix ValueError when turning on blebox light with brightness set to 0 (#170769)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-19 08:07:18 +00:00
Daniil Karpenko 47d8adc77c Add tilt controls for UpDownSheerScreen in Overkiz (#170563)
Co-authored-by: Mick Vleeshouwer <mick@imick.nl>
2026-05-19 08:01:00 +00:00
Keith Roehrenbeck 356e6a691b Fix Apple TV keyboard focus binary_sensor missing on cold start (#170360) 2026-05-19 08:00:58 +00:00
Florent Thoumie b26c2f3854 Improve iaqualink 429 handling (#170231) 2026-05-19 08:00:56 +00:00
Luka Matijević 0830988687 Bump qbittorrent-api to 2026.5.1 (#170181) 2026-05-19 08:00:54 +00:00
Franck Nijhof 456202325a 2026.5.2 (#170840) 2026-05-15 22:55:45 +02:00
Franck Nijhof 1e47149764 Fix hassfest warning 2026-05-15 20:26:51 +00:00
Franck Nijhof 116b63ca3a Bump version to 2026.5.2 2026-05-15 20:13:00 +00:00
Ronald van der Meer 3096bcf8a9 Bump python-duco-connectivity to 0.4.0 (#170661) 2026-05-15 20:12:26 +00:00
Ronald van der Meer a4027029d0 Migrate Duco to python-duco-connectivity and remove temperature sensors (#170237) 2026-05-15 20:11:35 +00:00
Bram Kragten fffc9d0695 Update frontend to 20260429.4 (#170567) 2026-05-15 20:06:23 +00:00
G Johansson 3ca5cf5add Add missing optional category strings in workday (#170505)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 20:06:21 +00:00
Jan Bouwhuis 087cb77042 Fix MQTT settings in device subentry device settings are not recalled when reconfiguring the device (#170484) 2026-05-15 20:06:19 +00:00
Michael Keck 8bd1c07ec9 Increase WebDAV client timeout from 10 to 30 seconds (#170476) 2026-05-15 20:06:17 +00:00
J. Nick Koston 9ecb59590b Bump aioharmony to 1.0.3 (#170459) 2026-05-15 20:02:46 +00:00
Rob Bierbooms e14eb9fbc5 Fix influxdb reconfigure for v1 configuration (#170448) 2026-05-15 20:01:59 +00:00
TheJulianJES 149c796227 Fix fractional setpoints in Matter climate not rounded (#170442) 2026-05-15 20:01:11 +00:00
J. Nick Koston 3383e5b1e9 Bump aioesphomeapi to 44.24.1 (#170428) 2026-05-15 20:00:24 +00:00
Åke Strandberg 05862c6dc8 Bump pymiele version to 0.6.2 (#170419) 2026-05-15 19:59:37 +00:00
Petar Petrov b35ac41470 Apply unit_of_measurement to energy combined power sensor (#170404) 2026-05-15 19:58:50 +00:00
James Nimmo 20cec56512 Bump pyintesishome to 1.8.7 (#170382) 2026-05-15 19:58:03 +00:00
puddly 74580262b6 Bump serialx to 1.7.3 (#170368) 2026-05-15 19:57:16 +00:00
Pascal Brunot f75cdae602 Bump serialx to 1.7.2 (#170272) 2026-05-15 19:56:59 +00:00
Jan Bouwhuis 8c95f4f7ae Fix duplicate doorbell events when entity becomes unavailable (#170354)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 19:54:02 +00:00
Robert Svensson c3ec51c471 Bump axis to v71 (#170347) 2026-05-15 19:54:00 +00:00
Raman Gupta 0f80a4bc18 Cancel previous Debouncer timer handle in _schedule_timer (#170339)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 19:53:58 +00:00
Maciej Bieniek 0761d618f1 Fix Shelly media player availability (#170319) 2026-05-15 19:53:57 +00:00
Stefan Agner 03e3c46faf Fix hassio.backup_partial AttributeError when folders are specified (#170312)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 19:53:55 +00:00
Craig Dean d1962b0df2 Bump renault-api to 0.5.8 (#170309) 2026-05-15 19:53:53 +00:00
Florent Thoumie 7a38a2303a iaqualink: set system specific polling interval (#170279) 2026-05-15 19:53:51 +00:00
Maciej Bieniek 6f5c2a8614 Bump imgw-pib to 2.1.2 (#170274) 2026-05-15 19:53:49 +00:00
Sören Beye ff36498698 fix: Do not forget segments from state when a new config arrives (#170265)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 19:53:47 +00:00
Willem-Jan van Rootselaar 23e19ea2e4 Handle empty BSB-LAN heating circuits (#170249) 2026-05-15 19:53:46 +00:00
Ronald van der Meer c33f174041 Bump python-duco-client to 0.5.0 (#170065) 2026-05-15 19:52:32 +00:00
Ronald van der Meer bbe64d74e3 Bump python-duco-client to 0.4.2 (#170027) 2026-05-15 19:52:30 +00:00
Ronald van der Meer ed3a71f2ee Add API version to Duco diagnostics for support triage (#169802) 2026-05-15 19:51:21 +00:00
Ronald van der Meer 46c49daba4 Add system health platform for Duco integration (#169517) 2026-05-15 19:48:52 +00:00
Ronald van der Meer a2f2ded188 Add target flow level and mode end time sensors to Duco integration (#169298) 2026-05-15 19:47:15 +00:00
Simone Chemelli 7be061796d Fix entities refresh for UptimeRobot (#170217) 2026-05-15 19:32:16 +00:00
Jan Bouwhuis 27c7d8de0c Fix MQTT device discovery not using shared QoS and encoding options (#170195)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-15 19:32:14 +00:00
Simone Chemelli 07542523b5 Reinit API on stale session for Vodafone Station (#170190) 2026-05-15 19:32:12 +00:00
puddly 18597bb653 Set serial port description from description, not product (#170160)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2026-05-15 19:32:10 +00:00
Christian Lackas c4be57a294 homematicip_cloud: fix HmIP-FLC lock state polarity (#170159) 2026-05-15 19:32:08 +00:00
Christian Lackas 7ceaebb086 Fix homematicip_cloud config entry setup crash after migration to 2026.5.0 (#170156) 2026-05-15 19:32:06 +00:00
Mick Vleeshouwer 7c5ef09734 Fix local API incorrectly marking devices as unavailable in Overkiz (#170118)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
2026-05-15 19:32:05 +00:00
Thijs W. b4d8ba66fe Update afsapi to 1.0.1 (#170073) 2026-05-15 19:32:02 +00:00
puddly 308221ce67 Migrate ZBT-1 and ZBT-2 to use serial number for unique_id (#169879)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2026-05-15 19:30:56 +00:00
Simone Chemelli 1344213335 Fix non unique_id for Comelit (#169756)
Co-authored-by: Copilot <copilot@github.com>
2026-05-15 19:26:54 +00:00
r2xj 7e405e9014 Only use SmartThings switch for light if it should (#166424)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-15 19:26:52 +00:00
LG-ThinQ-Integration b0c45132ed Fix ValueError for non-numeric value in LG ThinQ (#166300)
Co-authored-by: YunseonPark-LGE <yunseon.park@lge.com>
2026-05-15 19:26:49 +00:00
Franck Nijhof 7d7738303a 2026.5.1 (#170146) 2026-05-08 22:07:51 +02:00
Franck Nijhof dd0cdc4fc4 Bump version to 2026.5.1 2026-05-08 18:54:08 +00:00
Mick Vleeshouwer 18ea40c46d Fix tilt support for UpDownVenetianBlind (rts:VenetianBlindRTSComponent) in Overkiz (#170047) 2026-05-08 18:53:57 +00:00
Mick Vleeshouwer a23131efc8 Fix is_closed state for DynamicGate covers in Overkiz (#170130) 2026-05-08 18:53:10 +00:00
bkobus-bbx 4940a0abae Bump blebox_uniapi to v2.5.3 (#170115) 2026-05-08 18:53:08 +00:00
Willem-Jan van Rootselaar 5f98d5ae52 Bump python-bsblan to 5.2.1 (#170100) 2026-05-08 18:53:06 +00:00
TheJulianJES ba18cded30 Bump ZHA to 1.3.1 (#170095) 2026-05-08 18:53:04 +00:00
TheJulianJES fb7504e9df Fix Z-Wave discovery crash with unknown node firmware version (#170090) 2026-05-08 18:53:02 +00:00
Mick Vleeshouwer 106f815a1e Fix sensors getting wrong unit from MeasuredValueType attribute in Overkiz (#170088) 2026-05-08 18:53:00 +00:00
Mick Vleeshouwer 167757762b Set is_closed state to None when a cover state returns "unknown" in Overkiz (#170081) 2026-05-08 18:52:58 +00:00
Robert Resch 3a902e1a16 Bump deebot-client to 18.3.0 (#170066) 2026-05-08 18:52:56 +00:00
Mick Vleeshouwer 85c11672d8 Bump pyOverkiz to 1.20.3 (#170060) 2026-05-08 18:52:54 +00:00
Mick Vleeshouwer 89649df20d Fix cover controls for UpDownBioclimaticPergola in Overkiz (#170058) 2026-05-08 18:52:52 +00:00
Mick Vleeshouwer 7b749b95ce Fix tilt controls for TiltOnlyVenetianBlind in Overkiz (#170055)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-08 18:52:50 +00:00
Mick Vleeshouwer cc140be85c Fix is_closed state for DynamicGarageDoor in Overkiz (#170052) 2026-05-08 18:52:47 +00:00
Robert Svensson e1ad765414 Fix websocket certificate verification Bump axis to v70 (#170038) 2026-05-08 18:48:55 +00:00
Michael 44b1fea745 Proper handling of malformed data during FRITZ!Box Tools setup (#170030) 2026-05-08 18:48:54 +00:00
Ronald van der Meer 5dd04363b2 Bump python-duco-client to 0.4.1 (#169991) 2026-05-08 18:48:51 +00:00
Ronald van der Meer 03aa979309 Bump python-duco-client to 0.4.0 (#169776) 2026-05-08 18:48:49 +00:00
Daniel Hjelseth Høyer 6fabbb354b Bump pyTibber to 0.37.5 (#169981)
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-05-08 18:45:50 +00:00
Erik Montnemery f644448d0f Add support for options to todo triggers (#169947) 2026-05-08 18:45:48 +00:00
G Johansson 4e61581cd8 Bump holidays to 0.96 (#169939) 2026-05-08 18:45:47 +00:00
puddly 6f87d02b72 Bump serialx to 1.7.1 (#169928) 2026-05-08 18:45:45 +00:00
Joakim Plate 348f6149b4 Update gardena ble to 2.8.1 (#169914) 2026-05-08 18:45:43 +00:00
Stefan Agner a4227ef1bc Fix hassio auth IndexError on Supervisor Unix socket requests (#169911) 2026-05-08 18:45:41 +00:00
Jeef aac49a567f Fix IntelliFire setup recovery (#169739) 2026-05-08 18:45:39 +00:00
Rob Treacy 76b878b136 Fix WiZ Light config flow timeout by properly closing UDP connections (#168456) 2026-05-08 18:45:37 +00:00
th3spis 2d05931683 Added wfsens as a occupancy source in wiz (#166799)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
2026-05-08 18:45:35 +00:00
Franck Nijhof b10582b0a9 2026.5.0 (#169484) 2026-05-06 17:22:09 +02:00
Franck Nijhof b193d951d7 Bump version to 2026.5.0 2026-05-06 15:01:09 +00:00
Franck Nijhof 4cd0d9dcec Bump version to 2026.5.0b4 2026-05-06 13:27:18 +00:00
Daniel Hjelseth Høyer 32f65b2e11 Bump pyTibber to 0.37.4 (#169907) 2026-05-06 13:27:09 +00:00
Erik Montnemery 8c79d1e44b Remove _get_tracked_value method from EntityConditionBase (#169906) 2026-05-06 13:27:07 +00:00
Erik Montnemery 8d53f7a520 Exclude incompatible humidifier entities from humidifier automations (#169905) 2026-05-06 13:27:05 +00:00
Erik Montnemery cc83ee88fb Exclude incompatible water_heater entities from water_heater automations (#169904) 2026-05-06 13:27:03 +00:00
Erik Montnemery 0c5b02eff3 Exclude incompatible climate entities from climate automations (#169903) 2026-05-06 13:27:02 +00:00
Erik Montnemery 9da9f8fd50 Unload scripts and conditions created by template entities (#169366) 2026-05-06 13:27:00 +00:00
Franck Nijhof d70ffcd3e9 Bump version to 2026.5.0b3 2026-05-06 11:16:10 +00:00
Erik Montnemery 3e26d0dfe3 Exclude incompatible entities from temperature automations (#169901) 2026-05-06 11:15:56 +00:00
Erik Montnemery eab9747b32 Exclude incompatible entities from humidity automations (#169898) 2026-05-06 11:15:54 +00:00
Erik Montnemery 9e955d8294 Add media_player volume condition (#169897) 2026-05-06 11:15:52 +00:00
Bram Kragten f08cd01ff8 Update frontend to 20260429.3 (#169893) 2026-05-06 11:10:49 +00:00
Erik Montnemery eabaf3b0fe Add media_player muted conditions (#169892) 2026-05-06 11:10:47 +00:00
Tom Matheussen 65ca790d15 Bump satel_integra to 1.3.1 (#169889) 2026-05-06 11:10:45 +00:00
Joost Lekkerkerker d177944f7a Fix Zinvolt select options (#169886) 2026-05-06 11:10:43 +00:00
Erik Montnemery 7f186f4430 Add media_player volume triggers (#169885) 2026-05-06 11:10:41 +00:00
Erik Montnemery 4f4f4642a7 Add method _should_include to EntityConditionBase (#169884)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-06 11:10:39 +00:00
Erik Montnemery 12e443cd31 Improve entity trigger tests (#169881) 2026-05-06 11:10:37 +00:00
Erik Montnemery 22a7daabe7 Add method _should_include to EntityTriggerBase (#169837) 2026-05-06 11:10:35 +00:00
Erik Montnemery c139e99abd Improve condition test helper docstrings (#169871) 2026-05-06 11:09:06 +00:00
Erik Montnemery 2bfdb96a3f Improve trigger test helper docstrings (#169869) 2026-05-06 11:09:04 +00:00
puddly 4b24ca924b Bump serialx to 1.7.0 (#169867) 2026-05-06 11:09:02 +00:00
Michael Hansen 1d3d714e4f Bump intents to 2026.5.5 (#169855) 2026-05-06 11:09:00 +00:00
Erik Montnemery ffae6eda8a Validate yaml matches implementation in automation options_supported tests (#169798) 2026-05-06 11:05:41 +00:00
Erik Montnemery 4dd996b728 Add trigger media_player.unmuted (#169797) 2026-05-06 11:05:40 +00:00
Erik Montnemery afad1e8dac Improve mobile_app device tracker tests (#169724) 2026-05-06 11:05:38 +00:00
Manu 8e41933251 Record notification from legacy notify action in Mobile App (#169749) 2026-05-06 11:00:21 +00:00
Erik Montnemery c581eaad53 Add trigger timer.time_remaining (#169763) 2026-05-06 10:58:59 +00:00
Ludovic BOUÉ 3050e79d06 Expose SET_SPEED for all fans via PercentSetting in Matter (#169696)
Co-authored-by: Ludovic BOUÉ <132135057+lboue@users.noreply.github.com>
2026-05-06 08:55:15 +00:00
Andres Ruiz 0e8ecd1065 Catch additional errors as potentially retryable errors during energy data updates (#169646) 2026-05-06 08:55:13 +00:00
Paulus Schoutsen 94732139f4 Bump version to 2026.5.0b2 2026-05-05 10:29:37 -04:00
Denis Shulyaka c5e08b2409 Return the requested format for OpenAI TTS (#169839)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-05 10:29:30 -04:00
Joost Lekkerkerker c12e1b5f4a Add Zunzunbee Zigbee brand (#169838) 2026-05-05 10:29:29 -04:00
Joost Lekkerkerker 6cfedb55e6 Add Sensereo matter brand (#169836) 2026-05-05 10:29:27 -04:00
Åke Strandberg af4cb9530b Add missing code for miele washing machine (#169795) 2026-05-05 10:29:26 -04:00
Matthias Alphart 58e97e7d5f Update xknxproject to 3.9.0 (#169775) 2026-05-05 10:29:25 -04:00
Daniel Hjelseth Høyer 2945b51617 Bump pyTibber to 0.37.3 (#169762) 2026-05-05 10:29:24 -04:00
Keilin Bickar 9d0e2df627 bump sense-energy to 0.14.1 (#169761) 2026-05-05 10:29:23 -04:00
Steve Syrell 643ae080db Bump Insteon-panel to 0.6.2 (#169757) 2026-05-05 10:29:22 -04:00
G Johansson a7eaa51179 Fix config flow validation in Nord Pool (#169751) 2026-05-05 10:29:21 -04:00
Petro31 e15852ff38 Fix uptime template sensor (#169743) 2026-05-05 10:29:20 -04:00
Diogo Gomes f6dec34136 Bump pytrydan to 1.0.0 (#169742) 2026-05-05 10:29:19 -04:00
Raj Laud 53905fbc49 Bump victron-ble-ha-parser to 0.7.0 (#169736)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-05 10:29:17 -04:00
Thomas D 8218ff0fe8 Add missing initialization charging power status option to Volvo (#169727) 2026-05-05 10:29:16 -04:00
kernelpanic85 663f7e3e6b Add Celsius and Fahrenheit to Smartthings UNITS mapping (#169686) 2026-05-05 10:29:15 -04:00
Nathan Spencer 4dfa2b8b88 Limit power status binary sensor to non-LR5 devices (#169659) 2026-05-05 10:29:14 -04:00
Nathan Spencer f828b165b1 Bump pylitterbot to 2025.4.0 (#169652) 2026-05-05 10:29:13 -04:00
shbatm c56c506648 Add precipitation device class to WeatherFlow Cloud accumulation sensors (#169638)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 10:29:12 -04:00
Artur Pragacz 8e5bf2a35f Fix async_unload teardown race in scripts (#169562) 2026-05-05 10:29:10 -04:00
Erik Montnemery 4d575e69a4 Improve template reload (#169480) 2026-05-05 10:29:09 -04:00
Christian Lackas 4f78bbccc0 Use all_devices in ViCare diagnostics for completeness (#169429) 2026-05-05 10:29:08 -04:00
Erik Montnemery 2d66ebe54a Add trigger media_player.muted (#156736) 2026-05-05 10:29:07 -04:00
Paulus Schoutsen a3e1209778 Bump version to 2026.5.0b1 2026-05-04 12:44:42 -04:00
Paul Bottein 7c44a0b88d Update frontend to 20260429.2 (#169748) 2026-05-04 12:44:23 -04:00
Manu 126058e0fa Bump bring-api to 1.1.2 (#169729) 2026-05-04 12:44:22 -04:00
Thomas D 28742822cb Ignore location FORBIDDEN response for the Volvo integration (#169713) 2026-05-04 12:44:21 -04:00
karwosts 179d370c2a Use uptime device_class for Uptime sensor (#169692) 2026-05-04 12:44:20 -04:00
Allen Porter 2d8f3691cf Update Nest doorbell event to use standard DoorbellEventType.RING (#169691) 2026-05-04 12:44:19 -04:00
Tom ce4fc9e880 Improve ProxmoxVE config flow preparing bug fixing (#169682)
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-05-04 12:44:18 -04:00
Ronald van der Meer 9e357e7e5a Bump python-duco-client to 0.3.10 (#169677) 2026-05-04 12:44:17 -04:00
OMEGA_RAZER ed35b23e62 Updated prowlpy to 1.1.5 (#169671) 2026-05-04 12:44:17 -04:00
Tom Matheussen 191d2d1f12 Bump satel_integra to 1.3.0 (#169668) 2026-05-04 12:44:16 -04:00
SeifEddineMezned b165d8251f Fix grammar in mqtt/strings.json: "Minimal one" → "At least one" (#169666) 2026-05-04 12:44:15 -04:00
Midori Kochiya 5e8886aeb7 Fix M1S-T500 update error (#169651) 2026-05-04 12:44:14 -04:00
Michael bdb66635f8 Pass None config entry to schluter coordinator (#169621) 2026-05-04 12:44:13 -04:00
Michael 5ba6e348da Fix detection of CPU temperature sensor support on olde FRITZ!Box models (#169620) 2026-05-04 12:44:12 -04:00
Petro31 ed52b0ce80 Change vacuum template config names for clean area (#169599)
Co-authored-by: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com>
2026-05-04 12:44:11 -04:00
Jan-Philipp Benecke 33ee3d6967 Decrease WebDAV client timeout (#169591) 2026-05-04 12:44:10 -04:00
tronikos f36676c32c Bump opower to 0.18.2 (#169588) 2026-05-04 12:44:09 -04:00
Ronald van der Meer 77beddb1e7 Fix Duco unknown node type not re-evaluated after becoming known (#169579) 2026-05-04 12:42:31 -04:00
SeifEddineMezned 1677e410b3 Fix possessive apostrophe errors in mqtt/strings.json (#169576)
Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
2026-05-04 12:38:37 -04:00
SeifEddineMezned 1be09347cd Fix grammar and clarity in samsungtv/strings.json (#169574) 2026-05-04 12:38:36 -04:00
Simone Chemelli c30ac2c0f3 Bump pyuptimerobot to 25.0.0 (#169572) 2026-05-04 12:37:45 -04:00
Shay Levy 145c7435a5 Bump aioshelly to 13.25.0 (#169569) 2026-05-04 12:36:21 -04:00
Paul Bottein 60f3b3bcc0 Update frontend to 20260429.1 (#169565) 2026-05-04 12:36:20 -04:00
Dan Raper 03e6d3bd30 Bump ohme to 1.9.0 (#169556) 2026-05-04 12:36:19 -04:00
Abílio Costa ee4d150e13 Use the correct schema for triggers/conditions "for" option (#169539) 2026-05-04 12:35:29 -04:00
bkobus-bbx 148603a10e Bump blebox_uniapi to 2.5.2 (#169534) 2026-05-04 12:33:13 -04:00
Erik Montnemery 1dbd933d3c Enable duration support in all entity conditions (#169532)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: frenck <195327+frenck@users.noreply.github.com>
2026-05-04 12:32:30 -04:00
Matthias Alphart f7ee7423fe Update knx-frontend to 2026.4.30.60856 (#169529) 2026-05-04 12:26:31 -04:00
Tomer 6322f1e37a Victron GX: Bug fix: parent device is mapped to the wrong device (#169525)
Co-authored-by: Copilot <copilot@github.com>
2026-05-04 12:26:30 -04:00
Manu 0d8c7fbb9d Fix: Migrate also device entries to subentry in GitHub integration (#169523) 2026-05-04 12:26:29 -04:00
Boris Bolshem 70e30b02a4 Fix KeyError in telegram_bot media group download debug log (#169519) 2026-05-04 12:26:28 -04:00
Simone Chemelli ebd21ea9b2 Fix uptime sensor for Synology DSM (#169512) 2026-05-04 12:26:27 -04:00
Erik Montnemery 9aa092cd34 Correct wake_on_lan entity behavior when entity_id changes (#169486)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-04 12:26:26 -04:00
TheJulianJES b274fe85b7 Re-interview ZHA device on websocket reconfigure (#169483) 2026-05-04 12:26:25 -04:00
Erik Montnemery 777c36998c Remove scripts from DATA_SCRIPTS on unload (#169415) 2026-05-04 12:26:24 -04:00
Kurt Chrisford a3977428f9 Implement current setpoint method in actron air integration (#169358) 2026-05-04 12:26:23 -04:00
Simone Chemelli 2d626c263c Storage problem management for Comelit Serial Bridge (#169297) 2026-05-04 12:26:22 -04:00
Jeef d1461f2e68 Bump weatherflow4py to 1.5.4 (#168994) 2026-05-04 12:26:21 -04:00
bkobus-bbx 3b778d2cc7 fix: incorrect position inversion for blebox gateBox cover (#168893) 2026-05-04 12:26:20 -04:00
Yuval Weiss 67b7d17a2f Add Broadlink infrared emitter support (#168889) 2026-05-04 12:26:18 -04:00
Tomer 1afeadc342 Victron GX: bug fix for missing translation key (#168461)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-04 12:26:17 -04:00
jftkcs f6aa4e2092 Fix reasoning summary handling for OpenAI o-models (#168093)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Denis Shulyaka <Shulyaka@gmail.com>
2026-05-04 12:26:16 -04:00
Khole 3b00c5bb96 Check device registration before completing Hive reauth flow (#168035)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Erwin Douna <e.douna@gmail.com>
2026-05-04 12:26:15 -04:00
Franck Nijhof ef7eed579b Bump version to 2026.5.0b0 2026-04-29 16:40:46 +00:00
Franck Nijhof 568a0085fe Bump version to 2026.5.0 2026-04-29 15:50:10 +00:00
1216 changed files with 73573 additions and 16823 deletions
+9
View File
@@ -0,0 +1,9 @@
{
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {
"version": "1.1.0",
"resolved": "ghcr.io/devcontainers/features/github-cli@sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671",
"integrity": "sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671"
}
}
}
+1
View File
@@ -15,6 +15,7 @@ Dockerfile.dev linguist-language=Dockerfile
# Generated files
CODEOWNERS linguist-generated=true
homeassistant/generated/*.py linguist-generated=true
pylint/plugins/pylint_home_assistant/generated/*.py linguist-generated=true
machine/* linguist-generated=true
mypy.ini linguist-generated=true
requirements.txt linguist-generated=true
+1
View File
@@ -25,6 +25,7 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- When entering a new environment or worktree, run `script/setup` to set up the virtual environment with all development dependencies (pylint, pre-commit hooks, etc.). This is required before committing.
- .vscode/tasks.json contains useful commands used for development.
- After finishing a code session, run `uv run prek run --all-files` to check for linting and formatting issues.
## Python Syntax Notes
+5 -5
View File
@@ -281,7 +281,7 @@ jobs:
echo "::add-matcher::.github/workflows/matchers/check-executables-have-shebangs.json"
echo "::add-matcher::.github/workflows/matchers/codespell.json"
- name: Run prek
uses: j178/prek-action@6ad80277337ad479fe43bd70701c3f7f8aa74db3 # v2.0.3
uses: j178/prek-action@bdca6f102f98e2b4c7029491a53dfd366469e33d # v2.0.4
env:
PREK_SKIP: no-commit-to-branch,mypy,pylint,gen_requirements_all,hassfest,hassfest-metadata,hassfest-mypy-config,zizmor
RUFF_OUTPUT_FORMAT: github
@@ -302,7 +302,7 @@ jobs:
with:
persist-credentials: false
- name: Run zizmor
uses: j178/prek-action@6ad80277337ad479fe43bd70701c3f7f8aa74db3 # v2.0.3
uses: j178/prek-action@bdca6f102f98e2b4c7029491a53dfd366469e33d # v2.0.4
with:
extra-args: --all-files zizmor
@@ -1421,7 +1421,7 @@ jobs:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'true'
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
fail_ci_if_error: true
flags: full-suite
@@ -1592,7 +1592,7 @@ jobs:
pattern: coverage-*
- name: Upload coverage to Codecov
if: needs.info.outputs.test_full_suite == 'false'
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }} # zizmor: ignore[secrets-outside-env]
@@ -1620,7 +1620,7 @@ jobs:
with:
pattern: test-results-*
- name: Upload test results to Codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
with:
report_type: test_results
fail_ci_if_error: true
+2 -2
View File
@@ -28,11 +28,11 @@ jobs:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
with:
category: "/language:python"
+4 -4
View File
@@ -55,11 +55,11 @@ jobs:
- name: Generate app token
id: token
# Pinned to a specific version of the action for security reasons
# v1.7.0
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
# v3.2.0
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
with:
app_id: ${{ secrets.ISSUE_TRIAGE_APP_ID }} # zizmor: ignore[secrets-outside-env]
private_key: ${{ secrets.ISSUE_TRIAGE_APP_PEM }} # zizmor: ignore[secrets-outside-env]
app-id: ${{ secrets.ISSUE_TRIAGE_APP_ID }} # zizmor: ignore[secrets-outside-env]
private-key: ${{ secrets.ISSUE_TRIAGE_APP_PEM }} # zizmor: ignore[secrets-outside-env]
# The 90 day stale policy for issues
# Used for:
+2
View File
@@ -565,6 +565,7 @@ homeassistant.components.technove.*
homeassistant.components.tedee.*
homeassistant.components.telegram_bot.*
homeassistant.components.teleinfo.*
homeassistant.components.teltonika.*
homeassistant.components.teslemetry.*
homeassistant.components.text.*
homeassistant.components.thethingsnetwork.*
@@ -609,6 +610,7 @@ homeassistant.components.valve.*
homeassistant.components.velbus.*
homeassistant.components.velux.*
homeassistant.components.victron_gx.*
homeassistant.components.vistapool.*
homeassistant.components.vivotek.*
homeassistant.components.vlc_telnet.*
homeassistant.components.vodafone_station.*
+1
View File
@@ -15,6 +15,7 @@ This repository contains the core of Home Assistant, a Python 3 based home autom
- When entering a new environment or worktree, run `script/setup` to set up the virtual environment with all development dependencies (pylint, pre-commit hooks, etc.). This is required before committing.
- .vscode/tasks.json contains useful commands used for development.
- After finishing a code session, run `uv run prek run --all-files` to check for linting and formatting issues.
## Python Syntax Notes
Generated
+8 -6
View File
@@ -236,8 +236,8 @@ CLAUDE.md @home-assistant/core
/tests/components/blebox/ @bbx-a @swistakm @bkobus-bbx
/homeassistant/components/blink/ @fronzbot
/tests/components/blink/ @fronzbot
/homeassistant/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23
/tests/components/blue_current/ @gleeuwen @NickKoepr @jtodorova23
/homeassistant/components/blue_current/ @gleeuwen @jtodorova23
/tests/components/blue_current/ @gleeuwen @jtodorova23
/homeassistant/components/bluemaestro/ @bdraco
/tests/components/bluemaestro/ @bdraco
/homeassistant/components/blueprint/ @home-assistant/core
@@ -945,8 +945,6 @@ CLAUDE.md @home-assistant/core
/tests/components/knx/ @Julius2342 @farmio @marvin-w
/homeassistant/components/kodi/ @OnFreund
/tests/components/kodi/ @OnFreund
/homeassistant/components/konnected/ @heythisisnate
/tests/components/konnected/ @heythisisnate
/homeassistant/components/kostal_plenticore/ @stegm
/tests/components/kostal_plenticore/ @stegm
/homeassistant/components/kraken/ @eifinger
@@ -1413,8 +1411,8 @@ CLAUDE.md @home-assistant/core
/tests/components/pushover/ @engrbm87
/homeassistant/components/pvoutput/ @frenck
/tests/components/pvoutput/ @frenck
/homeassistant/components/pvpc_hourly_pricing/ @azogue
/tests/components/pvpc_hourly_pricing/ @azogue
/homeassistant/components/pvpc_hourly_pricing/ @azogue @chiro79
/tests/components/pvpc_hourly_pricing/ @azogue @chiro79
/homeassistant/components/pyload/ @tr4nt0r
/tests/components/pyload/ @tr4nt0r
/homeassistant/components/qbittorrent/ @geoffreylagaisse @finder39
@@ -1932,6 +1930,8 @@ CLAUDE.md @home-assistant/core
/tests/components/victron_remote_monitoring/ @AndyTempel
/homeassistant/components/vilfo/ @ManneW
/tests/components/vilfo/ @ManneW
/homeassistant/components/vistapool/ @fdebrus
/tests/components/vistapool/ @fdebrus
/homeassistant/components/vivotek/ @HarlemSquirrel
/tests/components/vivotek/ @HarlemSquirrel
/homeassistant/components/vizio/ @raman325
@@ -2056,6 +2056,8 @@ CLAUDE.md @home-assistant/core
/homeassistant/components/yi/ @bachya
/homeassistant/components/yolink/ @matrixd2
/tests/components/yolink/ @matrixd2
/homeassistant/components/yoto/ @cdnninja @piitaya
/tests/components/yoto/ @cdnninja @piitaya
/homeassistant/components/youless/ @gjong
/tests/components/youless/ @gjong
/homeassistant/components/youtube/ @joostlek
+1 -1
View File
@@ -134,7 +134,7 @@ class AuthManagerFlowManager(
"""
flow = cast(LoginFlow, flow)
if result["type"] != FlowResultType.CREATE_ENTRY:
if result["type"] is not FlowResultType.CREATE_ENTRY:
return result
# we got final result
@@ -11,7 +11,7 @@
"service": "mdi:dialpad"
},
"alarm_toggle_chime": {
"service": "mdi:abc"
"service": "mdi:bell-ring"
}
}
}
@@ -39,7 +39,6 @@ from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_SUPPORTED_FEATURES,
ATTR_UNIT_OF_MEASUREMENT,
CLOUD_NEVER_EXPOSED_ENTITIES,
CONF_DESCRIPTION,
CONF_NAME,
UnitOfTemperature,
@@ -373,9 +372,6 @@ def async_get_entities(
"""Return all entities that are supported by Alexa."""
entities: list[AlexaEntity] = []
for state in hass.states.async_all():
if state.entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
continue
if state.domain not in ENTITY_ADAPTERS:
continue
@@ -1,9 +1,13 @@
"""Alexa Devices integration."""
import asyncio
import contextlib
from homeassistant.const import CONF_COUNTRY, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client, config_validation as cv
from homeassistant.helpers import aiohttp_client, config_validation as cv, httpx_client
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.ssl import SSL_ALPN_HTTP11_HTTP2
from .const import _LOGGER, CONF_LOGIN_DATA, CONF_SITE, COUNTRY_DOMAINS, DOMAIN
from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
@@ -12,6 +16,7 @@ from .services import async_setup_services
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.EVENT,
Platform.NOTIFY,
Platform.SENSOR,
Platform.SWITCH,
@@ -34,6 +39,27 @@ async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bo
await coordinator.async_config_entry_first_refresh()
await coordinator.sync_history_state()
async def _on_http2_reauth_required() -> None:
entry.async_start_reauth(hass)
async def _cancel_http2() -> None:
http2_task.cancel()
with contextlib.suppress(asyncio.CancelledError):
await http2_task
alexa_httpx_client = httpx_client.get_async_client(
hass,
alpn_protocols=SSL_ALPN_HTTP11_HTTP2,
)
http2_task = await coordinator.api.start_http2_processing(
alexa_httpx_client, on_reauth_required=_on_http2_reauth_required
)
entry.async_on_unload(_cancel_http2)
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
@@ -8,13 +8,13 @@ from aioamazondevices.exceptions import (
CannotConnect,
CannotRetrieveData,
)
from aioamazondevices.structures import AmazonDevice
from aioamazondevices.structures import AmazonDevice, AmazonVocalRecord
from aiohttp import ClientSession
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.debounce import Debouncer
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@@ -73,6 +73,11 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
if routine.domain == Platform.BUTTON
}
self._vocal_records: dict[str, AmazonVocalRecord] = {}
self.api.on_history_event.append(self.history_state_event_handler)
self.api.on_history_event.freeze()
async def _async_update_data(self) -> dict[str, AmazonDevice]:
"""Update device data."""
try:
@@ -91,7 +96,6 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
translation_placeholders={"error": repr(err)},
) from err
except CannotAuthenticate as err:
# pylint: disable-next=home-assistant-exception-translation-key-missing
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",
@@ -150,3 +154,38 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
)
if entity_id:
entity_registry.async_remove(entity_id)
async def sync_history_state(self) -> None:
"""Sync history state."""
try:
self._vocal_records = await self.api.sync_history_state()
except CannotAuthenticate as e:
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="invalid_auth",
translation_placeholders={"error": repr(e)},
) from e
except CannotConnect as e:
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="cannot_connect_with_error",
translation_placeholders={"error": repr(e)},
) from e
except BaseException as e:
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="cannot_retrieve_data_with_error",
translation_placeholders={"error": repr(e)},
) from e
async def history_state_event_handler(
self, vocal_records: dict[str, AmazonVocalRecord]
) -> None:
"""Handle pushed vocal record events."""
self._vocal_records = {**self._vocal_records, **vocal_records}
self.async_update_listeners()
@property
def vocal_records(self) -> dict[str, AmazonVocalRecord]:
"""Vocal records of devices."""
return self._vocal_records
@@ -0,0 +1,86 @@
"""Support for events."""
from typing import Final
from homeassistant.components.event import EventEntity, EventEntityDescription
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import _LOGGER
from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
from .entity import AmazonEntity
# Coordinator is used to centralize the data updates
PARALLEL_UPDATES = 0
EVENTS: Final = {
EventEntityDescription(
key="voice_event",
translation_key="voice_event",
),
}
EVENT_TYPE = "triggered"
async def async_setup_entry(
hass: HomeAssistant,
entry: AmazonConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Alexa Devices events based on a config entry."""
coordinator = entry.runtime_data
known_devices: set[str] = set()
def _check_device() -> None:
current_devices = set(coordinator.data)
new_devices = current_devices - known_devices
if new_devices:
known_devices.update(new_devices)
async_add_entities(
AlexaVoiceEvent(coordinator, serial_num, event_desc)
for event_desc in EVENTS
for serial_num in new_devices
)
_check_device()
entry.async_on_unload(coordinator.async_add_listener(_check_device))
class AlexaVoiceEvent(AmazonEntity, EventEntity):
"""Representation of an Alexa voice event."""
_attr_event_types = [EVENT_TYPE]
coordinator: AmazonDevicesCoordinator
_last_seen_timestamp: int | None = None
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
if not (
vocal_record := self.coordinator.vocal_records.get(
self.device.serial_number
)
):
_LOGGER.debug(
"No vocal record found for device %s [%s]",
self.device.account_name,
self.device.serial_number,
)
return
if vocal_record.timestamp == self._last_seen_timestamp:
return
self._last_seen_timestamp = vocal_record.timestamp
self._trigger_event(
EVENT_TYPE,
{
"intent": vocal_record.intent,
"voice_command": vocal_record.title,
"voice_reply": vocal_record.sub_title,
},
)
self.async_write_ha_state()
@@ -1,5 +1,10 @@
{
"entity": {
"event": {
"voice_event": {
"default": "mdi:chat-processing"
}
},
"sensor": {
"voc_index": {
"default": "mdi:molecule"
@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "platinum",
"requirements": ["aioamazondevices==13.7.0"]
"requirements": ["aioamazondevices==13.8.0"]
}
@@ -58,6 +58,18 @@
}
},
"entity": {
"event": {
"voice_event": {
"name": "Voice event",
"state_attributes": {
"event_type": {
"state": {
"triggered": "Triggered"
}
}
}
}
},
"notify": {
"announce": {
"name": "Announce"
@@ -102,6 +114,9 @@
"entry_not_loaded": {
"message": "Entry not loaded: {entry}"
},
"invalid_auth": {
"message": "Invalid authentication credentials: {error}"
},
"invalid_device_id": {
"message": "Invalid device ID specified: {device_id}"
},
@@ -7,10 +7,11 @@ from altruistclient import AltruistClient, AltruistDeviceModel, AltruistError
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo
from .const import CONF_HOST, DOMAIN
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
@@ -1,6 +1,3 @@
"""Constants for the Altruist integration."""
DOMAIN = "altruist"
# pylint: disable-next=home-assistant-duplicate-const
CONF_HOST = "host"
@@ -10,13 +10,12 @@ import logging
from altruistclient import AltruistClient, AltruistError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_HOST
_LOGGER = logging.getLogger(__name__)
UPDATE_INTERVAL = timedelta(seconds=15)
+52 -18
View File
@@ -5,8 +5,12 @@ from typing import Any
import voluptuous as vol
from homeassistant.components import labs, websocket_api
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.components.hassio import HassioNotReadyError
from homeassistant.config_entries import SOURCE_SYSTEM, ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import discovery_flow
from homeassistant.helpers.start import async_at_started
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.hass_dict import HassKey
@@ -49,6 +53,7 @@ CONFIG_SCHEMA = vol.Schema(
)
DATA_COMPONENT: HassKey[Analytics] = HassKey(DOMAIN)
_DATA_SNAPSHOTS_URL: HassKey[str | None] = HassKey(f"{DOMAIN}_snapshots_url")
LABS_SNAPSHOT_FEATURE = "snapshots"
@@ -57,18 +62,39 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the analytics integration."""
analytics_config = config.get(DOMAIN, {})
snapshots_url: str | None = None
if CONF_SNAPSHOTS_URL in analytics_config:
await labs.async_update_preview_feature(
hass, DOMAIN, LABS_SNAPSHOT_FEATURE, enabled=True
)
snapshots_url = analytics_config[CONF_SNAPSHOTS_URL]
else:
snapshots_url = None
hass.data[_DATA_SNAPSHOTS_URL] = snapshots_url
discovery_flow.async_create_flow(
hass, DOMAIN, context={"source": SOURCE_SYSTEM}, data={}
)
websocket_api.async_register_command(hass, websocket_analytics)
websocket_api.async_register_command(hass, websocket_analytics_preferences)
hass.http.register_view(AnalyticsDevicesView)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Analytics from a config entry."""
snapshots_url = hass.data.get(_DATA_SNAPSHOTS_URL)
analytics = Analytics(hass, snapshots_url)
# Load stored data
await analytics.load()
try:
await analytics.load()
except HassioNotReadyError as err:
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="supervisor_not_ready",
) from err
started = False
@@ -80,26 +106,30 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
if started:
await analytics.async_schedule()
async def start_schedule(_event: Event) -> None:
"""Start the send schedule after the started event."""
async def start_schedule(hass: HomeAssistant) -> None:
"""Start the send schedule once Home Assistant has started."""
nonlocal started
started = True
await analytics.async_schedule()
labs.async_subscribe_preview_feature(
hass, DOMAIN, LABS_SNAPSHOT_FEATURE, _async_handle_labs_update
entry.async_on_unload(
labs.async_subscribe_preview_feature(
hass, DOMAIN, LABS_SNAPSHOT_FEATURE, _async_handle_labs_update
)
)
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, start_schedule)
websocket_api.async_register_command(hass, websocket_analytics)
websocket_api.async_register_command(hass, websocket_analytics_preferences)
hass.http.register_view(AnalyticsDevicesView)
entry.async_on_unload(async_at_started(hass, start_schedule))
hass.data[DATA_COMPONENT] = analytics
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload an Analytics config entry."""
analytics = hass.data.pop(DATA_COMPONENT)
analytics.cancel_scheduled()
return True
@callback
@websocket_api.require_admin
@websocket_api.websocket_command({vol.Required("type"): "analytics"})
@@ -109,7 +139,9 @@ def websocket_analytics(
msg: dict[str, Any],
) -> None:
"""Return analytics preferences."""
analytics = hass.data[DATA_COMPONENT]
if (analytics := hass.data.get(DATA_COMPONENT)) is None:
connection.send_error(msg["id"], websocket_api.ERR_NOT_FOUND, "Not loaded")
return
connection.send_result(
msg["id"],
{ATTR_PREFERENCES: analytics.preferences, ATTR_ONBOARDED: analytics.onboarded},
@@ -130,8 +162,10 @@ async def websocket_analytics_preferences(
msg: dict[str, Any],
) -> None:
"""Update analytics preferences."""
if (analytics := hass.data.get(DATA_COMPONENT)) is None:
connection.send_error(msg["id"], websocket_api.ERR_NOT_FOUND, "Not loaded")
return
preferences = msg[ATTR_PREFERENCES]
analytics = hass.data[DATA_COMPONENT]
await analytics.save_preferences(preferences)
await analytics.async_schedule()
+16 -10
View File
@@ -299,12 +299,8 @@ class Analytics:
self._data = AnalyticsData.from_dict(stored)
if self.supervisor and not self.onboarded:
# This may raise HassioNotReadyError if Supervisor was unreachable
# during setup of the Supervisor integration. That will fail setup
# of this integration. However there is no better option at this time
# since we need to get the diagnostic setting from Supervisor to correctly
# setup this integration and we can't raise ConfigEntryNotReady to
# trigger a retry from async_setup.
# This may raise HassioNotReadyError if Supervisor was unreachable.
# The caller is responsible for handling this and triggering a retry.
supervisor_info = hassio.get_supervisor_info(self._hass)
# User have not configured analytics, get this setting from the supervisor
@@ -349,10 +345,10 @@ class Analytics:
await self._save()
if self.supervisor:
# get_supervisor_info was called during setup so we can't get here
# if it raised. The others may raise HassioNotReadyError if only some
# data was successfully fetched from Supervisor
supervisor_info = hassio.get_supervisor_info(hass)
# Try to pull Supervisor information, but don't fail if some or all
# of it is unavailable due to setup failures in the hassio integration.
with contextlib.suppress(hassio.HassioNotReadyError):
supervisor_info = hassio.get_supervisor_info(hass)
with contextlib.suppress(hassio.HassioNotReadyError):
operating_system_info = hassio.get_os_info(hass)
with contextlib.suppress(hassio.HassioNotReadyError):
@@ -630,6 +626,16 @@ class Analytics:
err,
)
@callback
def cancel_scheduled(self) -> None:
"""Cancel all scheduled analytics tasks."""
if self._basic_scheduled is not None:
self._basic_scheduled()
self._basic_scheduled = None
if self._snapshot_scheduled is not None:
self._snapshot_scheduled()
self._snapshot_scheduled = None
async def async_schedule(self) -> None:
"""Schedule analytics."""
if not self.onboarded:
@@ -0,0 +1,19 @@
"""Config flow for Analytics integration."""
from typing import Any
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from .const import DOMAIN
class AnalyticsConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Analytics."""
VERSION = 1
async def async_step_system(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle the initial step."""
return self.async_create_entry(title="Analytics", data={})
@@ -3,6 +3,7 @@
"name": "Analytics",
"after_dependencies": ["energy", "hassio", "recorder"],
"codeowners": ["@home-assistant/core"],
"config_flow": true,
"dependencies": ["api", "websocket_api", "http"],
"documentation": "https://www.home-assistant.io/integrations/analytics",
"integration_type": "system",
@@ -14,5 +15,6 @@
"report_issue_url": "https://github.com/OHF-Device-Database/device-database/issues/new"
}
},
"quality_scale": "internal"
"quality_scale": "internal",
"single_config_entry": true
}
@@ -1,4 +1,9 @@
{
"exceptions": {
"supervisor_not_ready": {
"message": "Supervisor was not ready during setup, will retry"
}
},
"preview_features": {
"snapshots": {
"description": "We're creating the [Open Home Foundation Device Database](https://www.home-assistant.io/blog/2026/02/02/about-device-database/): a free, open source community-powered resource to help users find practical information about how smart home devices perform in real installations.\n\nYou can help us build it by opting in to share anonymized data about your devices. This data will only ever include device-specific details (like model or manufacturer) never personally identifying information (like the names you assign).\n\nFind out how we process your data (should you choose to contribute) in our [Data Use Statement](https://www.openhomefoundation.org/device-database-data-use-statement).",
@@ -226,7 +226,7 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
) -> SubentryFlowResult:
"""Set initial options."""
# abort if entry is not loaded
if self._get_entry().state != ConfigEntryState.LOADED:
if self._get_entry().state is not ConfigEntryState.LOADED:
return self.async_abort(reason="entry_not_loaded")
hass_apis: list[SelectOptionDict] = [
@@ -89,7 +89,7 @@ class AOSmithWaterHeaterEntity(AOSmithStatusEntity, WaterHeaterEntity):
def supported_features(self) -> WaterHeaterEntityFeature:
"""Return the list of supported features."""
supports_vacation_mode = any(
supported_mode.mode == AOSmithOperationMode.VACATION
supported_mode.mode is AOSmithOperationMode.VACATION
for supported_mode in self.device.supported_modes
)
@@ -122,7 +122,7 @@ class AOSmithWaterHeaterEntity(AOSmithStatusEntity, WaterHeaterEntity):
@property
def is_away_mode_on(self) -> bool:
"""Return True if away mode is on."""
return self.device.status.current_mode == AOSmithOperationMode.VACATION
return self.device.status.current_mode is AOSmithOperationMode.VACATION
async def async_set_operation_mode(self, operation_mode: str) -> None:
"""Set new target operation mode."""
@@ -369,7 +369,7 @@ class AppleTVManager(DeviceListener):
attrs[ATTR_MODEL] = (
dev_info.raw_model
if dev_info.model == DeviceModel.Unknown and dev_info.raw_model
if dev_info.model is DeviceModel.Unknown and dev_info.raw_model
else model_str(dev_info.model)
)
attrs[ATTR_SW_VERSION] = dev_info.version
@@ -63,7 +63,7 @@ class AppleTVKeyboardFocused(AppleTVEntity, BinarySensorEntity, KeyboardListener
# Listen to keyboard updates
atv.keyboard.listener = self
# Set initial state based on current focus state
self._update_state(atv.keyboard.text_focus_state == KeyboardFocusState.Focused)
self._update_state(atv.keyboard.text_focus_state is KeyboardFocusState.Focused)
@callback
def async_device_disconnected(self) -> None:
@@ -78,7 +78,7 @@ class AppleTVKeyboardFocused(AppleTVEntity, BinarySensorEntity, KeyboardListener
This is a callback function from pyatv.interface.KeyboardListener.
"""
self._update_state(new_state == KeyboardFocusState.Focused)
self._update_state(new_state is KeyboardFocusState.Focused)
def _update_state(self, new_state: bool) -> None:
"""Update and report."""
@@ -354,7 +354,7 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
"name": self.atv.name,
"type": (
dev_info.raw_model
if dev_info.model == DeviceModel.Unknown and dev_info.raw_model
if dev_info.model is DeviceModel.Unknown and dev_info.raw_model
else model_str(dev_info.model)
),
}
@@ -441,12 +441,12 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
return await self.async_step_password()
# Figure out, depending on protocol, what kind of pairing is needed
if service.pairing == PairingRequirement.Unsupported:
if service.pairing is PairingRequirement.Unsupported:
_LOGGER.debug("%s does not support pairing", self.protocol)
return await self.async_pair_next_protocol()
if service.pairing == PairingRequirement.Disabled:
if service.pairing is PairingRequirement.Disabled:
return await self.async_step_protocol_disabled()
if service.pairing == PairingRequirement.NotNeeded:
if service.pairing is PairingRequirement.NotNeeded:
_LOGGER.debug("%s does not require pairing", self.protocol)
self.credentials[self.protocol.value] = None
return await self.async_pair_next_protocol()
@@ -457,7 +457,7 @@ class AppleTVConfigFlow(ConfigFlow, domain=DOMAIN):
pair_args: dict[str, Any] = {}
if self.protocol in {Protocol.AirPlay, Protocol.Companion, Protocol.DMAP}:
pair_args["name"] = "Home Assistant"
if self.protocol == Protocol.DMAP:
if self.protocol is Protocol.DMAP:
pair_args["zeroconf"] = await zeroconf.async_get_instance(self.hass)
# Initiate the pairing process
@@ -24,6 +24,7 @@ from pyatv.interface import (
PushListener,
PushUpdater,
)
from yarl import URL
from homeassistant.components import media_source
from homeassistant.components.media_player import (
@@ -139,7 +140,7 @@ class AppleTvMediaPlayer(
all_features = atv.features.all_features()
for feature_name, support_flag in SUPPORT_FEATURE_MAPPING.items():
feature_info = all_features.get(feature_name)
if feature_info and feature_info.state != FeatureState.Unsupported:
if feature_info and feature_info.state is not FeatureState.Unsupported:
self._attr_supported_features |= support_flag
# No need to schedule state update here as that will happen when the first
@@ -188,14 +189,14 @@ class AppleTvMediaPlayer(
return MediaPlayerState.OFF
if (
self._is_feature_available(FeatureName.PowerState)
and self.atv.power.power_state == PowerState.Off
and self.atv.power.power_state is PowerState.Off
):
return MediaPlayerState.OFF
if self._playing:
state = self._playing.device_state
if state in (DeviceState.Idle, DeviceState.Loading):
return MediaPlayerState.IDLE
if state == DeviceState.Playing:
if state is DeviceState.Playing:
return MediaPlayerState.PLAYING
if state in (DeviceState.Paused, DeviceState.Seeking, DeviceState.Stopped):
return MediaPlayerState.PAUSED
@@ -345,7 +346,10 @@ class AppleTvMediaPlayer(
play_item = await media_source.async_resolve_media(
self.hass, media_id, self.entity_id
)
media_id = async_process_play_media_url(self.hass, play_item.url)
if play_item.path and self._is_feature_available(FeatureName.StreamFile):
media_id = str(play_item.path)
else:
media_id = async_process_play_media_url(self.hass, play_item.url)
media_type = MediaType.MUSIC
if self._is_feature_available(FeatureName.StreamFile) and (
@@ -353,11 +357,16 @@ class AppleTvMediaPlayer(
):
_LOGGER.debug("Streaming %s via RAOP", media_id)
await self.atv.stream.stream_file(media_id)
elif self._is_feature_available(FeatureName.PlayUrl):
elif self._is_feature_available(FeatureName.PlayUrl) and (
(parsed_url := URL(media_id)).is_absolute() and parsed_url.host
):
_LOGGER.debug("Playing %s via AirPlay", media_id)
await self.atv.stream.play_url(media_id)
else:
_LOGGER.error("Media streaming is not possible with current configuration")
_LOGGER.error(
"Media streaming is not possible with current configuration for %s",
media_id,
)
@property
def media_image_hash(self) -> str | None:
@@ -446,7 +455,7 @@ class AppleTvMediaPlayer(
def shuffle(self) -> bool | None:
"""Boolean if shuffle is enabled."""
if self._playing and self._is_feature_available(FeatureName.Shuffle):
return self._playing.shuffle != ShuffleState.Off
return self._playing.shuffle is not ShuffleState.Off
return None
def _is_feature_available(self, feature: FeatureName) -> bool:
@@ -506,7 +515,7 @@ class AppleTvMediaPlayer(
and (self._is_feature_available(FeatureName.TurnOff))
and (
not self._is_feature_available(FeatureName.PowerState)
or self.atv.power.power_state == PowerState.On
or self.atv.power.power_state is PowerState.On
)
):
await self.atv.power.turn_off()
@@ -59,7 +59,7 @@ def _check_keyboard_focus(atv: AppleTVInterface) -> None:
translation_domain=DOMAIN,
translation_key="keyboard_not_available",
) from err
if focus_state != KeyboardFocusState.Focused:
if focus_state is not KeyboardFocusState.Focused:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="keyboard_not_focused",
+5 -1
View File
@@ -193,7 +193,11 @@ async def async_setup_entry(
Aranet4BluetoothSensorEntity, async_add_entities
)
)
entry.async_on_unload(entry.runtime_data.async_register_processor(processor))
entry.async_on_unload(
entry.runtime_data.async_register_processor(
processor, AranetSensorEntityDescription
)
)
class Aranet4BluetoothSensorEntity(
@@ -16,6 +16,13 @@ from homeassistant.helpers.service_info.ssdp import ATTR_UPNP_UDN, SsdpServiceIn
from .const import DEFAULT_NAME, DEFAULT_PORT, DOMAIN
STEP_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
}
)
class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle config flow."""
@@ -31,13 +38,22 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
await self.async_set_unique_id(uuid)
self._abort_if_unique_id_configured({CONF_HOST: host, CONF_PORT: port})
async def _async_try_connect(self, host: str, port: int) -> None:
"""Verify the device is reachable."""
async def _async_try_connect(self, host: str, port: int) -> dict[str, str]:
"""Verify the device is reachable; return errors keyed by reason."""
client = Client(host, port)
try:
await client.start()
except socket.gaierror:
return {"base": "invalid_host"}
except TimeoutError:
return {"base": "timeout_connect"}
except ConnectionRefusedError:
return {"base": "connection_refused"}
except ConnectionFailed, OSError:
return {"base": "cannot_connect"}
finally:
await client.stop()
return {}
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -53,19 +69,10 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
user_input[CONF_HOST], user_input[CONF_PORT], uuid
)
try:
await self._async_try_connect(
user_input[CONF_HOST], user_input[CONF_PORT]
)
except socket.gaierror:
errors["base"] = "invalid_host"
except TimeoutError:
errors["base"] = "timeout_connect"
except ConnectionRefusedError:
errors["base"] = "connection_refused"
except ConnectionFailed, OSError:
errors["base"] = "cannot_connect"
else:
errors = await self._async_try_connect(
user_input[CONF_HOST], user_input[CONF_PORT]
)
if not errors:
return self.async_create_entry(
title=f"{DEFAULT_NAME} ({user_input[CONF_HOST]})",
data={
@@ -74,16 +81,46 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
},
)
fields = {
vol.Required(CONF_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): int,
}
schema = vol.Schema(fields)
schema = STEP_DATA_SCHEMA
if user_input is not None:
schema = self.add_suggested_values_to_schema(schema, user_input)
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration of an existing entry."""
errors: dict[str, str] = {}
reconfigure_entry = self._get_reconfigure_entry()
if user_input is not None:
uuid = await get_uniqueid_from_host(
async_get_clientsession(self.hass), user_input[CONF_HOST]
)
if uuid:
await self.async_set_unique_id(uuid)
self._abort_if_unique_id_mismatch()
errors = await self._async_try_connect(
user_input[CONF_HOST], user_input[CONF_PORT]
)
if not errors:
return self.async_update_reload_and_abort(
reconfigure_entry,
data_updates={
CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT],
},
)
schema = self.add_suggested_values_to_schema(
STEP_DATA_SCHEMA, user_input or reconfigure_entry.data
)
return self.async_show_form(
step_id="reconfigure", data_schema=schema, errors=errors
)
async def async_step_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -113,9 +150,7 @@ class ArcamFmjFlowHandler(ConfigFlow, domain=DOMAIN):
await self._async_set_unique_id_and_update(host, port, uuid)
try:
await self._async_try_connect(host, port)
except ConnectionFailed, OSError:
if await self._async_try_connect(host, port):
return self.async_abort(reason="cannot_connect")
self.host = host
@@ -263,9 +263,9 @@ class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
def media_channel(self) -> str | None:
"""Channel currently playing."""
source = self._state.get_source()
if source == SourceCodes.DAB:
if source is SourceCodes.DAB:
value = self._state.get_dab_station()
elif source == SourceCodes.FM:
elif source is SourceCodes.FM:
value = self._state.get_rds_information()
else:
value = None
@@ -274,7 +274,7 @@ class ArcamFmj(ArcamFmjEntity, MediaPlayerEntity):
@property
def media_artist(self) -> str | None:
"""Artist of current playing media, music track only."""
if self._state.get_source() == SourceCodes.DAB:
if self._state.get_source() is SourceCodes.DAB:
value = self._state.get_dls_pdt()
else:
value = None
@@ -3,7 +3,9 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"unique_id_mismatch": "Please ensure you reconfigure against the same device."
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
@@ -16,6 +18,13 @@
"confirm": {
"description": "Do you want to add Arcam FMJ on `{host}` to Home Assistant?"
},
"reconfigure": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"description": "[%key:component::arcam_fmj::config::step::user::description%]"
},
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
@@ -1355,7 +1355,7 @@ class PipelineRun:
) -> bool:
"""Return true if all targeted entities were in the same area as the device."""
if (
intent_response.response_type != intent.IntentResponseType.ACTION_DONE
intent_response.response_type is not intent.IntentResponseType.ACTION_DONE
or not intent_response.matched_states
):
return False
@@ -9,12 +9,11 @@ import voluptuous as vol
from homeassistant.components import usb
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import ATTR_SERIAL_NUMBER, CONF_ADDRESS, CONF_PORT
from homeassistant.const import ATTR_MODEL, ATTR_SERIAL_NUMBER, CONF_ADDRESS, CONF_PORT
from homeassistant.core import HomeAssistant
from .const import (
ATTR_FIRMWARE,
ATTR_MODEL,
DEFAULT_ADDRESS,
DEFAULT_INTEGRATION_TITLE,
DOMAIN,
@@ -19,8 +19,4 @@ DEVICES = "devices"
MANUFACTURER = "ABB"
ATTR_DEVICE_NAME = "device_name"
# pylint: disable-next=home-assistant-duplicate-const
ATTR_DEVICE_ID = "device_id"
# pylint: disable-next=home-assistant-duplicate-const
ATTR_MODEL = "model"
ATTR_FIRMWARE = "firmware"
@@ -13,6 +13,7 @@ from homeassistant.components.sensor import (
SensorStateClass,
)
from homeassistant.const import (
ATTR_MODEL,
ATTR_SERIAL_NUMBER,
EntityCategory,
UnitOfElectricCurrent,
@@ -31,7 +32,6 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import (
ATTR_DEVICE_NAME,
ATTR_FIRMWARE,
ATTR_MODEL,
DEFAULT_DEVICE_NAME,
DOMAIN,
MANUFACTURER,
+4 -4
View File
@@ -251,12 +251,12 @@ class AuthProvidersView(HomeAssistantView):
def _prepare_result_json(result: AuthFlowResult) -> dict[str, Any]:
"""Convert result to JSON serializable dict."""
if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY:
if result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY:
return {
key: val for key, val in result.items() if key not in ("result", "data")
}
if result["type"] != data_entry_flow.FlowResultType.FORM:
if result["type"] is not data_entry_flow.FlowResultType.FORM:
return result # type: ignore[return-value]
data = dict(result)
@@ -289,11 +289,11 @@ class LoginFlowBaseView(HomeAssistantView):
result: AuthFlowResult,
) -> web.Response:
"""Convert the flow result to a response."""
if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY:
if result["type"] is not data_entry_flow.FlowResultType.CREATE_ENTRY:
# @log_invalid_auth does not work here since it returns HTTP 200.
# We need to manually log failed login attempts.
if (
result["type"] == data_entry_flow.FlowResultType.FORM
result["type"] is data_entry_flow.FlowResultType.FORM
and (errors := result.get("errors"))
and errors.get("base")
in (
@@ -142,9 +142,9 @@ def websocket_depose_mfa(
def _prepare_result_json(result: data_entry_flow.FlowResult) -> dict[str, Any]:
"""Convert result to JSON serializable dict."""
if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY:
if result["type"] is data_entry_flow.FlowResultType.CREATE_ENTRY:
return dict(result)
if result["type"] != data_entry_flow.FlowResultType.FORM:
if result["type"] is not data_entry_flow.FlowResultType.FORM:
return result # type: ignore[return-value]
data = dict(result)
+44 -4
View File
@@ -1,5 +1,6 @@
"""Light platform for Avea."""
from collections.abc import Callable
from contextlib import suppress
import logging
from typing import Any
@@ -19,6 +20,7 @@ from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.device_registry import CONNECTION_BLUETOOTH, DeviceInfo
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
@@ -27,7 +29,7 @@ from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import color as color_util
from . import AveaConfigEntry
from .const import DOMAIN, INTEGRATION_TITLE, UNKNOWN_NAME
from .const import DOMAIN, INTEGRATION_TITLE, MODEL, UNKNOWN_NAME
_LOGGER = logging.getLogger(__name__)
UPDATE_EXCEPTIONS = (BleakError, OSError, RuntimeError)
@@ -42,6 +44,13 @@ def _normalize_name(name: str | None) -> str | None:
return name
def _read_device_info_value(read: Callable[[], str | None]) -> str | None:
"""Read a device information value from an Avea bulb."""
with suppress(*UPDATE_EXCEPTIONS):
return _normalize_name(read())
return None
def _ha_brightness_to_avea(brightness: int) -> int:
"""Convert Home Assistant brightness to Avea brightness."""
return round((brightness / 255) * AVEA_MAX_BRIGHTNESS)
@@ -96,7 +105,8 @@ async def async_setup_entry(
) -> None:
"""Set up the Avea light platform."""
async_add_entities(
[AveaLight(entry.runtime_data, entry.title)], update_before_add=True
[AveaLight(entry.runtime_data, entry.data[CONF_ADDRESS])],
update_before_add=True,
)
@@ -180,14 +190,42 @@ class AveaLight(LightEntity):
"""Representation of an Avea."""
_attr_color_mode = ColorMode.HS
_attr_has_entity_name = True
_attr_name = None
_attr_supported_color_modes = {ColorMode.HS}
def __init__(self, light: avea.Bulb, entry_title: str) -> None:
def __init__(self, light: avea.Bulb, address: str) -> None:
"""Initialize an AveaLight."""
self._light = light
self._attr_name = entry_title
self._attr_unique_id = address
self._attr_brightness = light.brightness
self._last_brightness = 255
self._device_info_updated = False
self._attr_device_info = DeviceInfo(
connections={(CONNECTION_BLUETOOTH, address)},
model=MODEL,
)
def _update_device_info(self) -> None:
"""Fetch device information from the Avea bulb."""
device_info = self._attr_device_info
assert device_info is not None
manufacturer = _read_device_info_value(self._light.get_manufacturer_name)
hardware_revision = _read_device_info_value(self._light.get_hardware_revision)
firmware_version = _read_device_info_value(self._light.get_fw_version)
serial_number = _read_device_info_value(self._light.get_serial_number)
if manufacturer:
device_info["manufacturer"] = manufacturer
if hardware_revision:
device_info["hw_version"] = hardware_revision
if firmware_version:
device_info["sw_version"] = firmware_version
if serial_number:
device_info["serial_number"] = serial_number
self._device_info_updated = True
def turn_on(self, **kwargs: Any) -> None:
"""Instruct the light to turn on."""
@@ -214,6 +252,8 @@ class AveaLight(LightEntity):
connected = self._light.connect()
try:
if not self._device_info_updated:
self._update_device_info()
brightness = self._light.get_brightness()
rgb_color = self._light.get_rgb()
finally:
+2 -1
View File
@@ -17,10 +17,11 @@ from homeassistant.components.backup import (
OnProgressCallback,
suggested_filename,
)
from homeassistant.const import CONF_PREFIX
from homeassistant.core import HomeAssistant, callback
from . import S3ConfigEntry
from .const import CONF_BUCKET, CONF_PREFIX, DATA_BACKUP_AGENT_LISTENERS, DOMAIN
from .const import CONF_BUCKET, DATA_BACKUP_AGENT_LISTENERS, DOMAIN
from .helpers import async_list_backups_from_s3
_LOGGER = logging.getLogger(__name__)
@@ -8,6 +8,7 @@ from botocore.exceptions import ClientError, ConnectionError, ParamValidationErr
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_PREFIX
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.selector import (
TextSelector,
@@ -20,7 +21,6 @@ from .const import (
CONF_ACCESS_KEY_ID,
CONF_BUCKET,
CONF_ENDPOINT_URL,
CONF_PREFIX,
CONF_SECRET_ACCESS_KEY,
DEFAULT_ENDPOINT_URL,
DESCRIPTION_AWS_S3_DOCS_URL,
-2
View File
@@ -11,8 +11,6 @@ CONF_ACCESS_KEY_ID = "access_key_id"
CONF_SECRET_ACCESS_KEY = "secret_access_key"
CONF_ENDPOINT_URL = "endpoint_url"
CONF_BUCKET = "bucket"
# pylint: disable-next=home-assistant-duplicate-const
CONF_PREFIX = "prefix"
AWS_DOMAIN = "amazonaws.com"
DEFAULT_ENDPOINT_URL = f"https://s3.eu-central-1.{AWS_DOMAIN}/"
@@ -8,10 +8,11 @@ from aiobotocore.client import AioBaseClient as S3Client
from botocore.exceptions import BotoCoreError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PREFIX
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import CONF_BUCKET, CONF_PREFIX, DOMAIN
from .const import CONF_BUCKET, DOMAIN
from .helpers import async_list_backups_from_s3
SCAN_INTERVAL = timedelta(hours=6)
@@ -5,15 +5,10 @@ from typing import Any
from homeassistant.components.backup import DATA_MANAGER as BACKUP_DATA_MANAGER
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_PREFIX
from homeassistant.core import HomeAssistant
from .const import (
CONF_ACCESS_KEY_ID,
CONF_BUCKET,
CONF_PREFIX,
CONF_SECRET_ACCESS_KEY,
DOMAIN,
)
from .const import CONF_ACCESS_KEY_ID, CONF_BUCKET, CONF_SECRET_ACCESS_KEY, DOMAIN
from .coordinator import S3ConfigEntry
from .helpers import async_list_backups_from_s3
+25 -19
View File
@@ -2,13 +2,12 @@
from collections.abc import Mapping
from ipaddress import ip_address
from typing import Any
from typing import TYPE_CHECKING, Any
from urllib.parse import urlsplit
import voluptuous as vol
from homeassistant.config_entries import (
SOURCE_IGNORE,
SOURCE_REAUTH,
SOURCE_RECONFIGURE,
ConfigEntry,
@@ -50,6 +49,9 @@ from .const import (
from .errors import AuthenticationRequired, CannotConnect
from .hub import AxisHub, get_axis_api
if TYPE_CHECKING:
import axis
AXIS_OUI = {"00:40:8c", "ac:cc:8e", "b8:a4:4f", "e8:27:25"}
DEFAULT_PORT = 443
DEFAULT_PROTOCOL = "https"
@@ -94,7 +96,8 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
errors["base"] = "cannot_connect"
else:
serial = api.vapix.serial_number
if (serial := self._get_serial_number(api)) is None:
return self.async_abort(reason="no_serial_number")
config = {
CONF_PROTOCOL: user_input[CONF_PROTOCOL],
CONF_HOST: user_input[CONF_HOST],
@@ -139,25 +142,15 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
async def _create_entry(self, serial: str) -> ConfigFlowResult:
"""Create entry for device.
Generate a name to be used as a prefix for device entities.
Use the discovered device name when available.
"""
model = self.config[CONF_MODEL]
same_model = [
entry.data[CONF_NAME]
for entry in self.hass.config_entries.async_entries(DOMAIN)
if entry.source != SOURCE_IGNORE and entry.data[CONF_MODEL] == model
]
name = model
for idx in range(len(same_model) + 1):
name = f"{model} {idx}"
if name not in same_model:
break
if (title_placeholders := self.context.get("title_placeholders")) is not None:
name = title_placeholders[CONF_NAME]
else:
name = f"{self.config[CONF_MODEL]} - {serial}"
self.config[CONF_NAME] = name
title = f"{model} - {serial}"
return self.async_create_entry(title=title, data=self.config)
return self.async_create_entry(title=name, data=self.config)
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
@@ -269,6 +262,19 @@ class AxisFlowHandler(ConfigFlow, domain=DOMAIN):
return await self.async_step_user()
@staticmethod
def _get_serial_number(api: axis.AxisDevice) -> str | None:
"""Retrieve the device serial number from the Axis API.
Tries basic_device_info first, then property_handler. Returns None if not found.
"""
vapix = api.vapix
if vapix.basic_device_info.initialized:
return vapix.basic_device_info["0"].serial_number
if vapix.params.property_handler.initialized:
return vapix.params.property_handler["0"].system_serial_number
return None
class AxisOptionsFlowHandler(OptionsFlow):
"""Handle Axis device options."""
@@ -2,8 +2,7 @@
import axis
from axis.errors import Unauthorized
from axis.interfaces.mqtt import mqtt_json_to_event
from axis.models.mqtt import ClientState
from axis.models.mqtt import ClientState, mqtt_json_to_event
from axis.stream_manager import Signal, State
from homeassistant.components import mqtt
+1 -1
View File
@@ -29,7 +29,7 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["axis"],
"requirements": ["axis==71"],
"requirements": ["axis==72"],
"ssdp": [
{
"manufacturer": "AXIS"
@@ -3,6 +3,7 @@
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"link_local_address": "Link local addresses are not supported",
"no_serial_number": "Could not retrieve a serial number from the device. Please check device connectivity and try again.",
"not_axis_device": "Discovered device not an Axis device",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
@@ -32,6 +32,7 @@ PLATFORMS = [
Platform.LIGHT,
Platform.SENSOR,
Platform.SWITCH,
Platform.UPDATE,
]
PARALLEL_UPDATES = 0
+31 -3
View File
@@ -3,7 +3,7 @@
from typing import Any
import blebox_uniapi.cover
from blebox_uniapi.cover import BleboxCoverState
from blebox_uniapi.cover import BleboxCoverState, UnifiedCoverType
from homeassistant.components.cover import (
ATTR_POSITION,
@@ -25,6 +25,19 @@ BLEBOX_TO_COVER_DEVICE_CLASSES = {
"shutter": CoverDeviceClass.SHUTTER,
}
UNIFIED_COVER_TYPE_TO_DEVICE_CLASS = {
UnifiedCoverType.AWNING: CoverDeviceClass.AWNING,
UnifiedCoverType.BLIND: CoverDeviceClass.BLIND,
UnifiedCoverType.CURTAIN: CoverDeviceClass.CURTAIN,
UnifiedCoverType.DAMPER: CoverDeviceClass.DAMPER,
UnifiedCoverType.DOOR: CoverDeviceClass.DOOR,
UnifiedCoverType.GARAGE: CoverDeviceClass.GARAGE,
UnifiedCoverType.GATE: CoverDeviceClass.GATE,
UnifiedCoverType.SHADE: CoverDeviceClass.SHADE,
UnifiedCoverType.SHUTTER: CoverDeviceClass.SHUTTER,
UnifiedCoverType.WINDOW: CoverDeviceClass.WINDOW,
}
BLEBOX_TO_HASS_COVER_STATES = {
None: None,
# all blebox covers
@@ -59,7 +72,6 @@ class BleBoxCoverEntity(BleBoxEntity[blebox_uniapi.cover.Cover], CoverEntity):
def __init__(self, feature: blebox_uniapi.cover.Cover) -> None:
"""Initialize a BleBox cover feature."""
super().__init__(feature)
self._attr_device_class = BLEBOX_TO_COVER_DEVICE_CLASSES[feature.device_class]
self._attr_supported_features = (
CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
)
@@ -76,6 +88,21 @@ class BleBoxCoverEntity(BleBoxEntity[blebox_uniapi.cover.Cover], CoverEntity):
| CoverEntityFeature.CLOSE_TILT
)
if feature.tilt_only:
self._attr_supported_features &= ~(
CoverEntityFeature.OPEN
| CoverEntityFeature.CLOSE
| CoverEntityFeature.SET_POSITION
| CoverEntityFeature.STOP
)
@property
def device_class(self) -> CoverDeviceClass | None:
"""Return the device class based on cover type when available."""
if (cover_type := self._feature.cover_type) is not None:
return UNIFIED_COVER_TYPE_TO_DEVICE_CLASS[cover_type]
return BLEBOX_TO_COVER_DEVICE_CLASSES[self._feature.device_class]
@property
def current_cover_position(self) -> int | None:
"""Return the current cover position."""
@@ -118,7 +145,8 @@ class BleBoxCoverEntity(BleBoxEntity[blebox_uniapi.cover.Cover], CoverEntity):
async def async_open_cover_tilt(self, **kwargs: Any) -> None:
"""Fully open the cover tilt."""
await self._feature.async_set_tilt_position(0)
position = 50 if self._feature.is_tilt_180 else 0
await self._feature.async_set_tilt_position(position)
async def async_close_cover_tilt(self, **kwargs: Any) -> None:
"""Fully close the cover tilt."""
@@ -7,6 +7,6 @@
"integration_type": "device",
"iot_class": "local_polling",
"loggers": ["blebox_uniapi"],
"requirements": ["blebox-uniapi==2.5.3"],
"requirements": ["blebox-uniapi==2.5.4"],
"zeroconf": ["_bbxsrv._tcp.local."]
}
+141
View File
@@ -0,0 +1,141 @@
"""BleBox update entities implementation."""
from datetime import timedelta
from typing import Any, Final
from blebox_uniapi.error import ConnectionError as BleBoxConnectionError, Error
import blebox_uniapi.update
from homeassistant.components.update import (
UpdateDeviceClass,
UpdateEntity,
UpdateEntityFeature,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.event import async_call_later
from . import BleBoxConfigEntry
from .entity import BleBoxEntity
SCAN_INTERVAL = timedelta(hours=1)
_POLL_INTERVAL_SECONDS: Final = 10
_MAX_POLL_ATTEMPTS: Final = 30
async def async_setup_entry(
hass: HomeAssistant,
config_entry: BleBoxConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up a BleBox update entry."""
entities = [
BleBoxUpdateEntity(feature)
for feature in config_entry.runtime_data.features.get("updates", [])
]
async_add_entities(entities, True)
class BleBoxUpdateEntity(BleBoxEntity[blebox_uniapi.update.Update], UpdateEntity):
"""Representation of BleBox updates."""
_attr_device_class = UpdateDeviceClass.FIRMWARE
_attr_supported_features = (
UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS
)
def __init__(self, feature: blebox_uniapi.update.Update) -> None:
"""Initialize the update entity."""
super().__init__(feature)
self._in_progress_old_version: str | None = None
self._poll_cancel: CALLBACK_TYPE | None = None
self._poll_attempts: int = 0
@property
def in_progress(self) -> bool:
"""Return True while the device hasn't yet rebooted to the new firmware."""
return (
self._in_progress_old_version is not None
and self._in_progress_old_version == self._feature.installed_version
)
def _sync_sw_version(self) -> None:
"""Sync installed firmware version to the device registry."""
if self.device_entry:
dr.async_get(self.hass).async_update_device(
self.device_entry.id,
sw_version=self._feature.installed_version,
)
async def async_update(self) -> None:
"""Update state and refresh sw_version in device registry."""
try:
await self._feature.async_update()
except Error as ex:
raise HomeAssistantError(ex) from ex
self._sync_sw_version()
@property
def installed_version(self) -> str | None:
"""Version installed and in use."""
return self._feature.installed_version
@property
def latest_version(self) -> str | None:
"""Latest version available for install."""
return self._feature.latest_version
def _cancel_poll(self) -> None:
if self._poll_cancel is not None:
self._poll_cancel()
self._poll_cancel = None
def _reset_progress(self) -> None:
self._in_progress_old_version = None
self._poll_attempts = 0
self.async_write_ha_state()
async def async_install(
self, version: str | None, backup: bool, **kwargs: Any
) -> None:
"""Install an update."""
self._cancel_poll()
self._in_progress_old_version = self._feature.installed_version
self._poll_attempts = 0
self.async_write_ha_state()
try:
await self._feature.async_install()
except Error as ex:
self._reset_progress()
raise HomeAssistantError(ex) from ex
self._poll_cancel = async_call_later(
self.hass, _POLL_INTERVAL_SECONDS, self._poll_until_updated
)
async def async_will_remove_from_hass(self) -> None:
"""Cancel any pending poll timer when the entity is removed."""
self._cancel_poll()
async def _poll_until_updated(self, _now: Any) -> None:
"""Poll device until the installed version changes after OTA reboot."""
self._poll_cancel = None
self._poll_attempts += 1
try:
await self._feature.async_update()
except BleBoxConnectionError:
pass
except Error:
self._reset_progress()
return
else:
self._sync_sw_version()
if self.in_progress and self._poll_attempts < _MAX_POLL_ATTEMPTS:
self._poll_cancel = async_call_later(
self.hass, _POLL_INTERVAL_SECONDS, self._poll_until_updated
)
else:
self._reset_progress()
@@ -1,7 +1,7 @@
{
"domain": "blue_current",
"name": "Blue Current",
"codeowners": ["@gleeuwen", "@NickKoepr", "@jtodorova23"],
"codeowners": ["@gleeuwen", "@jtodorova23"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/blue_current",
"integration_type": "hub",
@@ -124,7 +124,9 @@ async def async_setup_entry(
BlueMaestroBluetoothSensorEntity, async_add_entities
)
)
entry.async_on_unload(coordinator.async_register_processor(processor))
entry.async_on_unload(
coordinator.async_register_processor(processor, SensorEntityDescription)
)
class BlueMaestroBluetoothSensorEntity(
+10 -5
View File
@@ -11,6 +11,7 @@ from bluetooth_adapters import (
ADAPTER_CONNECTION_SLOTS,
ADAPTER_HW_VERSION,
ADAPTER_MANUFACTURER,
ADAPTER_PASSIVE_SCAN,
ADAPTER_SW_VERSION,
DEFAULT_ADDRESS,
DEFAULT_CONNECTION_SLOTS,
@@ -69,6 +70,7 @@ from .api import (
async_register_callback,
async_register_scanner,
async_remove_scanner,
async_request_active_scan,
async_scanner_by_source,
async_scanner_count,
async_scanner_devices_by_address,
@@ -79,7 +81,6 @@ from .const import (
BLUETOOTH_DISCOVERY_COOLDOWN_SECONDS,
CONF_ADAPTER,
CONF_DETAILS,
CONF_PASSIVE,
CONF_SOURCE_CONFIG_ENTRY_ID,
CONF_SOURCE_DEVICE_ID,
CONF_SOURCE_DOMAIN,
@@ -93,7 +94,7 @@ from .manager import HomeAssistantBluetoothManager
from .match import BluetoothCallbackMatcher, IntegrationMatcher
from .models import BluetoothCallback, BluetoothChange
from .storage import BluetoothStorage
from .util import adapter_title
from .util import adapter_title, resolve_scanning_mode
if TYPE_CHECKING:
from homeassistant.helpers.typing import ConfigType
@@ -128,6 +129,7 @@ __all__ = [
"async_register_callback",
"async_register_scanner",
"async_remove_scanner",
"async_request_active_scan",
"async_scanner_by_source",
"async_scanner_count",
"async_scanner_devices_by_address",
@@ -387,12 +389,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
raise ConfigEntryNotReady(
f"Bluetooth adapter {adapter} with address {address} not found"
)
passive = entry.options.get(CONF_PASSIVE)
adapters = await manager.async_get_bluetooth_adapters()
mode = BluetoothScanningMode.PASSIVE if passive else BluetoothScanningMode.ACTIVE
details = adapters[adapter]
mode = resolve_scanning_mode(entry.options)
# AUTO needs passive scanning support to flip on demand; without it
# the scanner would start passive on hardware that can't do passive.
if mode is BluetoothScanningMode.AUTO and not details.get(ADAPTER_PASSIVE_SCAN):
mode = BluetoothScanningMode.ACTIVE
scanner = HaScanner(mode, adapter, address)
scanner.async_setup()
details = adapters[adapter]
if entry.title == address:
hass.config_entries.async_update_entry(
entry, title=adapter_title(adapter, details)
@@ -68,9 +68,20 @@ class ActiveBluetoothProcessorCoordinator[_DataT](
| None = None,
poll_debouncer: Debouncer[Coroutine[Any, Any, None]] | None = None,
connectable: bool = True,
scan_interval: float | None = None,
scan_duration: float | None = None,
) -> None:
"""Initialize the processor."""
super().__init__(hass, logger, address, mode, update_method, connectable)
super().__init__(
hass,
logger,
address,
mode,
update_method,
connectable,
scan_interval,
scan_duration,
)
self._needs_poll_method = needs_poll_method
self._poll_method = poll_method
+31 -6
View File
@@ -130,17 +130,26 @@ def async_register_callback(
callback: BluetoothCallback,
match_dict: BluetoothCallbackMatcher | None,
mode: BluetoothScanningMode,
*,
scan_interval: float | None = None,
scan_duration: float | None = None,
) -> Callable[[], None]:
"""Register to receive a callback on bluetooth change.
mode is currently not used as we only support active scanning.
Passive scanning will be available in the future. The flag
is required to be present to avoid a future breaking change
when we support passive scanning.
When ``mode`` is not PASSIVE and ``match_dict["address"]`` is set,
the address is registered with habluetooth's active-scan scheduler
so AUTO-mode scanners flip ACTIVE on demand for that device.
``scan_interval`` / ``scan_duration`` default to habluetooth's
DEFAULT_ACTIVE_SCAN_* (5 minutes / 10 seconds) when not provided;
integrations that need a different cadence can pass explicit
values. Without an address in the matcher the active-scan request
is skipped; the callback itself still fires normally.
Returns a callback that can be used to cancel the registration.
"""
return _get_manager(hass).async_register_callback(callback, match_dict)
return _get_manager(hass).async_register_callback(
callback, match_dict, mode, scan_interval, scan_duration
)
async def async_process_advertisements(
@@ -161,7 +170,7 @@ async def async_process_advertisements(
done.set_result(service_info)
unload = _get_manager(hass).async_register_callback(
_async_discovered_device, match_dict
_async_discovered_device, match_dict, mode, scan_duration=timeout
)
try:
@@ -275,3 +284,19 @@ def async_set_fallback_availability_interval(
) -> None:
"""Override the fallback availability timeout for a MAC address."""
_get_manager(hass).async_set_fallback_availability_interval(address, interval)
async def async_request_active_scan(
hass: HomeAssistant, duration: float | None = None
) -> None:
"""Run an on-demand active sweep across every AUTO scanner.
Intended for config-flow discovery and other one-shot probes that
need fresh advertisements without waiting for the periodic
rediscovery cadence. Awaits ``duration`` seconds so the caller can
then read newly discovered advertisements. Defaults to habluetooth's
on-demand sweep duration when ``duration`` is not provided; the
scheduler clamps the value to its allowed range. Concurrent callers
dedupe to a single bus-wide window.
"""
await _get_manager(hass).async_request_active_scan(duration)
@@ -12,7 +12,7 @@ from bluetooth_adapters import (
adapter_model,
get_adapters,
)
from habluetooth import get_manager
from habluetooth import BluetoothScanningMode, get_manager
import voluptuous as vol
from homeassistant.components import onboarding
@@ -22,33 +22,64 @@ from homeassistant.config_entries import (
ConfigFlowResult,
OptionsFlow,
)
from homeassistant.const import CONF_SOURCE
from homeassistant.core import callback
from homeassistant.helpers.schema_config_entry_flow import (
SchemaCommonFlowHandler,
SchemaFlowFormStep,
SchemaOptionsFlowHandler,
)
from homeassistant.helpers.selector import (
SelectSelector,
SelectSelectorConfig,
SelectSelectorMode,
)
from homeassistant.helpers.typing import DiscoveryInfoType
from .const import (
CONF_ADAPTER,
CONF_DETAILS,
CONF_MODE,
CONF_PASSIVE,
CONF_SOURCE,
CONF_SOURCE_CONFIG_ENTRY_ID,
CONF_SOURCE_DEVICE_ID,
CONF_SOURCE_DOMAIN,
CONF_SOURCE_MODEL,
DOMAIN,
)
from .util import adapter_title
from .util import adapter_title, resolve_scanning_mode
OPTIONS_SCHEMA = vol.Schema(
{
vol.Required(CONF_PASSIVE, default=False): bool,
}
_MODE_SELECTOR = SelectSelector(
SelectSelectorConfig(
options=[
BluetoothScanningMode.AUTO.value,
BluetoothScanningMode.ACTIVE.value,
BluetoothScanningMode.PASSIVE.value,
],
translation_key="mode",
mode=SelectSelectorMode.DROPDOWN,
)
)
async def _options_schema(handler: SchemaCommonFlowHandler) -> vol.Schema:
"""Build the options schema with the saved mode as the default."""
current = resolve_scanning_mode(handler.options).value
return vol.Schema({vol.Required(CONF_MODE, default=current): _MODE_SELECTOR})
async def _validate_options(
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
) -> dict[str, Any]:
"""Mirror CONF_MODE into the legacy CONF_PASSIVE for downgrade safety."""
user_input[CONF_PASSIVE] = (
user_input[CONF_MODE] == BluetoothScanningMode.PASSIVE.value
)
return user_input
OPTIONS_FLOW = {
"init": SchemaFlowFormStep(OPTIONS_SCHEMA),
"init": SchemaFlowFormStep(_options_schema, validate_user_input=_validate_options),
}
+6 -2
View File
@@ -7,17 +7,21 @@ from habluetooth import ( # noqa: F401
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
SCANNER_WATCHDOG_INTERVAL,
SCANNER_WATCHDOG_TIMEOUT,
BluetoothScanningMode,
)
from homeassistant.const import CONF_MODE # noqa: F401
DOMAIN = "bluetooth"
CONF_ADAPTER = "adapter"
CONF_DETAILS = "details"
# CONF_PASSIVE is the legacy boolean option; we keep writing it alongside
# CONF_MODE so a downgrade to a pre-AUTO release reads a sensible value.
CONF_PASSIVE = "passive"
DEFAULT_MODE = BluetoothScanningMode.AUTO.value
# pylint: disable-next=home-assistant-duplicate-const
CONF_SOURCE: Final = "source"
CONF_SOURCE_DOMAIN: Final = "source_domain"
CONF_SOURCE_MODEL: Final = "source_model"
CONF_SOURCE_CONFIG_ENTRY_ID: Final = "source_config_entry_id"
+25 -3
View File
@@ -21,7 +21,11 @@ from habluetooth import (
)
from homeassistant import config_entries
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, EVENT_LOGGING_CHANGED
from homeassistant.const import (
CONF_SOURCE,
EVENT_HOMEASSISTANT_STOP,
EVENT_LOGGING_CHANGED,
)
from homeassistant.core import (
CALLBACK_TYPE,
Event,
@@ -33,7 +37,6 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.util.package import is_docker_env
from .const import (
CONF_SOURCE,
CONF_SOURCE_CONFIG_ENTRY_ID,
CONF_SOURCE_DEVICE_ID,
CONF_SOURCE_DOMAIN,
@@ -202,6 +205,9 @@ class HomeAssistantBluetoothManager(BluetoothManager):
self,
callback: BluetoothCallback,
matcher: BluetoothCallbackMatcher | None,
mode: BluetoothScanningMode = BluetoothScanningMode.ACTIVE,
scan_interval: float | None = None,
scan_duration: float | None = None,
) -> Callable[[], None]:
"""Register a callback."""
callback_matcher = BluetoothCallbackMatcherWithCallback(callback=callback)
@@ -216,15 +222,31 @@ class HomeAssistantBluetoothManager(BluetoothManager):
connectable = callback_matcher[CONNECTABLE]
self._callback_index.add_callback_matcher(callback_matcher)
# If the matcher targets a specific address and the caller
# didn't explicitly ask for PASSIVE, wire it into habluetooth's
# active-scan scheduler so AUTO-mode scanners flip ACTIVE on
# demand for this device. ``scan_interval``/``scan_duration``
# default to habluetooth's DEFAULT_ACTIVE_SCAN_* when None.
cancel_active_scan: Callable[[], None] | None = None
if (
mode is not BluetoothScanningMode.PASSIVE
and (address := callback_matcher.get(ADDRESS)) is not None
):
cancel_active_scan = self.async_register_active_scan(
address, scan_interval, scan_duration
)
def _async_remove_callback() -> None:
self._callback_index.remove_callback_matcher(callback_matcher)
if cancel_active_scan is not None:
cancel_active_scan()
# If we have history for the subscriber, we can trigger the callback
# immediately with the last packet so the subscriber can see the
# device.
history = self._connectable_history if connectable else self._all_history
service_infos: Iterable[BluetoothServiceInfoBleak] = []
if address := callback_matcher.get(ADDRESS):
if (address := callback_matcher.get(ADDRESS)) is not None:
if service_info := history.get(address):
service_infos = [service_info]
else:
@@ -15,12 +15,12 @@
],
"quality_scale": "internal",
"requirements": [
"bleak==2.1.1",
"bleak-retry-connector==4.6.0",
"bluetooth-adapters==2.1.0",
"bluetooth-auto-recovery==1.5.3",
"bluetooth-data-tools==1.29.11",
"dbus-fast==5.0.0",
"habluetooth==6.1.0"
"bleak==3.0.2",
"bleak-retry-connector==4.6.1",
"bluetooth-adapters==2.3.0",
"bluetooth-auto-recovery==1.6.4",
"bluetooth-data-tools==1.29.18",
"dbus-fast==5.0.14",
"habluetooth==6.7.4"
]
}
@@ -298,9 +298,13 @@ class PassiveBluetoothProcessorCoordinator[_DataT](BasePassiveBluetoothCoordinat
mode: BluetoothScanningMode,
update_method: Callable[[BluetoothServiceInfoBleak], _DataT],
connectable: bool = False,
scan_interval: float | None = None,
scan_duration: float | None = None,
) -> None:
"""Initialize the coordinator."""
super().__init__(hass, logger, address, mode, connectable)
super().__init__(
hass, logger, address, mode, connectable, scan_interval, scan_duration
)
self._processors: list[PassiveBluetoothDataProcessor[Any, _DataT]] = []
self._update_method = update_method
self.last_update_success = True
@@ -48,9 +48,21 @@
"step": {
"init": {
"data": {
"passive": "Passive scanning"
"mode": "Scanning mode"
},
"data_description": {
"mode": "Auto is recommended for most setups. It saves battery on your Bluetooth devices while still catching new devices and updates quickly."
}
}
}
},
"selector": {
"mode": {
"options": {
"active": "Active (uses more device battery, fastest updates)",
"auto": "Auto (recommended, saves device battery)",
"passive": "Passive (lowest device battery use, some details may be missing)"
}
}
}
}
@@ -30,6 +30,8 @@ class BasePassiveBluetoothCoordinator(ABC):
address: str,
mode: BluetoothScanningMode,
connectable: bool,
scan_interval: float | None = None,
scan_duration: float | None = None,
) -> None:
"""Initialize the coordinator."""
self.hass = hass
@@ -38,6 +40,8 @@ class BasePassiveBluetoothCoordinator(ABC):
self.connectable = connectable
self._on_stop: list[CALLBACK_TYPE] = []
self.mode = mode
self._scan_interval = scan_interval
self._scan_duration = scan_duration
self._last_unavailable_time = 0.0
self._last_name = address
# Subclasses are responsible for setting _available to True
@@ -92,6 +96,8 @@ class BasePassiveBluetoothCoordinator(ABC):
address=self.address, connectable=self.connectable
),
self.mode,
scan_interval=self._scan_interval,
scan_duration=self._scan_duration,
)
)
self._on_stop.append(
+23 -1
View File
@@ -1,5 +1,9 @@
"""The bluetooth integration utilities."""
from collections.abc import Mapping
import logging
from typing import Any
from bluetooth_adapters import (
ADAPTER_ADDRESS,
ADAPTER_MANUFACTURER,
@@ -9,14 +13,32 @@ from bluetooth_adapters import (
adapter_unique_name,
)
from bluetooth_data_tools import monotonic_time_coarse
from habluetooth import get_manager
from habluetooth import BluetoothScanningMode, get_manager
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from .const import CONF_MODE, CONF_PASSIVE, DEFAULT_MODE
from .models import BluetoothServiceInfoBleak
from .storage import BluetoothStorage
_LOGGER = logging.getLogger(__name__)
def resolve_scanning_mode(options: Mapping[str, Any]) -> BluetoothScanningMode:
"""Resolve CONF_MODE, falling back to legacy CONF_PASSIVE or DEFAULT_MODE."""
if (mode_value := options.get(CONF_MODE)) is not None:
try:
return BluetoothScanningMode(mode_value)
except TypeError, ValueError:
_LOGGER.warning("Unknown bluetooth scanning mode %r", mode_value)
return BluetoothScanningMode(DEFAULT_MODE)
if (legacy_passive := options.get(CONF_PASSIVE)) is True:
return BluetoothScanningMode.PASSIVE
if legacy_passive is False:
return BluetoothScanningMode.ACTIVE
return BluetoothScanningMode(DEFAULT_MODE)
class InvalidConfigEntryID(HomeAssistantError):
"""Invalid config entry id."""
@@ -273,7 +273,7 @@ async def ws_subscribe_scanner_details(
def _async_registration_changed(registration: HaScannerRegistration) -> None:
added_event = HaScannerRegistrationEvent.ADDED
event_type = "add" if registration.event == added_event else "remove"
event_type = "add" if registration.event is added_event else "remove"
_async_event_message({event_type: [registration.scanner.details]})
manager = _get_manager(hass)
@@ -9,7 +9,14 @@ from pybravia import BraviaAuthError, BraviaClient, BraviaError, BraviaNotSuppor
import voluptuous as vol
from homeassistant.config_entries import SOURCE_REAUTH, ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_CLIENT_ID, CONF_HOST, CONF_MAC, CONF_NAME, CONF_PIN
from homeassistant.const import (
ATTR_MODEL,
CONF_CLIENT_ID,
CONF_HOST,
CONF_MAC,
CONF_NAME,
CONF_PIN,
)
from homeassistant.helpers import instance_id
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.service_info.ssdp import (
@@ -23,7 +30,6 @@ from homeassistant.util.network import is_host_valid
from .const import (
ATTR_CID,
ATTR_MAC,
ATTR_MODEL,
CONF_NICKNAME,
CONF_USE_PSK,
CONF_USE_SSL,
@@ -6,8 +6,6 @@ from typing import Final
ATTR_CID: Final = "cid"
ATTR_MAC: Final = "macAddr"
ATTR_MANUFACTURER: Final = "Sony"
# pylint: disable-next=home-assistant-duplicate-const
ATTR_MODEL: Final = "model"
CONF_NICKNAME: Final = "nickname"
CONF_USE_PSK: Final = "use_psk"
+4 -1
View File
@@ -158,7 +158,10 @@ def process_service_info(
)
# If payload is encrypted and the bindkey is not verified then we need to reauth
if data.encryption_scheme != EncryptionScheme.NONE and not data.bindkey_verified:
if (
data.encryption_scheme is not EncryptionScheme.NONE
and not data.bindkey_verified
):
entry.async_start_reauth(hass, data={"device": data})
return update
@@ -59,7 +59,7 @@ class BTHomeConfigFlow(ConfigFlow, domain=DOMAIN):
self._discovery_info = discovery_info
self._discovered_device = device
if device.encryption_scheme == EncryptionScheme.BTHOME_BINDKEY:
if device.encryption_scheme is EncryptionScheme.BTHOME_BINDKEY:
return await self.async_step_get_encryption_key()
return await self.async_step_bluetooth_confirm()
@@ -125,7 +125,7 @@ class BTHomeConfigFlow(ConfigFlow, domain=DOMAIN):
self._discovery_info = discovery.discovery_info
self._discovered_device = discovery.device
if discovery.device.encryption_scheme == EncryptionScheme.BTHOME_BINDKEY:
if discovery.device.encryption_scheme is EncryptionScheme.BTHOME_BINDKEY:
return await self.async_step_get_encryption_key()
return self._async_get_or_create_entry()
@@ -164,7 +164,7 @@ class BTHomeConfigFlow(ConfigFlow, domain=DOMAIN):
self._discovery_info = device.last_service_info
if device.encryption_scheme == EncryptionScheme.BTHOME_BINDKEY:
if device.encryption_scheme is EncryptionScheme.BTHOME_BINDKEY:
return await self.async_step_get_encryption_key()
# Otherwise there wasn't actually encryption so abort
+1
View File
@@ -17,6 +17,7 @@ BTHOME_BLE_EVENT: Final = "bthome_ble_event"
EVENT_CLASS_BUTTON: Final = "button"
EVENT_CLASS_DIMMER: Final = "dimmer"
EVENT_CLASS_COMMAND: Final = "command"
CONF_EVENT_CLASS: Final = "event_class"
CONF_EVENT_PROPERTIES: Final = "event_properties"
@@ -28,6 +28,7 @@ from .const import (
DOMAIN,
EVENT_CLASS,
EVENT_CLASS_BUTTON,
EVENT_CLASS_COMMAND,
EVENT_CLASS_DIMMER,
EVENT_TYPE,
)
@@ -43,6 +44,7 @@ EVENT_TYPES_BY_EVENT_CLASS = {
"hold_press",
},
EVENT_CLASS_DIMMER: {"rotate_left", "rotate_right"},
EVENT_CLASS_COMMAND: {"off", "on", "toggle", "step_up", "step_down"},
}
TRIGGER_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
+6
View File
@@ -16,6 +16,7 @@ from . import format_discovered_event_class, format_event_dispatcher_name
from .const import (
DOMAIN,
EVENT_CLASS_BUTTON,
EVENT_CLASS_COMMAND,
EVENT_CLASS_DIMMER,
EVENT_PROPERTIES,
EVENT_TYPE,
@@ -43,6 +44,11 @@ DESCRIPTIONS_BY_EVENT_CLASS = {
translation_key="dimmer",
event_types=["rotate_left", "rotate_right"],
),
EVENT_CLASS_COMMAND: EventEntityDescription(
key=EVENT_CLASS_COMMAND,
translation_key="command",
event_types=["off", "on", "toggle", "step_up", "step_down"],
),
}
@@ -20,5 +20,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/bthome",
"iot_class": "local_push",
"requirements": ["bthome-ble==3.17.0"]
"requirements": ["bthome-ble==3.23.2"]
}
+12
View File
@@ -192,6 +192,12 @@ SENSOR_DESCRIPTIONS = {
native_unit_of_measurement=LIGHT_LUX,
state_class=SensorStateClass.MEASUREMENT,
),
# Light level (-)
(BTHomeExtendedSensorDeviceClass.LIGHT_LEVEL, None): SensorEntityDescription(
key=str(BTHomeExtendedSensorDeviceClass.LIGHT_LEVEL),
state_class=SensorStateClass.MEASUREMENT,
translation_key="light_level",
),
# Mass sensor (kg)
(BTHomeSensorDeviceClass.MASS, Units.MASS_KILOGRAMS): SensorEntityDescription(
key=f"{BTHomeSensorDeviceClass.MASS}_{Units.MASS_KILOGRAMS}",
@@ -287,6 +293,12 @@ SENSOR_DESCRIPTIONS = {
state_class=SensorStateClass.MEASUREMENT,
translation_key="rotational_speed",
),
# Settings revision (-)
(BTHomeExtendedSensorDeviceClass.SETTINGS_REVISION, None): SensorEntityDescription(
key=str(BTHomeExtendedSensorDeviceClass.SETTINGS_REVISION),
entity_category=EntityCategory.DIAGNOSTIC,
translation_key="settings_revision",
),
# Signal Strength (RSSI) (dB)
(
BTHomeSensorDeviceClass.SIGNAL_STRENGTH,
@@ -36,13 +36,19 @@
"long_double_press": "Long Double Press",
"long_press": "Long Press",
"long_triple_press": "Long Triple Press",
"off": "Off",
"on": "On",
"press": "Press",
"rotate_left": "Rotate Left",
"rotate_right": "Rotate Right",
"step_down": "Step Down",
"step_up": "Step Up",
"toggle": "Toggle",
"triple_press": "Triple Press"
},
"trigger_type": {
"button": "Button \"{subtype}\"",
"command": "Command \"{subtype}\"",
"dimmer": "Dimmer \"{subtype}\""
}
},
@@ -68,6 +74,19 @@
}
}
},
"command": {
"state_attributes": {
"event_type": {
"state": {
"off": "Off",
"on": "On",
"step_down": "Step down",
"step_up": "Step up",
"toggle": "Toggle"
}
}
}
},
"dimmer": {
"state_attributes": {
"event_type": {
@@ -98,6 +117,9 @@
"gyroscope": {
"name": "Gyroscope"
},
"light_level": {
"name": "Light level"
},
"packet_id": {
"name": "Packet ID"
},
@@ -110,6 +132,9 @@
"rotational_speed": {
"name": "Rotational speed"
},
"settings_revision": {
"name": "Settings revision"
},
"text": {
"name": "Text"
},
@@ -1,12 +1,11 @@
"""Config flow for the Cert Expiry platform."""
from collections.abc import Mapping
import logging
from typing import Any
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigFlow, ConfigFlowResult
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PORT
from .const import DEFAULT_PORT, DOMAIN
@@ -19,8 +18,6 @@ from .errors import (
)
from .helper import get_cert_expiry_timestamp
_LOGGER = logging.getLogger(__name__)
class CertexpiryConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow."""
@@ -75,9 +72,6 @@ class CertexpiryConfigFlow(ConfigFlow, domain=DOMAIN):
title=title,
data={CONF_HOST: host, CONF_PORT: port},
)
if self.source == SOURCE_IMPORT:
_LOGGER.error("Config import failed for %s", user_input[CONF_HOST])
return self.async_abort(reason="import_failed")
else:
user_input = {}
user_input[CONF_HOST] = ""
@@ -2,7 +2,6 @@
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
"import_failed": "Import from config failed",
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]"
},
"error": {
@@ -14,6 +14,7 @@ from homeassistant.components.notify import (
)
from homeassistant.const import (
CONF_API_KEY,
CONF_LANGUAGE,
CONF_NAME,
CONF_RECIPIENT,
CONF_USERNAME,
@@ -29,8 +30,6 @@ BASE_API_URL = "https://rest.clicksend.com/v3"
HEADERS = {"Content-Type": CONTENT_TYPE_JSON}
# pylint: disable-next=home-assistant-duplicate-const
CONF_LANGUAGE = "language"
CONF_VOICE = "voice"
MALE_VOICE = "male"
@@ -32,7 +32,6 @@ from homeassistant.components.homeassistant.exposed_entities import (
async_should_expose,
)
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES
from homeassistant.core import Event, HomeAssistant, callback, split_entity_id
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er, start
@@ -275,9 +274,6 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
def _should_expose_legacy(self, entity_id: str) -> bool:
"""If an entity should be exposed."""
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
return False
entity_configs = self._prefs.alexa_entity_configs
entity_config = entity_configs.get(entity_id, {})
entity_expose: bool | None = entity_config.get(PREF_SHOULD_EXPOSE)
@@ -308,8 +304,6 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
"""If an entity should be exposed."""
entity_filter: EntityFilter = self._config[CONF_FILTER]
if not entity_filter.empty_filter:
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
return False
return entity_filter(entity_id)
return async_should_expose(self.hass, CLOUD_ALEXA, entity_id)
@@ -22,7 +22,6 @@ from homeassistant.components.homeassistant.exposed_entities import (
async_should_expose,
)
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES
from homeassistant.core import (
CoreState,
Event,
@@ -276,15 +275,16 @@ class CloudGoogleConfig(AbstractConfig):
)
)
def should_expose(self, state: State) -> bool:
"""If a state object should be exposed."""
return self._should_expose_entity_id(state.entity_id)
def should_expose(self, entity_id: str) -> bool:
"""If an entity should be exposed."""
entity_filter: EntityFilter = self._config[CONF_FILTER]
if not entity_filter.empty_filter:
return entity_filter(entity_id)
return async_should_expose(self.hass, CLOUD_GOOGLE, entity_id)
def _should_expose_legacy(self, entity_id: str) -> bool:
"""If an entity ID should be exposed."""
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
return False
entity_configs = self._prefs.google_entity_configs
entity_config = entity_configs.get(entity_id, {})
entity_expose: bool | None = entity_config.get(PREF_SHOULD_EXPOSE)
@@ -312,16 +312,6 @@ class CloudGoogleConfig(AbstractConfig):
and _supported_legacy(self.hass, entity_id)
)
def _should_expose_entity_id(self, entity_id: str) -> bool:
"""If an entity should be exposed."""
entity_filter: EntityFilter = self._config[CONF_FILTER]
if not entity_filter.empty_filter:
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES:
return False
return entity_filter(entity_id)
return async_should_expose(self.hass, CLOUD_GOOGLE, entity_id)
@property
def agent_user_id(self) -> str:
"""Return Agent User Id to use for query responses."""
@@ -473,7 +463,7 @@ class CloudGoogleConfig(AbstractConfig):
entity_id = event.data["entity_id"]
if not self._should_expose_entity_id(entity_id):
if not self.should_expose(entity_id):
return
self.async_schedule_google_sync_all()
@@ -496,8 +486,7 @@ class CloudGoogleConfig(AbstractConfig):
# Check if any exposed entity uses the device area
if not any(
entity_entry.area_id is None
and self._should_expose_entity_id(entity_entry.entity_id)
entity_entry.area_id is None and self.should_expose(entity_entry.entity_id)
for entity_entry in er.async_entries_for_device(
er.async_get(self.hass), event.data["device_id"]
)
+2 -5
View File
@@ -29,7 +29,6 @@ from homeassistant.components.homeassistant import exposed_entities
from homeassistant.components.http import KEY_HASS, HomeAssistantView, require_admin
from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.components.system_health import get_info as get_system_health_info
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
@@ -973,7 +972,7 @@ async def google_assistant_get(
return
entity = google_helpers.GoogleEntity(hass, gconf, state)
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES or not entity.is_supported():
if not entity.is_supported():
connection.send_error(
msg["id"],
websocket_api.ERR_NOT_SUPPORTED,
@@ -1075,9 +1074,7 @@ async def alexa_get(
"""Get data for a single alexa entity."""
entity_id: str = msg["entity_id"]
if entity_id in CLOUD_NEVER_EXPOSED_ENTITIES or not entity_supported_by_alexa(
hass, entity_id
):
if not entity_supported_by_alexa(hass, entity_id):
connection.send_error(
msg["id"],
websocket_api.ERR_NOT_SUPPORTED,
@@ -17,10 +17,11 @@ from homeassistant.components.backup import (
OnProgressCallback,
suggested_filename,
)
from homeassistant.const import CONF_PREFIX
from homeassistant.core import HomeAssistant, callback
from . import R2ConfigEntry
from .const import CONF_BUCKET, CONF_PREFIX, DATA_BACKUP_AGENT_LISTENERS, DOMAIN
from .const import CONF_BUCKET, DATA_BACKUP_AGENT_LISTENERS, DOMAIN
_LOGGER = logging.getLogger(__name__)
CACHE_TTL = 300
@@ -13,6 +13,7 @@ from botocore.exceptions import (
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_PREFIX
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.selector import (
TextSelector,
@@ -25,7 +26,6 @@ from .const import (
CONF_ACCESS_KEY_ID,
CONF_BUCKET,
CONF_ENDPOINT_URL,
CONF_PREFIX,
CONF_SECRET_ACCESS_KEY,
DEFAULT_ENDPOINT_URL,
DESCRIPTION_R2_AUTH_DOCS_URL,
@@ -11,8 +11,6 @@ CONF_ACCESS_KEY_ID = "access_key_id"
CONF_SECRET_ACCESS_KEY = "secret_access_key"
CONF_ENDPOINT_URL = "endpoint_url"
CONF_BUCKET = "bucket"
# pylint: disable-next=home-assistant-duplicate-const
CONF_PREFIX = "prefix"
# R2 is S3-compatible. Endpoint should be like:
# https://<accountid>.r2.cloudflarestorage.com
@@ -6,5 +6,4 @@ ATTR_URL = "color_extract_url"
DOMAIN = "color_extractor"
DEFAULT_NAME = "Color extractor"
# pylint: disable-next=home-assistant-duplicate-const
SERVICE_TURN_ON = "turn_on"
SERVICE_GET_COLOR = "get_color"
@@ -1,5 +1,8 @@
{
"services": {
"get_color": {
"service": "mdi:select-color"
},
"turn_on": {
"service": "mdi:lightbulb-on"
}
@@ -3,6 +3,7 @@
import asyncio
import io
import logging
from typing import Any
import aiohttp
from colorthief import ColorThief
@@ -14,16 +15,17 @@ from homeassistant.components.light import (
DOMAIN as LIGHT_DOMAIN,
LIGHT_TURN_ON_SCHEMA,
)
from homeassistant.const import SERVICE_TURN_ON as LIGHT_SERVICE_TURN_ON
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.const import SERVICE_TURN_ON
from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import aiohttp_client, config_validation as cv
from .const import ATTR_PATH, ATTR_URL, DOMAIN, SERVICE_TURN_ON
from .const import ATTR_PATH, ATTR_URL, DOMAIN, SERVICE_GET_COLOR
_LOGGER = logging.getLogger(__name__)
# Extend the existing light.turn_on service schema
SERVICE_SCHEMA = vol.All(
TURN_ON_SERVICE_SCHEMA = vol.All(
cv.has_at_least_one_key(ATTR_URL, ATTR_PATH),
cv.make_entity_service_schema(
{
@@ -34,6 +36,14 @@ SERVICE_SCHEMA = vol.All(
),
)
GET_COLOR_SERVICE_SCHEMA = vol.All(
cv.has_at_least_one_key(ATTR_URL, ATTR_PATH),
{
vol.Exclusive(ATTR_PATH, "color_extractor"): cv.isfile,
vol.Exclusive(ATTR_URL, "color_extractor"): cv.url,
},
)
def _get_file(file_path: str) -> str:
"""Get a PIL acceptable input file reference.
@@ -141,10 +151,54 @@ async def async_handle_service(service_call: ServiceCall) -> None:
service_data[ATTR_RGB_COLOR] = color
await service_call.hass.services.async_call(
LIGHT_DOMAIN, LIGHT_SERVICE_TURN_ON, service_data, blocking=True
LIGHT_DOMAIN, SERVICE_TURN_ON, service_data, blocking=True
)
async def async_handle_get_color(
service_call: ServiceCall,
) -> dict[str, Any]:
"""Handle get_color service call."""
service_data = dict(service_call.data)
try:
if ATTR_URL in service_data:
image_type = "URL"
image_reference = service_data.pop(ATTR_URL)
color = await _async_extract_color_from_url(
service_call.hass, image_reference
)
elif ATTR_PATH in service_data:
image_type = "file path"
image_reference = service_data.pop(ATTR_PATH)
color = await service_call.hass.async_add_executor_job(
_extract_color_from_path, service_call.hass, image_reference
)
except UnidentifiedImageError as ex:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_image",
translation_placeholders={
"image_type": image_type,
"image_reference": image_reference,
},
) from ex
if color is None:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_image",
translation_placeholders={
"image_type": image_type,
"image_reference": image_reference,
},
)
return {"color": color}
@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Register the services."""
@@ -153,5 +207,13 @@ def async_setup_services(hass: HomeAssistant) -> None:
DOMAIN,
SERVICE_TURN_ON,
async_handle_service,
schema=SERVICE_SCHEMA,
schema=TURN_ON_SERVICE_SCHEMA,
)
hass.services.async_register(
DOMAIN,
SERVICE_GET_COLOR,
async_handle_get_color,
schema=GET_COLOR_SERVICE_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
@@ -11,3 +11,13 @@ turn_on:
example: /opt/images/logo.png
selector:
text:
get_color:
fields:
color_extract_url:
example: https://www.example.com/images/logo.png
selector:
text:
color_extract_path:
example: /opt/images/logo.png
selector:
text:
@@ -6,7 +6,26 @@
}
}
},
"exceptions": {
"invalid_image": {
"message": "Bad image {image_reference} from {image_type} provided, are you sure it's an image?"
}
},
"services": {
"get_color": {
"description": "Gets the predominant RGB color found in the image provided by URL or file path.",
"fields": {
"color_extract_path": {
"description": "The full system path to the image we want to extract RGB values from. Must be allowed in allowlist_external_dirs.",
"name": "[%key:common::config_flow::data::path%]"
},
"color_extract_url": {
"description": "The URL of the image we want to extract RGB values from. Must be allowed in allowlist_external_urls.",
"name": "[%key:common::config_flow::data::url%]"
}
},
"name": "Get predominant color"
},
"turn_on": {
"description": "Sets the light RGB to the predominant color found in the image provided by URL or file path.",
"fields": {
@@ -110,7 +110,7 @@ class ComelitAlarmEntity(
@property
def available(self) -> bool:
"""Return True if alarm is available."""
if self._area.human_status == AlarmAreaState.UNKNOWN:
if self._area.human_status is AlarmAreaState.UNKNOWN:
return False
return super().available
@@ -124,7 +124,7 @@ class ComelitAlarmEntity(
self._area.human_status,
self._area.armed,
)
if self._area.human_status == AlarmAreaState.ARMED:
if self._area.human_status is AlarmAreaState.ARMED:
if self._area.armed == ALARM_AREA_ARMED_STATUS[AWAY]:
return AlarmControlPanelState.ARMED_AWAY
if self._area.armed == ALARM_AREA_ARMED_STATUS[NIGHT]:
@@ -43,7 +43,7 @@ BINARY_SENSOR_TYPES: Final[tuple[ComelitBinarySensorEntityDescription, ...]] = (
device_class=BinarySensorDeviceClass.PROBLEM,
is_on_fn=lambda obj: cast(ComelitVedoAreaObject, obj).anomaly,
available_fn=lambda obj: (
cast(ComelitVedoAreaObject, obj).human_status != AlarmAreaState.UNKNOWN
cast(ComelitVedoAreaObject, obj).human_status is not AlarmAreaState.UNKNOWN
),
),
ComelitBinarySensorEntityDescription(
@@ -67,7 +67,7 @@ BINARY_SENSOR_TYPES: Final[tuple[ComelitBinarySensorEntityDescription, ...]] = (
object_type=ALARM_ZONE,
device_class=BinarySensorDeviceClass.PROBLEM,
is_on_fn=lambda obj: (
cast(ComelitVedoZoneObject, obj).human_status == AlarmZoneState.FAULTY
cast(ComelitVedoZoneObject, obj).human_status is AlarmZoneState.FAULTY
),
available_fn=lambda obj: (
cast(ComelitVedoZoneObject, obj).human_status
@@ -65,11 +65,9 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str,
translation_placeholders={"error": repr(err)},
) from err
except aiocomelit_exceptions.CannotAuthenticate as err:
# pylint: disable-next=home-assistant-exception-placeholder-mismatch
raise InvalidAuth(
translation_domain=DOMAIN,
translation_key="cannot_authenticate",
translation_placeholders={"error": repr(err)},
) from err
finally:
await api.logout()
+2 -2
View File
@@ -166,12 +166,12 @@ class ComelitVedoSensorEntity(
@property
def available(self) -> bool:
"""Sensor availability."""
return self._zone_object.human_status != AlarmZoneState.UNAVAILABLE
return self._zone_object.human_status is not AlarmZoneState.UNAVAILABLE
@property
def native_value(self) -> StateType:
"""Sensor value."""
if (status := self._zone_object.human_status) == AlarmZoneState.UNKNOWN:
if (status := self._zone_object.human_status) is AlarmZoneState.UNKNOWN:
return None
return cast(str, status.value)
@@ -148,7 +148,7 @@ def _prepare_config_flow_result_json(
prepare_result_json: Callable[[data_entry_flow.FlowResult], dict[str, Any]],
) -> dict[str, Any]:
"""Convert result to JSON."""
if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY:
if result["type"] is not data_entry_flow.FlowResultType.CREATE_ENTRY:
return prepare_result_json(result)
data = {key: val for key, val in result.items() if key not in ("data", "context")}

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