Продолжаю натурно-стендовые эксперименты по спариванию ужей с ежами, на этот раз буду пробовать китайские железки Xiaomi Mi Home и пробовать их связать с openhab без всяких китайских облаков.

На просторах алиэкспресса были приобретены 4 "умных" устройства из экосистемы Xiaomi Mi Smart Home, которые с одной стороны мне интересны в практическом плане с другой есть в списке гарантированно-поддерживаемых в документации openhab (https://www.openhab.org/addons/bindings/mihome/#supported-devices)

  1. Последняя версия шлюза Xiaomi Smart Gateway v3
  2. Круглая "таблетка" сенсор температуры, влажности Xiaomi Smart Temperature and Humidity Sensor (round one)
  3. Датчик движения, совмещенный с датчиком освещения Xiaomi Aqara Motion Sensor (with light intensity support)
  4. Круглая кнопка-таблетка Xiaomi Wireless Switch (round one)

Все это достаточно быстро приехало. Хаб был подключен в розетку, громко выматерился на китайском после чего с помощью штатного, весьма корявого как мне показалось, приложения все было сопряжено и проверено на предмет общей работоспособности.

Тут важно было только выбрать "Материковый Китай" в настройках и в принципе, не считая странного приложения, все заработало так как ожидалось, но кто этому рад, сразу приступил к интеграции с openhab.

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

Далее, согласно инструкции к штатному плагину, необходимо либо в PaperUI подключить бондинг, либо вручную его прописать в things. Для второго варианта это выглядит примерно так:

Bridge mihome:bridge:7811dcdec5e0 "Xiaomi Gateway" [ serialNumber="7811dcdec5e0", ipAddress="192.168.1.191", port=9898, key="91bg8zfkf9vd6uw7", pollingInterval=500 ] {
    
}

pollingInterval я не до конца понял назначение, в разных источниках его значение видел и 5 и 500 и 5000, у меня с 500 работает и ладненько, но может уточните.

Тут, опять таки согласно инструкции, должны в inbox подхватиться все датчики. НО как я не старался нажимать на F5 - у меня этого не произошло (далее расскажу почему).

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

  1. мерять уровень освещения
  2. является цветным диммером для встроенной лампы/ночника
  3. умеет встроенным динамиком всякие сигналы издавать (вроде сирены и весьма громкой)

Тут я был приятно удивлен, сам шлюз даже без внешних датчиков, уже является достаточно ценным в хозяйстве openhab устройством. Из приятного оказалось что после присоединения к опенхабу хаб не потерялся в родном приложении, как это часто  бывает. Потому вполне возможно часть сценариев делать в родном приложении, часть в опенхабе для постепенной миграции.

Полный набор items которые умеет мой хаб (есть как минимум 3 версии) выглядит так:

// ночник (вкл,выкл)
Switch Gateway_LightSwitch <light> (xiaomi, OfficeRoom) { channel="mihome:gateway:gw1:brightness" }
// яркость ночника (диммер)
Dimmer Gateway_Brightness <dimmablelight> (xiaomi, OfficeRoom) { channel="mihome:gateway:gw1:brightness" }
// переход в режим сопряжения с устройствами
Switch Gateway_AddDevice (xiaomi, OfficeRoom) { channel="mihome:gateway:gw1:joinPermission" }

// RGB цвет подсветки ночника
Color Gateway_Color <rgb> (xiaomi, OfficeRoom) { channel="mihome:gateway:gw1:color" }
// диммер температуры подсветки
Dimmer Gateway_ColorTemperature <heating> (xiaomi, OfficeRoom) { channel="mihome:gateway:gw1:colorTemperature" }
// датчик освещенности
Number Gateway_AmbientLight <sun> (xiaomi, OfficeRoom) { channel="mihome:gateway:gw1:illumination" }
// набор звуков "сирены"
Number Gateway_Sound <soundvolume-0> (xiaomi, OfficeRoom) { channel="mihome:gateway:gw1:sound" }
// Mute динамика
Switch Gateway_SoundSwitch <soundvolume_mute> (xiaomi, OfficeRoom) { channel="mihome:gateway:gw1:enableSound" }
// громкость динамика
Dimmer Gateway_SoundVolume <soundvolume> (xiaomi, OfficeRoom) { channel="mihome:gateway:gw1:volume" }


Фиаско, братан

Изменение состояния хаба (например цвет подсветки) тут-же отображается в openhab. Я, воодушевленный удачным приобретением, уже хотел начинать прописывать датчики но, все-же, решил для чистоты эксперимента попробовать попереключать подсветку хаба со стороны openhab. И вот тут меня ждал полный провал. Простейшие сценарии (регулирование яркости ночника кнопкой на вебморде openhab) у меня не заработали, просто ничего не происходило. Я изучил логи, команда устройству уходит и дальше тишина. Устройство работало как-то в одну сторону, я видел все изменения от него но не мог сам ничего поменять. Гугление лишь добавило вопросов - у людей либо не работало совсем (неправильные настройки, файрвол и тп) либо работало полностью, но не так как у меня. Я почти неделю тратил по пару часов на ковыряние, готов был плюнуть и признать весь эксперимент провалом. Перед окончательным забиванием решил все-же плотнее изучить логи в verbose режиме. Оказалось что шлюз все-же не молчит в ответ на мою команду а говорит что "одноразовый ключ запроса не подходит". Я немного поковырялся в исходниках плагина (достоинство опенсорса) и увидел что действительно бондинг ждет что ему бродкастом прилетает от шлюза специальный "секретный" ключ которым он подписывает следующие запросы. Тоесть шлюз у себя ключ меняет, рассылает бродкастом а openhab его почему-то не принимает и шлет запрос шлюзу со старым ключем. 

Далее процесс ковыряния пошел веселее, я поставил бродкаст снифер рядом с openhab и начал наблюдать, видел самые разные бродкаст-пакеты кроме того что нужно. Поставил снифер на телефон, как устройство которое гарантированно принимает все пакеты от Xiaomi (приложение же родное работает) и на телефоне я его действительно увидел в снифере и даже с ключем. Ситуация прояснилась, есть бродкаст который получают не все, запрос шлюз посылал на специальный мультикаст IP в сети 224.0.0.0 про который википедия мне торжественно сообщила что эта сеть мультикаста единственная "Routable - No", роутер такой бродкасат-пакет не отправляет в соседние сети по определению, а у меня WiFi и проводная сети (openhab) действительно в разных подсетях жили. Я потратил еще 2 дня на реорганизацию сети, и все сразу заработало. 

Другими словами китайцы даже тут сделали все чуть-чуть по дебильному, но в итоге победа.

Погнали дальше

В очередной раз воодушевленный успехом я решил все-же поиграться наконец с датчиками. Датчик температуры/влажности оказался самым скучным, он просто работает и отдает информацию в опенхаб:

// Xiaomi Temperature and Humidity Sensor
// температура
Number HT_Temperature <temperature> (xiaomi, OfficeRoom)  { channel="mihome:sensor_ht:temp_hum1:temperature" }
// влажность
Number HT_Humidity <humidity> (xiaomi, OfficeRoom)  { channel="mihome:sensor_ht:temp_hum1:humidity" }
// заряд батарейки
Number HT_Battery <battery> (xiaomi, OfficeRoom)  { channel="mihome:sensor_ht:temp_hum1:batteryLevel" }
// низкий уровень батарейки
Switch HT_BatteryLow <energy> (xiaomi, OfficeRoom)  { channel="mihome:sensor_ht:temp_hum1:lowBattery" }

Замечу что items batteryLevel lowBattery характерны для всех трех батарейных датчиков (и думаю вообще всех), далее я их буду опускать.

Детектор движения повеселее:

// Xiaomi Motion Sensor
Switch MotionSensor_MotionStatus <motion> (xiaomi, OfficeRoom)  { channel="mihome:sensor_motion_aq2:motion1:motion" }
Number MotionSensor_AmbientLight <sun> (xiaomi, OfficeRoom)  { channel="mihome:sensor_motion_aq2:motion1:illumination" }
// minimum 5 seconds - remember that the sensor only triggers every minute to save energy
Number MotionSensor_MotionTimer <clock> (xiaomi, OfficeRoom) { channel="mihome:sensor_motion_aq2:motion1:motionOffTimer" }
DateTime MotionSensor_LastMotion "[%1$tY-%1$tm-%1$td  %1$tH:%1$tM]" <clock-on> { channel="mihome:sensor_motion_aq2:motion1:lastMotion" }

Он отдает факт движения и уровень освещения. Как оказалось датчик сам по себе достаточно хитрый, у него есть настройка motionOffTimer которая будучи установленной со стороны openhab указывает датчику через какое количество секунд "отлипнуть". То есть он не отдает каждый факт движения, он фиксирует первое движение и через указанное время говорит "движение пропало" если оно действительно пропало.

Наличие датчика освещения очень здоровская идея и делает датчик более ценным устройством, но он весьма своеобразный, просто покажу суточный график освещенности стоящего возле окна датчика:


Мало того что у него какие-то провалы при большом уровне освещения так еще и он плохо дружит с низким освещением. Но для определения "темно"/"не темно" в принципе достаточно.

Кнопка самая загадочная штука, у нее ровно items - batteryLevel lowBattery. И Все. Зато кнопка генерирует события в специальный канал button:

rule "Xiaomi Switch"
when
    Channel "mihome:sensor_switch:switch1:button" triggered
then
    var actionName = receivedEvent.getEvent()
    switch(actionName) {
        case "SHORT_PRESSED": {
           // клик
        }
        case "DOUBLE_PRESSED": {
            // даблклик
        }
        case "LONG_PRESSED": {
            // длинное нажатие (фронт)
        }
        case "LONG_RELEASED": {
            // длинное нажатие (отпускание)
        }
    }
end


Традиционные живые сценарии.

Включение подсветки ночника(шлюз Xiaomi в 100% яркости) в офисе если датчик движения засек движение и темно (используется встроенный детектор освещения). Датчик у меня строит на рабочем столе. Когда движение пропадает - освещение ночника возвращается в уровень который был до 100% яркости

var PercentType last_brightness = null

rule "Office motion light"
when
    Item MotionSensor_MotionStatus changed
then
    if (MotionSensor_MotionStatus.state == ON) {
        
        if ((MotionSensor_AmbientLight.state as DecimalType< 1) {
            last_brightness = Gateway_Brightness.state as PercentType;
            Gateway_Brightness.sendCommand(new PercentType(100));
        }
    } else {
        if (last_brightness !== null) {
            Gateway_Brightness.sendCommand(last_brightness);
        }
    }
end

Включение подсветки сада при нажатии на беспроводную кнопку на кухне

rule "Xiaomi Switch"
when
    Channel "mihome:sensor_switch:switch1:button" triggered
then
    var actionName = receivedEvent.getEvent()
    switch(actionName) {
        case "SHORT_PRESSED": {
            if (lRear_Switch.state == ON) {
                sendCommand(lRear_Switch, OFF);
            } else {
                sendCommand(lRear_Switch, ON);
            }
        }
    }
end

Тут подсветка сада реализована с помощью Sonoff реле и MQTT:

Switch lRear_Switch "Освітлення саду [%s]" <switch> (YR, Sonoff, gSecureLight)
    { mqtt=">[local:cmnd/sonoff1/POWER:command:*:default],
            <[local:stat/sonoff1/POWER:state:default]" }

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

Вывод

Экосистема Xiaomi Mi Smart Home у меня в составе openhab работает уже пару месяцев, за это время проблема была только на старте из-за нетривиального сетевого стека, во всех остальных моментах мне все очень понравилось и это абсолютно пригодно для применения, а с учетом цены устройств еще и очень выгодно.