Skip to content

Repository files navigation

Mitsubishi WF-RAC Integration

hacs_badge Current version Total downloads Latest release downloads installbadge Buy Me A Coffee Sponsor

This is a Home Assistant integration for Mitsubishi Heavy Industries air conditioners that use the WF-RAC WiFi module and the "Smart M-Air" app.

Not compatible with Mitsubishi Electric systems (e.g. those using a MAC-577IF2-E interface) or the MELCloud platform — those are a different manufacturer with a different app and protocol. If your unit uses MELCloud, see Home Assistant's built-in MELCloud integration instead.

Supported devices

Any Mitsubishi Heavy Industries air conditioner that ships with the WF-RAC WiFi module — the network interface controlled through the Smart M-Air app — should work. The integration talks to the module's local HTTP API rather than to a specific indoor/outdoor unit model, and it probes both plain HTTP and HTTPS on setup, so it doesn't matter which of the module's firmware branches (WF-RAC, WF-RAC-HTTPS, WCBN4612L) yours happens to run.

Confirmed working on a SRK20ZS-WF + SRK35ZS-WF multi-split on an SCM45ZS-W outdoor unit. Some entities are conditional on what the unit itself reports supporting — Occupancy and the Home Leave Mode entities, for example, only appear on units that report the corresponding capability, and a few diagnostic sensors depend on the model-identifier byte the unit sends back. An unsupported feature simply doesn't create its entity, rather than failing.

History

Created by @jeatheak. In July 2026, jeatheak transferred ownership of this repository to @blues-sechseck, who continues to maintain it. Thanks, jeatheak, for building this in the first place!

⚠️ Coming from the original repo? Check your automations

Since 2026.8, the fan_mode/swing_mode/swing_horizontal_mode state values were renamed to snake_case (e.g. "Up/Down Auto""up_down_auto", "Quiet""quiet", "3D Auto""3d_auto") to satisfy Home Assistant's own validation rules — the old capitalized values were never actually valid. If you have automations, scripts, or dashboards that call climate.set_fan_mode, select.select_option, or the set_horizontal_swing_mode/set_vertical_swing_mode services with the old capitalized strings, update them to the new lowercase values. See the 2026.8 release notes for the full list.

Todo 📃 and Bug report 🐞

See Github To Do & Bug List

Installation

Install using HACS

This integration is part of the HACS default list — no custom repository needed. In HACS, go to Integrations, search for "Mitsubishi WF-RAC", and install it from there.

Already installed? Jump straight to setup:

Open your Home Assistant instance and start setting up a new integration.

Install manually

Clone or copy this repository and copy the folder custom_components/mitsubishi_wf_rac into /custom_components/mitsubishi_wf_rac.

Removing the integration

This integration follows standard integration removal — no extra steps (like disabling a cloud account) are required.

  1. Go to Settings > Devices & Services.
  2. Find the Mitsubishi WF-RAC integration and select it.
  3. Select the three-dot menu next to the entry, then Delete.

If you installed manually rather than through HACS, also delete the custom_components/mitsubishi_wf_rac folder and restart Home Assistant.

Entities

This integration creates one device per airco with the following entities.

Climate

Entity Attribute Available values Description
climate.<name> hvac_mode off, auto, cool, heat, dry, fan_only Operating mode of the unit.
fan_mode auto, quiet, low, medium, high Fan speed.
swing_mode up_down_auto, highest, middle, normal, lowest, 3d_auto Vertical louver position. 3d_auto hands vertical and horizontal swing over to the unit's own automatic mode.
swing_horizontal_mode left_right_auto, left_left, left_center, center_center, center_right, right_right, left_right, right_left, 3d_auto Horizontal louver position. 3d_auto behaves as above.
target_temperature 16–30 °C (cool), 18–30 °C (other modes) Setpoint. Cooling accepts a lower minimum than heating/auto/dry in practice; heating below 18 °C isn't a reliable plain setpoint (see Home Leave Mode for that instead).
current_temperature °C Indoor temperature as measured by the unit, corrected by the "Indoor Temp. Sensor Offset" option if set.
hvac_action off, idle, cooling, heating, drying, fan What the unit is actually doing right now. idle means the unit is on but the compressor is stopped (e.g. setpoint satisfied) - same signal as the Compressor Demand binary sensor below. In auto mode, cooling/heating reflects the unit's own cool/heat decision, not just the configured mode.

Sensors

The diagnostic operation-data sensors below are disabled by default. Enabling a sensor makes the integration request its value; leaving all of them disabled makes no extra request at all.

Entity Values Description
Indoor Temperature °C Same value as the climate entity's current_temperature, exposed as its own sensor.
Outdoor Temperature (shared on multi-split) °C Outdoor unit temperature, corrected by the "Outdoor Temp. Sensor Offset" option if set. On multi-split systems this is an outdoor-unit-level value - reads identically on every indoor unit sharing one outdoor unit, since there's only one outdoor sensor.
Target Temperature (disabled by default) °C Current setpoint, exposed as its own sensor. Off by default because the climate entity already carries the same value as its target_temperature attribute.
Energy Usage (current run) kWh, increasing Energy consumption of the current run, as reported by the unit in 0.25 kWh steps. The unit clears this counter to 0 every time it is switched on, and holds the last value while it is off — so a low or zero reading is normal, not a fault, and a run consuming less than 0.25 kWh reads 0 throughout. For a lifetime figure use Energy Usage Total below. Only created if the unit actually reports this value — not all models do.
Energy Usage Total kWh, increasing Lifetime total, accumulated by the integration from the counter above and kept across restarts. This is the one to put on the Energy dashboard. Reset it with the "Reset Energy Usage Total" button on the device page, or set it to a specific value with the mitsubishi_wf_rac.set_energy_total action (useful when carrying a figure over from an existing meter). Resetting it does not erase the history already recorded in Home Assistant's long-term statistics. Accuracy depends on run length: whatever a run consumes above its last completed 0.25 kWh step is never reported by the unit and cannot be accumulated, which costs about 0.125 kWh per run on average regardless of how long the run is. A unit cycling every half hour therefore totals noticeably low; one running for hours at a time is off by little. Use an external energy meter if you need exact figures on a frequently cycling unit.
Compressor Frequency (diagnostic, disabled by default, shared on multi-split) Hz Actual compressor speed, not just on/off. Outdoor-unit-level - identical on every indoor unit sharing one outdoor unit. On older firmware (mcu131/wireless010) this reads a constant 0 even with the compressor confirmed running (#207).
Compressor Frequency (raw) (diagnostic, disabled by default) unitless Undecoded operation-data value behind Compressor Frequency, useful for protocol work. Both bytes in this segment carry data.
Operating Current (diagnostic, disabled by default, shared on multi-split) A Compressor operating current. It has the same outdoor-unit-level sharing as Compressor Frequency and reads a constant 0 on older firmware (#207).
Operating Current (raw) (diagnostic, disabled by default) unitless Undecoded operation-data byte behind Operating Current, useful for protocol work.
Hot Gas Temperature (diagnostic, disabled by default, shared on multi-split) °C Compressor discharge (hot gas) temperature. It has the same outdoor-unit-level sharing as Compressor Frequency.
Hot Gas Temperature (raw) (diagnostic, disabled by default) unitless Undecoded operation-data byte behind Hot Gas Temperature, useful for protocol work.
EEV Pulses (diagnostic, disabled by default) pulses Electronic expansion valve position, raw pulse count (0-255).
EEV Position (diagnostic, disabled by default) % Same value as EEV Pulses, linearly mapped to 0-255=0-100%. The real full-open pulse count is unknown, so treat this as relative, not calibrated - useful for comparing indoor units on the same system.
Indoor Coil Temperature (diagnostic, disabled by default) °C Indoor heat-exchanger temperature (MHI's THI-R1). Per indoor unit, not shared. In cooling it drops as the coil gets cold and rises back to room temperature once the compressor stops - the clearest signal there is for what the unit is actually doing. Works in heating too, where the coil is the condenser and runs to 45 °C and beyond.
Indoor Coil Temperature (raw) (diagnostic, disabled by default) unitless Undecoded operation-data byte behind Indoor Coil Temperature, useful for protocol work.
Indoor Coil Outlet Temperature (diagnostic, disabled by default) °C Indoor heat-exchanger outlet, on the suction side (MHI's THI-R3). Also per indoor unit. Equal to Indoor Coil Temperature while the compressor is off; the difference between the two while it runs is the evaporator superheat.
Indoor Coil Outlet Temperature (raw) (diagnostic, disabled by default) unitless Undecoded operation-data byte behind Indoor Coil Outlet Temperature, useful for protocol work.
Outdoor Coil Temperature (raw) (diagnostic, disabled by default) unitless Undecoded operation-data byte behind the outdoor coil temperature, useful for protocol work.
Discharge Superheat (raw) (diagnostic, disabled by default) unitless Undecoded operation-data byte behind discharge superheat, useful for protocol work.
Protection Number (raw) (diagnostic, disabled by default) unitless Undecoded operation-data byte behind the protection number, useful for protocol work. No module tested here has answered this code, so it may remain unknown.
Airco ID (diagnostic, disabled by default) text Internal ID of the airco.
Operator ID (diagnostic, disabled by default) text Internal operator/account ID.
Device ID (diagnostic, disabled by default) text Internal device ID.
IP (diagnostic, disabled by default) text Local IP address of the WF-RAC module.
Accounts (diagnostic, disabled by default) number Number of app accounts currently connected to the unit.
Error (diagnostic) error code Raw error code reported by the unit; 00 means no error.
Updated By (diagnostic) text Which account last changed the unit's settings (this integration or the Smart M-Air app).
Account Expires (diagnostic, disabled by default) text Expiry of the current operator session.
LED Status (diagnostic, disabled by default) text State of the unit's status LED.
Auto Heating (diagnostic) text State of the unit's automatic heating assist.
Model Nr (diagnostic, disabled by default) number Raw model-identifier byte reported by the unit. Used to gate which optional features (occupancy, Home Leave) are exposed; mostly useful for diagnosing unsupported models.
Cool Hot Judge (diagnostic, disabled by default) cooling, heating Raw cool/heat state reported by the unit's compressor, independent of the configured mode. unknown while off or in fan_only. Useful for detecting the "wait/hold" state on multi-split systems where one indoor unit is blocked because the outdoor unit is already committed to the opposite mode for a sibling unit.

Binary sensors

Entity Values Description
Problem on/off On whenever the unit reports an error code (error_code attribute holds the raw code; error_description is added when the code is documented in the MHI service/user manuals).
Occupancy on/off Only created on units that report the "Vacant"/Home Leave bit (see Home Leave Mode below). This is not a physical presence/motion sensor - it just mirrors that bit, which is off unless Home Leave mode was actually entered. It will read "occupied" even in an empty room if Home Leave was never triggered.
Compressor Demand on/off Whether this indoor unit is currently calling for the compressor, as opposed to just being powered on (e.g. off while a setpoint is already satisfied). Comes from the same status poll as every other sensor - no extra request needed. On a single-split system that is the same thing as the compressor running. On a multi-split it is not: each indoor unit reports its own demand, so one can read "on" while a sibling on the same outdoor unit reads "off" at the same moment, and this sensor can go "off" while the shared compressor keeps running for the sibling. To tell whether the outdoor unit is running at all, either check this sensor across every indoor unit, or use Compressor Frequency, which is identical across all indoor units sharing one outdoor unit.

Update

Entity Values Description
Firmware Update (opt-in) on/off Reports whether newer WF-RAC module firmware is available, by comparing the version reported locally against the manufacturer's getFirmware endpoint. Only created if "Check for firmware updates" is enabled in the integration's options - off by default, since it's the only call this integration makes outside the local network. Read-only; installing an update isn't offered here.

Home Leave Mode

The unit's own frost-protection/low-power standby mode for when nobody's home, with independent cooling and heating away-targets. Only created on units confirmed to support it.

Entity Values Description
Home Leave Mode (select) off, away_cool, away_heat Enters/leaves Home Leave mode in either direction.
Home Leave Cooling/Heating Temp Rule (number, disabled by default) 10–50 °C Outdoor/room temperature threshold at which Home Leave engages for that mode.
Home Leave Cooling/Heating Temp Setting (number, disabled by default) 10–50 °C Target temperature while Home Leave is active for that mode.
Home Leave Cooling/Heating Airflow (select, disabled by default) auto, 14 Fan speed while Home Leave is active for that mode.

The number/select entities above stay unknown until the climate entity's "Request Home Leave Mode status" action has been called once - the unit omits these values from a plain poll otherwise. Writing to them before that is refused rather than guessed at. See the request_home_leave_mode_status/set_home_leave_mode climate actions.

Select

These duplicate the climate entity's swing/fan attributes as standalone entities, useful for dashboards or automations that prefer a plain select over a climate attribute. Installations that declined them during setup get them as disabled entities, to be enabled from the entity list when wanted.

Entity Values Description
Horizontal Swing Direction same as swing_horizontal_mode above
Vertical Swing Direction same as swing_mode above
Fan Speed same as fan_mode above

Data updates

The integration polls the WF-RAC module directly over the local network every 60 seconds — there is no cloud, push, or webhook involved. A single poll reads the unit's full state in one request (mode, setpoint, temperatures, energy counter, and so on). The diagnostic operation-data sensors described above (compressor frequency, coil temperatures, EEV position, etc.) cost one additional request per poll, made only for the segments an enabled sensor actually needs, and not at all if none of them are enabled.

The only outbound internet request this integration ever makes is the optional firmware-version check ("Check for firmware updates" under Options, off by default) — everything else, including every poll and every command, stays on the local network.

Use cases

  • Whole-home climate scheduling and automations — set mode, fan speed, swing and setpoint like any other climate entity, from automations, scripts, or dashboards.
  • Energy dashboard tracking — feed Energy Usage Total into Home Assistant's built-in Energy dashboard for a lifetime consumption figure per unit.
  • Presence-based energy saving — drive Home Leave Mode from a person/zone trigger instead of a plain schedule, so the unit throttles back to a frost-protection setpoint while everyone's away and returns to normal the moment someone gets home.
  • Reacting to what the compressor is actually doing — Compressor Demand and hvac_action distinguish "on but idle, setpoint satisfied" from "actively heating/cooling", useful for automations that should only fire while the unit is genuinely running.
  • Diagnosing multi-split behaviour — the diagnostic operation-data sensors (compressor frequency, indoor coil temperature, EEV position) make short cycling, an oversubscribed outdoor unit, or a struggling indoor unit visible in history graphs, without a service call or the app.

Examples

Set Home Leave Mode automatically when the last person leaves, back to normal when someone returns:

automation:
  - alias: "AC: enable Home Leave Mode when everyone's away"
    trigger:
      - trigger: state
        entity_id: zone.home
        to: "0"
    action:
      - action: select.select_option
        target:
          entity_id: select.<name>_home_leave_mode
        data:
          option: away_cool
  - alias: "AC: back to normal when someone gets home"
    trigger:
      - trigger: state
        entity_id: zone.home
        from: "0"
    action:
      - action: select.select_option
        target:
          entity_id: select.<name>_home_leave_mode
        data:
          option: "off"

Notify when the unit reports a fault:

automation:
  - alias: "AC: notify on error"
    trigger:
      - trigger: state
        entity_id: binary_sensor.<name>_problem
        to: "on"
    action:
      - action: notify.notify
        data:
          message: >-
            {{ state_attr('binary_sensor.<name>_problem', 'error_description')
               or state_attr('binary_sensor.<name>_problem', 'error_code') }}

Options

Configurable via the integration's "Configure" (options) flow. The host/IP address itself isn't here - it's connection-critical, so changing it goes through "Reconfigure" instead, which re-validates the new address against the device before saving it.

Option Range Description
Retry limit 3 or higher, default 3 Consecutive failed polls before the device is marked unavailable. At the 60 s poll interval, 3 is about 3 minutes - enough to ride through the module's hourly WiFi reassociation. Raise it on a weak link; it cannot be set lower.
Indoor Temp. Sensor Offset -15..15 °C Added to the unit's own indoor-sensor reading before it's shown as current_temperature / the Indoor Temperature sensor - display-only, doesn't change what the unit does.
Outdoor Temp. Sensor Offset -15..15 °C Same, for the Outdoor Temperature sensor.
Target Temp. Offset -5..5 °C Calibrates the setpoint sent to the unit - see "Target Temp. Offset sign convention" below. Applies to every hvac_mode unless overridden by the two options below.
Target Temp. Offset (Cooling) -5..5 °C, unset by default Overrides Target Temp. Offset for cool and dry mode. Leave unset to keep using Target Temp. Offset for those modes too.
Target Temp. Offset (Heating) -5..5 °C, unset by default Overrides Target Temp. Offset for heat mode. Leave unset to keep using Target Temp. Offset for heat too.
Check for firmware updates on/off, off by default Creates the Firmware Update entity (see Update above) and periodically checks the manufacturer's getFirmware endpoint. The only outbound internet call this integration makes - leave off to stay fully local.

Target Temp. Offset sign convention

The unit's internal temperature sensor is a return-air sensor built into the indoor unit, not a sensor sitting where you actually care about the temperature. It reads a biased version of the room - but which direction, and by how much, depends on your installation, not on cooling vs. heating alone:

  • Short-circuited airflow: the unit's own outflow gets pulled straight back into the return before it mixes into the room. In cooling this reads below the true room temperature.
  • Stratification: a high wall mount and a low fan speed let conditioned air pool near the ceiling instead of mixing down to where you live. In cooling this reads above the true room temperature - the opposite of the case above, and just as real. Fan speed matters here: on one multi-split installation, the unit capped at a low night-time fan speed showed a markedly larger bias than a sibling unit running medium/high in the same house.

There's no way to predict which case applies to your unit from its mode alone - you have to measure.

Target Temp. Offset corrects for this bias: true_room ≈ PresetTemp + offset. To land the room on the temperature you actually requested, the setpoint sent to the unit is commanded PresetTemp = requested − offset. Concretely: a negative offset raises the setpoint actually sent to the unit (a positive offset lowers it).

Measuring it: place a reference sensor away from the unit's own airflow, then average current_temperature (the Indoor Temperature sensor) minus that reference, split by the climate entity's hvac_action. Use the average while cooling (or heating) as your starting point for Target Temp. Offset (Cooling) / (Heating) - that's the state the thermostat loop actually regulates in, and it's not interchangeable with idle or off: on the installation above, the same unit's average bias moved by more than a kelvin between cooling and off. This is also why no single value is correct for both cool and heat at once, and why the offset isn't a fixed mounting/calibration error you can look up - it calibrates your installation's operating regime, and only a measurement of your unit, in the state it's actually controlling in, gets it right.

Known limitations

  • Self-clean cannot be started or monitored from Home Assistant. The official app has no way to trigger it either - the unit's self-clean cycle can only be started from its own IR remote, and nothing on the wire distinguishes "self-clean running" from "off" (see §6.4 of the protocol reference).
  • No external temperature sensor input. The unit always controls off its own built-in return-air sensor; there is currently no way to feed it a reading from an external Home Assistant sensor instead. The Indoor/Outdoor Temp. Sensor Offset options only correct what's displayed, not what the unit's own control loop uses.
  • The Firmware Update entity is read-only. It reports whether newer WF-RAC module firmware is available; installing it isn't offered through this integration - the module updates itself via the official app.
  • Not every entity appears on every unit. Occupancy, Home Leave Mode, and a few diagnostic sensors only get created if the unit itself reports support for the underlying feature (see the notes under Entities above) - missing rather than unavailable is expected there, not a bug.
  • One device, one connection. The WF-RAC module handles requests one at a time. This integration already serializes its own requests to respect that, but running a second tool (a custom script, another integration instance, the Smart M-Air app at the same moment) against the same unit can still cause slow or occasionally failed responses on either side.

Troubleshooting

Unit goes briefly unavailable about once an hour

The WF-RAC module drops and re-establishes its WiFi association roughly once an hour. This is designed behaviour of the module, confirmed by MHI support under ticket reference 813958: the interface does "connect / disconnect every one hour … to avoid too much cache by the communication" (source). The reassociation takes seconds to about a minute and cannot be turned off.

This integration polls every 60 seconds, so a reassociation can cost a poll. That does not become a visible outage: the device is only reported unavailable after three consecutive failed polls, about three minutes, which rides through the reassociation. If your link is weak enough that this still shows up, raise Retry limit in the options; three is the minimum, not a target.

A missed poll is not logged as a warning either — it is a debug line, and the log only speaks up when the device actually crosses the threshold and again when it comes back. Turn on debug logging for custom_components.mitsubishi_wf_rac if you want to watch the individual polls.

Unit goes unavailable for about an hour

This is the same hourly reassociation as above, but the module fails to re-bind port 51443 afterwards instead of coming back within a minute. The outage starts right on the hourly tick, not at a random point in between, which is what distinguishes it from the WiFi roaming problem below. There's no router setting that fixes this - it clears itself on the next hourly reassociation, so it's a matter of waiting it out.

Unit goes unavailable for 15-35 minutes

Outages in this range and starting at a random point (not on the hourly tick) are a network-side problem: the module mishandles WiFi roaming and steering management frames. Recommended setup:

  • A dedicated 2.4 GHz-only SSID. The module is 2.4 GHz only, and on a shared SSID band steering tries to push it onto a band it cannot join.
  • 802.11r (Fast Roaming) and 802.11v (BSS Transition / Handoff Suggestions) off on that SSID. Band steering is itself implemented via the same 802.11v frames.
  • Plain WPA2, not WPA2/WPA3 mixed mode. This also matters during initial pairing.
  • On UniFi, "Force WiFi 4 Mode" and "DTIM Interval Lock" under IoT Optimization are safe to enable.
  • Blocking the module's outbound internet access at the router removes these outages in some setups; this integration only needs LAN access. The hourly reassociation continues either way.

Protocol reference

If you are writing your own client against the WF-RAC module — or building a CNS/SPI replacement such as an ESP32 running MHI-AC-Ctrl — docs/wf-rac-module-reference.md documents the interface end to end: mDNS discovery, the HTTPS API and its envelope rules, the airconStat blob, the 18-byte state block and how it maps onto the CNS/SPI frame, the operation-data channel, and what the module deliberately does not forward. Every non-obvious claim carries a confidence tag saying whether it was observed on hardware, read out of a firmware image, or inferred.

It is written for people outside this project, so nothing in it assumes Home Assistant.

About

WF-RAC homeassistant integration

Topics

Resources

Stars

290 stars

Watchers

28 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages