Skip to content

Integration overhaul: modernization, robustness, event-loop hygiene, 66% coverage#28

Merged
foXaCe merged 18 commits into
mainfrom
refactor/integration-overhaul-2026-07-02
Jul 2, 2026
Merged

Integration overhaul: modernization, robustness, event-loop hygiene, 66% coverage#28
foXaCe merged 18 commits into
mainfrom
refactor/integration-overhaul-2026-07-02

Conversation

@foXaCe

@foXaCe foXaCe commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Résumé

Remise à niveau complète de l'intégration (15 commits, passes successives déployées et vérifiées une à une sur HA dev 2026.7) :

Modernisation HA

  • OptionsFlowWithReload (pattern déprécié supprimé), selectors modernes sur tout le config flow
  • Services d'entités enregistrés depuis async_setup (règle action-setup, API 2025.9) — vacuum_set_property/vacuum_call_action enfin déclarés et traduits
  • MQTT migré vers paho CallbackAPIVersion.VERSION2 (vérifié en direct)
  • quality_scale.yaml avec statuts honnêtes ; HA min : 2023.6 → 2025.8

Robustesse & bugs

  • 9 bugs réels corrigés : retry P-map bloqué à jamais, carte corrompue abortant la liste des cartes, fuite du cache d'URLs, set_credentials bytes≠str, 4 listeners fuités au reload, KeyError du renderer JSON, typo recorder, PNG ré-encodé sur l'event loop, optimize() V8 inline en coroutine (×4)
  • Backoff avec jitter partagé, logs DEBUG assainis (plus de payloads bruts)

Nettoyage

  • −1 540 lignes de code mort (dont le fallback Python de l'optimizer, jamais exécuté)
  • 7 clés de traduction manquantes ajoutées nativement dans 18 langues (parité 20/20)

Tests

  • 976 → 1 892 tests ; couche HA 100 % ; moteur : decoder 81 %, status 63 %, device 50 %, editor 50 %, map ops 45 %, JSON renderer 100 %
  • Couverture globale 38,5 % → 66 % (seuil CI 35 → 65)

Garanties

  • 258/258 unique_id identiques (diff vide), aucune migration nécessaire
  • Boot : setup domaine ≤ 0,01 s, importtime −6,6 % vs baseline
  • ruff/mypy/hassfest verts, 0 erreur HA dev sur tous les déploiements

Baselines et mesures archivées dans docs/overhaul/.

Summary by CodeRabbit

  • New Features

    • Added new vacuum services for calling device actions and setting device properties.
    • Added map display options for square rendering and smoother room edges.
    • Added a new issue notification when map segments change, with updated translations.
  • Bug Fixes

    • Improved map rendering and decoding reliability for missing data and partial failures.
    • Fixed several retry, caching, and unload behaviors to make the integration more stable.
    • Reduced noisy debug logging and improved performance by offloading heavier work.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@foXaCe, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 41 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5d7bb245-518e-418f-9978-3c96b20dfd94

📥 Commits

Reviewing files that changed from the base of the PR and between bc26d79 and 6221fa8.

📒 Files selected for processing (94)
  • .codespellrc
  • .github/workflows/ci.yml
  • ARCHITECTURE.md
  • CHANGELOG.md
  • CI_STATUS.md
  • README.md
  • custom_components/dreame_vacuum/__init__.py
  • custom_components/dreame_vacuum/binary_sensor.py
  • custom_components/dreame_vacuum/button.py
  • custom_components/dreame_vacuum/camera.py
  • custom_components/dreame_vacuum/config_flow.py
  • custom_components/dreame_vacuum/const.py
  • custom_components/dreame_vacuum/coordinator.py
  • custom_components/dreame_vacuum/dreame/__init__.py
  • custom_components/dreame_vacuum/dreame/const.py
  • custom_components/dreame_vacuum/dreame/device_info.py
  • custom_components/dreame_vacuum/dreame/device_setters.py
  • custom_components/dreame_vacuum/dreame/device_status/_core.py
  • custom_components/dreame_vacuum/dreame/exceptions.py
  • custom_components/dreame_vacuum/dreame/map_data_json_renderer.py
  • custom_components/dreame_vacuum/dreame/map_decoder.py
  • custom_components/dreame_vacuum/dreame/map_manager.py
  • custom_components/dreame_vacuum/dreame/map_optimizer.py
  • custom_components/dreame_vacuum/dreame/map_renderer/_core.py
  • custom_components/dreame_vacuum/dreame/protocol.py
  • custom_components/dreame_vacuum/dreame/resilience.py
  • custom_components/dreame_vacuum/dreame/resources.py
  • custom_components/dreame_vacuum/entity.py
  • custom_components/dreame_vacuum/icons.json
  • custom_components/dreame_vacuum/manifest.json
  • custom_components/dreame_vacuum/number.py
  • custom_components/dreame_vacuum/quality_scale.yaml
  • custom_components/dreame_vacuum/recorder.py
  • custom_components/dreame_vacuum/select.py
  • custom_components/dreame_vacuum/services.py
  • custom_components/dreame_vacuum/services.yaml
  • custom_components/dreame_vacuum/strings.json
  • custom_components/dreame_vacuum/switch.py
  • custom_components/dreame_vacuum/translations/ca.json
  • custom_components/dreame_vacuum/translations/cs.json
  • custom_components/dreame_vacuum/translations/de.json
  • custom_components/dreame_vacuum/translations/en.json
  • custom_components/dreame_vacuum/translations/es.json
  • custom_components/dreame_vacuum/translations/fr.json
  • custom_components/dreame_vacuum/translations/hu.json
  • custom_components/dreame_vacuum/translations/it.json
  • custom_components/dreame_vacuum/translations/ko.json
  • custom_components/dreame_vacuum/translations/nl.json
  • custom_components/dreame_vacuum/translations/pl.json
  • custom_components/dreame_vacuum/translations/pt-BR.json
  • custom_components/dreame_vacuum/translations/pt.json
  • custom_components/dreame_vacuum/translations/ro.json
  • custom_components/dreame_vacuum/translations/ru.json
  • custom_components/dreame_vacuum/translations/sl.json
  • custom_components/dreame_vacuum/translations/sv.json
  • custom_components/dreame_vacuum/translations/uk.json
  • custom_components/dreame_vacuum/translations/zh-Hans.json
  • custom_components/dreame_vacuum/translations/zh-Hant.json
  • custom_components/dreame_vacuum/vacuum.py
  • docs/overhaul/ha-backend-state-of-art-2026-07-02.md
  • docs/overhaul/perf-after-2026-07-02.txt
  • docs/overhaul/perf-before-2026-07-02.txt
  • docs/overhaul/uids-after-2026-07-02.txt
  • docs/overhaul/uids-before-2026-07-02.txt
  • hacs.json
  • pyproject.toml
  • requirements-dev.txt
  • requirements_dev.txt
  • tests/conftest.py
  • tests/test_camera.py
  • tests/test_camera_views.py
  • tests/test_config_flow.py
  • tests/test_device_actions.py
  • tests/test_device_core.py
  • tests/test_device_map_ops.py
  • tests/test_device_setters.py
  • tests/test_device_status.py
  • tests/test_device_status_core.py
  • tests/test_diagnostics.py
  • tests/test_exceptions.py
  • tests/test_init.py
  • tests/test_map_data_json_renderer.py
  • tests/test_map_decoder.py
  • tests/test_map_decoder_paths.py
  • tests/test_map_editor.py
  • tests/test_map_manager.py
  • tests/test_map_optimizer.py
  • tests/test_map_renderer.py
  • tests/test_platform_setup.py
  • tests/test_protocol_mihome.py
  • tests/test_resilience.py
  • tests/test_select.py
  • tests/test_services.py
  • tests/test_vacuum.py
📝 Walkthrough

Walkthrough

This PR overhauls the dreame_vacuum integration: entity services move to a dedicated services.py registered from async_setup; the options flow modernizes to OptionsFlowWithReload with HA selectors; camera rendering offloads to executor threads; the map optimizer drops its pure-Python fallback for JS-only execution; map manager, protocol, and resilience code gain reliability fixes; exceptions and resources are trimmed; translations add new services/options across 20 locales; documentation, quality-scale metadata, and a large characterization test suite are added.

Changes

Core Integration Refactor

Layer / File(s) Summary
Service registration moved to services.py
custom_components/dreame_vacuum/services.py, __init__.py, vacuum.py, camera.py, select.py, const.py, icons.json, services.yaml, strings.json
New async_register_services registers all entity services from async_setup; per-platform entity_platform service registration is removed from vacuum/camera/select; vacuum_set_property/vacuum_call_action services and icons added; unused CONF_TYPE constant removed.
Options flow modernization
config_flow.py
Options flow becomes OptionsFlowWithReload; login/2FA/captcha/device/options schemas switch to selector helpers (_TEXT, _PASSWORD, _BOOL, _select, _multi_select); async_get_options_flow no longer passes config_entry.
Entity listener lifecycle
button.py, number.py, select.py
Coordinator listener registrations are wrapped in entry.async_on_unload(...) across platforms, with added docstrings.
Camera executor offload
camera.py
async_camera_image/wifi_map_data offload device.get_map_for_render to the executor; history_map_image branching becomes explicit; many docstrings added.
Coordinator/entity/support fixes
coordinator.py, entity.py, binary_sensor.py, switch.py, recorder.py, manifest.json
Docstrings added; a recorder attribute typo fixed; paho-mqtt version bound tightened.

Dreame Engine Reliability Fixes

Layer / File(s) Summary
Exception hierarchy simplification
dreame/exceptions.py, dreame/__init__.py
AuthenticationError, CircuitOpenError, InvalidResponseError removed along with their re-exports.
Resilience/backoff and MQTT protocol
dreame/resilience.py, dreame/protocol.py
retry_with_backoff/CircuitBreaker.check replaced by backoff_delay; MQTT migrates to paho CallbackAPIVersion.VERSION2; credential/token handling and debug logging updated.
Map optimizer JS-only rewrite
dreame/map_optimizer.py
Pure-Python fallback removed; optimize() always uses a lazily cached MiniRacer JS context; adds close().
Map manager fixes
dreame/map_manager.py
Request-queue reservations always released; expired URL cache entries pruned; request_map_list continues on per-item failures; optimizer.close() called on disconnect.
Map render caching / renderer fixes
dreame/map_renderer/_core.py, dreame/map_data_json_renderer.py, dreame/map_decoder.py
Default/disconnected map images cached as bytes; JSON renderer guards missing IMAGE layer; debug logs truncated.
Status/setters/const cleanup
dreame/device_status/_core.py, dreame/device_setters.py, dreame/const.py, dreame/device_info.py, dreame/resources.py
No-op branches removed; unused helpers/dead code deleted; Final typing added to mapping dicts; unused cache helpers removed.

Translation Updates

Layer / File(s) Summary
Locale updates
translations/*.json (20 locales)
Adds square/vector_rooms map options, a segments_changed issue, and vacuum_call_action/vacuum_set_property service translations.

Documentation and Project Metadata

Layer / File(s) Summary
Architecture/changelog/quality docs
ARCHITECTURE.md, CHANGELOG.md, README.md, quality_scale.yaml, .codespellrc
Adds an Unreleased changelog, updated coverage/architecture claims, and a new quality_scale.yaml.
Overhaul reference docs
docs/overhaul/*
Adds refactor state-of-the-art doc, performance snapshots, and UID mapping files.
Project config
hacs.json, pyproject.toml, requirements-dev.txt
Raises minimum HA version and coverage floor; updates dev tooling.

Test Suite Additions

Layer / File(s) Summary
Config/init/exceptions/protocol tests
tests/conftest.py, tests/test_config_flow.py, tests/test_init.py, tests/test_exceptions.py, tests/test_protocol_mihome.py, tests/test_diagnostics.py, tests/test_platform_setup.py
Updated to match new services/options-flow/exception behavior.
Device core/actions/setters/status tests
tests/test_device_*.py
New comprehensive characterization suites for device mixins.
Map engine tests
tests/test_map_*.py
New/updated suites for decoder, editor, manager, optimizer, renderer, and JSON renderer.
Camera tests
tests/test_camera.py, tests/test_camera_views.py
Rewritten/extended camera and HTTP view coverage.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • foXaCe/dreame-vacuum#19: Both PRs modify the same set_credentials/_discovered characterization test behavior in tests/test_protocol_mihome.py.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the PR’s main themes: modernization, robustness, event-loop hygiene, and increased coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/integration-overhaul-2026-07-02

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.


def test_default_map_image_deterministic_bytes(self) -> None:
renderer = DreameVacuumMapDataJsonRenderer()
assert renderer.default_map_image == renderer.default_map_image
Comment thread tests/test_camera.py
patch.object(type(entity), "map_data_json", new=property(lambda self: False)),
patch.object(entity, "_get_proxy_image", return_value=b"IMG") as proxy,
):
result = await entity.history_map_image(2, False, True, False, True, False)
Comment thread tests/test_device_core.py
d._message_callback(msg)

d._handle_properties.assert_called_once()
(properties_arg,), _kwargs = d._handle_properties.call_args
@foXaCe foXaCe force-pushed the refactor/integration-overhaul-2026-07-02 branch from b648462 to bc26d79 Compare July 2, 2026 08:34

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
pyproject.toml (1)

3-3: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Version mismatch failing CI.

Pipeline logs show the version-consistency check failing: pyproject.toml version (6.5.3) does not match custom_components/dreame_vacuum/manifest.json. This blocks CI/lint and must be reconciled before merge.

🔍 Verification script
#!/bin/bash
# Compare pyproject.toml and manifest.json versions
rg -n '"version"' custom_components/dreame_vacuum/manifest.json
rg -n '^version' pyproject.toml
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyproject.toml` at line 3, The project version is inconsistent between
pyproject.toml and custom_components/dreame_vacuum/manifest.json, causing the CI
version check to fail. Update the version value in pyproject.toml and the
manifest.json version field so they match exactly, using the existing
package/version constants for dreame_vacuum as the source of truth. Re-run the
version-consistency check to confirm both files report the same version before
merging.

Source: Pipeline failures

custom_components/dreame_vacuum/coordinator.py (1)

699-711: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Always forward update errors to the base coordinator
async_set_update_error() skips super().async_set_update_error(ex) once _available is already False, so the coordinator stops refreshing last_exception and notifying listeners for later device errors. Keep the transition log guarded, but call the base method for every error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@custom_components/dreame_vacuum/coordinator.py` around lines 699 - 711, In
async_set_update_error on the Dreame coordinator, the base coordinator update
error handler is only called when _available is currently true, which prevents
later errors from updating last_exception and notifying listeners. Keep the
availability change and LOGGER.warning guard inside the _available transition
logic, but always invoke super().async_set_update_error(ex) for every error
regardless of the current availability state so the coordinator continues to
record and propagate update failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@custom_components/dreame_vacuum/services.py`:
- Around line 402-410: The schema for async_remote_control_move_step in
services.py allows wider values than the documented
vacuum_remote_control_move_step ranges, so update the vol.Clamp bounds for
INPUT_VELOCITY and INPUT_ROTATION to match the translated service descriptions
and keep the validation consistent with the advertised limits. Use the
SERVICE_MOVE_REMOTE_CONTROL_STEP registration and the
async_remote_control_move_step handler as the key places to adjust so the
accepted schema matches the documented range exactly.

In `@custom_components/dreame_vacuum/services.yaml`:
- Around line 653-669: The `vacuum_set_property.value` field is marked optional,
but `async_set_property` still requires a `value` argument. Either update the
service definition in `vacuum_set_property` to make `value` required, or change
the `async_set_property` signature in `vacuum.py` to accept a default of `None`
so the service can be called without it; keep the schema and handler behavior
aligned.

In `@custom_components/dreame_vacuum/translations/pt-BR.json`:
- Around line 117-118: The pt-BR translation for data_description.square is
still in English, so update the square string in both the config options section
and the options-flow init section to a proper Portuguese translation, keeping it
consistent with the nearby square/vector_rooms entries. Use the existing
translation keys in the pt-BR JSON (including data_description.square) to find
and replace both occurrences.

In `@docs/overhaul/ha-backend-state-of-art-2026-07-02.md`:
- Around line 12-205: The markdown content in this document still triggers
codespell, so normalize the prose to remove or correct misspelled terms and
nonstandard spellings throughout the affected sections, using the document’s
existing headings and lists as anchors. Check the repeated technical phrases and
acronyms in the overview and quality-scale sections, and if any intentional
domain terms must remain, add them to the repo’s codespell whitelist or
exclusion rather than leaving raw failures in the text.

In `@tests/test_map_decoder_paths.py`:
- Around line 588-607: The inline comment in
test_decode_legacy_funiture_info_format is being misread by the
python-use-type-annotations hook as an old-style type comment. Reword or remove
the phrasing in that assertion comment so it no longer starts with “type 8,”
while keeping the note that legacy furniture type 8 maps to ROUND_COFFEE_TABLE.
Focus on the comment text only; no test logic changes are needed.

---

Outside diff comments:
In `@custom_components/dreame_vacuum/coordinator.py`:
- Around line 699-711: In async_set_update_error on the Dreame coordinator, the
base coordinator update error handler is only called when _available is
currently true, which prevents later errors from updating last_exception and
notifying listeners. Keep the availability change and LOGGER.warning guard
inside the _available transition logic, but always invoke
super().async_set_update_error(ex) for every error regardless of the current
availability state so the coordinator continues to record and propagate update
failures.

In `@pyproject.toml`:
- Line 3: The project version is inconsistent between pyproject.toml and
custom_components/dreame_vacuum/manifest.json, causing the CI version check to
fail. Update the version value in pyproject.toml and the manifest.json version
field so they match exactly, using the existing package/version constants for
dreame_vacuum as the source of truth. Re-run the version-consistency check to
confirm both files report the same version before merging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9cc0d9fd-df1b-4d38-88aa-68a144e769f9

📥 Commits

Reviewing files that changed from the base of the PR and between 984e318 and bc26d79.

📒 Files selected for processing (93)
  • .codespellrc
  • ARCHITECTURE.md
  • CHANGELOG.md
  • CI_STATUS.md
  • README.md
  • custom_components/dreame_vacuum/__init__.py
  • custom_components/dreame_vacuum/binary_sensor.py
  • custom_components/dreame_vacuum/button.py
  • custom_components/dreame_vacuum/camera.py
  • custom_components/dreame_vacuum/config_flow.py
  • custom_components/dreame_vacuum/const.py
  • custom_components/dreame_vacuum/coordinator.py
  • custom_components/dreame_vacuum/dreame/__init__.py
  • custom_components/dreame_vacuum/dreame/const.py
  • custom_components/dreame_vacuum/dreame/device_info.py
  • custom_components/dreame_vacuum/dreame/device_setters.py
  • custom_components/dreame_vacuum/dreame/device_status/_core.py
  • custom_components/dreame_vacuum/dreame/exceptions.py
  • custom_components/dreame_vacuum/dreame/map_data_json_renderer.py
  • custom_components/dreame_vacuum/dreame/map_decoder.py
  • custom_components/dreame_vacuum/dreame/map_manager.py
  • custom_components/dreame_vacuum/dreame/map_optimizer.py
  • custom_components/dreame_vacuum/dreame/map_renderer/_core.py
  • custom_components/dreame_vacuum/dreame/protocol.py
  • custom_components/dreame_vacuum/dreame/resilience.py
  • custom_components/dreame_vacuum/dreame/resources.py
  • custom_components/dreame_vacuum/entity.py
  • custom_components/dreame_vacuum/icons.json
  • custom_components/dreame_vacuum/manifest.json
  • custom_components/dreame_vacuum/number.py
  • custom_components/dreame_vacuum/quality_scale.yaml
  • custom_components/dreame_vacuum/recorder.py
  • custom_components/dreame_vacuum/select.py
  • custom_components/dreame_vacuum/services.py
  • custom_components/dreame_vacuum/services.yaml
  • custom_components/dreame_vacuum/strings.json
  • custom_components/dreame_vacuum/switch.py
  • custom_components/dreame_vacuum/translations/ca.json
  • custom_components/dreame_vacuum/translations/cs.json
  • custom_components/dreame_vacuum/translations/de.json
  • custom_components/dreame_vacuum/translations/en.json
  • custom_components/dreame_vacuum/translations/es.json
  • custom_components/dreame_vacuum/translations/fr.json
  • custom_components/dreame_vacuum/translations/hu.json
  • custom_components/dreame_vacuum/translations/it.json
  • custom_components/dreame_vacuum/translations/ko.json
  • custom_components/dreame_vacuum/translations/nl.json
  • custom_components/dreame_vacuum/translations/pl.json
  • custom_components/dreame_vacuum/translations/pt-BR.json
  • custom_components/dreame_vacuum/translations/pt.json
  • custom_components/dreame_vacuum/translations/ro.json
  • custom_components/dreame_vacuum/translations/ru.json
  • custom_components/dreame_vacuum/translations/sl.json
  • custom_components/dreame_vacuum/translations/sv.json
  • custom_components/dreame_vacuum/translations/uk.json
  • custom_components/dreame_vacuum/translations/zh-Hans.json
  • custom_components/dreame_vacuum/translations/zh-Hant.json
  • custom_components/dreame_vacuum/vacuum.py
  • docs/overhaul/ha-backend-state-of-art-2026-07-02.md
  • docs/overhaul/perf-after-2026-07-02.txt
  • docs/overhaul/perf-before-2026-07-02.txt
  • docs/overhaul/uids-after-2026-07-02.txt
  • docs/overhaul/uids-before-2026-07-02.txt
  • hacs.json
  • pyproject.toml
  • requirements-dev.txt
  • requirements_dev.txt
  • tests/conftest.py
  • tests/test_camera.py
  • tests/test_camera_views.py
  • tests/test_config_flow.py
  • tests/test_device_actions.py
  • tests/test_device_core.py
  • tests/test_device_map_ops.py
  • tests/test_device_setters.py
  • tests/test_device_status.py
  • tests/test_device_status_core.py
  • tests/test_diagnostics.py
  • tests/test_exceptions.py
  • tests/test_init.py
  • tests/test_map_data_json_renderer.py
  • tests/test_map_decoder.py
  • tests/test_map_decoder_paths.py
  • tests/test_map_editor.py
  • tests/test_map_manager.py
  • tests/test_map_optimizer.py
  • tests/test_map_renderer.py
  • tests/test_platform_setup.py
  • tests/test_protocol_mihome.py
  • tests/test_resilience.py
  • tests/test_select.py
  • tests/test_services.py
  • tests/test_vacuum.py
💤 Files with no reviewable changes (10)
  • CI_STATUS.md
  • custom_components/dreame_vacuum/dreame/init.py
  • custom_components/dreame_vacuum/const.py
  • requirements_dev.txt
  • tests/test_diagnostics.py
  • custom_components/dreame_vacuum/dreame/resources.py
  • custom_components/dreame_vacuum/dreame/exceptions.py
  • tests/test_map_decoder.py
  • custom_components/dreame_vacuum/dreame/device_setters.py
  • custom_components/dreame_vacuum/dreame/device_status/_core.py

Comment on lines +402 to +410
register(
SERVICE_MOVE_REMOTE_CONTROL_STEP,
{
vol.Required(INPUT_VELOCITY): vol.All(vol.Coerce(int), vol.Clamp(min=-600, max=600)),
vol.Required(INPUT_ROTATION): vol.All(vol.Coerce(int), vol.Clamp(min=-360, max=360)),
vol.Optional("prompt"): cv.boolean,
},
"async_remote_control_move_step",
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Clamp bounds wider than documented service ranges.

vol.Clamp(min=-600, max=600) for velocity and min=-360, max=360 for rotation don't match the translated field descriptions ("100 to -300" for velocity, "-128 to 128" for rotation in vacuum_remote_control_move_step). Since vol.Clamp silently clamps rather than rejecting, users following the documented range see correct behavior, but anyone reading the schema/passing edge values gets a much wider accepted range than advertised.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@custom_components/dreame_vacuum/services.py` around lines 402 - 410, The
schema for async_remote_control_move_step in services.py allows wider values
than the documented vacuum_remote_control_move_step ranges, so update the
vol.Clamp bounds for INPUT_VELOCITY and INPUT_ROTATION to match the translated
service descriptions and keep the validation consistent with the advertised
limits. Use the SERVICE_MOVE_REMOTE_CONTROL_STEP registration and the
async_remote_control_move_step handler as the key places to adjust so the
accepted schema matches the documented range exactly.

Comment on lines +653 to +669
vacuum_set_property:
target:
entity:
integration: dreame_vacuum
domain: vacuum
fields:
key:
example: suction_level
required: true
selector:
text:
value:
example: 2
required: false
selector:
text:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

vacuum_set_property.value marked optional but implementation requires it.

async_set_property(self, key: Any, value: Any) in vacuum.py has no default for value, unlike every other optional field in this file (which all have matching Python defaults, e.g. map_id: Any = None). Calling vacuum_set_property without value — a normal usage per required: false — will raise TypeError: async_set_property() missing 1 required positional argument: 'value'.

🐛 Proposed fix (pick one)

Option A — make the schema match current behavior:

     value:
       example: 2
-      required: false
+      required: true
       selector:
         text:

Option B — make the implementation match the schema (requires editing vacuum.py, outside this diff):

-    async def async_set_property(self, key: Any, value: Any) -> None:
+    async def async_set_property(self, key: Any, value: Any = None) -> None:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
vacuum_set_property:
target:
entity:
integration: dreame_vacuum
domain: vacuum
fields:
key:
example: suction_level
required: true
selector:
text:
value:
example: 2
required: false
selector:
text:
vacuum_set_property:
target:
entity:
integration: dreame_vacuum
domain: vacuum
fields:
key:
example: suction_level
required: true
selector:
text:
value:
example: 2
required: true
selector:
text:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@custom_components/dreame_vacuum/services.yaml` around lines 653 - 669, The
`vacuum_set_property.value` field is marked optional, but `async_set_property`
still requires a `value` argument. Either update the service definition in
`vacuum_set_property` to make `value` required, or change the
`async_set_property` signature in `vacuum.py` to accept a default of `None` so
the service can be called without it; keep the schema and handler behavior
aligned.

Comment on lines +117 to +118
"square": "Display the map as a square instead of its original aspect ratio.",
"vector_rooms": "Anti-aliasing das bordas diagonais de cômodos e paredes para um mapa mais limpo e refinado."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Untranslated square description in pt-BR.

data_description.square is left in English ("Display the map as a square instead of its original aspect ratio.") in both the config options step and the options-flow init step, while its sibling square/vector_rooms strings are translated.

🌐 Suggested translation
-          "square": "Display the map as a square instead of its original aspect ratio.",
+          "square": "Exibir o mapa como um quadrado em vez de sua proporção original.",

(apply to both occurrences: line 117 and line 1437)

Also applies to: 1437-1438

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@custom_components/dreame_vacuum/translations/pt-BR.json` around lines 117 -
118, The pt-BR translation for data_description.square is still in English, so
update the square string in both the config options section and the options-flow
init section to a proper Portuguese translation, keeping it consistent with the
nearby square/vector_rooms entries. Use the existing translation keys in the
pt-BR JSON (including data_description.square) to find and replace both
occurrences.

Comment on lines +12 to +205
- **Version stable actuelle : Home Assistant Core 2026.7** — "Automations that speak your language",
sortie le **1er juillet 2026**. (https://www.home-assistant.io/blog/2026/07/01/release-20267/)
- Cadence mensuelle inchangée (release le 1er jeudi/vendredi du mois, patchs `.1`, `.2`… ensuite).
- 3 dernières stables :
1. **2026.7** (2026-07-01) — renommage massif de triggers/conditions "purpose-specific"
(`battery.low`→`battery.became_low`, `vacuum.docked`→`vacuum.returned_to_dock`, etc.), timeline
Activity/logbook refaite.
2. **2026.6** (2026-06-03) — "Pick a card, any card" : nouveau card picker, migration de plusieurs
checks Quality Scale de hassfest vers pylint (`parallel-updates`, `diagnostics`,
`config-entry-unloading`, `reauthentication-flow`), renommage des comportements de triggers Labs
(`any`→`each`, `last`→`all`), Bluetooth scanning mode par défaut passé à `Auto`.
3. **2026.5** (2026-05-06) — "We're on the same frequency now" : support RF (radio-fréquence) pour
stores/portails, nouveau dashboard Maintenance (suivi batteries), suppression des triggers/conditions
`entered home`/`left home`/`is home`/`is not home` sur Person/Device Tracker.
- **Constat important** : depuis courant 2025, les *release notes* utilisateur (home-assistant.io/blog)
ne contiennent plus de section "For developers" détaillée — elles renvoient systématiquement vers le
**developer blog** (https://developers.home-assistant.io/blog/) pour tout ce qui concerne le
développement d'intégrations. C'est désormais la source de vérité à surveiller en continu.

---

## 2. Dépréciations ACTIVES à corriger (avec version de retrait)

Classées par urgence pour ce refactor. **Beaucoup de délais sont déjà expirés** vu que hacs.json cible
encore HA 2023.6.0 — l'intégration tourne probablement déjà en mode dégradé/warnings sur une install à jour.

| # | Dépréciation | Déprécié depuis | Retrait prévu | Statut au 2026-07-02 | Action |
|---|---|---|---|---|---|
| 1 | `StateVacuumEntity` : constantes `STATE_*` pour l'état → **`VacuumActivity` enum** + propriété `activity` (au lieu de `state`) | 2025.1 | **2026.1** | **DÉJÀ RETIRÉ** (6 mois) | Urgent : si le code fixe encore `self._attr_state = STATE_CLEANING` ou équivalent, ça ne fonctionne plus du tout sur HA récent |
| 2 | `StateVacuumEntity` : constantes de feature flags → **`VacuumEntityFeature` enum** | 2024.10 (période "officielle"; dépréciation technique dès 2022.5 sans annonce propre) | 2025.10 | **DÉJÀ RETIRÉ** | Urgent : vérifier `SUPPORT_*` remplacés par `VacuumEntityFeature.*` |
| 3 | `StateVacuumEntity.battery_level` / `battery_icon` (properties) | **2025.8** | **2026.8** (dans ~1 mois) | ACTIF, retrait imminent | Remplacer par une entité `sensor` séparée `device_class: battery` (+ éventuellement un `binary_sensor` `device_class: charging`) |
| 4 | Camera : `frontend_stream_type` (property), `async_handle_web_rtc_offer`, `async_register_rtsp_to_web_rtc_provider` | 2024.12 | 2025.6 | **DÉJÀ RETIRÉ** | Si la plateforme camera fait du WebRTC (flux caméra embarquée du robot), migrer vers `async_handle_async_webrtc_offer` / `async_register_webrtc_provider`, utiliser le WS `camera/capabilities` côté frontend |
| 5 | `OptionsFlow` : assignation manuelle de `self.config_entry` dans `__init__` du flow / `OptionsFlowWithConfigEntry` | annoncé 2024.11 | avertissement jusqu'à **2025.12**, retrait non daté précisément mais **la fenêtre d'avertissement est déjà passée** | ACTIF — logge un warning demandant d'ouvrir une issue sur le repo custom si toujours assigné manuellement | Supprimer le paramètre `config_entry` du constructeur d'`OptionsFlowHandler`, utiliser `self.config_entry` / `self._config_entry_id` fournis nativement |
| 6 | Combiner un **config entry update listener** (`add_update_listener`) avec des méthodes de reload dans le config flow (`async_update_reload_and_abort`, `reload_on_update` implicite) | **2026.6** (ce mois-ci) | **2026.12** | TRÈS RÉCENT, ACTIF | Risque de reload en double / race condition. Choisir une seule stratégie : soit retirer le listener, soit `async_update_and_abort()`, soit `reload_on_update=False` sur `_abort_if_unique_id_configured()` |
| 7 | `quality_scale` déclaré comme clé dans `manifest.json` | changement de mécanisme depuis **2024.11.20** | pas un retrait à proprement parler pour les intégrations custom (non validé par hassfest hors core), mais c'est l'ancien pattern | À migrer par bonne pratique | Le nouveau modèle est un fichier **`quality_scale.yaml`** à la racine de l'intégration listant chaque règle avec statut (`done`/`exempt`+raison/`todo`). Pour une intégration custom ce fichier n'a pas d'effet runtime mais sert de checklist/preuve vis-à-vis de HACS et des reviewers |
| 8 | `FlowHandler.show_advanced_options` / `context['show_advanced_options']` | **2026.5** | **2027.6** | ACTIF, marge confortable | Remplacer le gating par "advanced mode" par une organisation en *sections* dans le config/options flow |
| 9 | `paho-mqtt` `CallbackAPIVersion.VERSION1` (API historique de callbacks) | dépréciée côté lib paho-mqtt (pas HA) | retrait prévu en **paho-mqtt 3.0** | À surveiller | Migrer les callbacks (`on_connect`, `on_message`, etc.) vers `CallbackAPIVersion.VERSION2`, plus cohérent MQTT 3.x/5.x |
| 10 | Détection des appels bloquants dans l'event loop (`requests`, `urllib`, `time.sleep`, I/O fichier sync) | renforcée depuis **2024.7** | pas une deadline unique — mais les logs `Detected blocking call to X inside the event loop by custom integration 'dreame_vacuum'` sont visibles par tous les utilisateurs et nourrissent les demandes HACS/quality-scale. Certains cas (import bloquant, sleep bloquant) sont déjà remontés comme quasi-erreurs bloquantes selon les composants internes | ACTIF EN PERMANENCE | `requests` est **structurellement incompatible** avec la règle Platinum `async-dependency` et dégrade l'expérience (warnings visibles, potentiel throttling futur). Tout appel `requests.get/post` doit passer par `hass.async_add_executor_job(...)` a minima, et à terme être remplacé par `aiohttp`/`httpx` async natif |

**Non trouvé de dépréciation spécifique 2024-2026 sur** : `DataUpdateCoordinator` (API stable, juste enrichie,
voir §3), `EntityDescription`/`_attr_has_entity_name` (pattern stable depuis 2023, toujours recommandé,
`has-entity-name` reste une règle Bronze obligatoire), `select`/`number`/`button`/`time`/`switch` platforms
(pas de breaking change identifié sur la période).

---

## 3. Nouvelles API à adopter (avec version d'introduction)

| API / pattern | Introduit en | Ce que ça apporte pour `dreame_vacuum` |
|---|---|---|
| **`ConfigEntry.runtime_data`** (ConfigEntry générique typé, `type MyConfigEntry = ConfigEntry[MyData]`) | 2024.4 (blog 2024-04-30), usage généralisé dès 2024.6-2024.8 | Remplace `hass.data[DOMAIN][entry.entry_id]`. C'est aussi la règle **Bronze `runtime-data`** — actuellement quasi-certainement non respectée vu le manifest `quality_scale: gold` déclaré avec une base HA 2023.6 |
| **`DataUpdateCoordinator._async_setup()`** | 2024.8 (blog 2024-08-05) | Hook async dédié appelé une fois via `async_config_entry_first_refresh()`, pour init unique (ex: découverte des appareils MQTT, fetch config initiale) sans polluer `_async_update_data` avec des flags `if not initialized` |
| **Helpers reauth/reconfigure** : `self._get_reauth_entry()`, `self._get_reconfigure_entry()`, `self._abort_if_unique_id_mismatch()`, `async_update_reload_and_abort(..., data_updates=...)` | 2024.10-2024.11 (blog 2024-10-21, 2024-11-04) | Pattern standard pour la règle Silver `reauthentication-flow` et Gold `reconfiguration-flow`. Entries doivent être liées via `entry_id` et récupérées localement à chaque step, pas cachées en attribut de classe |
| **OptionsFlow natif** : `self.config_entry` fourni automatiquement, `self._config_entry_id` | 2024.11 (blog 2024-11-12) | Supprimer le paramètre `config_entry` du `__init__` de l'OptionsFlowHandler |
| **Config Subentries** (`ConfigSubentryFlow`) | 2025.2 (blog 2025-02-16), ajustements 2025-03-24 | Pertinent si un compte cloud Dreame gère plusieurs robots/hubs — permettrait de modéliser chaque robot comme sous-entrée plutôt qu'un flot config unique |
| **`service.async_register_platform_entity_service`** appelé depuis `async_setup` de l'intégration (plus depuis le platform setup) | 2025.9 (blog 2025-09-25) | Remplace `platform.async_register_entity_service` historique ; découple l'enregistrement des services (actions) du timing de setup des plateformes — pertinent pour les actions custom vacuum (ex: `go_to`, `clean_zone`, `clean_segment`) |
| **Description placeholders pour traductions d'actions de service** | 2025.11 (blog 2025-11-27) | Permet des messages d'erreur/traductions d'actions plus riches — aligné avec la règle Gold `exception-translations` |
| **DataUpdateCoordinator "Retry-After"** | 2025.11 (blog 2025-11-17) | Le coordinator respecte un `retry_after` renvoyé par l'API cloud lors d'un throttle — utile pour l'API cloud Dreame |
| **Coordinator retriggering** | 2025.10 (blog 2025-10-05) | Permet de forcer un refresh coordonné depuis un event MQTT push sans dupliquer la logique |
| **Terminologie "services → actions"**, icônes via `icons.json` (depuis 2024.2, schéma étendu 2024-08-27) | 2024.7 (services→actions), 2024.2/2024.8 (icons.json) | `services.yaml` reste le nom de fichier technique mais toute la doc/UX parle d'"actions" ; les icônes d'entités/services doivent passer par `icons.json` plutôt que la property `icon` codée en dur |
| **`prek`** remplace `pre-commit` comme runner de hooks | 2026.1 (blog 2026-01-13) | Déjà fait côté ce repo (commit `81794d8` "adopt prek as the pre-commit hook runner", `f4ee2d5` "migrate to prek-only") — rien à faire |
| **Quality Scale : migration de checks hassfest → pylint** (`parallel-updates`, `diagnostics`, `config-entry-unloading`, `reauthentication-flow`) | 2026.6 | Pour du custom (hors core), hassfest/pylint core ne s'appliquent pas directement, mais les mêmes règles de style doivent être répliquées manuellement / via `quality_scale.yaml` en checklist |

---

## 4. Règles Quality Scale du jour (liste complète des identifiants, par palier)

Source : https://developers.home-assistant.io/docs/core/integration-quality-scale/rules/ (état au 2026-07-02)

### 🥉 Bronze
1. `action-setup` — Les actions de service sont enregistrées dans `async_setup`
2. `appropriate-polling` — Intervalle de polling approprié (si intégration à polling)
3. `brands` — Assets de branding disponibles
4. `common-modules` — Patterns communs dans des modules communs
5. `config-flow-test-coverage` — Couverture de test complète du config flow
6. `config-flow` — Configurable via l'UI
7. `dependency-transparency` — Transparence des dépendances
8. `docs-actions` — Doc décrit les actions de service fournies
9. `docs-triggers` — Doc décrit les triggers fournis
10. `docs-conditions` — Doc décrit les conditions fournies
11. `docs-high-level-description` — Description haut niveau de la marque/produit/service
12. `docs-installation-instructions` — Instructions d'installation pas-à-pas + prérequis
13. `docs-removal-instructions` — Instructions de suppression
14. `entity-event-setup` — Abonnement aux events dans les bonnes méthodes de cycle de vie
15. `entity-unique-id` — Entités avec unique ID
16. `has-entity-name` — Entités avec `has_entity_name = True`
17. `runtime-data` — Usage de `ConfigEntry.runtime_data`
18. `test-before-configure` — Test de connexion dans le config flow
19. `test-before-setup` — Vérification à l'init que le setup est possible
20. `unique-config-entry` — Empêche le double setup du même device/service

### 🥈 Silver
1. `action-exceptions` — Les actions lèvent des exceptions en cas d'échec
2. `config-entry-unloading` — Support du unload du config entry
3. `docs-configuration-parameters` — Doc décrit toutes les options de config
4. `docs-installation-parameters` — Doc décrit tous les paramètres d'installation
5. `entity-unavailable` — Entité marquée indisponible si pertinent
6. `integration-owner` — Un owner d'intégration désigné
7. `log-when-unavailable` — Log une fois à la perte de connexion, une fois à la reconnexion
8. `parallel-updates` — `PARALLEL_UPDATES` spécifié
9. `reauthentication-flow` — Réauthentification disponible via l'UI
10. `test-coverage` — >95% de couverture de test sur tous les modules

### 🥇 Gold
1. `devices` — Crée des devices
2. `diagnostics` — Implémente les diagnostics
3. `discovery-update-info` — Utilise les infos de discovery pour mettre à jour les infos réseau
4. `discovery` — Devices découvrables
5. `docs-data-update` — Doc décrit comment les données sont mises à jour
6. `docs-examples` — Doc fournit des exemples d'automatisation
7. `docs-known-limitations` — Doc décrit les limitations connues
8. `docs-supported-devices` — Doc décrit devices supportés/non supportés
9. `docs-supported-functions` — Doc décrit fonctionnalités/entités supportées
10. `docs-troubleshooting` — Doc fournit infos de dépannage
11. `docs-use-cases` — Doc décrit les cas d'usage
12. `dynamic-devices` — Devices ajoutés après le setup initial de l'intégration
13. `entity-category` — Entités avec `EntityCategory` approprié
14. `entity-device-class` — Entités avec device class quand possible
15. `entity-disabled-by-default` — Entités peu utiles/bruyantes désactivées par défaut
16. `entity-translations` — Noms d'entités traduits
17. `exception-translations` — Messages d'exception traduisibles
18. `icon-translations` — Icônes via traductions (`icons.json`)
19. `reconfiguration-flow` — Flow de reconfiguration disponible
20. `repair-issues` — Repair issues / repair flows utilisés quand une intervention est nécessaire
21. `stale-devices` — Suppression des devices obsolètes

### 🏆 Platinum
1. `async-dependency` — La dépendance externe est async
2. `inject-websession` — La dépendance supporte l'injection d'une websession
3. `strict-typing` — Typage strict (mypy)

**Point critique pour ce refactor** : `paho-mqtt` (thread-based, non-asyncio natif) et `requests` (100%
sync) rendent `async-dependency` et `inject-websession` **non atteignables tels quels**. Pour viser
Platinum il faudrait soit un wrapper asyncio-natif autour de paho-mqtt (ou migrer vers une lib MQTT async
type `aiomqtt`), soit au minimum isoler tous les appels bloquants dans l'executor et documenter
l'exemption dans `quality_scale.yaml` avec justification. `quality_scale: gold` déclaré dans le manifest
actuel est donc probablement **non tenable en l'état** tant que `requests` reste sur le chemin critique
(cf. règle Gold implicite de stabilité/robustesse — pas listée nommément mais couverte par
`action-exceptions`, `entity-unavailable`, `log-when-unavailable`).

---

## 5. Points spécifiques vacuum / camera / mqtt / requests-sync

### Vacuum
- **Obligatoire dès maintenant** (fenêtres déjà expirées) : propriété `activity` retournant un
`VacuumActivity` (pas `state`), et `VacuumEntityFeature` pour les feature flags. Si le fork n'a pas
encore été migré, c'est la priorité n°1 — l'entité vacuum est probablement cassée ou en fallback dégradé
sur toute install HA ≥ 2026.1.
- `battery_level`/`battery_icon` sur l'entité vacuum : à sortir vers un `sensor` `device_class: battery`
dédié + `binary_sensor` `device_class: charging` avant **2026.8** (dans ~1 mois). C'est un changement
d'UX visible (l'indicateur batterie quitte la carte vacuum pour devenir une entité séparée) — à
documenter dans le changelog utilisateur du fork.
- Pas de nouvelle contrainte identifiée sur `map_data`/coordonnées de carte, actions `clean_zone`/`clean_segment`/`go_to` au-delà du pattern générique "services → actions" et `async_register_platform_entity_service`.

### Camera
- Si `dreame_vacuum` expose un flux caméra embarquée (certains robots ont une caméra), toute logique
WebRTC doit être sur `async_handle_async_webrtc_offer` / `async_register_webrtc_provider`
(les anciennes méthodes sont retirées depuis 2025.6).
- Le rendu de carte (pillow/numpy) via `camera.Image` / `async_camera_image` doit rester **dans
l'executor** (`hass.async_add_executor_job`) — Pillow/numpy sont CPU-bound et synchrones, donc jamais
d'appel direct dans une coroutine du event loop.

### MQTT (paho-mqtt)
- Vérifier la version de `paho-mqtt` épinglée dans `manifest.json` → `requirements`. Si `< 2.0`,
planifier la migration `CallbackAPIVersion.VERSION1` → `VERSION2` (retrait prévu en paho-mqtt 3.0,
côté lib externe, pas HA).
- Paho-mqtt fonctionne avec sa propre boucle réseau en thread — s'assurer que tous les callbacks
(`on_message`, `on_connect`, etc.) qui touchent l'état HA passent bien par
`hass.loop.call_soon_threadsafe(...)` ou `async_add_executor_job`, jamais d'appel direct à des
coroutines/`hass.states` depuis le thread paho.
- C'est ce pattern (dépendance non-async) qui bloque structurellement la règle Platinum
`async-dependency` (cf. §4).

### `requests` (sync)
- Incompatible avec les bonnes pratiques asyncio de HA depuis toujours, mais la détection des appels
bloquants dans l'event loop s'est **renforcée depuis 2024.7** (logs `Detected blocking call to X
inside the event loop by custom integration 'dreame_vacuum'` visibles par tous les utilisateurs dans
leurs logs HA — mauvais pour la réputation/HACS).
- Recommandation minimale immédiate : englober tout appel `requests.*` dans
`await hass.async_add_executor_job(...)`.
- Recommandation cible : migrer vers `aiohttp` (déjà une dépendance systématique de HA, réutilisable via
`aiohttp_client.async_get_clientsession(hass)`) ou `httpx.AsyncClient`, ce qui débloque aussi
`inject-websession` (Platinum) et simplifie les tests (mock de session).

### Divers pertinents (hub / multi-device)
- `integration_type: hub` + potentiellement plusieurs robots par compte cloud → évaluer **Config
Subentries** (2025.2) pour modéliser chaque robot, au lieu d'un unique config entry monolithique.
- `hacs.json` : `homeassistant >= 2023.6.0` est très en retard par rapport à toutes les API listées
ci-dessus (runtime_data 2024.4, coordinator `_async_setup` 2024.8, reauth/reconfigure helpers
2024.10-11, OptionsFlow natif 2024.11, subentries 2025.2, service registration API 2025.9). Si le
refactor adopte ces patterns, il faut remonter le minimum HA en conséquence (au minimum
2024.11/2025.1, idéalement une version 2025.x-2026.x récente) — sinon le fork ne sera plus installable
correctement sur les anciennes versions déclarées compatibles.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Fix the codespell failures before merge.

This document still trips the pre-commit codespell stage across multiple sections, so CI will stay red until the prose is normalized or the file is explicitly excluded/whitelisted.

🧰 Tools
🪛 GitHub Actions: CI / 2_Pre-commit (prek).txt

[error] 14-14: codespell found potential spelling issue. 'patchs' => 'patches, paths'.


[error] 40-40: codespell found potential spelling issue. 'fixe' => 'fixed, fixes, fix, fixme, fixer'.


[error] 41-41: codespell found potential spelling issue. 'annonce' => 'announce'.


[error] 45-46: codespell found potential spelling issues. 'implicite' => 'implicit, implicitly'; 'raison' => 'reason, raisin'.


[error] 47-47: codespell found potential spelling issues. 'marge' => 'merge'; 'confortable' => 'comfortable'.


[error] 49-49: codespell found potential spelling issues. 'visibles' => 'visible'; 'composants' => 'components'; 'potentiel' => 'potential'.


[error] 62-64: codespell found potential spelling issues. 'Remplace' => 'Replace'; 'mattribut' => 'attribute'.


[error] 67-72: codespell found potential spelling issues. 'Remplace' => 'Replace'; 'remplace' => 'replace'.


[error] 95-95: codespell found potential spelling issue. 'vie' => 'via'.


[error] 99-110: codespell found potential spelling issues. 'connexion' => 'connection' (occurs at lines 99 and 110).


[error] 121-121: codespell found potential spelling issue. 'exemples' => 'examples'.


[error] 140-140: codespell found potential spelling issue. 'supporte' => 'supported, supporter'.


[error] 149-149: codespell found potential spelling issue. 'implicite' => 'implicit, implicitly'.


[error] 189-191: codespell found potential spelling issues. 'visibles' => 'visible'; 'Recommandation' => 'Recommendation'.


[error] 193-193: codespell found potential spelling issue. 'Recommandation' => 'Recommendation'.

🪛 GitHub Actions: CI / Pre-commit (prek)

[error] 14-14: codespell failed (exit code 65). Replace "patchs" with "patches, paths".


[error] 40-40: codespell failed (exit code 65). Replace "fixe" with "fixed, fixes, fix, fixme, fixer".


[error] 41-41: codespell failed (exit code 65). Replace "annonce" with "announce".


[error] 45-45: codespell failed (exit code 65). Replace "implicite" with "implicit, implicitly".


[error] 46-46: codespell failed (exit code 65). Replace "raison" with "reason, raisin".


[error] 47-47: codespell failed (exit code 65). Replace "marge" with "merge".


[error] 47-47: codespell failed (exit code 65). Replace "confortable" with "comfortable".


[error] 49-49: codespell failed (exit code 65). Replace "visibles" with "visible".


[error] 49-49: codespell failed (exit code 65). Replace "composants" with "components".


[error] 49-49: codespell failed (exit code 65). Replace "visibles" with "visible".


[error] 49-49: codespell failed (exit code 65). Replace "potentiel" with "potential".


[error] 62-62: codespell failed (exit code 65). Replace "Remplace" with "Replace".


[error] 64-64: codespell failed (exit code 65). Replace "attribut" with "attribute".


[error] 64-64: codespell failed (exit code 65). Replace "classe" with "class, classes".


[error] 67-67: codespell failed (exit code 65). Replace "Remplace" with "Replace".


[error] 72-72: codespell failed (exit code 65). Replace "remplace" with "replace".


[error] 95-95: codespell failed (exit code 65). Replace "vie" with "via".


[error] 99-99: codespell failed (exit code 65). Replace "connexion" with "connection".


[error] 110-110: codespell failed (exit code 65). Replace "connexion" with "connection".


[error] 121-121: codespell failed (exit code 65). Replace "exemples" with "examples".


[error] 140-140: codespell failed (exit code 65). Replace "supporte" with "supported, supporter".


[error] 149-149: codespell failed (exit code 65). Replace "implicite" with "implicit, implicitly".


[error] 189-189: codespell failed (exit code 65). Replace "visibles" with "visible".


[error] 191-191: codespell failed (exit code 65). Replace "Recommandation" with "Recommendation".


[error] 193-193: codespell failed (exit code 65). Replace "Recommandation" with "Recommendation".

🪛 LanguageTool

[typographical] ~14-~14: Caractère d’apostrophe incorrect.
Context: ...e le 1er jeudi/vendredi du mois, patchs .1, .2… ensuite). - 3 dernières stables ...

(APOS_INCORRECT)


[typographical] ~14-~14: Caractère d’apostrophe incorrect.
Context: ...er jeudi/vendredi du mois, patchs .1, .2… ensuite). - 3 dernières stables : 1....

(APOS_INCORRECT)


[style] ~15-~15: Il est déconseillé de placer des chiffres en début de phrase. Souhaitez-vous reformuler la phrase ?
Context: ...du mois, patchs .1, .2… ensuite). - 3 dernières stables : 1. 2026.7 (20...

(NOMBRES_EN_LETTRES)


[typographical] ~17-~17: Caractère d’apostrophe incorrect.
Context: ...ers/conditions "purpose-specific" (battery.lowbattery.became_low, vacuum.docked→`...

(APOS_INCORRECT)


[typographical] ~17-~17: Caractère d’apostrophe incorrect.
Context: ... "purpose-specific" (battery.lowbattery.became_low, vacuum.docked→`vacuum.returned_to_do...

(APOS_INCORRECT)


[typographical] ~17-~17: Caractère d’apostrophe incorrect.
Context: ... (battery.lowbattery.became_low, vacuum.dockedvacuum.returned_to_dock, etc.), timel...

(APOS_INCORRECT)


[typographical] ~17-~17: Caractère d’apostrophe incorrect.
Context: ...battery.became_low, vacuum.dockedvacuum.returned_to_dock`, etc.), timeline Activity/logbook ...

(APOS_INCORRECT)


[typographical] ~20-~20: Caractère d’apostrophe incorrect.
Context: ...s Quality Scale de hassfest vers pylint (parallel-updates, diagnostics, `...

(APOS_INCORRECT)


[typographical] ~20-~20: Caractère d’apostrophe incorrect.
Context: ...ssfest vers pylint (parallel-updates, diagnostics, config-entry-unloading, `reauth...

(APOS_INCORRECT)


[typographical] ~20-~20: Caractère d’apostrophe incorrect.
Context: ...int (parallel-updates, diagnostics, config-entry-unloading, reauthentication-flow), renommage de...

(APOS_INCORRECT)


[typographical] ~21-~21: Caractère d’apostrophe incorrect.
Context: ...ostics, config-entry-unloading, reauthentication-flow`), renommage des comportements de trigge...

(APOS_INCORRECT)


[typographical] ~22-~22: Caractère d’apostrophe incorrect.
Context: ...des comportements de triggers Labs (anyeach, lastall), Bluetooth sc...

(APOS_INCORRECT)


[typographical] ~22-~22: Caractère d’apostrophe incorrect.
Context: ...mportements de triggers Labs (anyeach, lastall), Bluetooth scanning...

(APOS_INCORRECT)


[typographical] ~22-~22: Caractère d’apostrophe incorrect.
Context: ...ts de triggers Labs (anyeach, lastall), Bluetooth scanning mode par déf...

(APOS_INCORRECT)


[typographical] ~22-~22: Caractère d’apostrophe incorrect.
Context: ...riggers Labs (anyeach, lastall), Bluetooth scanning mode par défaut pa...

(APOS_INCORRECT)


[typographical] ~22-~22: Caractère d’apostrophe incorrect.
Context: ...etooth scanning mode par défaut passé à Auto. 3. 2026.5 (2026-05-06) — "We're ...

(APOS_INCORRECT)


[typographical] ~23-~23: Les deux parties de ce mot peuvent s’unir.
Context: ...n the same frequency now" : support RF (radio-fréquence) pour stores/portails, nouveau das...

(FR_SPLIT_WORDS_HYPHEN)


[typographical] ~24-~24: Caractère d’apostrophe incorrect.
Context: ...s), suppression des triggers/conditions entered home/left home/is home/is not home sur...

(APOS_INCORRECT)


[typographical] ~25-~25: Caractère d’apostrophe incorrect.
Context: ...triggers/conditions entered home/left home/is home/is not home sur Person/Devi...

(APOS_INCORRECT)


[typographical] ~25-~25: Caractère d’apostrophe incorrect.
Context: ...ditions entered home/left home/is home/is not home sur Person/Device Tracker...

(APOS_INCORRECT)


[style] ~28-~28: Cette structure peut être omise afin d’alléger la phrase.
Context: ...evelopers.home-assistant.io/blog/) pour tout ce qui concerne le développement d'intégrations. C'est d...

(TOUT_CE_QUI_CONCERNE)


[style] ~35-~35: Une autre structure plus directe dynamisera votre phrase.
Context: ...Beaucoup de délais sont déjà expirés* vu que hacs.json cible encore HA 2023.6.0 — l'...

(VU_QUE)


[style] ~39-~39: Un tiret long peut sembler plus approprié.
Context: ...révu | Statut au 2026-07-02 | Action | |---|---|---|---|---|---| | 1 | `StateVacuum...

(TIRET_LONG_1)


[style] ~39-~39: Un tiret long peut sembler plus approprié.
Context: ... | Statut au 2026-07-02 | Action | |---|---|---|---|---|---| | 1 | `StateVacuumEnti...

(TIRET_LONG_1)


[style] ~39-~39: Un tiret long peut sembler plus approprié.
Context: ...tatut au 2026-07-02 | Action | |---|---|---|---|---|---| | 1 | StateVacuumEntity ...

(TIRET_LONG_1)


[style] ~39-~39: Un tiret long peut sembler plus approprié.
Context: ...t au 2026-07-02 | Action | |---|---|---|---|---|---| | 1 | StateVacuumEntity : co...

(TIRET_LONG_1)


[style] ~39-~39: Un tiret long peut sembler plus approprié.
Context: ... 2026-07-02 | Action | |---|---|---|---|---|---| | 1 | StateVacuumEntity : consta...

(TIRET_LONG_1)


[style] ~39-~39: Un tiret long peut sembler plus approprié.
Context: ...6-07-02 | Action | |---|---|---|---|---|---| | 1 | StateVacuumEntity : constantes...

(TIRET_LONG_1)


[typographical] ~40-~40: Caractère d’apostrophe incorrect.
Context: ...y: constantesSTATE_* pour l'état → **VacuumActivityenum** + propriétéacti...

(APOS_INCORRECT)


[typographical] ~40-~40: Caractère d’apostrophe incorrect.
Context: ...um** + propriété activity (au lieu de state) | 2025.1 | 2026.1 | *DÉJÀ RETIRÉ...

(APOS_INCORRECT)


[typographical] ~41-~41: Caractère d’apostrophe incorrect.
Context: ...Entity : constantes de feature flags → **VacuumEntityFeature` enum** | 2024.10 (p...

(APOS_INCORRECT)


[typographical] ~42-~42: Caractère d’apostrophe incorrect.
Context: ...y(+ éventuellement unbinary_sensor device_class: charging) | | 4 | Camera : frontend_stream_type...

(APOS_INCORRECT)


[typographical] ~43-~43: Caractère d’apostrophe incorrect.
Context: ...ra : frontend_stream_type (property), async_handle_web_rtc_offer, `async_register_rtsp_to_web_rtc_provid...

(APOS_INCORRECT)


[typographical] ~43-~43: Caractère d’apostrophe incorrect.
Context: ...ers async_handle_async_webrtc_offer / async_register_webrtc_provider, utiliser le WS camera/capabilities c...

(APOS_INCORRECT)


[typographical] ~44-~44: Caractère d’apostrophe répété.
Context: ...aramètre config_entry du constructeur d'OptionsFlowHandler, utiliser `self.conf...

(APOS_INCORRECT)


[typographical] ~45-~45: Caractère d’apostrophe incorrect.
Context: ...ner un config entry update listener (add_update_listener) avec des méthodes ...

(APOS_INCORRECT)


[typographical] ~45-~45: Caractère d’apostrophe incorrect.
Context: ... méthodes de reload dans le config flow (async_update_reload_and_abort, `reload_...

(APOS_INCORRECT)


[typographical] ~45-~45: Caractère d’apostrophe incorrect.
Context: ...atégie : soit retirer le listener, soit async_update_and_abort(), soit reload_on_update=False sur `_ab...

(APOS_INCORRECT)


[typographical] ~46-~46: Caractère d’apostrophe incorrect.
Context: ...ique | Le nouveau modèle est un fichier quality_scale.yaml à la racine de l'i...

(APOS_INCORRECT)


[typographical] ~46-~46: Caractère d’apostrophe incorrect.
Context: ...ration listant chaque règle avec statut (done/exempt+raison/todo). Pour une ...

(APOS_INCORRECT)


[typographical] ~46-~46: Caractère d’apostrophe incorrect.
Context: ...listant chaque règle avec statut (done/exempt+raison/todo). Pour une intégra...

(APOS_INCORRECT)


[typographical] ~46-~46: Caractère d’apostrophe incorrect.
Context: ...ègle avec statut (done/exempt+raison/todo). Pour une intégration custom ce f...

(APOS_INCORRECT)


[style] ~46-~46: Une modification de la structure de la négation apportera de la légèreté et de la précision à cette phrase.
Context: ... Pour une intégration custom ce fichier n'a pas d'effet runtime mais sert de checklist/pre...

(NEGATION_AVOIR)


[typographical] ~48-~48: Caractère d’apostrophe incorrect.
Context: ...* | À surveiller | Migrer les callbacks (on_connect, on_message, etc.) vers `C...

(APOS_INCORRECT)


[typographical] ~48-~48: Caractère d’apostrophe incorrect.
Context: ...r | Migrer les callbacks (on_connect, on_message, etc.) vers `CallbackAPIVersion.VERSION...

(APOS_INCORRECT)


[typographical] ~48-~48: Caractère d’apostrophe incorrect.
Context: ...(on_connect, on_message, etc.) vers CallbackAPIVersion.VERSION2, plus cohérent MQTT 3.x/5.x | | 10 | Dé...

(APOS_INCORRECT)


[typographical] ~49-~49: Caractère d’apostrophe incorrect.
Context: ... des appels bloquants dans l'event loop (requests, urllib, time.sleep, I/O f...

(APOS_INCORRECT)


[typographical] ~49-~49: Caractère d’apostrophe incorrect.
Context: ...loquants dans l'event loop (requests, urllib, time.sleep, I/O fichier sync) | renf...

(APOS_INCORRECT)


[typographical] ~49-~49: Caractère d’apostrophe incorrect.
Context: ...ans l'event loop (requests, urllib, time.sleep, I/O fichier sync) | renforcée depuis *...

(APOS_INCORRECT)


[style] ~49-~49: Un autre verbe peut sembler plus formel et professionnel.
Context: ...r). Tout appel requests.get/post doit passer par hass.async_add_executor_job(...) a mi...

(PASSER_PAR)


[typographical] ~49-~49: Caractère d’apostrophe incorrect.
Context: ... a minima, et à terme être remplacé par aiohttp/httpx async natif | **Non trouvé de ...

(APOS_INCORRECT)


[typographical] ~52-~52: Caractère d’apostrophe incorrect.
Context: ... (API stable, juste enrichie, voir §3), EntityDescription/_attr_has_entity_name (pattern stable...

(APOS_INCORRECT)


[typographical] ~53-~53: Caractère d’apostrophe incorrect.
Context: ...ereste une règle Bronze obligatoire),select/number/button/time/switch` platf...

(APOS_INCORRECT)


[typographical] ~53-~53: Caractère d’apostrophe incorrect.
Context: ...une règle Bronze obligatoire), select/number/button/time/switch platforms (pas...

(APOS_INCORRECT)


[typographical] ~53-~53: Caractère d’apostrophe incorrect.
Context: ... Bronze obligatoire), select/number/button/time/switch platforms (pas de break...

(APOS_INCORRECT)


[typographical] ~53-~53: Caractère d’apostrophe incorrect.
Context: ...bligatoire), select/number/button/time/switch platforms (pas de breaking cha...

(APOS_INCORRECT)


[style] ~61-~61: Un tiret long peut sembler plus approprié.
Context: ... que ça apporte pour dreame_vacuum | |---|---|---| | **ConfigEntry.runtime_data...

(TIRET_LONG_1)


[style] ~61-~61: Un tiret long peut sembler plus approprié.
Context: ... ça apporte pour dreame_vacuum | |---|---|---| | ConfigEntry.runtime_data (...

(TIRET_LONG_1)


[style] ~61-~61: Un tiret long peut sembler plus approprié.
Context: ...apporte pour dreame_vacuum | |---|---|---| | ConfigEntry.runtime_data (Conf...

(TIRET_LONG_1)


[typographical] ~62-~62: Caractère d’apostrophe incorrect.
Context: ... pour dreame_vacuum | |---|---|---| | ConfigEntry.runtime_data (ConfigEntry...

(APOS_INCORRECT)


[typographical] ~62-~62: Caractère d’apostrophe incorrect.
Context: ...me_data** (ConfigEntry générique typé, type MyConfigEntry = ConfigEntry[MyData]`) | 2024.4 (blog 2024-04-30), usage géné...

(APOS_INCORRECT)


[typographical] ~62-~62: Caractère d’apostrophe incorrect.
Context: ...généralisé dès 2024.6-2024.8 | Remplace hass.data[DOMAIN][entry.entry_id]. C'est aussi la règle **Bronze `runtime...

(APOS_INCORRECT)


[typographical] ~62-~62: Caractère d’apostrophe incorrect.
Context: ...try_id]. C'est aussi la règle **Bronze runtime-data`** — actuellement quasi-certainement non...

(APOS_INCORRECT)


[typographical] ~63-~63: Caractère d’apostrophe incorrect.
Context: ...ld déclaré avec une base HA 2023.6 | | **DataUpdateCoordinator._async_setup()`** ...

(APOS_INCORRECT)


[typographical] ~63-~63: Caractère d’apostrophe incorrect.
Context: ... | Hook async dédié appelé une fois via async_config_entry_first_refresh(), pour init unique (ex: découverte des a...

(APOS_INCORRECT)


[typographical] ~64-~64: Caractère d’apostrophe incorrect.
Context: ...d| | **Helpers reauth/reconfigure** :self._get_reauth_entry(), self._get_reconfigure_entry(), self...

(APOS_INCORRECT)


[typographical] ~64-~64: Caractère d’apostrophe incorrect.
Context: ...nfigure** : self._get_reauth_entry(), self._get_reconfigure_entry(), self._abort_if_unique_id_mismatch(),...

(APOS_INCORRECT)


[typographical] ~64-~64: Caractère d’apostrophe incorrect.
Context: ...ry(), self._get_reconfigure_entry(), self._abort_if_unique_id_mismatch(), async_update_reload_and_abort(..., da...

(APOS_INCORRECT)


[typographical] ~64-~64: Caractère d’apostrophe incorrect.
Context: ... Silver reauthentication-flow et Gold reconfiguration-flow. Entries doivent être liées via `entry_...

(APOS_INCORRECT)


[typographical] ~66-~66: Caractère d’apostrophe incorrect.
Context: ...nsFlowHandler | | Config Subentries (ConfigSubentryFlow) | 2025.2 (blog 2025...

(APOS_INCORRECT)


[style] ~66-~66: Ce verbe peut être considéré comme familier dans un contexte formel.
Context: ...4 | Pertinent si un compte cloud Dreame gère plusieurs robots/hubs — permettrait de ...

(VERBES_FAMILIERS_PREMIUM)


[style] ~66-~66: Cette formulation peut être allégée afin de faciliter la lisibilité.
Context: ...oud Dreame gère plusieurs robots/hubs — permettrait de modéliser chaque robot comme sous-entrée plutôt q...

(PERMETTRAIT_DE_V_INF)


[typographical] ~67-~67: Caractère d’apostrophe incorrect.
Context: ...rée plutôt qu'un flot config unique | | **`service.async_register_platform_entity_s...

(APOS_INCORRECT)


[typographical] ~67-~67: Caractère d’apostrophe incorrect.
Context: ...ent pour les actions custom vacuum (ex: go_to, clean_zone, clean_segment) | | **D...

(APOS_INCORRECT)


[typographical] ~67-~67: Caractère d’apostrophe incorrect.
Context: ...les actions custom vacuum (ex: go_to, clean_zone, clean_segment) | | **Description pla...

(APOS_INCORRECT)


[typographical] ~67-~67: Caractère d’apostrophe incorrect.
Context: ...stom vacuum (ex: go_to, clean_zone, clean_segment) | | **Description placeholders pour tr...

(APOS_INCORRECT)


[style] ~71-~71: Un autre verbe peut sembler plus formel et professionnel.
Context: ...; les icônes d'entités/services doivent passer par icons.json plutôt que la property `ic...

(PASSER_PAR)


[typographical] ~72-~72: Caractère d’apostrophe incorrect.
Context: ...que la property icon codée en dur | | prek remplace pre-commit comme runn...

(APOS_INCORRECT)


[typographical] ~72-~72: L’écriture de cette unité de mesure semble inhabituelle.
Context: ...01-13) | Déjà fait côté ce repo (commit 81794d8 "adopt prek as the pre-commit hook run...

(ESPACE_UNITES)


[typographical] ~73-~73: Caractère d’apostrophe incorrect.
Context: ...migration de checks hassfest → pylint** (parallel-updates, diagnostics, `confi...

(APOS_INCORRECT)


[typographical] ~73-~73: Caractère d’apostrophe incorrect.
Context: ...assfest → pylint** (parallel-updates, diagnostics, config-entry-unloading, `reauthentic...

(APOS_INCORRECT)


[typographical] ~73-~73: Caractère d’apostrophe incorrect.
Context: ...t** (parallel-updates, diagnostics, config-entry-unloading, reauthentication-flow) | 2026.6 | Po...

(APOS_INCORRECT)


[typographical] ~73-~73: Caractère d’apostrophe incorrect.
Context: ...diagnostics, config-entry-unloading, reauthentication-flow`) | 2026.6 | Pour du custom (hors core),...

(APOS_INCORRECT)


[style] ~73-~73: Une phrase longue peut induire une perte de sens pour le lecteur. Celle-ci peut donc être divisée afin d’apporter de la clarté et du rythme.
Context: ...int core ne s'appliquent pas directement, mais les mêmes règles de style doivent être répl...

(POINT_MAIS)


[typographical] ~113-~113: Souhaitez-vous écrire une flèche ou le symbole « ≥ » ?
Context: ...disponible via l'UI 10. test-coverage — >95% de couverture de test sur tous les m...

(FLECHES)


[style] ~118-~118: Un autre mot peut sembler plus précis et percutant.
Context: ...— Utilise les infos de discovery pour mettre à jour les infos réseau 4.discovery` — Devic...

(METTRE_A_JOUR)


[style] ~122-~122: Un autre mot peut être plus précis.
Context: ...ocs-known-limitations— Doc décrit les limitations connues 8.docs-supported-devices` — Doc décri...

(CELEBRE)


[typographical] ~133-~133: Caractère d’apostrophe incorrect.
Context: ...-translations — Icônes via traductions (icons.json) 19. reconfiguration-flow` ...

(APOS_INCORRECT)


[typographical] ~146-~146: Caractère d’apostrophe incorrect.
Context: ...(ou migrer vers une lib MQTT async type aiomqtt), soit au minimum isoler tous les appel...

(APOS_INCORRECT)


[typographical] ~147-~147: Caractère d’apostrophe incorrect.
Context: ...quality_scale.yamlavec justification.quality_scale: gold` déclaré dans le man...

(APOS_INCORRECT)


[typographical] ~149-~149: Caractère d’apostrophe incorrect.
Context: ... pas listée nommément mais couverte par action-exceptions, entity-unavailable, `log-when-unavai...

(APOS_INCORRECT)


[typographical] ~150-~150: Caractère d’apostrophe incorrect.
Context: ... mais couverte par action-exceptions, entity-unavailable, log-when-unavailable). --- ## 5. P...

(APOS_INCORRECT)


[typographical] ~150-~150: Caractère d’apostrophe incorrect.
Context: ...tion-exceptions, entity-unavailable, log-when-unavailable`). --- ## 5. Points spécifiques vacuum...

(APOS_INCORRECT)


[typographical] ~158-~158: Caractère d’apostrophe incorrect.
Context: ...retournant un VacuumActivity(passtate), et VacuumEntityFeature` pour les fea...

(APOS_INCORRECT)


[style] ~159-~159: Ce mot apparaît déjà dans l’une des phrases précédant immédiatement celle-ci. Utilisez un synonyme pour apporter plus de variété à votre texte, excepté si la répétition est intentionnelle.
Context: ...t la priorité n°1 — l'entité vacuum est probablement cassée ou en fallback dégradé sur tou...

(FR_REPEATEDWORDS_PROBABLEMENT)


[typographical] ~161-~161: Caractère d’apostrophe incorrect.
Context: ...e install HA ≥ 2026.1. - battery_level/battery_icon sur l'entité vacuum : à so...

(APOS_INCORRECT)


[typographical] ~165-~165: Caractère d’apostrophe incorrect.
Context: ...s de nouvelle contrainte identifiée sur map_data/coordonnées de carte, actions `clean_zo...

(APOS_INCORRECT)


[typographical] ~165-~165: Caractère d’apostrophe incorrect.
Context: ...map_data/coordonnées de carte, actions clean_zone/clean_segment/go_to` au-delà du patt...

(APOS_INCORRECT)


[typographical] ~165-~165: Caractère d’apostrophe incorrect.
Context: ...rdonnées de carte, actions clean_zone/clean_segment/go_to au-delà du pattern générique "s...

(APOS_INCORRECT)


[typographical] ~165-~165: Caractère d’apostrophe incorrect.
Context: ...ttern générique "services → actions" et async_register_platform_entity_service. ### Camera - Si dreame_vacuum expos...

(APOS_INCORRECT)


[typographical] ~171-~171: Il manque une espace après le point.
Context: ... - Le rendu de carte (pillow/numpy) via camera.Image / async_camera_image doit rester **d...

(ESPACE_APRES_POINT)


[typographical] ~172-~172: Caractère d’apostrophe incorrect.
Context: ...mage doit rester **dans l'executor** (hass.async_add_executor_job`) — Pillow/n...

(APOS_INCORRECT)


[typographical] ~176-~176: Caractère d’apostrophe incorrect.
Context: ...o-mqttépinglée dansmanifest.jsonrequirements. Si < 2.0, planifier la migration ...

(APOS_INCORRECT)


[typographical] ~176-~176: Caractère d’apostrophe incorrect.
Context: ...ns manifest.jsonrequirements. Si < 2.0, planifier la migration `CallbackAPIV...

(APOS_INCORRECT)


[typographical] ~180-~180: Caractère d’apostrophe incorrect.
Context: ...ad — s'assurer que tous les callbacks (on_message, on_connect, etc.) qui tou...

(APOS_INCORRECT)


[typographical] ~180-~180: Caractère d’apostrophe incorrect.
Context: ...que tous les callbacks (on_message, on_connect, etc.) qui touchent l'état HA passent b...

(APOS_INCORRECT)


[typographical] ~181-~181: Caractère d’apostrophe incorrect.
Context: ...hass.loop.call_soon_threadsafe(...)ouasync_add_executor_job`, jamais d'appel direct à des coroutin...

(APOS_INCORRECT)


[typographical] ~182-~182: Il manque une espace après le point.
Context: ...amais d'appel direct à des coroutines/hass.states depuis le thread paho. - C'est ce patt...

(ESPACE_APRES_POINT)


[typographical] ~191-~191: Caractère d’apostrophe incorrect.
Context: ...: englober tout appel requests.* dans await hass.async_add_executor_job(...). - Recommandation cible : migrer vers `...

(APOS_INCORRECT)


[typographical] ~193-~193: Caractère d’apostrophe incorrect.
Context: ...ce systématique de HA, réutilisable via aiohttp_client.async_get_clientsession(hass)) ou httpx.AsyncClient, ce qui débloqu...

(APOS_INCORRECT)


[typographical] ~194-~194: Caractère d’apostrophe incorrect.
Context: ...ient.async_get_clientsession(hass)) ou httpx.AsyncClient, ce qui débloque aussi inject-webses...

(APOS_INCORRECT)


[style] ~203-~203: Préférez la voix active à une structure impersonnelle pour apporter du rythme à votre phrase.
Context: .... Si le refactor adopte ces patterns, il faut remonter le minimum HA en conséquence (au minimu...

(IL_FAUT_QUE)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/overhaul/ha-backend-state-of-art-2026-07-02.md` around lines 12 - 205,
The markdown content in this document still triggers codespell, so normalize the
prose to remove or correct misspelled terms and nonstandard spellings throughout
the affected sections, using the document’s existing headings and lists as
anchors. Check the repeated technical phrases and acronyms in the overview and
quality-scale sections, and if any intentional domain terms must remain, add
them to the repo’s codespell whitelist or exclusion rather than leaving raw
failures in the text.

Source: Pipeline failures

Comment on lines +588 to +607
def test_decode_legacy_funiture_info_format():
"""The legacy 'funiture_info' payload (furniture_version=1) swaps type 8<->25 and reads absolute rect fields."""
header = build_header(width=0, height=0)
furniture = [11, 8, 3, 40, 60, 0, 500, 600, 0, 45.0, 0, 0, 1.5, 2]
partial = make_partial(header, b"", {"funiture_info": [furniture]})

map_data, _ = DreameVacuumMapDecoder.decode_map_data_from_partial(partial, False)

assert map_data.furniture_version == 1
f = map_data.saved_furnitures[1]
assert (f.x, f.y) == (500, 600)
assert (f.x0, f.y0) == (480, 570)
assert (f.width, f.height) == (40, 60)
assert f.type == FurnitureType.ROUND_COFFEE_TABLE # type 8 swapped to 25
assert f.size_type == 2
assert f.angle == 45.0
assert f.scale == 1.5
assert f.furniture_id == 11
assert f.segment_id == 3

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Pre-commit CI failure: comment triggers python-use-type-annotations hook.

Line 601's # type 8 swapped to 25 comment is flagged by the pre-commit hook as an old-style type comment, failing CI. Reword the comment to avoid the pattern.

🔧 Proposed fix
-    assert f.type == FurnitureType.ROUND_COFFEE_TABLE  # type 8 swapped to 25
+    assert f.type == FurnitureType.ROUND_COFFEE_TABLE  # furniture code 8 remapped to 25
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_decode_legacy_funiture_info_format():
"""The legacy 'funiture_info' payload (furniture_version=1) swaps type 8<->25 and reads absolute rect fields."""
header = build_header(width=0, height=0)
furniture = [11, 8, 3, 40, 60, 0, 500, 600, 0, 45.0, 0, 0, 1.5, 2]
partial = make_partial(header, b"", {"funiture_info": [furniture]})
map_data, _ = DreameVacuumMapDecoder.decode_map_data_from_partial(partial, False)
assert map_data.furniture_version == 1
f = map_data.saved_furnitures[1]
assert (f.x, f.y) == (500, 600)
assert (f.x0, f.y0) == (480, 570)
assert (f.width, f.height) == (40, 60)
assert f.type == FurnitureType.ROUND_COFFEE_TABLE # type 8 swapped to 25
assert f.size_type == 2
assert f.angle == 45.0
assert f.scale == 1.5
assert f.furniture_id == 11
assert f.segment_id == 3
def test_decode_legacy_funiture_info_format():
"""The legacy 'funiture_info' payload (furniture_version=1) swaps type 8<->25 and reads absolute rect fields."""
header = build_header(width=0, height=0)
furniture = [11, 8, 3, 40, 60, 0, 500, 600, 0, 45.0, 0, 0, 1.5, 2]
partial = make_partial(header, b"", {"funiture_info": [furniture]})
map_data, _ = DreameVacuumMapDecoder.decode_map_data_from_partial(partial, False)
assert map_data.furniture_version == 1
f = map_data.saved_furnitures[1]
assert (f.x, f.y) == (500, 600)
assert (f.x0, f.y0) == (480, 570)
assert (f.width, f.height) == (40, 60)
assert f.type == FurnitureType.ROUND_COFFEE_TABLE # furniture code 8 remapped to 25
assert f.size_type == 2
assert f.angle == 45.0
assert f.scale == 1.5
assert f.furniture_id == 11
assert f.segment_id == 3
🧰 Tools
🪛 GitHub Actions: CI / 2_Pre-commit (prek).txt

[error] 601-601: pre-commit hook 'python-use-type-annotations' failed (exit code 1). Type annotations are required at: 'assert f.type == FurnitureType.ROUND_COFFEE_TABLE # type 8 swapped to 25'.

🪛 GitHub Actions: CI / Pre-commit (prek)

[error] 601-601: pre-commit hook python-use-type-annotations (type annotations not comments) failed (exit code 1). Offending code: "assert f.type == FurnitureType.ROUND_COFFEE_TABLE # type 8 swapped to 25".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_map_decoder_paths.py` around lines 588 - 607, The inline comment
in test_decode_legacy_funiture_info_format is being misread by the
python-use-type-annotations hook as an old-style type comment. Reword or remove
the phrasing in that assertion comment so it no longer starts with “type 8,”
while keeping the note that legacy furniture type 8 maps to ROUND_COFFEE_TABLE.
Focus on the comment text only; no test logic changes are needed.

Source: Pipeline failures

foXaCe added 18 commits July 2, 2026 10:58
- Migrate OptionsFlow to OptionsFlowWithReload (native config_entry
  property, automatic reload), replacing the deprecated constructor
  pattern and the manual add_update_listener/update_listener combo
- Unsubscribe dynamic-entity coordinator listeners on unload via
  entry.async_on_unload (number, button, select, camera) — previously
  leaked across config entry reloads
- map_optimizer: drop the never-invoked pure-Python fallback (~1200
  lines duplicating the MiniRacer JS algorithm) and the js_optimizer
  parameter; the JS path is the only production path
- map_manager: drop unused _request_t_map/_request_w_map, harmonize
  threading.Timer import
- resources: drop unused clear_cache/get_cache_stats
- device_setters: drop dead _update_self_clean_time twin and the
  commented-out AI privacy block
- device_status/_core: drop unused _LOGGER and 24 no-op
  'if value is not None: pass' vestiges
- const: drop unused CONF_TYPE; dreame/const: drop duplicated
  CLEAN_TANK_LEVEL mapping entry
- device_info: place module docstring before __future__ import so it
  is an actual docstring
- recorder: fix carpet_sensivity_list typo (exclusion was inoperative)
- vacuum: extract the 32 entity-service registrations into
  _async_register_services() (async_setup_entry 471 -> 12 lines)
- repo: delete obsolete CI_STATUS.md, merge requirements_dev.txt into
  requirements-dev.txt
- protocol: fix set_credentials bytes-vs-str comparison that reset the
  local connection on every call even with identical credentials
- protocol: add jitter to the retry backoff via a shared
  resilience.backoff_delay() (was duplicated inline 4x without jitter)
- protocol: truncate HTTP failure bodies in DEBUG logs (2 sites)
- map_manager: release _request_queue keys on failure (a failed
  map_id/frame_id request could never be retried)
- map_manager: one corrupted saved map no longer aborts the whole
  map-list processing (return -> continue, 2 sites)
- map_manager: purge expired entries from the _file_urls cache
  (unbounded growth, object names change with every map update)
- map_decoder: stop logging full raw maps / map JSON payloads in DEBUG
- map_data_json_renderer: guard against KeyError when the IMAGE layer
  has not been built yet
- map_optimizer: add close() and release the V8 context on unload
- resilience/exceptions: drop dead CircuitBreaker.check(),
  retry_with_backoff(), AuthenticationError, InvalidResponseError,
  CircuitOpenError (never raised/called anywhere)
- manifest: pin paho-mqtt<3.0 (CallbackAPIVersion.VERSION1 is removed
  in paho 3.0; VERSION2 migration deliberately deferred)
- camera: run device.get_map_for_render (may trigger the V8 map
  optimizer and deep copies) in the executor on all 4 async call
  sites (live image, history, recovery, wifi map)
- map_renderer: cache the encoded PNG of default_map_image and
  disconnected_map_image; both properties are read from the event
  loop and previously re-encoded (and re-blurred) on every access
- binary_sensor: fix overlong comment (last ruff E501)

Verified: battery is already exposed as a dedicated battery-class
sensor and the vacuum entity never sets battery_level, so the
HA 2026.8 StateVacuumEntity.battery_level removal does not affect
this integration.
- config_flow: replace bare voluptuous markers with modern selectors
  (TextSelector, password-typed credential fields, dropdown
  SelectSelector for countries/color schemes/icon sets/devices,
  multi-select SelectSelector for notifications/hidden map objects,
  BooleanSelector for toggles)
- translations: add the 7 missing keys (vector_rooms option,
  segments_changed repair issue) to the 18 out-of-sync languages with
  native translations; all 20 languages now at strict key parity with
  strings.json
- add 41 missing docstrings across the HA layer (camera, coordinator,
  config_flow, vacuum, select, number, button, entity)
- add quality_scale.yaml with honest per-rule statuses (documented
  todos: action-setup, lib test-coverage, full strict-typing;
  justified exemptions: discovery, async-dependency)
- hacs.json: raise minimum Home Assistant to 2025.8.0
  (OptionsFlowWithReload)
- dreame/const: add Final to the 5 remaining module-level mappings
- ruff format drift fixed on 4 files

Validated: ruff check+format clean, mypy ratchet green (51 files),
hassfest 0 invalid, 972 tests green
- camera: 63% -> 100% (all 7 HTTP views with real aiohttp response
  assertions, still stream, proxy caches, extra_state_attributes)
- new test_device_setters (90) / test_device_actions (44): optimistic
  update + rollback semantics, reflection dispatch, HH:MM parsing,
  zone/segment/spot payloads, all 14 consumable resets
- new test_map_manager (11) / test_map_optimizer (9): lock the Phase 4
  fixes (request-queue release on failure, expired URL purge,
  corrupted-map continue, optimizer.close) and exercise the real V8
  optimize path
- suite hygiene: drop dead conftest fixtures, ~25 existence-only
  tests, the never-injected mock_device_for_status fixture; replace
  with real device_status behaviour tests; platform imports now fail
  loudly instead of silently skipping
- coverage: 38.5% -> 46.0% global, HA layer 100% everywhere
  (entity.py 99%); raise coverage fail_under 35 -> 45
- CHANGELOG: full [Unreleased] section covering the overhaul
- ARCHITECTURE: coverage figures updated (HA layer 100%), executor
  invariant of the V8/PIL map pipeline documented, quality_scale.yaml
  referenced
- README: fix the iot_class description (cloud push over MQTT, not
  cloud polling)
- docs/overhaul: archive after-measurements (perf, unique_ids)
- remove the overhaul working-state file
- new services.py: all 39 entity services (34 vacuum, 4 select,
  1 camera) registered once from the integration's async_setup via
  service.async_register_platform_entity_service (HA 2025.9+), with
  entity methods referenced by name so no platform module is imported
  at setup time
- vacuum/select/camera platform setups no longer register services
- __init__: add async_setup + CONFIG_SCHEMA (config-entry-only)
- declare the previously undocumented vacuum_set_property and
  vacuum_call_action services in services.yaml, strings.json,
  icons.json and all 20 translations (native wording)
- quality_scale.yaml: action-setup -> done
- tests: new test_services.py (6 tests incl. services.yaml parity);
  platform tests updated
- Client is created with VERSION2; the dead paho 1.x branch is gone
- on_connect/on_disconnect use the v2 signatures and ReasonCode
- the auth-refused relogin moved from on_disconnect (where the v3->v5
  mapping collapses everything to 'Unspecified error') to on_connect,
  where 'Not authorized' (135) is precise
- keep paho-mqtt pinned <3.0 until 3.0 ships and is validated

Verified live on HA dev 2026.7: MQTT connects (paho v2 thread),
subscribes and receives map pushes; 0 errors.
- test_device_status_core.py: 331 cases over the 198 status properties
  (_core 63%, _consumables 100%, _station 100%)
- test_map_renderer.py: full render_map pipeline on synthetic maps with
  exact RGBA pixel asserts (_core 38%, _helpers 95%, _shapes 98%,
  _objects 59%), byte-cache identity checks for default/disconnected
  images
- test_map_data_json_renderer.py: Valetudo JSON in zTXt chunk verified
  point by point incl. exact RLE arrays (100%), KeyError regression
  locked
- test_map_decoder_paths.py: 43 tests on synthetic wire-format payloads
  (struct header + zlib + base64, AES-CBC path included) — decoder
  18% -> 81%
- test_map_editor.py (51) / test_device_map_ops.py (79): optimistic map
  edits, merge/split on real numpy grids, get_map_for_render branches,
  CRUD delegations — editor 9% -> 50%, map ops 6% -> 45%
- test_device_core.py (85): listeners/debounce, 50-item property
  batching, dynamic update intervals, credential redaction (caplog),
  consumable request paths — device.py 24% -> 50%
- coverage: global 46% -> 66%; raise fail_under 45 -> 65
- HA >= 2025.8 requires Python >= 3.13; on 3.12 pip resolves an older
  Home Assistant lacking async_register_platform_entity_service
- the mypy job must match HA's target runtime (3.14 resolves an HA
  build with 3.14-only syntax that mypy python_version 3.13 rejects)
- codespell: skip docs/overhaul in both walk and explicit-path modes
- reword a comment that tripped python-use-type-annotations
@foXaCe foXaCe force-pushed the refactor/integration-overhaul-2026-07-02 branch from bc26d79 to 6221fa8 Compare July 2, 2026 08:59
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.33333% with 11 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
custom_components/dreame_vacuum/dreame/protocol.py 63.63% 8 Missing ⚠️
...m_components/dreame_vacuum/dreame/map_optimizer.py 92.00% 2 Missing ⚠️
...tom_components/dreame_vacuum/dreame/map_manager.py 87.50% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@foXaCe foXaCe enabled auto-merge (squash) July 2, 2026 09:03
@foXaCe foXaCe merged commit 053bc96 into main Jul 2, 2026
16 checks passed
@foXaCe foXaCe deleted the refactor/integration-overhaul-2026-07-02 branch July 2, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants