[Custom integration] Daikin Madoka (BRC1H) — Bluetooth thermostat via HA or ESPHome

:round_pushpin: News and support live on GitHub

I maintain this integration alone and unpaid, so everything is tracked in one place: report a bug or ask a question.

New versions no longer need announcing here: HACS offers you the update, release notes included. Otherwise, the RSS feed https://github.com/dasimon135/daikin_madoka/releases.atom, or Watch → Releases on the repository.

This thread stays open for user-to-user help. The README is the reference and stays current — this post does not.

Introduction

Hey everyone!

Sharing my custom integration and ESPHome component for controlling Daikin Madoka BRC1H thermostats from Home Assistant via Bluetooth.

The Madoka BRC1H is a sleek wall-mounted thermostat by Daikin, controllable only via the Daikin mobile app (Bluetooth). No Wi-Fi, no usable cloud API. This project brings full control directly into HA.

Two approaches available:

  1. HA Custom Integration — direct Bluetooth connection from HA server
  2. ESPHome Component — ESP32 acts as a Bluetooth proxy (place it anywhere)

GitHub: GitHub - dasimon135/daikin_madoka: Home Assistant custom component integration for the BRC1H thermostat (madoka) · GitHub (branch madoka)


Features

  • Full HVAC control: mode (heat, cool, auto, fan, dry), target temperature, fan speed
  • Indoor temperature sensor reading
  • Fan modes: Auto, Low, Mid, High
  • Temperature range: 16°C - 32°C (61°F - 90°F)
  • Automatic BLE reconnection
  • Compatible with ESP32 and ESP32-S3 (M5Stack Atom Lite / Atom S3 Lite)

Option 1: HA Custom Integration (Direct Bluetooth)

Requirements

  • Home Assistant with Bluetooth access (USB BLE adapter if needed)
  • If HA runs in Docker: DBUS access required (see below)
  • Thermostat must be within Bluetooth range (~10m / ~30ft)

Installation

  1. Download from GitHub
  2. Copy to custom_components/daikin_madoka/ in your HA config
  3. Restart Home Assistant

Bluetooth Pairing (mandatory)

This is the critical step. The BRC1H requires secure pairing:

# 1. Disconnect the thermostat from any other device (Madoka BT menu → Forget)

# 2. On the HA server (or the machine with the BT adapter):
bluetoothctl
agent KeyboardDisplay
remove <BRC1H_MAC>          # Remove any previous pairing
scan on                      # Wait for BRC1H to appear
scan off
pair <BRC1H_MAC>            # Accept the prompt + confirm on thermostat

:warning: Confirm quickly on the thermostat after pair, otherwise the pairing times out.

HA Configuration

Go to Settings → Integrations → Add → Daikin Madoka and provide:

  • Bluetooth MAC address of the BRC1H
  • Bluetooth adapter name (usually hci0)

Two entities are created:

  • Climate: full control (mode, temperature, fan)
  • Sensor: measured indoor temperature

Docker / VM: DBUS Configuration

If HA runs in Docker, DBUS must be accessible:

# docker-compose.yml
volumes:
  - /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
privileged: true

To verify it works:

docker exec -ti <container_id> /bin/bash
bleak-lescan -i hci0

Option 2: ESP32 Proxy via ESPHome (recommended)

This is what I use daily. An M5Stack Atom Lite (or any ESP32) placed near the thermostat acts as a Bluetooth proxy.

Why this approach?

  • No need for the HA server to be within Bluetooth range
  • Works flawlessly in Docker/VM without DBUS hassle
  • Single ESP32 can handle multiple thermostats
  • Excellent reliability with automatic reconnection

Tested Hardware

Platform Chip Framework Status
M5Stack Atom Lite ESP32 ESP-IDF :white_check_mark:
M5Stack Atom S3 Lite ESP32-S3 ESP-IDF 5.x :white_check_mark:
Generic ESP32 DevKit ESP32 ESP-IDF / Arduino :white_check_mark:

Component Installation

external_components:
  # From GitHub directly:
  - source: github://dasimon135/daikin_madoka@madoka
    components: [ madoka, ble_client ]

  # OR locally (copy esphome_components/ to your ESPHome config):
  # - source:
  #     type: local
  #     path: esphome_components
  #   components: [ madoka, ble_client ]

The included ble_client is a patched version compatible with ESPHome 2025.10.0+ (fixes consume_connection_slots removal).

Full Config (M5Stack Atom Lite)

substitutions:
  name: madoka-proxy
  friendly_name: "Madoka BLE Proxy"

esphome:
  name: ${name}
  friendly_name: ${friendly_name}

esp32:
  board: m5stack-atom
  framework:
    type: esp-idf

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

logger:
  level: DEBUG

api:
  encryption:
    key: !secret api_key

ota:

external_components:
  - source: github://dasimon135/daikin_madoka@madoka
    components: [ madoka, ble_client ]

esp32_ble_tracker:
  max_connections: 2

# IMPORTANT: disable classic BT proxy
bluetooth_proxy:
  active: false

ble_client:
  - mac_address: "F0:B3:1E:87:AF:FE"  # ← Your Madoka MAC
    id: madoka_living
    on_disconnect:
      then:
        - ble_client.connect: madoka_living

climate:
  - platform: madoka
    name: "Madoka Living Room"
    ble_client_id: madoka_living
    update_interval: 15s

ESP32-S3 Config (M5Stack Atom S3 Lite)

For ESP32-S3, add the BLE security configuration:

esp32:
  board: m5stack-atoms3
  variant: esp32s3
  framework:
    type: esp-idf
    version: recommended
    sdkconfig_options:
      CONFIG_BT_BLE_50_FEATURES_SUPPORTED: y
      CONFIG_BT_BLE_42_FEATURES_SUPPORTED: y

# REQUIRED for Madoka pairing on ESP32-S3
esp32_ble:
  io_capability: display_yes_no

Pairing Process (ESP32)

  1. Flash the config and open ESPHome logs
  2. The ESP32 connects to the Madoka and initiates pairing
  3. A 6-digit code appears in the logs:
╔══════════════════════════════════════════════════════════╗
║  PAIRING CODE: 790440                                   ║
║  Check that this code matches the one on the Madoka     ║
║  and CONFIRM on the thermostat!                         ║
╚══════════════════════════════════════════════════════════╝
  1. The same code appears on the thermostat screen
  2. Confirm on the thermostat (press OK) — the ESP32 auto-confirms on its side
  3. Subsequent connections are automatic (bonding is saved)

Multiple Thermostats

A single ESP32 can handle 2 thermostats simultaneously (max_connections: 2). Add a second ble_client + climate:

ble_client:
  - mac_address: "F0:B3:1E:87:AF:FE"
    id: madoka_living
    on_disconnect:
      then:
        - ble_client.connect: madoka_living
  - mac_address: "1C:54:9E:90:E3:0E"
    id: madoka_bedroom
    on_disconnect:
      then:
        - ble_client.connect: madoka_bedroom

climate:
  - platform: madoka
    name: "Madoka Living Room"
    ble_client_id: madoka_living
    update_interval: 15s
  - platform: madoka
    name: "Madoka Bedroom"
    ble_client_id: madoka_bedroom
    update_interval: 15s

Troubleshooting

Issue Solution
“device not found” in HA integration Thermostat is connected to another device (mobile app). Forget it first.
“cannot connect” DBUS not available (Docker) or BT adapter missing
Pairing fails (error 0x52) On ESP32-S3: ensure esp32_ble: io_capability: display_yes_no is set
2nd thermostat won’t connect Normal — they connect one at a time. Wait for the 1st to pair.
AttributeError: consume_connection_slots Use the patched ble_client included in this repo

Credits

  • Original HA integration: @mduran80 / pymadoka
  • ESPHome madoka component: Petapton/esphome
  • ESPHome 2025.10+ compatibility fixes & ESP32-S3 support: this repo

GitHub: GitHub - dasimon135/daikin_madoka: Home Assistant custom component integration for the BRC1H thermostat (madoka) · GitHub (branch madoka)

If you have Madoka thermostats at home, give it a try and let me know how it goes! :slightly_smiling_face:

2 Likes

I actually used some shelly gen 3’s to achieve the same (and still use them as light switch too).

All I had to do was re-flash them with esphome :innocent:

Thanks for the integration; this is exactly what I’m looking for. My HA machine is within 2 meters from the Daikin Madoka, so it should be possible to use the BT adapter on my HA machine to connect with the Madoka. Some ‘noob’ questions:

  • How can I upload the integration to the custom_components/daikin_madoka/? I have downloaded the ZIP file from Github, but how to install it on my HA machine? I have installed integrations via HACS, but never manually.
  • Which add-on is used for the BT pairing process?

Thanks in advance.

Just uploaded to ZIP file to the HA server, unpacked it via Terminal & SSH in custom_components. Also succesfully paired my Madoka via bluetoothctl. But when I want to add the integration via Settings → Integrations → Add → Daikin Madoka, I got this failure:

What I am doing wrong…? Information from log:

Logger: homeassistant.config_entries
Source: config_entries.py:3969
First occurred: 2:43:35 PM (4 occurrences)
Last logged: 3:51:19 PM

Error occurred loading flow for integration daikin_madoka: cannot import name ‘discover’ from ‘bleak’ (/usr/local/lib/python3.13/site-packages/bleak/init.py)

Bei mir kommt der gleiche Fehler: Der Konfigurationsfluss konnte nicht geladen werden: {“message”:“Invalid handler specified”}

Logger: homeassistant.config_entries
Quelle: config_entries.py:3969
Erstmals aufgetreten: 06:01:51 (1 Vorkommnis)
Zuletzt protokolliert: 06:01:51

Error occurred loading flow for integration daikin_madoka: cannot import name ‘discover’ from ‘bleak’ (/usr/local/lib/python3.13/site-packages/bleak/init.py)

Logger: homeassistant.util.loop
Quelle: util/loop.py:137
Erstmals aufgetreten: 06:01:51 (1 Vorkommnis)
Zuletzt protokolliert: 06:01:51
Detected blocking call to import_module with args (‘custom_components.daikin_madoka.config_flow’,) inside the event loop by integration ‘config’ at homeassistant/components/config/config_entries.py, line 195: return await super()._post_impl(request, data) (offender: /usr/src/homeassistant/homeassistant/loader.py, line 1307: return importlib.import_module(f"{self.pkg_path}.{platform_name}")), please create a bug report at GitHub · Where software is built For developers, please see Blocking operations with asyncio | Home Assistant Developer Docs Traceback (most recent call last): File “”, line 198, in _run_module_as_main File “”, line 88, in _run_code File “/usr/src/homeassistant/homeassistant/main.py”, line 229, in sys.exit(main()) File “/usr/src/homeassistant/homeassistant/main.py”, line 215, in main exit_code = runner.run(runtime_conf) File “/usr/src/homeassistant/homeassistant/runner.py”, line 289, in run return loop.run_until_complete(setup_and_run_hass(runtime_config)) File “/usr/local/lib/python3.13/asyncio/base_events.py”, line 712, in run_until_complete self.run_forever() File “/usr/local/lib/python3.13/asyncio/base_events.py”, line 683, in run_forever self._run_once() File “/usr/local/lib/python3.13/asyncio/base_events.py”, line 2050, in _run_once handle._run() File “/usr/local/lib/python3.13/asyncio/events.py”, line 89, in _run self._context.run(self._callback, *self._args) File “/usr/local/lib/python3.13/site-packages/aiohttp/web_protocol.py”, line 510, in _handle_request resp = await request_handler(request) File “/usr/local/lib/python3.13/site-packages/aiohttp/web_app.py”, line 569, in _handle return await handler(request) File “/usr/local/lib/python3.13/site-packages/aiohttp/web_middlewares.py”, line 117, in impl return await handler(request) File “/usr/src/homeassistant/homeassistant/components/http/security_filter.py”, line 92, in security_filter_middleware return await handler(request) File “/usr/src/homeassistant/homeassistant/components/http/forwarded.py”, line 87, in forwarded_middleware return await handler(request) File “/usr/src/homeassistant/homeassistant/components/http/request_context.py”, line 26, in request_context_middleware return await handler(request) File “/usr/src/homeassistant/homeassistant/components/http/ban.py”, line 86, in ban_middleware return await handler(request) File “/usr/src/homeassistant/homeassistant/components/http/auth.py”, line 242, in auth_middleware return await handler(request) File “/usr/src/homeassistant/homeassistant/components/http/headers.py”, line 41, in headers_middleware response = await handler(request) File “/usr/src/homeassistant/homeassistant/helpers/http.py”, line 73, in handle result = await handler(request, **request.match_info) File “/usr/src/homeassistant/homeassistant/components/http/decorators.py”, line 83, in with_admin return await func(self, request, *args, **kwargs) File “/usr/src/homeassistant/homeassistant/components/http/data_validator.py”, line 74, in wrapper return await method(view, request, data, *args, **kwargs) File “/usr/src/homeassistant/homeassistant/components/config/config_entries.py”, line 188, in post return await self._post_impl(request, data) File “/usr/src/homeassistant/homeassistant/components/config/config_entries.py”, line 195, in _post_impl return await super()._post_impl(request, data)

hi. I have an issue with my madoka controllers. I have connected them and can switch them on/off and read their status however i cannot change the temperature. here is the logs commented. can you please help out?

Setpoint writes are not applied by the device (on/off works) — SetPoint UPDATE returns an all-zero 55-byte response

Summary

Using the HA Integration (Option 1, direct Bluetooth), turning the unit on/off works and all reads work, but changing the target temperature has no effect. The write command is sent with the correct value, but the device does not apply it: an immediate re-read on the same connection still shows the previous setpoint, and the requested value never appears in any subsequent read.

Changing the setpoint directly from the physical BRC1H wall controller works fine, so it is not a unit-level lock or a centralized-controller restriction.

Environment

  • daikin_madoka v2.3.0 (manifest pins pymadoka @ git+https://github.com/dasimon135/pymadoka.git@dc8663253c8f6883511de8081406760248c87574)
  • Home Assistant Core 2026.6.4, Home Assistant OS 18.0
  • Raspberry Pi 3 (raspberrypi3-64), built-in Bluetooth adapter hci0
  • Option 1 (direct BLE from the HA host)
  • Device: Daikin BRC1H (Madoka), operation_mode = COOL. Reproduced on multiple units (full-range 16–32 and a range-restricted 22–27 unit).

What works vs. what doesn't

  • :white_check_mark: PowerState write (on/off) — applied and reflected on next read
  • :white_check_mark: All queries (temperatures, set point, operation mode, fan speed, …)
  • :cross_mark: climate.set_temperature (cooling/heating set point write) — never applied

Evidence (debug log)

Single controller, COOL mode, current cooling_set_point = 26. User requests 20 °C. The value is encoded correctly (20 * 128 = 2560 = 0x0A00):

[pymadoka.connection] Sending cmd payload: 3d00404020020a0021020d00...
        (attr 0x20 = 0x0A00 = 20 ; attr 0x21 = 0x0D00 = 26)
[pymadoka.connection] CMD 16448. Chunk #1..4/4 sent
[pymadoka.feature]    SetPoint UPDATE response received (55 bytes)
[pymadoka.feature]    SetPoint status updated, new value:
{"cooling_set_point": 0, "heating_set_point": 0, "range_enabled": 0, "mode": 0,
 ... every field 0 ...}

Immediately afterwards, on the same connection, the integration re-reads the set point:

[pymadoka.feature]    SetPoint QUERY response received (61 bytes)
{"cooling_set_point": 26, ... "cooling_lowerlimit": 16, "cooling_upperlimit": 32}   <-- unchanged

Across the entire session, cooling_set_point is never equal to the requested value (20). A second write attempt behaves identically. By contrast, a PowerState UPDATE in the same session is applied and reflected on the next read.

Observations / likely cause

  1. The UPDATE (write) response is 55 bytes, whereas a normal QUERY response is 61 bytes. SetPointStatus.set_values() parses the UPDATE response using the same field offsets as a query, which yields all-zeros — so the write response is a different structure (possibly a NAK/error frame) and/or is being mis-parsed as a valid status.
  2. In pymadoka/features/setpoint.py, get_values() encodes the two set points correctly (value * 128) but sets every other field to a fixed value (range_enabled = 0, mode = 2, and all *_lowerlimit / *_upperlimit / min_* / symbol fields = 0). It is plausible the device rejects a set-point write whose range/limit fields are zeroed or inconsistent with its current state.
  3. Reads decode correctly (/128) and the physical controller changes the set point fine, so the unit does accept set-point changes in principle.

Request

Could you review (a) the SetPoint UPDATE command payload — specifically whether the zeroed range_enabled / limit fields cause the device to reject the write — and (b) the parsing of the 55-byte UPDATE response? Happy to provide full debug logs and to test patches.

Thanks for the outstanding write-up — your hypothesis #2 is exactly right, and this is already fixed in v2.4.0.

Root cause. In v2.3.0, SetPointStatus.get_values() builds the write payload from scratch and zeroes range_enabled, mode and every min/max limit + symbol field. The BRC1H silently rejects a setpoint write whose range/limit fields are zeroed or inconsistent with its configured state — so the value is never applied, while reads and on/off keep working (exactly what you saw). The 55-byte all-zero UPDATE response is a red herring for the “not applied” symptom: the library doesn’t use the parsed UPDATE response to set the status, so that mis-parse is only a cosmetic debug line — the real issue is the outgoing payload, as you suspected in #2.

Fix (v2.4.0). pymadoka now captures the device’s own raw parameters when it reads the setpoint and echoes them back on write, changing only the two setpoints — so the payload stays consistent with the unit’s actual limits/range and the device accepts it. The integration also sends a copy of the last device-read status instead of a freshly-zeroed one.

I verified this on a full-range BRC1H: climate.set_temperature to 23 °C is applied and reads back correctly (0x0B80 = 23×128), with the limit fields preserved. It should work for your range-restricted (22–27) unit too, since the echo carries that unit’s real limits.

Please update to v2.4.0 (via HACS, or reinstall + restart). Notes:

  • It connects through Home Assistant’s Bluetooth stack, which works fine with your Pi’s built-in hci0 adapter; your existing bluetoothctl pairing stays valid.
  • The library is now pymadoka-ng on PyPI (installed automatically); the old git-pinned pymadoka is no longer used.
  • Displayed entity names may change slightly (has_entity_name); entity IDs and history are preserved.

If a setpoint write still doesn’t stick after upgrading, reopen with debug logs and I’ll dig in — but this should resolve it. Thanks again for the precise diagnosis!

You’re on a build older than v2.3.0. That error — cannot import name 'discover' from 'bleak' — comes from an old pinned version of the pymadoka library that imported discover from bleak, which bleak removed in ≥ 0.20. When the library fails to import, config_flow.py can’t load, which is why Home Assistant reports “Invalid handler specified” and refuses to add/open the integration. (The “Detected blocking call to import_module” traceback is a secondary side-effect of that failed import; it clears on its own once the integration loads cleanly.)

This was fixed in v2.3.0 and improved further in v2.4.0 — please update to v2.4.0 and restart Home Assistant. v2.4.0 connects through Home Assistant’s own Bluetooth stack and uses the pymadoka-ng library from PyPI (installed automatically), so the bleak/discover incompatibility is gone.

How to update:

  • HACS: open the Daikin Madoka integration in HACS → update to the latest release → restart HA. If HACS still offers only the old version, remove and re-add the repository, or use Redownload and pick the latest.
  • Manual: replace custom_components/daikin_madoka/ with the v2.4.0 release contents and restart.

After the update the config flow will load normally. If you still see the discover/bleak error afterwards, it usually means an old cached pymadoka is still installed — a full HA restart (not just a reload) clears it; if it persists, post your logs and I’ll help.

sorry for the late response.
You’re not doing anything wrong in Home Assistant — the ZIP you uploaded is an old version of the integration (pre‑v2.3.0). That build pins an old pymadoka library that imports discover from bleak, which bleak removed in ≥ 0.20. When the library fails to import, config_flow.py can’t load, so “Add integration → Daikin Madoka” fails. Pairing via bluetoothctl is fine and unrelated — the failure is purely the outdated code.

Fix: install v2.4.0 specifically.

  1. Download it from the Releases page of this repo (make sure it’s dasimon135/daikin_madoka, not the original upstream): Release v2.4.0 — Modern Bluetooth: ESPHome proxy support, auto-discovery, self-healing · dasimon135/daikin_madoka · GitHub → Assets → source code (zip). (The green Code → Download ZIP of the main branch also works — both are current.)
  2. Replace the folder custom_components/daikin_madoka/ on your server with the custom_components/daikin_madoka/ from that download (delete the old one first so no stale files remain).
  3. Fully restart Home Assistant (Settings → System → Restart — not just “reload”). On restart, HA installs the new requirement.

Why this works cleanly: v2.4.0 connects through Home Assistant’s own Bluetooth stack and depends on pymadoka-ng from PyPI (a renamed library). Because the dist name is different from the old pymadoka, HA installs it fresh — the broken bleak/discover import is gone. Your existing bluetoothctl pairing stays valid.

After the restart, “Add integration → Daikin Madoka” will load the config flow normally (it will also auto‑discover the thermostat if it’s advertising nearby). If you still get the discover error, an old cached pymadoka is lingering — a full restart clears it; if not, post your logs.

Tip: for automatic future updates, install via HACS as a custom repository (https://github.com/dasimon135/daikin_madoka, category Integration) instead of the manual ZIP.

Hi everyone,

I maintain the custom integration for Daikin BRC1H “Madoka” Bluetooth thermostats (a fork of the original work by @mduran80, actively maintained). The new v2.4.0 “Modern Bluetooth” release is a big step up and has been validated end‑to‑end on real hardware, so I wanted to share it.

Repo: GitHub - dasimon135/daikin_madoka: Home Assistant custom component integration for the BRC1H thermostat (madoka) · GitHub

What it does

Full control of your BRC1H over Bluetooth, straight from Home Assistant — mode (heat/cool/auto/dry/fan), target temperature, fan speed, plus indoor/outdoor temperature, filter alert, filter‑reset button and display (LED) brightness. No cloud, no app, 100% local.

:sparkles: New in v2.4.0

  • :satellite_antenna: Works through ESPHome Bluetooth proxies — your HA host no longer needs to be within BLE range of the thermostat. An ESP32 proxy in the room is enough.
  • :magnifying_glass_tilted_left: Automatic discovery — nearby BRC1H units are detected and offered in the UI; no need to type the MAC.
  • :counterclockwise_arrows_button: Self‑healing polling — a dropped BLE link recovers on the next poll, no reload needed.
  • :thermometer: Dual setpoint in AUTO (separate heating/cooling targets when the unit has range mode enabled) and min/max limits read from the device itself.
  • :bug: Setpoint writes fixed — a long‑standing bug where changing the temperature had no effect (the write was rejected because the payload zeroed the device’s range/limit fields) is resolved: writes now echo the unit’s own parameters and are accepted.
  • :toolbox: Options flow (poll interval), downloadable diagnostics, RSSI diagnostic sensor, device model/firmware in the registry, a proper brand icon (HA ≥ 2026.3), visible errors on failed commands, and en/es/fr translations.

:warning: Important if you use a Bluetooth proxy

The BRC1H requires authenticated (MITM) pairing and silently ignores unauthenticated clients. The stock ESPHome bluetooth‑proxy firmware can’t complete this pairing — you need to add a few lines to your proxy’s YAML (a display_yes_no IO capability + a numeric‑comparison responder). The exact, hardware‑validated config is in the README “Requirements” section. On the first connection, accept the pairing prompt on the thermostat’s display (once per proxy). If you use HA’s own local Bluetooth adapter instead, a one‑time bluetoothctl pairing is all you need.

Installation

Not in the HACS default store yet (submission pending), so add it as a custom repository:

  1. HACS → ⋮ → Custom repositories → URL https://github.com/dasimon135/daikin_madoka, category Integration → Add.
  2. Install Daikin Madoka, restart HA.
  3. Settings → Devices & Services → it should be auto‑discovered, or Add Integration → Daikin Madoka.

(Manual install: copy custom_components/daikin_madoka/ from the v2.4.0 release and restart.)

Upgrading from an older version

Just update and restart. Entity IDs and history are preserved (displayed names may change slightly). The bundled library moved to pymadoka-ng on PyPI and installs automatically. If you’re seeing cannot import name 'discover' from 'bleak' or “Invalid handler specified”, you’re on a pre‑2.3.0 build — updating to 2.4.0 fixes it.

What’s next

A dial‑style Madoka Card (round dial, state‑colored glow, setpoint arc, fan/brightness/graph) is already in the dev branch and will ship in the next release, along with a guided setup/pairing assistant.

Feedback, issues and testers very welcome — either here or on GitHub Issues. Enjoy your local Madoka control! :snowflake::fire:

## :rocket: Daikin Madoka (BRC1H) v3.0.0 — a dial card, guided recovery & rock-solid reconnection

Hi everyone,

v3.0.0 of the Daikin BRC1H “Madoka” custom integration is out. After 2.4.0 (“Modern Bluetooth”), this release is all about the dashboard experience and reliability — validated on real hardware.

Repo: GitHub - dasimon135/daikin_madoka: Home Assistant custom component integration for the BRC1H thermostat (madoka) · GitHub

:control_knobs: The Madoka Card

The headline feature: a dial-style Lovelace card shipped inside the integration (auto-registered, no separate install). It mirrors the physical BRC1H — a glowing halo that changes color with the mode, a setpoint arc, fan segments, controls, a mode switcher, an eye-brightness slider, a 12 h temperature sparkline and filter/signal chips.

Three layouts:

type: custom:madoka-cardentity: climate.my_madoka# layout: full | compact | tile
  • full — the complete dial · compact — dial + controls · tile — an ultra-compact row that lines up with HA’s native tile cards

It auto-discovers the related entities (outdoor temp, brightness, filter, signal) from the same device — you only give it the climate.* entity. Follows your theme and language (modes from HA’s own climate translations; card words in en/fr/es/de/it/nl).

:wrench: Guided recovery

A repair issue (“device unreachable”, links the pairing/proxy docs) that clears itself on recovery, and a Reconnect button to force a link re-establishment.

:counterclockwise_arrows_button: Self-healing reconnect fixed

A real bug was caught and fixed: after a BLE drop, reconnection could fail with Insufficient authentication (the bond is stored per proxy). The integration now re-pairs on every reconnect → clean recovery. Validated live on hardware. (Requires pymadoka-ng 0.3.5, installed automatically.)

:high_voltage: Also

Adaptive polling (commands reflect immediately) and an operating-time sensor (cumulative powered-on hours, persisted).

:warning: Bluetooth proxy reminder

Unchanged since 2.4.0: the BRC1H requires authenticated (MITM) pairing. The stock bluetooth-proxy firmware can’t do it — a few lines in the proxy YAML, all in the README Requirements section.

:up_arrow: Upgrading

Update via HACS and restart. Entity IDs and history preserved; library moves to pymadoka-ng 0.3.5 on PyPI (auto-installed). Hard-refresh the browser once for the card.

(Not in the HACS default store yet — add https://github.com/dasimon135/daikin_madoka as a custom repository, category Integration.)

Feedback and testers welcome — here or on GitHub Issues. Enjoy your local Madoka control! :snowflake::fire:

Daikin Madoka (BRC1H) v3.2.0 — multi-proxy robustness: no more thermostats dying without explanation

Hi all,

v3.2.0 of the Daikin Madoka (BRC1H) custom integration is out. This release is entirely about reliability in homes with several Bluetooth proxies and several thermostats — hardware-validated on a 4-thermostat / 4-proxy installation, including a live replay of the exact failure that motivated the work.

The problem it fixes

The BRC1H stores its Bluetooth pairing per proxy (one bond per ESPHome node), but Home Assistant routes connections through whichever proxy has the strongest signal. When those two disagree — a nearby proxy that was never paired wins the pick — the thermostat refuses the link (Insufficient authentication)… and all you used to see was an unavailable entity with zero explanation.

What’s new

  • Sticky proxy: the integration remembers which proxy last authenticated with each thermostat and tries it first on every reconnect. An unbonded closer proxy can no longer steal the connection and take the thermostat down.

  • pairing_required repair: when every path refuses the link for lack of a bond, an actionable repair appears that names the refusing proxies, so you know exactly which one to pair (or set to passive). It clears itself on recovery.

  • Pairing codes as HA notifications: the new ESPHome proxy reference ships pairing responders that push the 6-digit numeric-comparison code to Home Assistant the moment the prompt appears on the thermostat screen. With several thermostats you finally know which unit is pairing through which proxy — just match the code on the physical screen.

  • No more graph holes: 1–2 transient poll failures no longer flip entities to unavailable — sensors keep their last value for a short grace period while the connection recovers. Real outages (and pairing failures) still surface immediately.

  • Saner discovery & onboarding: advertisements below −90 dBm no longer produce discovery cards (no more your neighbour’s BRC1H showing up), and the config flow now performs a full authenticated connection test before creating the entry — a config entry stuck forever in setup_retry is no longer possible.

  • Registry hygiene: orphaned devices left behind by removed entries are purged at startup; devices can be deleted individually from the device page.

  • Requires pymadoka-ng 0.3.7 (installed automatically).

The golden rule (multi-proxy homes)

If several active proxies are in range: every active proxy must be paired with each thermostat (the prompt appears once per proxy — now with the code in your notifications), otherwise set the proxy to bluetooth_proxy: active: false. Full YAML and a troubleshooting table are in the ESPHome proxy reference.

Upgrading

Via HACS (custom repository dasimon135/daikin_madoka if you haven’t added it yet), then restart. Entity IDs and history are preserved. A pairing prompt may appear on a thermostat after the restart — that’s a proxy regularizing its bond: confirm it once and you’re set.

Feedback welcome here or on GitHub!

Hi, I’m currently using the ESP32 method with success but I would like to remove dhe dual Setpoint feature as it shows even when not using the AUTO mode of my a/c unit.
I was also trying to connetc the device using the home assistant integration directly with bluetooth but when I try to connect to the device it doesn’t even show on the BRCH1 the confirmation code and seems to refuse the connection. The device is correctly detected by my bluetooth adapter but can’t connect to it, any suggestion on why does this happen? thanks

Hey :waving_hand: — glad the ESP32 proxy route is working for you! Two separate topics here, let me split them.

1. Getting rid of the dual setpoint

The integration exposes the heat/cool range (the two-handle “low / high” target) only when both of these are true:

  • the climate entity is in AUTO (heat_cool) mode, and
  • the BRC1H itself has its dual-setpoint / range feature enabled.

In Cool / Heat / Dry / Fan it’s always a single setpoint — so in principle you shouldn’t see the range unless the entity is in AUTO.

:red_question_mark: Quick question, because this matters: you mention it shows up even when not in AUTO. Could you tell me:

  • which integration version you’re on, and
  • the exact HVAC mode the climate entity shows in HA (Cool / Heat / Auto…) when the range appears?

That tells me whether this is just AUTO doing its thing, or an actual bug on my side — and it changes what the fix is.

In the meantime, two ways to get rid of it:

  • Easiest: don’t use AUTO in HA — pick Cool or Heat and you get one target temperature.
  • Keep AUTO but single target: disable the dual/range setpoint on the BRC1H itself (it’s a thermostat setting). Once the device stops advertising the range as enabled, HA follows and shows a single setpoint.

There’s currently no toggle inside the integration for this — it mirrors the thermostat on purpose — but depending on your answer above, adding an option to force a single setpoint is something I’m happy to look at.

2. Direct Bluetooth won’t connect / no code on the BRC1H

This one is expected, and it’s exactly why the ESP32 proxy needs special config. :slightly_smiling_face:

The BRC1H only accepts authenticated (MITM) pairing with numeric comparison, and it silently ignores any client that doesn’t request it — no confirmation code, connection refused. Your adapter seeing the advertisement isn’t the same as being allowed to bond.

To pair via the HA host’s own adapter, the BlueZ agent has to ask for that mode. From a shell on the host:

bluetoothctl
agent KeyboardDisplay
default-agent
remove <MAC>      # clear any stale bond first
scan on
# wait for the device to appear
scan off
pair <MAC>
# the 6-digit code now appears on the BRC1H — accept it within a few seconds

If no code ever shows up, the agent isn’t KeyboardDisplay (a none agent produces exactly your symptom). Heads-up: on HAOS / Docker this is finicky — you need the dbus socket mounted + privileged mode, and HAOS doesn’t give you an easy interactive bluetoothctl.

My honest recommendation

Since your ESP32 proxy already works, I’d just stay on it. The direct-adapter path buys you nothing extra and is much more painful to pair. The bond is stored per-adapter anyway, so the proxy is the validated, recommended setup. :+1:

Let me know your version + the HVAC mode and we’ll take it from there!

Thanks for the quick and helpful response.
I am completely a newbie in the esp32 world and bought one just for using it with your integration. I installed it using this code:

external_components:

So I guess it is using 2.2.0 version but could find out if It is the lastest or not.

I confirm you that in any HVAC mode I try (Auto, Cool, Heat, Fan, Dry) it keeps showing me the dual setpoint, even when using cards like bubble card climate or any other.

Tomorrow I will try and disable range mode on my brch1 as I will need to pair back my phone with the Daikin madoka app,then see if It will solve.

Thanks again.

Ah, that detail is the key — thanks! You’re using the ESPHome component (components: [madoka]), not the HA integration, so the dual setpoint comes from a different piece of code than I first described. Sorry for the detour.

In that ESPHome component, the two-target (low/high) setpoint is hardcoded on — it shows in every HVAC mode and there’s currently no option to turn it off. And importantly:

:warning: Please don’t spend time disabling range mode on the BRC1H tomorrow — it won’t change anything here. This behaviour lives in the ESP firmware, not in the thermostat, so re-pairing your phone with the Daikin app won’t help. I’d hate for you to go through that for nothing.

Also, ref: v2.2.0 is not outdated — it’s the pinned version I recommend for that component, so you did everything right.

If a single setpoint matters to you, the clean way is to use your ESP32 as a standard ESPHome Bluetooth proxy together with my Home Assistant integration (Option 1 in the README) instead of the madoka ESPHome component. That path shows a single target outside AUTO mode, it’s the version I actively maintain — and as a bonus it also solves the “direct connection refused” issue you hit, because you’d be going through the proxy rather than pairing directly. It’s a bit more setup, but it’s the recommended route and you already have the right io_capability line.

If you’d rather keep the ESPHome component as-is, that’s fine too — just know the double setpoint can’t be hidden there today. I’m looking at whether I can add an option for it.

Want me to write up the exact proxy + integration config for your ESP32? Happy to.

So, I tried also connecting as keyboard agent but still have the same issue not connectiong to the brch1. In the first attempt it attempted to pair but didn’t show the prompt on the madoka, then started giving me the same error as yesterday with authenticationcanceled error.
If it doesn’t bother you it would be helpful if You wrote me the config for my setup. thanks again

Hi Julian,

That symptom pins it down: keyboard is the wrong I/O capability for the BRC1H, and the missing piece is a pairing responder.

The BRC1H asks for numeric comparison — both sides display a 6-digit code and each confirms “yes, same code”. A keyboard agent offers passkey entry instead (“type the code”), which answers a question the thermostat never asked. The pairing starts, nothing satisfies the comparison, no confirmable prompt lands on the screen, and the link dies as AuthenticationCanceled. Exactly what you saw.

Two things are needed, and the README’s minimal Option 2 config only shows the first — sorry, that’s my omission:

esp32_ble:
  io_capability: display_yes_no      # you already had this

ble_client:
  - mac_address: "AA:BB:CC:DD:EE:FF"   # your BRC1H
    id: my_madoka
    # THIS is what was missing — it answers the numeric comparison
    on_numeric_comparison_request:
      then:
        - ble_client.numeric_comparison_reply:
            id: my_madoka
            accept: true
    on_disconnect:
      then:
        - delay: 10s
        - ble_client.connect: my_madoka

climate:
  - platform: madoka
    name: "Living Room"
    ble_client_id: my_madoka
    update_interval: 15s

Before you reflash, un-jam the thermostat. Repeated failed pairing attempts leave the BRC1H’s Bluetooth stack stuck — it will refuse even a correct config until you reset it:

  1. On the thermostat: Bluetooth menu → forget / un-pair the existing entry.
  2. Toggle its Bluetooth off, then on again.
  3. Then flash the config above and watch for the prompt — accept it within a few seconds.

Also — the dual setpoint option you asked about now exists. I said I’d look into it; it’s in v3.8.0:

external_components:
  - source:
      type: git
      url: https://github.com/dasimon135/daikin_madoka
      ref: v3.8.0        # was v2.2.0
      path: esphome/components
    components: [madoka]

It now defaults to a single setpoint. Add dual_setpoint: true under the climate: block only if range mode is enabled on the thermostat. ESPHome sends climate traits once at entity-listing time, so unlike the HA integration this has to be a build-time choice — but you no longer have to live with the double handle.

If you’d still rather move to the standard-proxy + HA-integration route, say the word and I’ll write that config too — but try the responder first, it’s a two-line change to what you already have.

I think you can also manually use the pairing from developer tools:

and

Not sure if it helps here, but I vaguely remember i used it with the 1st dailin AC i ever paired

If It isn’t of much trouble for you it would be nice if you could help me with the HA integration. Thanks again