Часть 4: Объединяй и властвуй (с)

Логическое продолжение Части 1, 2,3. Итак, научился принимать данные от zigbee устройств на стик Zigbee2Mqtt и на Алису. Да вот беда, одновременно и там и там данные от одного устройства штатными средствами не получить. А только на Алису завязывать все устройства бы не хотелось, так как:

  • сценарии ограничены только поддерживаемым Алисой оборудованием. Например мои WiFi реле Sonoff DIY 3 которые я использую на батареях Алисой естественно не поддерживаются .
  • Нет интернета — нет «умного» дома

Потому и возникла задача перебросить некоторые устройства которые есть на стике на Алису. Например датчик температуры. И в принципе штатное решение есть от самого Яндекса. Правда они выбрали весьма трудоёмкое для этого способа решение: необходимо создать виртуальное устройство, написать для него API и подключить в качестве стороннего навыка «Умный дом». Т.е. схема взаимодействия вырисовывается такая:

Т.е. что нужно сделать по шагам:

  1. Создать навык-приложение для «Умного дома»
  2. Сделать API «виртуального устройства» по документации Яндекса
  3. Подключить виртуальное устройство в навык
  4. Добавить навык в Яндекс Алису

Создание навыка

Для этого переходим по ссылке https://dialogs.yandex.ru/developer/smart-home и создаём новый навык.

Важно! Тип доступа сделать приватным, тогда модерация будет «мгновенной», ну и собственно навык не будет доступен никому кроме вас.

После заполнения основных данных, далее необходимо создать приложение https://oauth.yandex.ru/ с типом платформы WEB:

Так вы получите необходимые для дальнейшей настройки навыка ClientID и Client secret, которые необходимо будет прописать в навыке на странице «Связка аккаунтов»:

Кроме того необходимо прописать эндпоинты которые будут отвечать за авторизацию навыка на вашем сервере, обновление и получение токенов.

API виртуального устройства

Ну собственно на стороне сервера необходимо создать эндпоинты, которые сначала авторизуют навык, а затем отдадут данные «виртуального устройства». Например датчика температуры. Далее накидаю рабочие куски кода-шаблоны, которые пойдут как основа для «допила».

auth.php:

$redirect_uri = $_GET["redirect_uri"];
$state = $_GET["state"];
$client_id = $_GET["client_id"];
header("Location: ".$redirect_uri."?code=1234&state=$state&client_id=$client_id");

token.php:

$code=$_POST["code"];
$client_secret=$_POST["client_secret"];
$grant_type=$_POST["grant_type"];
$client_id=$_POST["client_id"];
$redirect_uri=$_POST["redirect_uri"];

$json='{
    "access_token": "ACCESS_TOKEN",
    "token_type": "bearer",
    "expires_in": 2592000,
    "refresh_token": "REFRESH_TOKEN",
    "scope": "read",
    "uid": 100101,
    "info": {
      "name": "donpadlo",
      "email": "info@mail.ru"
    }
  }';

 header('Content-Type: application/json');
 echo $json;

refresh.php:

$json='{
    "access_token": "ACCESS_TOKEN",
    "refresh_token": "'.$_GET['refresh_token'].'",
    "token_type": "bearer",
    "expires_in": 2592000 
  }';

header('Content-Type: application/json');
 echo $json;

Всё что выше — это авторизация навыка. После чего он полезет на Redirect URI  указанный в приложении, где будет ожидать ответа в формате json на два вида возможных запроса REQUEST_URI:

  • /v1.0/user/devices — получить список поддерживаемых устройств
  • /v1.0/user/devices/query — получить обновления данных

Данные для отдачи (температура, влажность) берем из очереди mqtt. В самом простом случае, шаблон устройства может выглядеть как-то так:

index.php

if ($_SERVER["REQUEST_URI"]=="/v1.0/user/devices"){
    $json='{
        "request_id": "'.$_SERVER["HTTP_X_REQUEST_ID"].'",
        "payload": {
          "user_id": "gribov_pi",
          "devices": [{
            "id": "abc-123",
            "name": "Розовая комната",
            "description": "Измеряет температуру и влажность",
            "room": "спальня",
            "type": "devices.types.sensor.climate",
            "custom_data": {
              "foo": 1,
              "bar": "two",
              "baz": false,
              "qux": [1, "two", false],
              "quux": {
                "quuz": {
                  "corge": []
                }
              }
            },
            "capabilities": [],      
            "properties": '.get_properties().',
            "device_info": {
              "manufacturer": "Грибовы.рф",
              "model": "Датчик температуры",
              "hw_version": "1.0",
              "sw_version": "1.0"
            }
          }]
        }}';
};
if ($_SERVER["REQUEST_URI"]=="/v1.0/user/devices/query"){
  $json='{
    "request_id": "'.$_SERVER["HTTP_X_REQUEST_ID"].'",
    "payload": {
      "devices": [{
        "id": "abc-123",
        "capabilities": [],
        "properties": '.get_properties().'
      }]
    }
  }';  
};
header('Content-Type: application/json');
echo $json;
die();


function get_properties(){
  return '[
    {
      "type": "devices.properties.float",
      "retrievable": true,
      "reportable": true,
      "parameters": {
        "instance": "temperature",
        "unit": "unit.temperature.celsius"
      },
      "state": {
        "instance": "temperature",
        "value": 30
      },
      "last_updated": "2025-12-29 12:12:12"
    },
    {
      "type": "devices.properties.float",
      "retrievable": true,
      "reportable": true,
      "parameters": {
        "instance": "humidity",
        "unit": "unit.percent"
      },
      "state": {
        "instance": "humidity",
        "value": 70
      },
      "last_updated": "2025-12-29 12:12:12"
    }
  ]';  
};

Подключение виртуального устройства к Алисе

Переходим в раздел «Тестирование» и подключаем вышесозданый навык:

Как видим виртуальное устройство отображается, данные в нём обновляются, и его можно штатно использовать для создания различных сценариев в Алисе:

Часть 3. Получение данных с Zigbee устройства.

В первой и второй части, был прошит и настроен стик, а так-же установлен брокер сообщений и ZigBee2mqtt, Теперь попробуем подключить устройство и получить с него данные по температуре. Устройство — самый раздешевый датчик температуры, купленный на AliExpress за 267 рублей:

Заходим в админ панель ZigBee2mqtt, по адресу: http://ip:8080 и нажимаем кнопку «Разрешить подключения». После чего на устройстве жмем кнопку reset скрепкой. Если всё удачно, то в течении нескольких секунд устройство будет обнаружено:

Далее собственно при получении данных с устройства, ZigBee2mqtt будет отсылать данные по нему в json формате в очередь вида zigbee2mqtt/0xa4c13812c854f0f7. Хвост у данной очереди — МАС адрес устройства. Нашей дальнейшей задачей является эти данные собственно принять и обработать. Для этого воспользуемся python библиотекой paho-mqtt:

pip install paho-mqtt --break-system-packages

Оформить подписку можно например так:

import context  
import paho.mqtt.subscribe as subscribe

def print_msg(client, userdata, message):
    print(message.topic)
    print(message.payload)

subscribe.callback(print_msg, "#", hostname="localhost",auth={'username':"уакукауы","password":"укепукепк"})

context.py:

# -*- coding: utf-8 -*-

# Ensure can import paho package
try:
    import paho

except ImportError:
    # This part is only required to run the examples from within the examples
    # directory when the module itself is not installed.
    import inspect
    import os
    import sys

    cmd_subfolder = os.path.realpath(
        os.path.abspath(
            os.path.join(
                os.path.split(
                    inspect.getfile(inspect.currentframe())
                )[0],
                "..",
                "src"
            )
        )
    )
    if cmd_subfolder not in sys.path:
        sys.path.insert(0, cmd_subfolder)

    import paho

Ну и далее на свой вкус и цвет дальше можно эти данные уже как угодно обрабатывать

ZigBee на Raspberry, часть 2, установка софта

Итак, в первой части мы прошили стикер CC2531, теперь наша малинка готова стать шлюзом. Обычно схема работы Raspberry следующая:

ZigBee2MQTT — нужен для того чтобы принять пакеты со стика CC2531 и отправить их в брокер сообщений. Которые в дальнейшем уже обработают скрипты для автоматизации чего-либо.

Сначала поставим брокер сообщений mosquitto (как самое легкое):

sudo apt-get install mosquitto

И настроим файл конфигурации /etc/mosquitto/mosquitto.conf :

pid_file /run/mosquitto/mosquitto.pid
persistence true
persistence_location /var/lib/mosquitto/
log_dest file /var/log/mosquitto/mosquitto.log
include_dir /etc/mosquitto/conf.d
allow_anonymous true   # доступ для всех
listener 1883 0.0.0.0  # слушаем все сетевые адреса

Можно задать логин пароль для доступа, и разрешить слушать только localhost, но я это считаю излишним, т.к. всё равно чаще всего raspberry pi лежит в локальной сети и в интернет не смотрит. Далее включаем службу и стартуем её:

sudo systemctl enable mosquitto
sudo systemctl restart mosquitto

Теперь ставим и настраиваем ZigBee2MQTT. Можно по туториалу на сайте разработчиков, а можно так, как описано ниже (без подробностей).

sudo apt-get install nodejs git
corepack enable
sudo mkdir /opt/zigbee2mqtt
sudo chown -R ${USER}: /opt/zigbee2mqtt
git clone --depth 1 https://github.com/Koenkk/zigbee2mqtt.git /opt/zigbee2mqtt
cd /opt/zigbee2mqtt
pnpm install --frozen-lockfile
cd /opt/zigbee2mqtt
pnpm start

После успешного старта будет доступен веб интерфейс для дальнейшей настройки в браузере http://ip:8080.

Необходимо зайти, выбрать порт где расположен стик, и ввести данные для подключения к брокеру. После сохранить. Конфиг будет сохранен в /opt/zigbee2mqtt/data/configuration.yaml

Далее оформим запуск ZigBee2MQTT как службу, создав и сохранив файл:

[Unit]
Description=zigbee2mqtt
After=network.target

[Service]
Environment=NODE_ENV=production
Type=notify
ExecStart=/usr/bin/node index.js
WorkingDirectory=/opt/zigbee2mqtt
StandardOutput=inherit
# Or use StandardOutput=null if you don't want Zigbee2MQTT messages filling syslog, for more options see systemd.exec(5)
StandardError=inherit
WatchdogSec=10s
Restart=always
RestartSec=10s
User=pi

[Install]
WantedBy=multi-user.target
sudo systemctl enable zigbee2mqtt
sudo systemctl start zigbee2mqtt

Ну и теоретически всё. По идее можно подключать устройства zigbee. В следующей части собственно это и рассмотрю.

Update: в итоге решил таки не хранить все яйца в одном месте, и брокер сообщений mosquitto перенес с raspberry на другое устройство. В этом случае нужно установить пароль и чуть перенастроить конфигурационный файл

Установка пароля:

sudo mosquitto_passwd -c /etc/mosquitto/passwd логин
chown mosquitto /etc/mosquitto/passwd
chmod 0700 /etc/mosquitto/passwd

И конфиг в конечном итоге у меня выглядит так:

pid_file /run/mosquitto/mosquitto.pid
persistence true
persistence_location /var/lib/mosquitto/
log_dest file /var/log/mosquitto/mosquitto.log
include_dir /etc/mosquitto/conf.d
allow_anonymous false   # доступ для всех
password_file /etc/mosquitto/passwd
listener 1883 0.0.0.0 

ZigBee на Raspberry, часть 1, прошивка CC2531

Задача: научится принимать пакеты по протоколу Zigbee на устройстве Raspberry PI 3. Сама по себе, без внешних дополнительных устройств, Raspberry этого не умеет. Нужен модем, который может принимать сигналы на этой частоте. Для этого было куплено на Aliexpress устройство Zigbee CC2531. Покупателю оно приходит пустое, без прошивки, поэтому перед работой с ним, его необходимо прошить. Есть несколько вариантов, самый простой сделать это при помощи самой же RaspberryPI. Одно Но! (и даже их несколько):

  • нужно делать самому подключение проводками
  • на модеме ножки очень тонкие, и стандартные проводки от Arduino например не подойдут. Кто-то городил огород при помощи малярного скотча, а кто-то отгибал ножки и припаивался к ним на прямую.

Общая схема подключения для прошивки следующая:

После того как наколхозите этот переходник, можно начинать прошивку. Но сначала убедимся, что модем вообще виден в системе, выполнив команду lsusb. Должно выйти что-то вроде:

Если всё ок, двигаемся к следующему шагу: скачиваем утилиту для прошивки:

git clone https://github.com/jmichault/flash_cc2531.git
cd flash_cc2531
./cc_chipid

И скорее всего получим фигу вида:

root@raspberrypi:/home/donpadlo/flash_cc2531# ./cc_chipid
bash: ./cc_chipid: cannot execute: required file not found

Я подозреваю, что этой утилите не хватает библиотеки wiringPi. Установить её можно так:

git clone https://github.com/WiringPi/WiringPi.git
cd WiringPi
 ./build

И далее снова попробовать выполнить :

./cc_chipid

И я снова получил туже самую ошибку. Ну чтож..попробуем собрать банарник из исходных годов сами:

cd flash_cc2531
make

После чего, команда уже выполнится корректно:

./cc_read
ID = b534

Т.е. устройство прошивальщик теперь видит. Скачаем последнюю версию прошивки (можно прям по этой ссылке, т.к. версии более не разрабатываются для этого модема):

wget https://github.com/Koenkk/Z-Stack-firmware/raw/refs/heads/master/coordinator/Z-Stack_3.0.x/bin/CC2531_20190425.zip
unzip CC2531_20190425.zip
./cc_erase 
./cc_write CC2531ZNP-with-SBL.hex

И собственно всё. Теперь стик рабочий. Осталось научится получать с него данные. А это уже в следующих частях..

1С: таблица значений на форме

Задача: скрыть отдельную колонку или показать все колонки в таблице значений расположенной на форме.

Решение: напишем две процедуры, одна из которых скрывает текущую выбранную мышкой колонку, а другая показывает все колонки в выбранной таблице.

&НаКлиенте
Процедура СкрытьКолонку(Команда)
      ТЭ_имя_объект=ТекущийЭлемент.Имя;
      ТЭ_поле=ТекущийЭлемент.ТекущийЭлемент.имя;
      имяКолонки=СтрЗаменить( ТЭ_поле,ТЭ_имя_объект,"");	  
	  элементы[ТЭ_поле].Видимость=Ложь;
КонецПроцедуры

&НаКлиенте
Процедура ПоказатьВсеКолонки(Команда)
      ТЭ_имя_объект=ТекущийЭлемент.Имя;
      ТЭ_поле=ТекущийЭлемент.ТекущийЭлемент.имя;
      имяКолонки=СтрЗаменить( ТЭ_поле,ТЭ_имя_объект,"");
	  для каждого стр из элементы[ТЭ_имя_объект].ПодчиненныеЭлементы цикл
		  стр.Видимость=Истина;
	  конеццикла;	  
КонецПроцедуры

Функционал можно например навесить на контекстное меню

1 2 3 300