Skip to content

Цепочка багов: crash config flow → Unknown entity → unavailable сенсоры | Polaris PWK-1725CGLD #2

@IlyaSochinskij

Description

@IlyaSochinskij

Цепочка багов: crash config flow → Unknown entity → unavailable сенсоры

Устройство

  • Чайник: Polaris PWK-1725CGLD (devtype 86, firmware 2.30, MAC d8:bc:38:ae:cf:ff)

Другие Syncleo-устройства в сети (не управляются этой интеграцией, но влияют на config flow)

  • Polaris PCM-1540WiFi (devtype 279, MAC 48:e7:29:72:00:29) — кофемашина, управляется через polaris-mqtt
  • Polaris PVCRDC-5004 (devtype 202, MAC ec:41:f9:b8:c5:07) — пылесос, управляется через polaris-mqtt

Все три устройства используют протокол Syncleo и видны в Zeroconf под _syncleo._udp. Config flow отображает их все в списке, и крэш вызывается именно тем, что кофемашина отдаёт basetype: 'Unknown'.

  • Версия интеграции: v1.0.1
  • Home Assistant: 2026.3.x

Описание проблемы

Интеграция не работает с устройствами, которые при Zeroconf discovery отдают пустые или запоздавшие TXT-записи. Это приводит к цепочке из четырёх последовательных багов, каждый из которых блокирует работу.


Баг 1: Config flow падает с ValueError — окно добавления крашится

Воспроизведение

  1. Открыть «Добавить интеграцию» → Syncleo Kettle
  2. В списке устройств отображаются: 202: ec41f9b8c507 (Polaris PVCR-5001), Unknown: d8bc38aecfff, Unknown: 48e729720029
  3. Выбрать любое устройство → нажать «Подтвердить»
  4. Результат: «Unknown error occurred» / «Неверный формат данных»

Причина

В config_flow.py строка 63:

if int(device['basetype']) in POLARIS_DEVICE:

Кофемашина и чайник отдают basetype: 'Unknown' (TXT-записи не успели прийти). int('Unknown')ValueError. Из-за одного устройства с кривыми данными крашится окно для всех устройств.

Скриншот

(см. приложенный скриншот 1 — «Unknown error occurred»)


Баг 2: После фикса config flow — KeyError: 'addresses'

Воспроизведение

  1. После патча config_flow (замена int() на безопасную проверку) чайник появляется в списке как 86: d8bc38aecfff (Polaris PWK-1725CGLD)
  2. Ввести токен → нажать «Подтвердить»
  3. Результат: «Не удалось подключиться к устройству»

Причина

validate_input() передаёт discovered_device_info из Zeroconf-кэша в coordinator.async_setup(). Но кэш содержит словарь из discovery.py (с ключами mac, vendor, devtype...), а coordinator ожидает объект ServiceInfo (с ключом addresses). Итог: KeyError: 'addresses'.

Скриншот

(см. приложенный скриншот 2 — «Не удалось подключиться к устройству»)


Баг 3: Чайник добавляется как «Unknown» с unavailable сенсорами

Воспроизведение

  1. После второго патча (передача None вместо device_info в validate_input) чайник успешно добавляется
  2. Но entity отображается как «Type Unknown (Unknown)», все сенсоры — «Недоступно»

Причина

__init__.pyasync_setup_entry() снова передаёт discovered_device_info из кэша в coordinator. Та же проблема: кривые данные из кэша → coordinator падает → сенсоры не инициализируются.


Баг 4: Чайник отваливается при рестарте HA

Воспроизведение

  1. После полного фикса (передача None в __init__.py) чайник работает
  2. Перезагрузить HA несколько раз подряд
  3. В 2–3 случаях из 5: entity уходят в unavailable, появляется «Этот объект больше не предоставляется интеграцией syncleo_kettle»

Причина — цепочка:

discovery.py: Метод _update_service добавляет устройство в кэш даже с пустыми properties. В mDNS информация приходит частями — сначала факт наличия устройства, потом TXT-записи (vendor, devtype). Если TXT ещё не прилетели, устройство записывается как devtype: 'Unknown'.

coordinator.py: async_setup делает одну попытку discovery с таймаутом 10 секунд. Если чайник не успел — создаётся заглушка devtype=00, model=Unknown. Entity регистрируется с этими данными. При следующем рестарте, если чайник отвечает нормально, HA видит другой device_info и решает что старая entity «больше не предоставляется».

Логи при проблемном старте

Discovered device: d8bc38aecfff - 192.168.1.120:41122 (devtype: Unknown, vendor: Unknown)
Discovered device has empty properties, continuing to wait...
Timeout waiting for device with properties
Device info from dict: vendor=Polaris, basetype=00, devtype=00, firmware=0.00, model=Unknown
Device service info not available, cannot start server
Kettle is not connected (connection check failed)

Логи при успешном старте

Discovered device: d8bc38aecfff - 192.168.1.120:41122 (devtype: 86, vendor: Polaris)
Device info: vendor=Polaris, basetype=00, devtype=86, firmware=2.30, model=PWK-1725CGLD
Connection status updated: ConnectionStatus.CONNECTED

Что исправлено

1. discovery.py — фильтр Unknown-устройств

Было: _update_service берёт всё из Zeroconf. Если TXT-записи не пришли — записывает устройство как devtype: Unknown, vendor: Unknown. Мусор попадает в кэш и отравляет всю цепочку.

Стало: Жёсткий фильтр. Если properties пустые или нет devtype/vendor — пакет игнорируется. Ждём следующего обновления от Zeroconf с реальными данными. Полностью убирает появление «призраков» в системе.

devtype = properties.get('devtype')
vendor = properties.get('vendor')
if not devtype or not vendor:
    _LOGGER.debug(
        "Ignoring device %s - missing critical properties, "
        "TXT records may not have arrived yet", mac
    )
    return

2. coordinator.py — стабильность подключения

Было: async_setup делает одну попытку с таймаутом 10 секунд. Не ответил — заглушка Unknown, и всё.

Стало:

Retry-логика: 3 попытки с увеличивающимся таймаутом (10/15/20 сек). HA не сдаётся сразу, а долбится в чайник до 45 секунд.

retry_timeouts = [10, 15, 20]
for attempt, timeout in enumerate(retry_timeouts, 1):
    service_info = await self._hass.async_add_executor_job(
        self.kettle.discover, True, timeout, zeroconf_instance
    )
    if service_info:
        break
    if attempt < len(retry_timeouts):
        await asyncio.sleep(3)

Восстановление device_info: Если девайс всё же создался как Unknown, координатор сам перепишет информацию в реестре HA, как только получит нормальные данные.

if (self.device_info and 
    self.device_info.get("model_id") == "00" and 
    self.kettle.device and self.kettle.device.si and 
    self.kettle.device.si.properties):
    self._create_device_info(self.kettle.device.si)
    self.async_update_listeners()

3. __init__.py — оптимизация и чистка

Было:

  • Глобальный запуск discovery в async_setup — работает всегда, даже если нет ни одного чайника. Источник race condition.
  • async_setup_entry передаёт мусорные данные из кэша в coordinator → KeyError: 'addresses' → сенсоры unavailable.
  • async_unload_entry падает с KeyError если setup завершился с ошибкой на полпути.

Стало:

  • async_setup удалён целиком. Внедрён _async_lookup_discovered_device — умный поиск в кэше с короткими итерациями. Старт не блокирует весь HA.
  • async_setup_entry передаёт None в coordinator — coordinator всегда проходит через полный discovery с retry, не полагаясь на потенциально мусорные данные.
  • Defensive Unload: при удалении/перезагрузке интеграция не падает с KeyError, корректно вычищает данные через .get() / .pop(..., None).
  • Единообразное использование DOMAIN из .const.
  • Очистка hass.data[DOMAIN] при выгрузке последней записи.

4. config_flow.py — валидация «на берегу»

Было:

  • int(device['basetype']) крашит весь config flow если basetype не число → ValueError → «Unknown error occurred» для всех устройств.
  • 3-секундная задержка discovery при каждой отправке формы, а не только при первом показе.
  • async_step_zeroconf запускал лишний discovery, хотя Zeroconf уже нашёл устройство.
  • validate_input передаёт кривой кэш в coordinator → KeyError: 'addresses'.
  • coordinator.shutdown() не вызывался при ошибке → утечка ресурсов.

Стало:

  • Безопасная проверка через _get_model_name()isdigit() перед int(), хелпер вынесен и используется единообразно.
  • Discovery только при первом показе формы.
  • Zeroconf заранее заполняет _discovered_devices — пропускает повторное 3-секундное сканирование.
  • validate_input передаёт None в coordinator — свежий discovery вместо мусора из кэша.
  • coordinator.shutdown() обёрнут в finally.
  • Вынесен _normalize_mac() — убрана дупликация.
  • start_discovery обёрнут в try/except.
  • Константа DISCOVERY_WAIT_SECONDS вместо хардкода 3.0.

Окружение

  • HA 2026.3.x (Python 3.14)
  • Polaris PWK-1725CGLD (devtype 86, firmware 2.30)
  • В сети также присутствуют PCM-1540WiFi (devtype 279) и PVCRDC-5004 (devtype 202) — оба на Syncleo, видны в config flow
  • Подключение: UDP (Syncleo protocol), Zeroconf discovery
  • Wi-Fi: RSSI стабильный, все устройства в одной подсети с HA

Приложенные файлы

Исправленные версии четырёх файлов + два скриншота:

  • discovery.py
  • coordinator.py
  • __init__.py
  • config_flow.py
  • Скриншот 1: crash config flow («Unknown error occurred»)
  • Скриншот 2: после частичного фикса («Не удалось подключиться к устройству»)
Image Image

fix.zip

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