Создание кастомного шаблона на примере ESP: различия между версиями

Материал из Sprut.hub Wiki
Нет описания правки
 
(не показано 7 промежуточных версий этого же участника)
Строка 31: Строка 31:
</syntaxhighlight>
</syntaxhighlight>


== Темплейт в хабе ==
== Шаблон для хаба ==
Темперь можно перейти к более сложному - созданию собственного темплейта для добавляемого устройства. Все готовые шаблоны устройств лежат по пути '''/home/makesimple/.SprutHub/data/Templates/'''. Нам необходимо создать свой шаблон в папке '''/home/makesimple/.SprutHub/data/Templates/MQTT/Custom'''.
Теперь можно перейти к более сложному - созданию собственного шаблона, который мы потом сможем добавить на хаб в [[Добавление кастомных шаблонов для любых устройств#Загрузка кастомного шаблона|соответствии с инструкцией]] в папку, где должны располагаться MQTT шаблоны


* '''MQTT''' - потому что мы будем использовать этот контроллер
В новом шаблоне, который мы позаимствовали у подобного устройства WirenBoard, мы удаляем строчку с '''"catalogId"''' потому как устройства которое мы добавляем скорее всего в каталоге нет. Топики для обнаружения и управления меняем на свои.  
* '''Custom''' - потому что нам не надо, что бы при обновлении ПО хаба наш шаблон перезаписался.


Можно посмотреть по шаблонам в других папках и подобрать нужный в зависимости от типа добавляемого устройства. В моем примере взят шаблон реле Wirenboard '''/home/makesimple/.SprutHub/data/Templates/MQTT/WirenBoard/WirenBoard/WB-MR6C.json'''. Копируем его в папку Custom и открываем на редактирование. В нем удаляем строчку с '''"catalogId"''' потому как устройства которое мы добавляем скорее всего в каталоге нет. Топики для обнаружения и управления меняем на свои. В результате у меня получилось как то так:
{{QuoteBlue|Если вы не знаете куда "приземлить" тот или иной параметр, у хаба есть [[Справочник типов сервисов и характеристик|справочник, в котором перечислены все поддерживаемые сервисы и характеристики]].}}
<code>{
  "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"
            }
          }
        }
      ]
    }
  ]
}</code>
Разберем по порядку.


===== <code>"manufacturer": "Itead"</code> =====
В результате у меня получилось как то так:<syntaxhighlight lang="json" line="1">
{
  "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"
            }
          }
        }
      ]
    }
  ]
}
</syntaxhighlight>Разберем по порядку.
 
<syntaxhighlight lang="json" line="0">"manufacturer": "Itead"</syntaxhighlight>
Тут пишем производителя устройства.
Тут пишем производителя устройства.


===== <code>"model": "Sonoff Basic"</code> =====
<syntaxhighlight lang="json" line="0">"model": "Sonoff Basic"</syntaxhighlight>
Тут пишем модель устройства.
Тут пишем модель устройства.


===== <code>"services"</code> =====
<syntaxhighlight lang="json" line="0">"services"</syntaxhighlight>
Описываем сервисы, которые будут использоваться в устройстве.
Описываем сервисы, которые будут использоваться в устройстве.


===== <code>"type": "Switch"</code> =====
<syntaxhighlight lang="json" line="0">"type": "Switch"</syntaxhighlight>
Указываем тип устройста, который в Spruthub стандартизирован гайдлайнами HomeKit
Указываем тип устройства, который в Spruthub стандартизирован гайдлайнами HomeKit


===== <code>"characteristics"</code> =====
<syntaxhighlight lang="json" line="0">"characteristics"</syntaxhighlight>
Описываем характеристики добавляемого устройства
Описываем характеристики добавляемого устройства


===== <code>"type": "String"</code> =====
<syntaxhighlight lang="json" line="0">"type": "String"</syntaxhighlight>
Изменяем тип передаваемых и принимаемых данных для управления устройством. Это актуально для ESPhome, потому что топики управляются сообщениями ON/OFF, но об этом дальше.
Изменяем тип передаваемых и принимаемых данных для управления устройством. Это актуально для ESPhome, потому что топики управляются сообщениями ON/OFF, но об этом дальше.


===== <code>"topicSearch": "esphome/switch/(.*)/state"</code> =====
<syntaxhighlight lang="json" line="0">"topicSearch": "esphome/switch/(.*)/state"</syntaxhighlight>
Это топик для определения самого устройства. Что бы не делать шаблон на каждое новое устройство достаточно правильно задать данный параметр. В нем можно использовать регулярные выражения. В моем случае ищется вхождение любой фразы в топике '''esphome/switch'''. Фраза эта запоминается и используется дальше.
Это топик для определения самого устройства. Что бы не делать шаблон на каждое новое устройство достаточно правильно задать данный параметр. В нем можно использовать регулярные выражения. В моем случае ищется вхождение любой фразы в топике '''esphome/switch'''. Фраза эта запоминается и используется дальше.


===== <code>"topicGet": "esphome/switch/(1)/state"</code> =====
<syntaxhighlight lang="json" line="0">"topicGet": "esphome/switch/(1)/state"</syntaxhighlight>
Топик, в котором считывается текущее состояние устройства.
Топик, в котором считывается текущее состояние устройства.


===== <code>"topicSet": "esphome/switch/(1)/command"</code> =====
<syntaxhighlight lang="json" line="0">"topicSet": "esphome/switch/(1)/command"</syntaxhighlight>
Топик которым управляется устройство. В случае ESPhome ему надо передать или OFF
Топик которым управляется устройство. В случае ESPhome ему надо передать ON или OFF


===== <code>"map"</code> =====
<syntaxhighlight lang="json" line="0">"map"</syntaxhighlight>
А тут самое интересное, о чем я писал в начале. Так как ESPhome надо передать для управления '''ON''' или '''OFF''', а хаб передает '''true''' или '''false''', нам необходимо переопределить эти параметры, что мы и делаем ниже:
А тут самое интересное, о чем я писал в начале. Так как ESPhome надо передать для управления '''ON''' или '''OFF''', а хаб передает '''true''' или '''false''', нам необходимо переопределить эти параметры, что мы и делаем ниже:


* <code>"false": "OFF"</code>
<syntaxhighlight lang="json" line="0">"false": "OFF"</syntaxhighlight>
* <code>"true": "ON"</code>
<syntaxhighlight lang="json" line="0">"true": "ON"</syntaxhighlight>


Еще один, но более сложный пример. Шаблон основан на '''/home/makesimple/.SprutHub/data/Templates/MQTT/WirenBoard/WirenBoard/WB-MSW.json''' и добавляет в хаб сенсоры, подключенные к esphome:
Еще один, но более сложный пример. Шаблон основан на шаблоне '''WirenBoard WB-MSW.json''' и добавляет в хаб сенсоры, подключенные к esphome:<syntaxhighlight lang="json" line="1">
<code>{
{
  "name": "Набор датчиков",
  "name": "Набор датчиков",
  "manufacturer": "ESPhome",
  "manufacturer": "ESPhome",
  "model": "Esp sensor",
  "model": "Esp sensor",
  "services": [
  "services": [
    {
    {
      "type": "TemperatureSensor",
      "type": "TemperatureSensor",
      "characteristics": [
      "characteristics": [
        {
        {
          "type": "CurrentTemperature",
          "type": "CurrentTemperature",
          "link": {
          "link": {
            "type": "Float",
            "type": "Float",
            "topicSearch": "esphome/sensor/(mh-z.*)_temperature/state",
            "topicSearch": "esphome/sensor/(mh-z.*)_temperature/state",
            "topicGet": "esphome/sensor/(1)_temperature/state"
            "topicGet": "esphome/sensor/(1)_temperature/state"
          }
          }
        }
        }
      ]
      ]
    },
    },
    {
    {
      "type": "HumiditySensor",
      "type": "HumiditySensor",
      "characteristics": [
      "characteristics": [
        {
        {
          "type": "CurrentRelativeHumidity",
          "type": "CurrentRelativeHumidity",
          "link": {
          "link": {
            "type": "Float",
            "type": "Float",
            "topicGet": "esphome/sensor/(1)_humidity/state"
            "topicGet": "esphome/sensor/(1)_humidity/state"
          }
          }
        }
        }
      ]
      ]
    },
    },
    {
    {
      "type": "AirQualitySensor",
      "type": "AirQualitySensor",
      "data": {
      "data": {
        "Logic": {
        "Logic": {
          "selected": "AirQualityFromVOCDensity"
          "selected": "AirQualityFromVOCDensity"
        }
        }
      },
      },
      "characteristics": [
      "characteristics": [
        {
        {
          "type": "VOCDensity",
          "type": "VOCDensity",
          "link": {
          "link": {
            "type": "Integer",
            "type": "Integer",
            "topicGet": "esphome/sensor/(1)_voc/state"
            "topicGet": "esphome/sensor/(1)_voc/state"
          }
          }
        }
        }
      ]
      ]
    },
    },
    {
    {
      "type": "CarbonDioxideSensor",
      "type": "CarbonDioxideSensor",
      "data": {
      "data": {
        "Logic": {
        "Logic": {
          "selected": "CarbonDioxideDetectedFromCarbonDioxideLevel"
          "selected": "CarbonDioxideDetectedFromCarbonDioxideLevel"
        }
        }
      },
      },
      "characteristics": [
      "characteristics": [
        {
        {
          "type": "CarbonDioxideLevel",
          "type": "CarbonDioxideLevel",
          "link": {
          "link": {
            "type": "Integer",
            "type": "Integer",
            "topicGet": "esphome/sensor/(1)_co2_value/state"
            "topicGet": "esphome/sensor/(1)_co2_value/state"
          }
          }
        }
        }
      ]
      ]
    },
    },
    {
    {
      "type": "LightSensor",
      "type": "LightSensor",
      "characteristics": [
      "characteristics": [
        {
        {
          "type": "CurrentAmbientLightLevel",
          "type": "CurrentAmbientLightLevel",
          "link": {
          "link": {
            "type": "Float",
            "type": "Float",
            "topicGet": "esphome/sensor/(1)_light_level/state"
            "topicGet": "esphome/sensor/(1)_light_level/state"
          }
          }
        }
        }
      ]
      ]
    },
    },
    {
    {
      "type": "MotionSensor",
      "type": "MotionSensor",
      "characteristics": [
      "characteristics": [
        {
        {
          "type": "MotionDetected",
          "type": "MotionDetected",
          "link": {
          "link": {
            "type": "Integer",
            "type": "Integer",
            "topicGet": "esphome/sensor/(1)_motion_level/state",
            "topicGet": "esphome/sensor/(1)_motion_level/state",
            "map": {
            "map": {
              "0": "OFF",
              "0": "OFF",
              "1": "ON"
              "1": "ON"
            }
            }
          },
          },
          "data": {
          "data": {
            "OnAfterLevel": 200
            "OnAfterLevel": 200
          }
          }
        }
        }
      ]
      ]
    },
    },
    {
    {
      "type": "ContactSensor",
      "type": "ContactSensor",
      "characteristics": [
      "characteristics": [
        {
        {
          "type": "ContactSensorState",
          "type": "ContactSensorState",
          "link": {
          "link": {
            "type": "String",
            "type": "String",
            "topicGet": "esphome/sensor/(1)_contact/state",
            "topicGet": "esphome/sensor/(1)_contact/state",
            "map": {
            "map": {
              "0": "OFF",
              "0": "OFF",
              "1": "ON"
              "1": "ON"
            }
            }
          }
          }
        }
        }
      ]
      ]
    }
    }
  ]
  ]
}</code>
}
Можно заметить две вещи.
</syntaxhighlight>Можно заметить две вещи.


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

Текущая версия от 20:18, 10 декабря 2023

Для начала необходимо установить на 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 вы получите бинарный датчик, который будет срабатывать при заданном вами пороге.