Skip to content

Solved: cannot_connect / MQTT code 5 on newer VIDAA firmware — pass MAC explicitly (Docker can't ARP) + disable protocol auto-detect #12

Description

@kushnirpasha-lang

Controlling a "too-new" Hisense VIDAA TV from Home Assistant — SOLVED

(Управление «слишком новым» телевизором Hisense VIDAA из Home Assistant — РЕШЕНО)

🇷🇺 Кратко для русскоязычных: если ваш новый Hisense VIDAA «не подключается» к Home Assistant (ошибка cannot_connect / MQTT code 5), дело почти наверняка НЕ в прошивке. Три причины: (1) сменился IP телевизора, (2) сломан авто-детект протокола, (3) динамический пароль считается из MAC, а Docker не видит MAC телевизора. Ниже — полное решение на английском; прогоните через переводчик/ИИ.

TL;DR: People keep reporting that newer Hisense VIDAA TVs "can't be controlled" from Home Assistant — the vidaa_tv / hisensetv MQTT integrations fail with cannot_connect or MQTT CONNACK code 5 (not authorized), and everyone blames "firmware too new". In our case it was not the firmware. It was three separate, fixable problems stacked on top of each other. After fixing all three, we got full native control — power, keys, state, and app launch (YouTube) — with a persistent token, on a TV that had refused every previous attempt.

Tested on: Hisense 55E7LE, VIDAA, software V0002.09.09B.P1001. Library: vidaa-control 1.1.0 (used by the ha-vidaa-tv custom integration). Home Assistant in Docker (Colima on macOS).


Symptoms we saw (probably the same as you)

  • HA integration config flow → cannot_connect.
  • Manual vidaa / hisensetv connect → Connection failed with code: 5 (MQTT not authorized), repeated.
  • Log line: Failed to fetch protocol info: <urlopen error timed out>Could not detect protocol. Using modern auth.
  • Earlier attempts (older tools) even showed the TV saying "update the app to the latest version" — which everyone reads as "firmware locked it down". It isn't.

The three real root causes

1. The TV's IP had changed (DHCP) — we were probing a dead address

The integration/tool connected fine "yesterday" and then just stopped. The TV had simply got a new DHCP lease. Probing the old IP gives an incomplete ARP entry / "host is down" / filtered ports — which looks exactly like "the service is gone".

Fix: don't hardcode the IP. Discover the current one via mDNS (the TV advertises _hap._tcp HomeKit with a name like Hisense 55E7LE-XXXXXX), or — better — set a DHCP reservation for the TV so its IP never changes.

2. Protocol auto-detection times out → wrong auth path

vidaa-control tries to GET http://<tv>:38400/MediaServer/rendererdevicedesc.xml to read transport_protocol and pick the auth method. On our TV port 38400 is filtered (only 36669 is open), so this times out, and the lib falls back to a guess that fails.

Fix: disable auto-detection and set the auth method explicitly:
auto_detect_protocol=False, auth_method=AuthMethod.MODERN (MODERN = transport ≥ 3290; try MIDDLE/LEGACY if MODERN gives code 5).

3. CONNACK code 5 = the dynamic MQTT password needs the TV's MAC — and Docker can't ARP it

Newer VIDAA firmware uses dynamic MQTT credentials computed from the TV's MAC address (reverse-engineered in credentials.py). The library resolves the MAC via ARP from the host running HA. Inside a Docker/NAT setup (Colima, HAOS supervised, etc.) the container cannot ARP a device on the physical LAN, so it gets no MAC → can't compute the password → CONNACK code 5.

Fix: get the MAC from a machine that IS on the LAN (arp -a | grep <tv-ip>) and pass it explicitly as mac_address="AA:BB:CC:DD:EE:FF".

Bonus gotcha: the pairing code lives ~25 seconds

async_start_pairing() shows a 4-digit code on the TV that expires in ~25 s and changes on every call. If your read→type→submit loop is slower than that, you'll never land a valid code. We used a small script that auto-re-requests the code every ~20 s and reads the PIN from a file, so the code is always fresh and gets injected the instant it's read.


The working recipe

import asyncio
from pathlib import Path
from vidaa import AsyncVidaaTV
from vidaa.protocol import AuthMethod
from vidaa.config.storage import TokenStorage

TV_IP  = "10.0.0.9"                 # discover via mDNS; set a DHCP reservation
TV_MAC = "AA:BB:CC:DD:EE:FF"        # YOUR TV's MAC: arp -a | grep <TV_IP>  (run from a host ON the LAN)

tv = AsyncVidaaTV(
    host=TV_IP, port=36669,
    auto_detect_protocol=False,     # fix #2: :38400 is filtered
    auth_method=AuthMethod.MODERN,  # try MIDDLE / LEGACY if code 5
    use_dynamic_auth=True,
    mac_address=TV_MAC,             # fix #3: required, container can't ARP it
    enable_persistence=True,
    storage=TokenStorage(Path("/config/.vidaa_tv_tokens.json")),
)

async def pair():
    await tv.async_connect(timeout=8)        # connects (authenticated=False on the very first run)
    await tv.async_start_pairing()           # a 4-digit code shows on the TV (~25 s!)
    await tv.async_authenticate("1234")      # enter it FAST
    # token is now saved; future connects reconnect with authenticated=True, no code

asyncio.run(pair())

After pairing once, a token is stored (keyed by MAC). Reconnects are authenticated automatically — you still pass mac_address (the CONNECT password is derived from it), but you never need the on-screen code again.

Verified working afterwards: async_get_state, async_get_apps, async_launch_app("YouTube") → True, power, async_send_key, navigation, volume, source.


If you're using the Home Assistant ha-vidaa-tv custom integration

Its config flow resolves the MAC with get_mac_from_ip(), which returns nothing inside Docker/NAT, so the flow dies at cannot_connect before it can pair. Until the integration lets you enter the MAC manually, pair once with the script above (writing the token to /config/.vidaa_tv_tokens.json) — or patch the flow to accept a MAC. (Maintainers: a "MAC address (optional)" field in the config flow would fix this for every Docker/HAOS user.)

Summary

It's almost never "the firmware locked it". Check, in order: (1) is the IP current? (2) disable protocol auto-detect + set auth method, (3) pass the TV's MAC explicitly (Docker can't ARP), and beat the ~25 s pairing window. All three together got a supposedly "unsupported, too-new" Hisense fully controllable.

Как взять под контроль «слишком новый» Hisense VIDAA из Home Assistant — РЕШЕНО

(Controlling a "too-new" Hisense VIDAA TV from Home Assistant — solved)

Коротко: повсюду пишут, что новые телевизоры Hisense VIDAA «невозможно» подключить к Home Assistant — интеграции vidaa_tv / hisensetv (MQTT) падают с cannot_connect или MQTT-ошибкой CONNACK code 5 (не авторизован), и все винят «слишком новую прошивку». В моём случае прошивка ни при чём. Виноваты три отдельные, решаемые проблемы, наложенные друг на друга. После того как я починил все три, получился полный нативный контроль — питание, кнопки, статус, запуск приложений (YouTube) — с постоянным токеном, на телевизоре, который до этого отказывал во всех попытках.

Проверено на: Hisense 55E7LE, VIDAA, ПО V0002.09.09B.P1001. Библиотека: vidaa-control 1.1.0 (её использует кастомная интеграция ha-vidaa-tv). Home Assistant в Docker (Colima на macOS).


Симптомы (наверняка те же, что у вас)

  • Мастер настройки интеграции → cannot_connect.
  • Ручное подключение через vidaa / hisensetvConnection failed with code: 5 (MQTT «не авторизован»), повторяется.
  • В логе: Failed to fetch protocol info: <urlopen error timed out>Could not detect protocol. Using modern auth.
  • Старые инструменты вообще показывали на телевизоре «обновите приложение до новейшей версии» — что все читают как «прошивка всё закрыла». Это НЕ так.

Три настоящие причины

1. У телевизора сменился IP (DHCP) — вы стучитесь в мёртвый адрес

Ещё «вчера» всё работало, а сегодня перестало. Телевизор просто получил новый IP по DHCP. Пинг/порты на старом адресе выглядят как «сервис пропал» (incomplete ARP, «host is down», порт «filtered»).

Решение: не хардкодьте IP. Определяйте текущий через mDNS (телевизор светит _hap._tcp HomeKit с именем вида Hisense 55E7LE-XXXXXX), а лучше — сделайте телевизору резервирование IP (DHCP reservation) на роутере.

2. Авто-определение протокола зависает → выбирается неправильная авторизация

vidaa-control пытается получить http://<tv>:38400/MediaServer/rendererdevicedesc.xml, чтобы прочитать transport_protocol и выбрать метод авторизации. На моём телевизоре порт 38400 закрыт/filtered (открыт только 36669), поэтому запрос зависает по таймауту, и библиотека уходит в неверную догадку.

Решение: отключить авто-детект и задать метод явно:
auto_detect_protocol=False, auth_method=AuthMethod.MODERN (MODERN = transport ≥ 3290; если code 5 — попробуйте MIDDLE / LEGACY).

3. CONNACK code 5 = динамический MQTT-пароль считается из MAC телевизора, а Docker не видит ARP

Новые прошивки VIDAA используют динамические MQTT-учётки, вычисляемые из MAC-адреса телевизора (алгоритм реверс-инженерен в credentials.py). Библиотека получает MAC через ARP с той машины, где крутится HA. Внутри Docker/NAT (Colima, HAOS supervised и т.п.) контейнер НЕ может сделать ARP устройству в физической локалке → нет MAC → нельзя вычислить пароль → CONNACK code 5.

Решение: возьмите MAC с машины, которая реально в локалке (arp -a | grep <ip-телевизора>), и передайте его явно: mac_address="AA:BB:CC:DD:EE:FF".

Бонус-грабли: код спаривания живёт ~25 секунд

async_start_pairing() показывает на телевизоре 4-значный код, который истекает за ~25 секунд и меняется при каждом вызове. Если ваш цикл «прочитал → ввёл → отправил» медленнее — вы никогда не попадёте валидным кодом. Я сделал скрипт, который сам перезапрашивает код каждые ~20 секунд и читает PIN из файла, чтобы код всегда был свежий и вводился мгновенно.


Рабочий рецепт

import asyncio
from pathlib import Path
from vidaa import AsyncVidaaTV
from vidaa.protocol import AuthMethod
from vidaa.config.storage import TokenStorage

TV_IP  = "10.0.0.9"                 # определить через mDNS; сделать DHCP-резервирование
TV_MAC = "AA:BB:CC:DD:EE:FF"        # MAC ВАШЕГО ТВ: arp -a | grep <TV_IP>  (с машины В локалке)

tv = AsyncVidaaTV(
    host=TV_IP, port=36669,
    auto_detect_protocol=False,     # причина №2: порт 38400 filtered
    auth_method=AuthMethod.MODERN,  # если code 5 — попробуйте MIDDLE / LEGACY
    use_dynamic_auth=True,
    mac_address=TV_MAC,             # причина №3: обязательно, контейнер не видит ARP
    enable_persistence=True,
    storage=TokenStorage(Path("/config/.vidaa_tv_tokens.json")),
)

async def pair():
    await tv.async_connect(timeout=8)        # подключается (authenticated=False на самом первом запуске)
    await tv.async_start_pairing()           # на ТВ появляется 4-значный код (~25 секунд!)
    await tv.async_authenticate("1234")      # вводите БЫСТРО
    # токен сохранён; дальше подключения авторизуются сами, код больше не нужен

asyncio.run(pair())

После первого спаривания токен сохраняется (привязан к MAC). Последующие подключения авторизуются автоматически — MAC всё равно передаём (из него считается пароль для CONNECT), но код с экрана больше никогда не нужен.

Проверено после спаривания: async_get_state, async_get_apps, async_launch_app("YouTube") → True, питание, async_send_key, навигация, громкость, выбор источника.


Если вы используете кастомную интеграцию ha-vidaa-tv для Home Assistant

Её мастер настройки берёт MAC через get_mac_from_ip(), который внутри Docker/NAT возвращает пусто, поэтому мастер умирает на cannot_connect ещё до спаривания. Пока в интеграции нельзя ввести MAC вручную — спарьтесь один раз скриптом выше (он сохранит токен в /config/.vidaa_tv_tokens.json), либо пропатчите config flow, чтобы принимал MAC. (Мейнтейнеру: поле «MAC address (optional)» в мастере починило бы это для всех, кто на Docker/HAOS.)

Итог

Почти никогда это не «прошивка всё закрыла». Проверьте по порядку: (1) актуален ли IP? (2) отключите авто-детект протокола и задайте метод авторизации, (3) передайте MAC телевизора явно (Docker не может ARP), и успевайте в окно кода ~25 секунд. Все три вместе сделали «неподдерживаемый, слишком новый» Hisense полностью управляемым.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions