Некоторое время назад попались мне на глаза достаточно интересные BLE метки от братьев-китайцев производства Holyiot. Я приобрел некоторое их количество и успешно пристроил для целей отслеживания ключей и пропусков. Но по прошествии некоторого времени у меня встал вопрос о мониторинге заряда их батарей. В итоге получилось достаточно интересно.
Практически все BLE устройства в моем хозяйстве интегрированы в HA с помощью ESPHome Bluetooth Proxy. Это действительно очень удобный механизм (хоть и не без недостатков конечно), позволяющий не думать о покрытии Bluetooth, просто добавляя в нужных местах и нужном количестве платы ESP32 с прошивкой ESPHome (Например M5Stack Atom Lite). Бонусом получая возможность некоторого Room Presence, например.
Не исключение и BLE метки Holyiot. В их магазине на Aliexpress большой выбор меток в разных форм-факторах, а так же с разным набором датчиков (кнопки, вибрация, температура и т.д.).
Вживую выглядят они как то так:
С помощью соответствующего приложения есть возможность изменить ряд настроек, например режим работы (beacon, ibeacon, eddystone), всевозможные ID или мощность и периодичность отправки пакетов (advertisement). Что ценно, особенно мощность сигнала и его периодичность. Уменьшив мощность и увеличив интервал можно существенно продлить срок службы батарейки.
Моя самая старая метка работает на одной батарейке уже больше года с остатком заряда ~50%.
На всякий случай – пароль по умолчанию для подключения к меткам Holyiot – aa14061112
И вот, озадачившись вопросом получения текущего заряда батареек этих меток я наткнулся на достаточно интересное решение позволяющее получить искомое. Для этого, правда, пришлось немного погрузиться в вопросы активного и пассивного сканирования эфира BLE и немного его поменять.
Дело в том, что компонент esp32_ble_tracker может работать как в пассивном (просто слушая эфир), так и в активном режиме (взаимодействуя с устройствами, самостоятельно отправляя запросы на которые устройства должны отвечать). При этом активный режим потребляет несколько больше энергии с т.з. конечных устройств, утилизируя заряд батареи более активно, что нам совершенно не нужно. Выдержка из документации:
active (Optional, boolean): Whether to actively send scan requests to request more data after having received an advertising packet. With some devices this is necessary to receive all data, but also drains those devices’ power a bit more. Some devices don’t need this, in that case you can save power and RF pollution by setting it tofalse
. Defaults totrue
.
Соответственно, для экономии заряда в идеале нужно все BT Proxy держать в пассивном режиме, но к сожалению это не позволяет получить данные заряда батареи. Метка отдает заряд только при активном сканировании.
Отсюда основная идея заключается в том, чтобы включать активный режим только в определенных ситуациях. Например, при получении первого advertise пакета от метки (срабатывает бинарный сенсор присутствия метки), или по расписанию. При таком подходе большую часть времени ESP слушает эфир в пассивном режиме, переключаясь в активный только по определенным событиям.
Далее будет представлен пример кода с комментариями. Все тайминги в свое время были рекомендованы авторами ESPHome Bluetooth Proxy, но с тех пор они поменяли свое мнение. Тем не менее, у меня данные параметры прекрасно себя зарекомендовали, поэтому оставлю их здесь без изменений. При необходимости можете самостоятельно попробовать менять значения interval/window и duration, желательно при этом опираясь на какую-либо документацию =)
esp32_ble_tracker: id: ble_tracker_id # На старте компонента мы указываем следующие параметры сканирования: scan_parameters: interval: 450ms window: 160ms duration: 10s active: true continuous: true # При получении пакета от определенного mac-адреса (метки) с нужным нам service_uuid (5242), # отправляем второй по счету (от 0) бит в нужный сенсор заряда батареи. on_ble_service_data_advertise: - mac_address: F9:9D:1D:40:23:40 service_uuid: '5242' # id(black1_battery) = id нужного нам template сенсора, x[1] = бит из сообщения, содержащий заряд батареи. # Найдено здесь - https://github.com/custom-components/ble_monitor/issues/1183 then: - lambda: !lambda |- id(black1_battery).publish_state(x[1]); # Все то же самое для второй метки - mac_address: E6:9F:37:5B:1D:DA service_uuid: '5242' then: - lambda: !lambda |- id(white1_battery).publish_state(x[1]); sensor: # Два template сенсора, в которые будут отправлены показания заряда батареек - platform: template name: Holy-IoT Black-1 Battery id: black1_battery device_class: battery unit_of_measurement: '%' entity_category: diagnostic accuracy_decimals: 0 disabled_by_default: false force_update: false update_interval: 60s - platform: template name: Holy-IoT White-1 Battery id: white1_battery device_class: battery unit_of_measurement: '%' entity_category: diagnostic accuracy_decimals: 0 disabled_by_default: false force_update: false update_interval: 60s binary_sensor: # binary_sensor присутствия метки, переключается в on при получении пакета с нужными ibeacon данными (uuid\major\minor) # для того, что бы сгладить возможный "дребезг" датчика из за потерянных пакетов, установлен delayed_off в 30 сек. # После перехода сенсора в on, запускается скрипт ble_active_scan, который и отвечает за изменение режимов. - platform: ble_presence name: Hallway Holy IoT Black-1 Presence ibeacon_uuid: fda50693-a4e2-4fb1-afcf-c6eb07647825 ibeacon_major: 10011 ibeacon_minor: 1 device_class: presence icon: mdi:key-chain-variant filters: - delayed_off: 30s on_press: - then: - script.execute: id: ble_active_scan disabled_by_default: false # То же самое для второй метки - platform: ble_presence name: Hallway Holy IoT White-1 Presence ibeacon_uuid: fda50693-a4e2-4fb1-afcf-c6eb07647826 ibeacon_major: 10011 ibeacon_minor: 3 device_class: presence icon: mdi:car-key filters: - delayed_off: 30s on_press: - then: - script.execute: id: ble_active_scan script: # Скрипт "на ходу" меняет режим (active\passive) и прочие настройки сканирования. # Краткое описание параметров: # set_scan_active = bool (true\false) # set_scan_duration = milliseconds # set_scan_interval = milliseconds / 0.625 (example: 450ms / 0.625 = 720) # set_scan_window = milliseconds / 0.625 (example: 160ms / 0.625 = 256) # set_scan_continuous = bool (true\false) - id: ble_active_scan # Режим запуска скрипта - single mode: single then: # Остановка текущего сканирования. - lambda: !lambda |- id(ble_tracker_id).stop_scan(); # Задержка 2 сек - delay: 2s # Настройка параметров и старт сканирования (Active). - lambda: !lambda |- id(ble_tracker_id).set_scan_active(true); id(ble_tracker_id).set_scan_duration(10); id(ble_tracker_id).set_scan_interval(720); id(ble_tracker_id).set_scan_window(256); id(ble_tracker_id).set_scan_continuous(true); id(ble_tracker_id).start_scan(); # Запись в лог. - logger.log: format: Active scan started level: INFO args: [] tag: main # Продолжительность активного сканирования 30 секунд. - delay: 30s # Запись в лог. - logger.log: format: Active scan stopped level: INFO args: [] tag: main # Остановка текущего (активного) сканирования. - lambda: !lambda |- id(ble_tracker_id).stop_scan(); # Задержка 2 сек. - delay: 2s # Настройка параметров и старт сканирования (Passive). - lambda: !lambda |- id(ble_tracker_id).set_scan_active(false); id(ble_tracker_id).set_scan_duration(10); id(ble_tracker_id).set_scan_interval(720); id(ble_tracker_id).set_scan_window(256); id(ble_tracker_id).set_scan_continuous(true); id(ble_tracker_id).start_scan(); interval: # Запуск активного сканирования по расписанию, раз в 30 минут. # Вполне можно изменить расписание на раз в 24 часа или вообще убрать. - interval: 30min then: - script.execute: id: ble_active_scan
У меня датчики батареек для всех меток созданы на одной единственной ESP, которая расположена в коридоре, возле входной двери. А т.к. все мои метки висят либо на ключах, либо на пропусках, данные батареек будут гарантированно получены как минимум при входе или выходе из квартиры.
А для оповещения о низком заряде батарейных устройств у меня есть соответствующая статья.