Skip to content

Releases: TohaRG2/sboom_ha

v0.37.1

Choose a tag to compare

@TohaRG2 TohaRG2 released this 20 Aug 12:07
dc36ed3

[0.37.1] — 2026-08-15

Fixed — ошибка «expected str … Got None» в панели

WS-команды панели (sboom/queue, sboom/state, sboom/subscribe и др.) падали
с ошибкой валидации, если панель обращалась к ним до загрузки списка колонок
(entry_id ещё null). Схема теперь принимает null — команда адресуется
первой доступной колонке. Симптом — «Ошибка очереди» при открытии каталога.

[0.37.0] — 2026-08-15

Added — эквалайзер и настройки колонки в панели и карточке

Панель и Lovelace-карточка SberBoom получили блок «Звук колонки»: эквалайзер
(включение, пресеты Flat/Рок/Поп/Джаз/Книга/Бас, 5-полосная регулировка) и полезные
переключатели колонки (детский режим, звуки ассистента, подсветка). Управление идёт
через сущности интеграции sberhome на том же устройстве колонки — интеграции
«поженены» общим идентификатором, поэтому карточка находит эквалайзер автоматически.
Блок сам скрывается, если у колонки нет эквалайзера или sberhome не установлена.

Новый переиспользуемый компонент sboom-equalizer.js (общий для панели и карточки).
Команда sboom/devices теперь отдаёт serial колонки — мост к сущностям sberhome.

[0.36.0] — 2026-08-08

Волна 5 (финальная) по итогам code review (REVIEW_REPORT.md): архитектурный рефакторинг. Поведение не меняется, внутренние границы — да (MINOR).

Changed

  • Единый командный слой coordinator.async_execute(action, value) (аудит #18). Один источник политики «команда → вызов клиента → optimistic-патч → refresh»: media_player, switch/number/select и ws-команда sboom/command панели стали тонкими адаптерами. Побочно чинит несогласованность: switch/number теперь делают optimistic-патч (переключатель mute и слайдер громкости больше не «отпрыгивают» в окне debounce), select/repeat — единообразно.
  • Единый источник deeplink и инфраструктурных хелперов (аудит #17, #19). Новый модуль _ha_helpers.py: get_zvuk_client(hass) (singleton, ключ const.ZVUK_CLIENT_KEY) и iter_coordinators(hass) вместо трёх скопированных реализаций в services/websocket_api/device_action. Маппинг zvuk-URL→deeplink — только в ZvukClient (см. 0.35.3).
  • Публичный client.send_raw_request(envelope, msg_id, timeout) (аудит #16). _deeplink.py больше не лезет в приватные client._ws/_pending/_lock — инкапсуляция корреляции request/response вынесена в клиент; _request_response переиспользует её.
  • Аппаратная подсистема выделена в HwMonitor (аудит #20). iio-датчики + Zigbee/Matter CLI со своим probe/poll и снапшот-полями — вне SboomCoordinator (SRP). Доступ сенсоров — coordinator.hw.*; старый coordinator.iio_cap/… сохранён как property-делегаты.
  • DRY-вычистки: общий _draw_frame_chrome для караоке- и idle-кадров (аудит #45); coordinator.device_state вместо трёх копий _dev() (аудит #46); LyricsManager._run_fetch + helpers.track_identity_key для обоих менеджеров (аудит #47).

Removed

  • Мёртвый код (аудит #43): api.AuthError, client.get_scanned_bt_devices (op=21 не использовался), image_render.draw_cover/draw_lyrics (заменены yandex-версиями), cli4242.matter_count, coordinator.matter_raw, SpeakerState.track.

Fixed

  • Инкапсуляция (аудит #44): публичные LyricsManager.supports_track() и coordinator.stopping вместо импорта приватного _synthetic_key в sensor.py и чтения coord._stopping в diagnostics.py; SboomSensorSpec.state_class/device_class типизированы enum'ами SensorStateClass/SensorDeviceClass вместо строковых литералов.

Docs

  • Зафиксирован трейдофф WSS без верификации сертификата (аудит #40) в разделе «Известные ограничения» README: self-signed сертификат колонки, модель угроз — доверенная LAN, возможный TOFU-пиннинг в будущем.

[0.35.6] — 2026-08-08

Волна 4 исправлений по итогам code review (REVIEW_REPORT.md): устойчивость, hardening парсеров, гигиена ошибок.

Fixed

  • Hardening парсеров на недоверенных payload'ах (все репродукции из ревью превращены в регрессионные тесты):
    • parse_track больше не теряет трек со скобками {/} в названии/имени артиста (аудит #1): вместо string-слепого backward-скана — перебор кандидатов-{ со string-aware балансировкой и проверкой охвата trackId.
    • parse_track принимает числовой trackId без кавычек (аудит #25), как это уже делал track_from_state.
    • parse_state не срывается на байте 0x7b в бинарном TLV-префиксе — пробует следующую { (аудит #27); DeviceState (~20 сенсоров) больше не пропадает на такой цикл.
    • parse_state переживает мусорный volume.percent (null/строка) — поле остаётся None, merge сохраняет прежнее (аудит #28; раньше TypeError превращал живую колонку в ConfigEntryNotReady).
    • decode/decode_repeated проверяют границы для length-delimited и fixed32 — обрезанный TLV-кадр даёт частичный результат, а не битые значения (аудит #26).
    • seek_to clamp'ит отрицательную позицию (аудит #12): varint кодировал −1 как 127 для колонки.
  • Сервисы отдают внятные ошибки (аудит #37). play_music/bluetooth_device оборачивают транспортные сбои колонки (RuntimeError/TimeoutError/ConnectionError/OSError) в HomeAssistantError вместо сырого traceback «Unknown error» в UI.
  • ZvukClient.search держит контракт «ошибка → пусто» (аудит #22). get_token перенесён внутрь try: недоступность zvuk.com на этапе токена больше не пробивает services.play_music сырым httpx-исключением.
  • Lrclib: битый 200-ответ не роняет background-task (аудит #23). ValueError (невалидный JSON) ловится, не-dict тело трактуется как сетевая аномалия (retry позже).
  • Камера не долбит лежащий CDN (аудит #39). Неудачный URL обложки кэшируется на 60 с — idle-стрим отдаёт фон-заглушку сразу, вместо скачивания с 10-секундным таймаутом на каждый кадр.
  • Diagnostics больше не дезинформирует (аудит #42). Поле last_update_success (всегда True — сбои полла глотаются намеренно) убрано из выгрузки; честная доступность — connected.

[0.35.5] — 2026-08-08

Волна 3 исправлений по итогам code review (REVIEW_REPORT.md): конкурентность и жизненные циклы ресурсов.

Fixed

  • Гонка last-write-wins poll vs push/optimistic закрыта (аудит #5). Введён generation-счётчик мутаций coordinator.state/track: push-события и optimistic-патчи инкрементируют его, а poll снимает снапшот перед await и отбрасывает свой ответ, если данные изменились во время ожидания. Раньше стейл-ответ poll'а откатывал optimistic-громкость слайдера и свежий push-трек (и стрелял дубль-событиями).
  • Жизненный цикл боковой панели согласован между entries (аудит #14). Панель существует, пока её хочет хотя бы один загруженный entry (_sync_panel): entry с panel_enabled=False больше не сносит панель другой колонки при своей перезагрузке; выгрузка последнего entry убирает пункт из меню (раньше он оставался ссылаться на мёртвый backend до рестарта HA); регистрация статики защищена от TOCTOU (маркер до await'ов).
  • Подписка панели переживает reload entry (аудит #32). Координатор получил async_add_stop_listener; sboom/subscribe шлёт терминальное событие {terminated: true} при остановке координатора, фронтенд переподписывается на новый инстанс. Раньше панель «замирала» на мёртвом координаторе до перезагрузки страницы.
  • ZvukClient.aclose() больше не мёртвый код (аудит #31). Разделяемый клиент закрывается по EVENT_HOMEASSISTANT_STOP; ключ singleton'а вынесен в const.ZVUK_CLIENT_KEY (раньше единственность держалась на совпадении строк в двух модулях).
  • CLI-опрос :4242 стал в ~10 раз быстрее (аудит #15). _drain выходит по тишине (idle-timeout 0.3 c) вместо сжигания полного read-timeout 3 c после последнего чанка: каждая команда стоила ~3.6 c внутри цикла координатора (Zigbee+Matter — ~7 c задержки poll'а и setup).
  • Стейл-результаты volatile-fetch отбрасываются (аудит #33). LyricsManager и CoverManager запоминают ключ последнего запрошенного трека: при смене A→B поздний результат A больше не перезаписывает слот (мерцание карточки, лишний повторный fetch).
  • fallback_cover не блокирует event loop (аудит #34). Первое чтение CC0-фонов с диска ушло в asyncio.to_thread.

[0.35.4] — 2026-08-08

Волна 2 исправлений по итогам code review (REVIEW_REPORT.md): заметные пользователю баги.

Fixed

  • Караоке-кадр не терял CJK-лирику и длинные слова (аудит #10). _layout_text переносил строки регэкспом, требующим пробел в конце фрагмента — текст без пробелов длиннее line_width молча терял начало (оставался только хвост). Заменено на textwrap.wrap(break_long_words=True).
  • Снапшот камеры не падает на тексте-простыне (аудит #11). Рекурсия уменьшения шрифта в _layout_text не имела нижней границы: длинный текст уводил font_size в ≤0 → ValueError из PIL. Введена граница 12.
  • MJPEG-стрим отдаёт кадры в BT-режиме без позиции (аудит #9). В _stream_idle начальное last_sec=None совпадало с cur_sec=None (у BT-трека позиции нет) — не рисовался ни один кадр. Теперь sentinel: первый кадр рисуется всегда.
  • Смена песни на BT/радио видна сразу, а не через volume-poll (аудит #7). Push GET_STATE теперь пересобирает now-playing (_maybe_update_track_from_state): раньше title/artist обновлялись с задержкой до 15 с. Каталожный трек не затирается — его обновляет OP_GET_META_DATA.
  • Поиск Звука оживает после протухания токена (аудит #6). _graphql и search при 401 делают один retry с get_token(force=True); раньше протухший анонимный токен молча убивал поиск/каталог/play_music до рестарта HA.
  • Device-триггеры работают для вручную добавленных колонок (аудит #4). Единый helpers.sber_device_id (fallback CONF_DEVICE_ID or host) используется и в identifiers устройства, и в событиях HA bus: раньше события несли device_id=None, а триггер фильтровал по host — все 4 device-триггера молча не срабатывали.
  • Позиция длинного трека не откатывается на 601-й секунде (аудит #13). Дельта экстраполяции...
Read more

v0.35.2

Choose a tag to compare

@TohaRG2 TohaRG2 released this 27 Jul 12:39
ec30f0e

[0.35.2]

Fixed

  • Ошибка customElements.define … "sboom-nowplaying" has already been used при открытии панели, когда на дашборде используется карточка sboom-card. Карточка импортирует общие компоненты без ?v, панель — с ?v=<версия>; разные URL заставляли браузер выполнять модуль дважды, а незащищённый define падал. Все shared-компоненты (sboom-nowplaying, sboom-controls, sboom-browse, sboom-track-row) теперь регистрируются идемпотентно (if (!customElements.get(...))), как sboom-toast/sboom-tile — двойная загрузка безопасна.

[0.35.1]

Changed

  • Рефактор фронтенда для переиспользования: data-feed логика панели (push-подписка sboom/subscribe + poll-fallback + devices/state/seek/toast-релей) вынесена в общий базовый класс SboomFeedBase (www/components/sboom-feed-base.js); дизайн-токены --sb-* — в www/components/sboom-tokens.css.js. Панель наследует базу без изменения поведения. Открывает путь для standalone Lovelace-карточки (sboom-card), переиспользующей те же компоненты и feed-слой.
  • Статика /sboom_panel теперь раздаётся всегда, независимо от опции panel_enabled — раздача www/ отвязана от регистрации боковой панели. Опция гейтит только пункт в боковом меню; общие компоненты остаются доступны для Lovelace-карточки sboom-card даже при выключенной панели.

[0.35.0]

Added

  • Встроенная премиум-панель плеера в боковом меню Home Assistant («SberBoom»): immersive-карточка (обложка во весь фон, фрост-стекло, ambient-glow из цвета обложки, скраббер, транспорт) + люксовые SVG-контролы. Оформление на нативных токенах темы HA (палитра/акцент/радиусы адаптируются к теме пользователя). Отображает версию интеграции; отзывчива до мобильного; на простое схлопывается.
  • Единый drill-down браузер список+поиск без табов: очередь → поиск (Исполнители/Альбомы/Треки/Плейлисты) → артист (аватар, топ-треки, дискография) → альбом (треклист) → трек. Навигационный стек, кнопка «назад», персистентный поиск.
  • Богатый поиск и метаданные Sber Звук — обложки, дискография, треклисты; работает с домашнего IP без VPN. Backend: standalone ZvukClient (get_tracks/get_artists/get_releases/get_artist/get_release, поиск, доминирующий цвет обложки) + WebSocket API панели.
  • Расширенные контролы: лайк/дизлайк (тюнинг персональной волны), скорость воспроизведения (0.75×–2×), найти пульт, shuffle/repeat, громкость.
  • Поддержка нескольких колонок — селектор устройства; команды/подписка/очередь/поиск адресуются по entry_id.
  • Опция включения/выключения панели (Settings → Integrations → SBoom → Configure → «Показывать панель плеера в боковом меню»).
  • Сервис sboom.play_music — запуск по ссылке zvuk.com, поисковому запросу или id+kind.

Fixed

  • Мгновенные обновления панели (push-подписка на координатор вместо 5-сек поллинга); прогресс-бар якорится к серверному снимку и идёт плавно, корректен после перезагрузки.
  • Оптимистичные кнопки транспорта (мгновенная реакция); дедуп play-команд; «потерянные/неверные» тапы устранены (shouldUpdate-гейт, захват трека на pointerdown, приручённый рефетч очереди) — по итогам глубокого аудита.
  • Убран мусорный playlist_title из now-playing.

[0.29.1]

Fixed

  • Matter-сенсор пропадал (unavailable) при подключении реального Matter-устройства к колонке. Probe async_matter_probe искал слово «matter» в выводе matter list, но реальная таблица устройства (| NodeId | XID | Serial number | Model ID | … |) его не содержит → probe ложно возвращал False → has_matter_cli=False → сенсор не пересоздавался при рестарте (становился restored/unavailable). Теперь probe определяет доступность по отсутствию error-вывода CLI (не по слову «matter»).
  • matter list парсится в структуру (node_id / serial / model / RSSI): сенсор показывает список устройств в атрибутах, а count больше не считает строку-заголовок за устройство. Формат подтверждён на реальном устройстве (MTFFF40002).

[0.29.0]

Added

  • Опция «Караоке-закраска» (Options Flow) — посимвольная подсветка строки по мере пропевания. По умолчанию выключена: строка показывается статично. Когда off — доля пропетости (frac) не идёт ни в рендер, ни в кэш-ключ стрима, так что лишних кадров нет.

Known limitations

  • Закраска рассчитана на письмо слева-направо; для RTL (арабский/иврит) направление заливки не инвертируется (нужны bidi + shaping, напр. libraqm) — отдельная задача. Т.к. закраска off по умолчанию, на поведение «из коробки» не влияет.

[0.28.0]

Added

  • Заглушка-фон вместо чёрного экрана, когда обложка не нашлась (редкий BT-трек, радио без арта, сбой загрузки). Бандл из 4 свободных градиентов (StockSnap/Flickr, CC0 1.0 — атрибуция не требуется, см. backgrounds/CREDITS.txt). Выбор стабильно-случайный по треку (title|artist → индекс) — фон не мигает от кадра к кадру, но у разных треков разный. Применяется и к караоке-кадру, и к idle. Общий вес ассетов ~64 КБ.

[0.27.0]

Added

  • Обложка для Bluetooth и радио по исполнителю+треку. У некаталожного контента нет Zvuk-release_id, поэтому обложки не было (карточка и караоке-фон пустые). Теперь ищется по title+artist в публичных Search API без ключей: iTunes (апскейл до 600×600) → Deezer (резерв). Волатильно (один in-memory слот на трек, без диск-кэша). Подхватывается и в media_image_url (карточка media_player), и в blur-фон караоке-камеры. Радио тоже получает обложку текущей песни (обложке синк не нужен).
  • Кэш обложки в караоке-камере переведён с track_id на URL — раньше некаталожные треки (без track_id) делили один ключ.

[0.26.1]

Fixed

  • Радио исключено из караоке. Колонка отдаёт для радио позицию ЭФИРА (напр. 1224 c при песне 36–250 c), а не песни — синхронизировать лирику нечем. Лирика для радио больше не запрашивается, караоке-камера показывает idle (обложка/заголовок), а не замороженную последнюю строку. Bluetooth (позиция песенная) — работает и синхронизируется корректно (подтверждено вживую).
  • Диагностический сенсор _lyrics больше не показывает no_track для Bluetooth: теперь состояние лирики отражается и для некаталожного контента (у которого нет Zvuk track_id).

[0.26.0]

Added

  • Караоке/лирика для Bluetooth и радио — тянется напрямую из провайдеров (Lrclib → NetEase), без персистентного диск-кэша. У BT/радио нет глобального каталожного track_id (он есть только у контента Zvuk), поэтому:
    • ключ — синтетический title|artists; кэш каталога (по track_id) не затрагивается и не засоряется;
    • один in-memory слот на текущий трек — API не дёргается каждый poll, но и на диск ничего не пишется (BT — почти всегда одноразовая чужая музыка, радио — эфемерно);
    • контент без исполнителя (радио-реклама, джинглы) не запрашивается (гейт по artists).
    • BT синхронизируется по позиции/длительности от колонки; у радио позиция эфирная, поэтому синк приблизительный (сам текст показывается).

[0.25.0]

Fixed

  • Радио и Bluetooth больше не показываются как IDLE с пустой карточкой. У радио и BT-аудио нет trackId, поэтому get_metadata (op 10) для них пуст → трек был None → media_player висел в IDLE, хотя звук идёт. Теперь now-playing для них добирается из GET_STATE:
    • Радио: станция → media_channel (штатное поле HA), текущая песня → media_title, исполнитель → media_artist. На паузе остаётся станция, state = PAUSED (не IDLE).
    • Bluetooth (app bluetooth_media_control, а не music): title/исполнитель/альбом из player.info; media_source="BLUETOOTH".
    • Прогресс-бар скрывается у радио (duration=0→None); обычная музыка/волна/подкаст (у них есть trackId) не затронуты.

Added

  • Сенсор sensor.<name>_playback_mode — тип воспроизведения: music / wave / podcast / radio / bluetooth. Единого поля-режима у колонки нет (radio по mode, wave/podcast по playlistType, bluetooth по источнику) — сенсор их сводит и работает даже когда media_player пуст.

[0.24.0]

Added

  • Слой межустройственной связки в GET_STATE. Раньше парсили только multiroom.mode/stereoPair.active; теперь читаем весь слой объединения устройств Sber:
    • Новый диагностический сенсор sensor.<name>_device_links (выкл. по умолчанию) — число связанных устройств (SberCast + группы селектора + саундбар), разбивка по источникам в атрибутах. На одиночной колонке 0; наполняется при объединении с SberBox/ТВ/другой колонкой (companion-протокол общий для платформы StarOS).
    • Сенсор multiroom_mode обогащён атрибутами стереопары: канал (L/R) и устройство-партнёр (multiroom.stereoPair.channelFromConfig/pairDeviceFromConfig).
    • Парсинг sbercast, deviceSelector (cast/ds/room/qc-группы), deviceGroups.soundBar — пустые источники отфильтровываются, ""-каналы нормализуются в None.

[0.23.2]

Changed

  • Честная документация op 18 (keep-alive). Эксперимент показал: транспортный WS ping/pong держит сессию сам (≥150 c idle без op 18 — соединение живо), а keep-alive-семантика op 18 нашим фаззингом не подтверждена. Комментарии в коде уточнены: op 18 оставлен как дешёвая application-level страховка, но его вклад в поддержание связи не изолирован. Поведение не менялось.

[0.23.1]

Changed

  • Документация синхронизирована под аппаратные возможности: карточка HACS info.md (датчики платы, инвентарь Zigbee/Matter) и дерево файлов README (iio_client.py, cli4242.py).

[0.23.0]

Added

Аппаратные возможности колонки (только на моделях, которые их реально имеют — напр. SberBoom R2; проверяется при старте, иначе сущности не создаются):

  • Сенсор освещённости (lux) и температуры SoC (°C) — с датчиков платы через libiio (:30431). Освещённость полезна для автоматизаций «стемнело → включи свет».
  • Инвентарь Zigbee (zigbee_inventory) — устройства умного дома, привязанные к колонке: количество + модели/производитель/R...
Read more

0.11.1

Choose a tag to compare

@TohaRG2 TohaRG2 released this 19 May 05:46

[0.11.1]

Fixed

  • Сенсор «Активное приложение» флапал каждый poll-цикл, перебирая music/news/bluetooth_media_control/… Причина: значение бралось из background_apps[0], а это самотасующийся z-order стек — первый элемент меняется сам по себе. Теперь active_app — приложение с реально активным плеером (state.player.playing); если ничего не играет — состояние unknown.

[0.11.0]

Управление Bluetooth: сопряжение, спаренные устройства, поиск пульта.

Added

  • Кнопка «Bluetooth-сопряжение» — переводит колонку в режим сопряжения по Bluetooth. Длительность окна видимости задаёт прошивка колонки.
  • Кнопка «Найти пульт» — команда поиска пульта ДУ.
  • Сенсор «Спаренные Bluetooth-устройства» — количество + список (MAC, имя, статус подключения) в атрибутах.
  • Сервис sboom_ha.bluetooth_device — подключить / отключить / удалить спаренное BT-устройство по MAC.
  • Методы клиента: find_remote(), bt_make_discoverable(), get_paired_bt_devices(), get_scanned_bt_devices(), bt_device_command().
  • TLV-кодек: decode_repeated() — декод с поддержкой повторяющихся тегов (для списков устройств).

[0.10.0]

Персист lyrics-кеша + парсер-слой очереди воспроизведения.

Added

  • Персист lyrics-кеша. lyrics_to_dict() / lyrics_from_dict() + HA Store (JSON в .storage/). Кеш текстов песен загружается при старте интеграции и сохраняется с debounce — раньше жил только в памяти и терялся при каждом рестарте HA.
  • Парсер-слой очереди воспроизведения (op=17): QueueTrack, parse_queue(), SberSpeakerClient.get_queue() возвращает list[QueueTrack]. Низкоуровневая утилита без UI-сущности — очередь колонки отдаёт только trackId без названий, а резолв через Zvuk API требует авторизации (отдаёт 401 без токена).

Fixed

  • Lyrics-сенсор _tick вызывал async_write_ha_state() из executor-потока (RuntimeError в логе на HA 2026.5 / Python 3.14). Добавлен декоратор @callback — таймер-колбэк теперь исполняется в event-loop.

[0.9.0]

Сенсоры подсистем устройства из GET_STATE.

Added

  • Платформа binary_sensor — новая, 6 сущностей.
  • 13 read-only сущностей из уже приходящего GET_STATE (раньше из него брались только volume/muted):
    • sensor: яркость дисплея (%), будильники, таймеры, активное приложение, персона ассистента; multiroom-режим и тип подключения — diagnostic.
    • binary_sensor: дисплей включён, колонка активна (device_class: running), стереопара; устройство подписки, домашняя безопасность, утреннее шоу — diagnostic.
  • DeviceState — модель подсистем устройства; SpeakerState.device — поле с ней.
  • parse_device_state() — парсер подсистем GET_STATE.

Changed

  • parse_state() теперь извлекает сбалансированный JSON-объект и парсит подсистемы устройства. При битом JSON — fallback на прежний regex по volume, device=None.

[0.8.0]

Управление скоростью воспроизведения.

Added

  • Select-сущность «Скорость воспроизведения» (select.*_playback_speed). Пресеты 0.5×–2.0× с шагом 0.25. Скрыта на dashboard «Auto» по умолчанию (entity_registry_visible_default = False) — как и остальные side-feature сущности.
  • SberSpeakerClient.set_playback_speed() — команда op=23 (OP_SET_PLAYBACK_SPEED). Скорость кодируется как float (TLV wire-type 5, 4 байта LE IEEE-754); varint и nested-JSON ломают playbackSpeedRate колонки в 0.0 (подтверждено в research/exp_22). Значение жёстко ограничивается диапазоном 0.5–2.0.
  • TrackInfo.playback_speed — playbackSpeedRate из метаданных трека (push- и state-формат).
  • TLV-кодек (_tlv.field) получил поддержку kind=5 (fixed32 float).

[0.7.4]

Правки по итогам code-аудита всей интеграции.

Fixed

  • ConfigEntryNotReady при недоступной колонке на старте. Раньше первый connect шёл в фоне — интеграция «поднималась» в заведомо мёртвом состоянии, даже если колонка офлайн. Теперь первый connect выполняется синхронно в async_setup_entry; при неудаче HA откладывает и повторяет setup. Дальнейшие реконнекты по-прежнему ведёт фоновый supervisor.
  • Команды сущностей оборачивают транспортные ошибки в HomeAssistantError. При мёртвом WS медиа-команды (media_player, button, switch, number, select) раньше всплывали в UI сырым RuntimeError/ConnectionError с traceback'ом. Теперь — понятное переводимое сообщение.
  • MJPEG-стрим камеры устойчив к ошибкам рендера. Исключение PIL/отрисовки внутри stream-loop больше не роняет весь поток с HTTP 500 — кадр логируется и поток продолжается.
  • pair_with_button отклоняет вызов при активном listen-loop (раньше они молча конкурировали бы за recv()).
  • Reconfigure-flow проверяет соединение с новым host/port перед сохранением — опечатка в IP больше не уводит entry в вечно-битое состояние, показывается инлайн-ошибка.

Changed

  • DataUpdateCoordinator теперь получает config_entry явно (устраняет deprecation, ломавшийся бы в HA 2026.8).
  • PARALLEL_UPDATES объявлен на всех платформах.
  • Чистка мёртвого кода в api.py: убраны неиспользуемые KEEPALIVE_INTERVAL_SEC, поле _keepalive_task; импорты подняты в шапку модуля.
  • quality_scale.yaml приведён в соответствие реальности (action-exceptions, parallel-updates → done).

Tests

  • Тесты герметичны. Если в окружении установлен настоящий пакет homeassistant (например, venv соседнего проекта), он перехватывал импорты вместо stub'ов — тесты падали с невнятным Frame helper not set up. Теперь install_stubs() распознаёт ситуацию и падает сразу с понятной инструкцией про чистый venv.
  • Добавлен test_audit_fixes.py (143 теста суммарно): обёртка команд, поведение poll при обрыве, отмена pending-запросов в listen-loop, устойчивость _handle_event.

[0.7.3]

Fixed

  • Шумные ERROR-traceback'и при штатном обрыве связи. Когда колонка сбрасывала WS-соединение (перезагрузка, Wi-Fi-мигание, idle-disconnect), интеграция за один обрыв писала ~56 ERROR-traceback'ов: listen loop crashed + по два get_state failed / get_metadata failed на каждый poll-цикл в окне до reconnect. Функционально всё восстанавливалось, но лог захламлялся.
    • api._listen_loop теперь отличает штатный ConnectionClosed/OSError (→ один INFO без traceback) от настоящего сбоя (→ ERROR).
    • При выходе из listen-loop выставляется disconnected-событие — супервизор реагирует на разрыв мгновенно, не дожидаясь следующего keepalive-цикла (было до ~25s задержки).
    • Ожидающие запросы при обрыве немедленно отменяются вместо 5-секундного таймаута.
    • coordinator пропускает poll, пока соединение в обрыве, и логирует in-flight сбой компактным WARNING вместо полного traceback.

0.7.2

Choose a tag to compare

@TohaRG2 TohaRG2 released this 17 May 18:53

[0.7.2]

Changed

  • Default volume_poll_interval поднят с 5s до 15s. Эмпирически подтверждено в research-сессии (см. research/PROTOCOL.md): метаданные трека приходят push-events через subscribe-stream (активируется первым get_metadata-запросом). Polling нужен только для volume/mute, которые в push-stream НЕ попадают. 15s — разумный компромисс. Юзер может настроить через Options Flow если хочет иначе.
  • В supervisor добавлен комментарий объясняющий subscribe-механизм.

Migration

  • Существующие установки с явно установленным volume_poll_interval в options — продолжат работать с тем значением что юзер выбрал. Только default-значение изменилось.

[0.7.1]

Fixed

  • Hassfest validation: services.yaml исправлен — target.device.integration ушёл, вместо него fields.device_id.selector.device.integration. Тот же UX, корректный формат.

[0.7.0]

Большое обновление жизненного цикла интеграции — закрыли почти все Bronze/Silver правила HA Quality Scale.

Added

  • Diagnostics — Settings → Devices → SBoom → ⋮ → Download diagnostics отдаёт JSON со state coordinator, track/state, options и entry.data (с redaction токена/host/serial).
  • Options Flow — Settings → Integrations → SBoom → Configure для тонкой настройки: volume_poll_interval, keepalive_interval, availability_threshold, lyrics_enabled. Авто-reload при изменении.
  • Reconfigure Flow — изменить IP/порт уже привязанной колонки без удаления интеграции (через ⋮ меню в Devices). PIN-токен сохраняется.
  • Reauth Flow — переавторизация без удаления entry. Запускается через service sboom_ha.reauth или будет триггериться автоматически когда выяснится сигнал колонки на invalid token.
  • Repairs platform — issue speaker_unreachable создаётся при недоступности колонки > 5 минут, удаляется при reconnect. Видно в Settings → System → Repairs.
  • System Health — метрики configured_speakers, connected_speakers, lrclib_reachable в Settings → System → System Information.
  • Custom services: sboom_ha.refresh_metadata (force-poll без ожидания цикла), sboom_ha.reauth (запустить переавторизацию). Поддержка таргетинга по device_id.
  • zeroconf reload_on_update=True — при смене IP колонки через mDNS-rediscover интеграция перезагружается автоматически, client начинает ходить на новый адрес.
  • async_migrate_entry + MINOR_VERSION=1 — заглушка миграций config entry для будущих изменений формата.
  • quality_scale: silver в manifest + quality_scale.yaml с пометками done/todo/exempt для движения к Gold.

Changed

  • Хардкоды → опции: volume_poll_interval (5s), keepalive_interval (25s), availability_threshold (3) перенесены из const.py в options. Дефолты сохранены.
  • Тестов: 105 → 135 (+30). Добавлены test_diagnostics, test_options, test_repairs, test_services, test_system_health.
  • Stubs HA: расширены до полной поддержки issue_registry, system_health, services, device_registry.async_get, ServiceCall, RepairsFlow, ConfirmRepairFlow.

[0.6.6]

Fixed

  • Brand-ассеты перенесены в правильное место: HA 2026.3+ использует Brands Proxy API — иконки кладутся в custom_components/{domain}/brand/, а home-assistant/brands репо больше не принимает PR для custom integrations. Папка brand-assets/ (не туда положенная в 0.6.5) удалена, файлы переехали в custom_components/sboom_ha/brand/{icon,icon@2x}.png.
  • Иконка теперь автоматически отображается в HA UI через /api/brands/integration/sboom_ha/icon.png — никаких сторонних PR не требуется.

[0.6.5]

Added

  • Brand-ассеты для HA UI: icon.png (256×256) и icon@2x.png (512×512), заимствованные из custom_integrations/sberdevices репо home-assistant/brands.
  • Изначально файлы были в brand-assets/, в 0.6.6 переехали в правильное место custom_components/sboom_ha/brand/.

[0.6.4]

Added

  • Индикация недоступности колонки: все entity (media_player, camera, sensor, button, switch, number, select) теперь показываются как Unavailable если WS-сессия с колонкой мертва. Реализовано через SboomCoordinator.connected + SboomEntity.available.
  • Event bus: sboom_connection_changed (поля connected, entry_id, device_id, host) — для автоматизаций «колонка отвалилась → push в Telegram» и подобных.

Changed

  • Debounce доступности (3 неудачные попытки): чтобы транзиентные мерцания сети не дёргали entity в Unavailable и обратно, флаг connected переключается в False только после 3 подряд неудачных reconnect'ов (DISCONNECT_THRESHOLD). Успешный connect мгновенно возвращает entity в available-состояние.
  • При connected-flip принудительно вызывается async_update_listeners() — UI перерисовывается мгновенно, без ожидания следующего poll-цикла.

[0.6.3]

Changed

  • CI: обновлены GitHub Actions до Node.js 24-совместимых версий — actions/checkout@v4 → @v6, actions/setup-python@v5 → @v6. Убрано предупреждение о deprecated Node.js 20 (forced removal 16 сентября 2026).

[0.6.2]

Fixed

  • Hassfest validation: ключи в manifest.json пересортированы (domain, name, далее алфавит) — требование Home Assistant для прохождения CI.

Changed

  • CI: убран hacs/action job — для приватного репо он бессмысленен (action ходит через публичный API без аутентификации). Hassfest и unit-тесты остались. Если репо когда-нибудь перейдёт в public и появится цель публиковаться через HACS-store — вернуть job обратно (см. git history) и добавить brand assets.

[0.6.1]

Added

  • Расширенный DeviceInfo в карточке устройства HA: firmware (sw_version) из mDNS-broadcast'а колонки и серийный номер (serial_number = device_id). Теперь видно прошивку и серийный сразу в Settings → Devices.
  • Soft-migration для существующих entries: при каждом zeroconf-rediscover (колонка регулярно сама анонсирует mDNS) firmware и model автоматически обновляются — старые установки получат firmware без переустановки.

Fixed

  • Firmware из mDNS извлекался при discovery, но не сохранялся в config entry — баг исправлен.

[0.6.0]

Added

  • HA event bus — интеграция выпускает события sboom_track_changed, sboom_playback_changed, sboom_volume_changed при изменениях на колонке. Триггерится через platform: event в автоматизациях. См. README раздел «События для автоматизаций».

Changed

  • Рефакторинг api.py — выделены модули _tlv.py (бинарный кодек), _parsers.py (JSON-парсеры payload'ов), _models.py (dataclasses TrackInfo/SpeakerState). api.py уменьшился с 692 до 399 строк. Публичный API сохранён через re-export в SberSpeakerClient.parse_track/parse_state.
  • Тестовая инфраструктура — добавлены tests/_ha_stubs.py (легковесные HA-stubs) и tests/_fakes.py (builders coordinator/track/state). Покрытие выросло с 48 до 94 unit-тестов: добавлены тесты media_player (entity properties), _tlv (изолированный кодек), coordinator_events (event bus).

Internal

  • pyproject.toml с конфигом для pytest и ruff (без [project] — версия остаётся в manifest.json).

[0.5.3]

Changed

  • Дополнительные сущности скрыты на dashboard по умолчанию (entity_registry_visible_default = False):
    • 7 buttons (next/prev/play_pause/like/dislike/remove_like/remove_dislike)
    • 1 number (volume)
    • 2 switches (shuffle, mute)
    • 1 select (repeat mode)
  • Эти entities остаются enabled (работают в автоматизациях, services, шаблонах) — просто не загромождают dashboard "Auto" / overview-карточки
  • Главные видимые entities: media_player + sensor.lyrics_current_line
  • Чтобы вернуть на dashboard: Settings → Devices → SBoom → нужная entity → toggle "Visible"

[0.5.2]

Added — first test suite

  • 48 unit-тестов: tests/test_helpers.py, test_lyrics_client.py, test_api_parsers.py, test_image_render.py
  • CI workflow для запуска тестов на каждом push (GitHub Actions)
  • Синтетические фикстуры payload-форматов (push + state-обёртка)

[0.5.1]

Refactor — подготовка к HACS publication

  • Вынесены общие утилиты в helpers.py (track_position, cover_url) — устранено дублирование между sensor.py, camera.py, media_player.py
  • SboomMediaPlayer теперь наследует от общего SboomEntity (DRY device_info)
  • device_info использует DeviceInfo dataclass вместо raw dict
  • Все вызовы asyncio.get_event_loop() заменены на asyncio.get_running_loop()
  • aiohttp.ClientTimeout вместо int в camera._fetch_cover_raw (совместимость с aiohttp 4.x)
  • Публичные методы media_remove_like(), media_remove_dislike(), parse_track(), parse_state() вместо вызова приватных
  • Использование MEDIA_CMD_* констант в api.py вместо magic numbers
  • Публичное свойство coordinator.http_session вместо обращения к _http
  • Исправлен двойной client.close() в config_flow.py pair-flow

[0.5.0]

Added

  • Tighter polling: update_interval 5s (было 30s) — громкость подтягивается быстрее
  • Прогресс-бар на camera — тонкая полоса + время MM:SS / MM:SS в стиле Яндекс.Музыки
  • Lyrics retry с fallback по artist+title если с album+duration не найдено

Fixed

  • Lrclib network errors теперь не кэшируются как "не найдено" — следующий push даёт retry

[0.4.1]

Added

  • Camera lyrics-overlay в стиле Яндекс.Музыки: blur-обложка + два уровня lyrics + footer с title/artist
  • Idle-режим с большой обложкой по центру + подписи

[0.4.0]

Added

  • Camera entity camera.<name>_lyrics_na_tv для karaoke-стрима
  • MJPEG-стрим с двумя строками lyrics (текущая + следующая) с переключением по таймингу LRC
  • Snapshot-mode для предпросмотра в HA UI
  • Возможность отправки на ТВ через media_player.play_media
  • Зависимость Pillow ...
Read more