Создание кастомного шаблона на примере ESP

Материал из Sprut.hub Wiki

Для начала необходимо установить на ESP прошивку, которая поддерживает MQTT отправку сообщений. Это может быть:

  • ESPhome
  • Tasmota
  • Esp easy
  • WiFi iot

Теперь в настройках устройства указываем в качестве MQTT брокера Spruthub.

  • IP брокера - IP вашего хаба
  • Логин - оставяем пустой
  • Пароль - оставляем пустой
  • Порт - 44444

Если смотреть на примере ESPhome, то настройка будет выглядеть так:

mqtt: 
  broker: 192.168.2.79
  port: 44444 
  topic_prefix: esphome 
  discovery: false

topic_prefix указывается, что бы в дальнейшем все устройства искать по одному шаблону а не вычленять нужные топики из корня.

Затем, надо узнать структуру MQTT топиков, которые типичны для прошивки, которую вы выбрали. Сделать это можно либо в мануале по соответсвующей прошивке, либо подключившись к SprutHub брокеру MQTT в приложении MqttExplorer.


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

[16:35:24][C][mqtt.switch:038]: MQTT Switch 'Heat': 
[16:35:24][C][mqtt.switch:039]: State Topic: 'esphome/switch/heat/state' 
[16:35:24][C][mqtt.switch:039]: Command Topic: 'esphome/switch/heat/command'

Шаблон для хаба

Теперь можно перейти к более сложному - созданию собственного шаблона, который мы потом сможем добавить на хаб в соответствии с инструкцией в папку, где должны располагаться MQTT шаблоны

В новом шаблоне, который мы позаимствовали у подобного устройства WirenBoard, мы удаляем строчку с "catalogId" потому как устройства которое мы добавляем скорее всего в каталоге нет. Топики для обнаружения и управления меняем на свои.

Если вы не знаете куда "приземлить" тот или иной параметр, у хаба есть справочник, в котором перечислены все поддерживаемые сервисы и характеристики.

В результате у меня получилось как то так:

{
  "manufacturer": "Itead",
  "model": "Sonoff Basic",
  "services": [
    {
      "type": "Switch",
      "characteristics": [
        {
          "type": "On",
          "link": {
            "type": "String",
            "topicSearch": "esphome/switch/(.*)/state",
            "topicGet": "esphome/switch/(1)/state",
            "topicSet": "esphome/switch/(1)/command",
            "map": {
              "false": "OFF",
              "true": "ON"
            }
          }
        }
      ]
    }
  ]
}

Разберем по порядку.

"manufacturer": "Itead"

Тут пишем производителя устройства.

"model": "Sonoff Basic"

Тут пишем модель устройства.

"services"

Описываем сервисы, которые будут использоваться в устройстве.

"type": "Switch"

Указываем тип устройства, который в Spruthub стандартизирован гайдлайнами HomeKit

"characteristics"

Описываем характеристики добавляемого устройства

"type": "String"

Изменяем тип передаваемых и принимаемых данных для управления устройством. Это актуально для ESPhome, потому что топики управляются сообщениями ON/OFF, но об этом дальше.

"topicSearch": "esphome/switch/(.*)/state"

Это топик для определения самого устройства. Что бы не делать шаблон на каждое новое устройство достаточно правильно задать данный параметр. В нем можно использовать регулярные выражения. В моем случае ищется вхождение любой фразы в топике esphome/switch. Фраза эта запоминается и используется дальше.

"topicGet": "esphome/switch/(1)/state"

Топик, в котором считывается текущее состояние устройства.

"topicSet": "esphome/switch/(1)/command"

Топик которым управляется устройство. В случае ESPhome ему надо передать ON или OFF

"map"

А тут самое интересное, о чем я писал в начале. Так как ESPhome надо передать для управления ON или OFF, а хаб передает true или false, нам необходимо переопределить эти параметры, что мы и делаем ниже:

"false": "OFF"
"true": "ON"

Еще один, но более сложный пример. Шаблон основан на шаблоне WirenBoard WB-MSW.json и добавляет в хаб сенсоры, подключенные к esphome:

{
  "name": "Набор датчиков",
  "manufacturer": "ESPhome",
  "model": "Esp sensor",
  "services": [
    {
      "type": "TemperatureSensor",
      "characteristics": [
        {
          "type": "CurrentTemperature",
          "link": {
            "type": "Float",
            "topicSearch": "esphome/sensor/(mh-z.*)_temperature/state",
            "topicGet": "esphome/sensor/(1)_temperature/state"
          }
        }
      ]
    },
    {
      "type": "HumiditySensor",
      "characteristics": [
        {
          "type": "CurrentRelativeHumidity",
          "link": {
            "type": "Float",
            "topicGet": "esphome/sensor/(1)_humidity/state"
          }
        }
      ]
    },
    {
      "type": "AirQualitySensor",
      "data": {
        "Logic": {
          "selected": "AirQualityFromVOCDensity"
        }
      },
      "characteristics": [
        {
          "type": "VOCDensity",
          "link": {
            "type": "Integer",
            "topicGet": "esphome/sensor/(1)_voc/state"
          }
        }
      ]
    },
    {
      "type": "CarbonDioxideSensor",
      "data": {
        "Logic": {
          "selected": "CarbonDioxideDetectedFromCarbonDioxideLevel"
        }
      },
      "characteristics": [
        {
          "type": "CarbonDioxideLevel",
          "link": {
            "type": "Integer",
            "topicGet": "esphome/sensor/(1)_co2_value/state"
          }
        }
      ]
    },
    {
      "type": "LightSensor",
      "characteristics": [
        {
          "type": "CurrentAmbientLightLevel",
          "link": {
            "type": "Float",
            "topicGet": "esphome/sensor/(1)_light_level/state"
          }
        }
      ]
    },
    {
      "type": "MotionSensor",
      "characteristics": [
        {
          "type": "MotionDetected",
          "link": {
            "type": "Integer",
            "topicGet": "esphome/sensor/(1)_motion_level/state",
            "map": {
              "0": "OFF",
              "1": "ON"
            }
          },
          "data": {
            "OnAfterLevel": 200
          }
        }
      ]
    },
    {
      "type": "ContactSensor",
      "characteristics": [
        {
          "type": "ContactSensorState",
          "link": {
            "type": "String",
            "topicGet": "esphome/sensor/(1)_contact/state",
            "map": {
              "0": "OFF",
              "1": "ON"
            }
          }
        }
      ]
    }
  ]
}

Можно заметить две вещи.

  1. topicSearch используется всего один раз. Если правильно задать имена сенсоров в ESPhome, то эта уловка сработает и вы получите универсальный шаблон для всех сенсоров на этой прошивке.
  2. Для датчика CO2 и датчика VOC используется виртуальная логика определения превышения допустимого уровня, в результате чего в HomeKit вы получите бинарный датчик, который будет срабатывать при заданном вами пороге.