Skip to content

Add Mitsubishi WF-RAC integration - #181403

Open
blues-sechseck wants to merge 6 commits into
home-assistant:devfrom
blues-sechseck:mitsubishi-wf-rac-initial
Open

Add Mitsubishi WF-RAC integration#181403
blues-sechseck wants to merge 6 commits into
home-assistant:devfrom
blues-sechseck:mitsubishi-wf-rac-initial

Conversation

@blues-sechseck

@blues-sechseck blues-sechseck commented Sep 5, 2026

Copy link
Copy Markdown

Proposed change

New integration for Mitsubishi Heavy Industries air conditioners fitted with the WF-RAC wireless LAN module (sold under that part number, marketed together with the Smart M-Air app). It talks to the module over the local network using the same HTTP API the app uses — no manufacturer account, no outbound connection, local_polling.

This first PR is deliberately minimal: a config flow with zeroconf discovery, and the climate platform. The integration has existed as a custom component for a while and covers eight platforms there; the remaining seven will follow as separate PRs once this one is in, the way the component checklist asks for.

Four things worth flagging before review:

  • The protocol layer is a separate library. pywfrac holds everything that builds and parses the module's airconStat frames. It takes an injected aiohttp session and an explicit certificate path rather than reaching into hass, is fully typed, and has its own test suite.
  • The domain is mitsubishi_wf_rac, next to the existing melcloud. Those are two different manufacturers: melcloud is Mitsubishi Electric, this is Mitsubishi Heavy Industries, with an unrelated protocol and no shared hardware.
  • async_migrate_entry and VERSION = 6 are included even though core has no old entries of its own. The domain matches the custom component's, so an installation that switches over carries config entries of versions 1–5 with it, and those would fail to load without the migration. The last step, v5 → v6, moves the host from options back into data, where the discovery helper that refreshes a changed address actually writes it.
  • The module presents a self-signed certificate. If ac_cert.pem is present in the configuration directory the integration pins it; without it the connection falls back to a permissive SSL context. const.py carries the filename and the openssl recipe that produces it.

quality_scale is declared as platinum. The two rules that were open when this PR was filed are met now: coverage is above 95 % in every module of the integration, and the documentation has gained an Examples section.

Verified locally: python3 -m script.hassfest (all 1,515 integrations, 0 invalid), python3 -m script.gen_requirements_all (no diff), ruff check and ruff format, mypy, pylint, and pytest tests/components/mitsubishi_wf_rac — 79 tests, 98 % overall with no module below 95 %.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies a diff between library versions and ideally a link to the changelog/release notes is added to the PR description.

To help with the load of incoming pull requests:

Copilot AI balanced review requested due to automatic review settings September 5, 2026 21:18

@home-assistant home-assistant Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hello @blues-sechseck,

When attempting to inspect the commits of your pull request for CLA signature status among all authors we encountered commit(s) which were not linked to a GitHub account, thus not allowing us to determine their status(es).

The commits that are missing a linked GitHub account are the following:

Unfortunately, we are unable to accept this pull request until this situation is corrected.

Here are your options:

  1. If you had an email address set for the commit that simply wasn't linked to your GitHub account you can link that email now and it will retroactively apply to your commits. The simplest way to do this is to click the link to one of the above commits and look for a blue question mark in a blue circle in the top left. Hovering over that bubble will show you what email address you used. Clicking on that button will take you to your email address settings on GitHub. Just add the email address on that page and you're all set. GitHub has more information about this option in their help center.

  2. If you didn't use an email address at all, it was an invalid email, or it's one you can't link to your GitHub, you will need to change the authorship information of the commit and your global Git settings so this doesn't happen again going forward. GitHub provides some great instructions on how to change your authorship information in their help center.

    • If you only made a single commit you should be able to run
      git commit --amend --author="Author Name <email@address.com>"
      
      (substituting "Author Name" and "email@address.com" for your actual information) to set the authorship information.
    • If you made more than one commit and the commit with the missing authorship information is not the most recent one you have two options:
      1. You can re-create all commits missing authorship information. This is going to be the easiest solution for developers that aren't extremely confident in their Git and command line skills.
      2. You can use this script that GitHub provides to rewrite history. Please note: this should be used only if you are very confident in your abilities and understand its impacts.
    • Whichever method you choose, I will come by to re-check the pull request once you push the fixes to this branch.

We apologize for this inconvenience, especially since it usually bites new contributors to Home Assistant. We hope you understand the need for us to protect ourselves and the great community we all have built legally. The best thing to come out of this is that you only need to fix this once and it benefits the entire Home Assistant and GitHub community.

Thanks, I look forward to checking this PR again soon! ❤️

@home-assistant

home-assistant Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

@home-assistant home-assistant Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When adding new integrations, limit included platforms to a single platform. While we appreciate the effort, reviewing larger than necessary PRs slows down the review process. Please reduce this PR to a single platform. See the review process for more details.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Config identity, command error handling, climate behavior, and quality-scale compliance contain unresolved issues.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds local polling support for Mitsubishi Heavy Industries air conditioners using WF-RAC modules.

Changes:

  • Adds configuration, discovery, migration, diagnostics, and climate support.
  • Introduces the pywfrac dependency and integration metadata.
  • Adds comprehensive fixtures, snapshots, and integration tests.
File summaries
File Description
.strict-typing Enables strict typing.
CODEOWNERS Adds integration owners.
mypy.ini Enables strict mypy checks.
requirements_all.txt Adds pywfrac.
homeassistant/generated/config_flows.py Registers the config flow.
homeassistant/generated/integrations.json Registers integration metadata.
homeassistant/generated/zeroconf.py Registers zeroconf discovery.
homeassistant/components/mitsubishi_wf_rac/__init__.py Implements lifecycle and migrations.
homeassistant/components/mitsubishi_wf_rac/climate.py Implements climate controls.
homeassistant/components/mitsubishi_wf_rac/config_flow.py Implements setup and options flows.
homeassistant/components/mitsubishi_wf_rac/const.py Defines protocol and entity constants.
homeassistant/components/mitsubishi_wf_rac/coordinator.py Implements polling and commands.
homeassistant/components/mitsubishi_wf_rac/diagnostics.py Provides redacted diagnostics.
homeassistant/components/mitsubishi_wf_rac/entity.py Adds the shared entity base.
homeassistant/components/mitsubishi_wf_rac/icons.json Defines entity-state icons.
homeassistant/components/mitsubishi_wf_rac/manifest.json Declares integration metadata.
homeassistant/components/mitsubishi_wf_rac/py.typed Marks the integration typed.
homeassistant/components/mitsubishi_wf_rac/quality_scale.yaml Declares quality-scale compliance.
homeassistant/components/mitsubishi_wf_rac/strings.json Adds user-facing translations.
tests/components/mitsubishi_wf_rac/__init__.py Defines shared test data.
tests/components/mitsubishi_wf_rac/conftest.py Provides integration fixtures.
tests/components/mitsubishi_wf_rac/fixtures/aircon_stat.json Adds a protocol response fixture.
tests/components/mitsubishi_wf_rac/snapshots/test_climate.ambr Captures climate entity state.
tests/components/mitsubishi_wf_rac/snapshots/test_diagnostics.ambr Captures diagnostics output.
tests/components/mitsubishi_wf_rac/test_climate.py Tests climate behavior.
tests/components/mitsubishi_wf_rac/test_config_flow.py Tests configuration flows.
tests/components/mitsubishi_wf_rac/test_coordinator.py Tests polling and recovery.
tests/components/mitsubishi_wf_rac/test_diagnostics.py Tests diagnostics redaction.
tests/components/mitsubishi_wf_rac/test_init.py Tests setup, removal, and migrations.
Review details

Suppressed comments (3)

homeassistant/components/mitsubishi_wf_rac/config_flow.py:314

  • Remove the name field from manual setup and generate the config-entry title from device information. Home Assistant integrations must not ask users to choose config-entry names during onboarding because entries and devices can be renamed afterward.
                field(CONF_NAME, vol.Required, "Airco unknown"): cv.string,

homeassistant/components/mitsubishi_wf_rac/config_flow.py:339

  • Remove name editing from reconfiguration. Reconfigure should update connection details only; changing the user-visible name belongs to Home Assistant's standard rename UI.
                field(CONF_NAME, vol.Required): cv.string,

homeassistant/components/mitsubishi_wf_rac/quality_scale.yaml:57

  • Mark docs-use-cases incomplete until the linked documentation explains a concrete user scenario or benefit. The page describes installation and supported controls, but it does not include the use-case illustration required by this no-exceptions rule.
  docs-use-cases: done
  • Files reviewed: 25/29 changed files
  • Comments generated: 16
  • Review effort level: Balanced

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.


# AUTO mode - use CoolHotJudge directly (unit tells us what it's doing)
if _mode == 0:
return HVACAction.HEATING if airco.CoolHotJudge else HVACAction.COOLING
Comment on lines +185 to +190
# An entry whose setup never got as far as storing its runtime data can
# still be unloaded - there is simply no coordinator to shut down then.
if (data := getattr(entry, "runtime_data", None)) is not None:
await data.device.async_shutdown()

if unload_ok:
class AircoClimate(WfRacEntity, ClimateEntity):
"""Representation of a climate entity."""

_attr_supported_features: ClimateEntityFeature = SUPPORT_FLAGS
Comment on lines +238 to +240
target_offset = self._resolve_target_offset(target_hvac_mode)
target_temp = set_temp - target_offset
target_temp = max(min_temp, min(max_temp, target_temp))
Comment on lines +206 to +208
info = await self._async_register_airco(
self.hass, user_input, allow_port_fallback=allow_port_fallback
)
Comment on lines +95 to +97
super().__init__(device)
self._attr_name = device.device_name
self._attr_unique_id = f"{DOMAIN}-{self._device.airco_id}-climate"
# Intentionally broad: this is the outermost boundary of the config
# flow step, so any bug here should show the user a graceful
# "unexpected_error" instead of crashing the flow.
_LOGGER.error("Unexpected exception")
Comment on lines +279 to +281
field(
CONF_NAME, vol.Required, f"Airco {self._discovery_info[CONF_NAME]}"
): str,
comment: >-
No credentials to renew - the operatorId registration is a device-side
account slot, not a login.
test-coverage: done
discovery: done
discovery-update-info: done
docs-data-update: done
docs-examples: done
@blues-sechseck
blues-sechseck force-pushed the mitsubishi-wf-rac-initial branch from 89e301a to 0e8534d Compare September 5, 2026 21:29

@home-assistant home-assistant Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @blues-sechseck

It seems you haven't yet signed a CLA. Please do so here.

Once you do that we will be able to review and accept this pull request.

Thanks!

Local control of Mitsubishi Heavy Industries air conditioners fitted with the
WF-RAC wireless module, over the module's own HTTP API on the local network.
No cloud account and no outbound internet connection.

This first PR is deliberately minimal: config flow plus the climate platform.
The protocol lives in pywfrac on PyPI; the remaining seven platforms follow as
separate PRs once this one is in.
Copilot AI review requested due to automatic review settings September 5, 2026 22:44
@blues-sechseck
blues-sechseck force-pushed the mitsubishi-wf-rac-initial branch from 0e8534d to a79b714 Compare September 5, 2026 22:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The config flow contains security- and correctness-adjacent issues (sensitive identifier logging, error logging behavior, and entry/ID handling) that should be addressed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

homeassistant/components/mitsubishi_wf_rac/config_flow.py:236

  • Log unexpected exceptions with stack context so failures are diagnosable while still surfacing the form error to the user.
            except Exception:  # noqa: BLE001  # pylint: disable=broad-except
                # Intentionally broad: this is the outermost boundary of the config
                # flow step, so any bug here should show the user a graceful
                # "unexpected_error" instead of crashing the flow.
                _LOGGER.error("Unexpected exception")
                errors[CONF_BASE] = "unexpected_error"

homeassistant/components/mitsubishi_wf_rac/config_flow.py:210

  • Set the config entry unique_id to the discovered airco_id before creating the entry so the same device can’t be added twice (e.g., when its IP changes).
                info = await self._async_register_airco(
                    self.hass, user_input, allow_port_fallback=allow_port_fallback
                )

                data_input = user_input.copy()
  • Files reviewed: 25/29 changed files
  • Comments generated: 6
  • Review effort level: Lite

Comment on lines +161 to +165
_LOGGER.info(
"Trying to register OperatorId[%s] on Airco[%s]",
data[CONF_OPERATOR_ID],
data[CONF_AIRCO_ID],
)
@@ -0,0 +1,224 @@
"""The WF-RAC sensor integration.""" # pylint: disable=invalid-name
Comment on lines +174 to +180
async def _async_fetch_operator_id(self) -> str:
"""Fetch UUID operator id if exists otherwise create it."""
entry = self._find_entry_matching(CONF_OPERATOR_ID, bool)
if entry:
return str(entry.data[CONF_OPERATOR_ID])
return f"hassio-{str(uuid4())[7:]}"

Comment on lines +181 to +186
async def _async_fetch_device_id(self) -> str:
"""Fetch unique device id if exists otherwise create it."""
entry = self._find_entry_matching(CONF_DEVICE_ID, bool)
if entry:
return str(entry.data[CONF_DEVICE_ID])
return f"homeassistant-device-{uuid4().hex[21:]}"
Comment on lines +215 to +216
data_input.pop(CONF_HOST)

Comment on lines +222 to +227
except KnownError as error:
_LOGGER.exception("create failed")
errors, placeholders = error.get_errors_and_placeholders(
data_schema.schema
)
errors.update(errors)
@blues-sechseck
blues-sechseck marked this pull request as ready for review September 5, 2026 22:51
@home-assistant
home-assistant Bot dismissed stale reviews from themself September 5, 2026 22:51

Stale

Host moves from entry.options back into entry.data (migration v5 -> v6).
It lived in options so it could be edited there, which the reconfigure flow
does now - and options was the wrong home for a second reason: the discovery
helper that refreshes a changed address only merges into entry.data, so the
refresh wrote a key setup never read and the address stayed stale.

Commands are awaited to their result. async_queue_command() now waits on the
flush its parameters ended up in, set_airco() raises a translated
HomeAssistantError instead of a library error, and async_shutdown() cancels a
command still in flight. The cost is that two blocking calls on the same
entity no longer coalesce - climate runs with PARALLEL_UPDATES = 1, so the
window is over before the second call starts. Reporting a refusal back to the
action that caused it is worth more than saving that frame.

The manual step had no unique id to abort on, so a unit reached at a second
address became a second entry whose entities collide with the first one's; it
now matches on the airco id the registration returns.

KnownError is a plain Exception: none of them leaves the config flow, they are
caught and turned into entries in the errors dict that async_show_form renders,
so they carry an error_name rather than a translation key.

quality_scale drops to bronze until two rules are honestly met: coverage is at
90% where test-coverage asks for 95%, and the documentation carries no
blueprint example.

Also: has_entity_name on the climate entity, the coordinator is only shut down
once unloading the platforms succeeded, AddConfigEntryEntitiesCallback,
climate imports through the component root, the swing parameter name, logger
levels in the config flow, force_update no longer stored on the entry, and a
CoolHotJudge comment that read as the opposite of what the parser does.

The unique id keeps its domain and platform segments deliberately: ~1900
installations of the custom component share this domain and shortening it
would orphan every entity they have named or wired into an automation.
Copilot AI review requested due to automatic review settings September 5, 2026 23:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The config flow’s zeroconf unique-id derivation and registration error handling can lead to unstable unique IDs and “unexpected_error” instead of a proper “cannot_connect” outcome.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (3)

Previously missed (3) — in code that hasn't changed since the last review.

homeassistant/components/mitsubishi_wf_rac/config_flow.py:392

  • Derive node_name with removesuffix() instead of blind slicing so non-.local hostnames don't get truncated incorrectly (which would break unique_id stability).
    homeassistant/components/mitsubishi_wf_rac/climate.py:239
  • Update the comment to describe applying target_offset (not CONF_INDOOR_OFFSET) and remove the reference to sensor.py, which is not part of this integration yet.
    homeassistant/components/mitsubishi_wf_rac/entity.py:95
  • Log the caught exception details when _update_state() fails so parsing/index errors are actionable during debugging.
  • Files reviewed: 25/29 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +153 to +157
result = await repository.update_account_info(airco_id, hass.config.time_zone)
if not result:
raise CannotConnect(reason="no answer to the registration request")
if int(result["result"]) == 2:
raise TooManyDevicesRegistered
Awaiting every command cost the consolidation window: with PARALLEL_UPDATES = 1
the second action does not start until the first has been sent, so a scene
that sets mode, temperature and fan speed left as three frames where it used
to leave as one.

The semaphore was never what protected the module. The coordinator holds a
send lock around the request and spaces requests by MIN_TIME_BETWEEN_REQUESTS;
the platform limit only kept actions issued together from reaching the
consolidation window together - which is precisely the case worth merging.
Zero restores that without giving up the error the caller now gets.

Actions issued one after another still leave as separate frames. There is no
window left to join once the first has been sent and answered, and that is
the honest cost of reporting a refusal to the action that caused it.
Copilot AI review requested due to automatic review settings September 5, 2026 23:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The write-lock retry delay calculation uses a local-time naive timestamp, which can produce incorrect retry timing on non-UTC systems.

Review details

Suppressed comments (4)

Previously missed (3) — in code that hasn't changed since the last review.

homeassistant/components/mitsubishi_wf_rac/coordinator.py:299

  • Compute the remaining write-lock time using an explicit UTC timestamp to avoid local-timezone skew affecting the retry delay.
    homeassistant/components/mitsubishi_wf_rac/config_flow.py:547
  • Fix the KnownError docstring to refer to the "error" translation section (not "errors") so future changes don’t follow the wrong contract.

This issue also appears on line 593 of the same file.
homeassistant/components/mitsubishi_wf_rac/strings.json:13

  • Update the invalid-name error text to match the current validation (minimum length) or add a name uniqueness check if that’s intended.

homeassistant/components/mitsubishi_wf_rac/config_flow.py:595

  • Correct the InvalidName docstring to describe a name validation error (not a hostname validation error).
class InvalidName(KnownError):
    """Error to indicate there is an invalid hostname."""

  • Files reviewed: 25/29 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Imports of climate constants go through the component root, the offset helper
stops shadowing typing.override with a local name, and the write-lock delay
reads the clock through homeassistant.util.dt.
Copilot AI review requested due to automatic review settings September 5, 2026 23:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

A few correctness issues were found (notably time calculations for write-lock retry and robustness around registration responses) that should be addressed before approval.

Review details

Suppressed comments (5)

Previously missed (2) — in code that hasn't changed since the last review.

homeassistant/components/mitsubishi_wf_rac/coordinator.py:301

  • Compute the remaining write-lock time using a UTC/epoch-based clock (not dt_util.naive_now().timestamp()) to avoid timezone-dependent delays.
    homeassistant/components/mitsubishi_wf_rac/strings.json:12
  • Align the "name_invalid" error text with the actual validation (currently only enforces a minimum length, not uniqueness).

homeassistant/components/mitsubishi_wf_rac/config_flow.py:166

  • Generate operator IDs using the full UUID so the stored operator_id is a valid UUID string and avoids surprising truncation.
        return f"hassio-{str(uuid4())[7:]}"

homeassistant/components/mitsubishi_wf_rac/config_flow.py:157

  • Handle unexpected updateAccountInfo responses defensively so a missing "result" key does not crash the config flow.
        result = await repository.update_account_info(airco_id, hass.config.time_zone)
        if not result:
            raise CannotConnect(reason="no answer to the registration request")
        if int(result["result"]) == 2:
            raise TooManyDevicesRegistered

homeassistant/components/mitsubishi_wf_rac/config_flow.py:173

  • Generate device IDs using the full UUID hex to reduce collision risk compared to taking only the last 11 hex digits.
        return f"homeassistant-device-{uuid4().hex[21:]}"
  • Files reviewed: 25/29 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Coverage was 90 % against the 95 % test-coverage asks for, so the rule was
marked todo and the manifest dropped to bronze. The gaps were the branches
that only show up on real hardware: every operation mode and the HVAC action
it maps to, the wider heating range PresetTempRange2 models report, the
cooling ceiling, a setpoint below the mode's floor, setting temperature and
mode in one call, 3D auto on both louvers, the write lock being waited out,
an evicted account re-registering mid-command, the repair issue for a full
account table appearing and clearing, a frame that answers but does not
parse, and a poll that never answers at all.

97 % overall now, no module below 95 %, so test-coverage and the platinum
declaration are back.
Copilot AI review requested due to automatic review settings September 5, 2026 23:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The PR description’s stated quality scale/versioning intent does not match the actual manifest.json/quality_scale.yaml/VERSION values in the code.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (4)

Previously missed (2) — in code that hasn't changed since the last review.

homeassistant/components/mitsubishi_wf_rac/config_flow.py:55

  • Update the PR description to match the actual config entry VERSION here (the description mentions VERSION=5 but the code ships VERSION=6 with a v5→v6 migration).
    homeassistant/components/mitsubishi_wf_rac/entity.py:95
  • Include exception details in this warning so unexpected parsing/indexing errors can be diagnosed from logs.

homeassistant/components/mitsubishi_wf_rac/quality_scale.yaml:53

  • Mark docs-examples accurately (the PR description says the documentation lacks an example/blueprint, but quality_scale.yaml currently marks docs-examples done).
  discovery-update-info: done
  docs-data-update: done
  docs-examples: done
  docs-known-limitations: done
  docs-supported-devices: done

homeassistant/components/mitsubishi_wf_rac/quality_scale.yaml:44

  • Mark the test-coverage rule accurately (the PR description states overall coverage is ~90% and below the rule threshold, but quality_scale.yaml currently marks it done).
  test-coverage: done
  • Files reviewed: 25/29 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +8 to +12
"iot_class": "local_polling",
"loggers": ["pywfrac"],
"quality_scale": "platinum",
"requirements": ["pywfrac==0.1.0"],
"zeroconf": ["_beaver._tcp.local."]
) -> bool:
"""Migrate old config entry."""

if entry.version == 1:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't there be no versions, since this is a new integration?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fair question, and it would be right for a genuinely new integration.

The catch is the domain. mitsubishi_wf_rac is not new to the installations
this lands on: it has existed as a custom component for a while, with roughly
1,900 installations, and it uses this exact domain. When one of them removes
the custom component and picks up the core integration, Home Assistant loads
the config entries that are already there — and those carry versions 1 through
5 from the custom component's own history.

Without the migration each of those entries fails to load with a "config entry
version is newer/older" error, and the user is left deleting and re-adding
every unit by hand. With it, they carry over.

The versions themselves are the custom component's, not invented for this PR.
The last step is the one this branch added: v5 → v6 moves the host from
options back into data, so the discovery helper that refreshes a changed
address writes it where setup reads it.

The write-lock retry read `expires`, an epoch stamp, back through a naive
local datetime; time.time() is what the helper's own docstring points at
for a relative time, and it takes the DST fold out of the arithmetic. The
delay itself had no test of its own - the retry count is the same either
way, so getting it wrong only showed up as a retry into a lock that had
not lapsed.

A registration answer without a "result" key ended the flow as an
unexpected error rather than as the connection problem it is.

Plus what the review found in passing: the entity update warning now
carries its traceback, InvalidName no longer describes a hostname, the
KnownError docstring points at the "error" section it actually uses, and
the name_invalid message no longer promises a uniqueness check that was
never made. The config entry fixture moves to version 6, which is what
"current version" has meant since the migration landed.
Copilot AI review requested due to automatic review settings September 6, 2026 07:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Correctness, security, identity handling, documentation, and quality-scale issues remain unresolved.

Review details

Suppressed comments (6)

Previously missed (6) — in code that hasn't changed since the last review.

tests/components/mitsubishi_wf_rac/test_climate.py:95

  • Verify the encoded command values rather than only checking that a write occurred. These cases all pass if a setter sends the wrong mode, fan, temperature, or swing value, leaving the protocol mappings effectively untested.
    homeassistant/components/mitsubishi_wf_rac/climate.py:247
  • Correct this explanation to refer to the target offset, not CONF_INDOOR_OFFSET. The implementation intentionally keeps the indoor sensor offset display-only and subtracts the separately configured target offset here, so the current comment contradicts both the code and the option description.
    homeassistant/components/mitsubishi_wf_rac/config_flow.py:55
  • Update the PR description to state VERSION = 6 and migrations through version 5. It currently promises version 5 and only versions 1–4, while this flow and async_migrate_entry include an additional v5-to-v6 migration.
    homeassistant/components/mitsubishi_wf_rac/coordinator.py:540
  • Use the full manufacturer name to avoid identifying these Mitsubishi Heavy Industries units as the unrelated Mitsubishi Electric brand.
    homeassistant/components/mitsubishi_wf_rac/strings.json:52
  • Describe address updates accurately for discovered devices. Zeroconf calls _abort_if_unique_id_configured(updates=info), so those entries do follow changed IP addresses; this blanket static-address warning, repeated in the linked documentation, contradicts the implemented discovery behavior.
    tests/components/mitsubishi_wf_rac/test_climate.py:479
  • Rename this test and correct its docstring to match what it actually verifies. With the fixture's preset_temp_range_2=False, the implementation returns 30 for heating and auto too; the 33 °C branch applies to cooling/dry only when that capability is enabled.
  • Files reviewed: 25/29 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants