Статья

Интеграция кондиционера GREE с Wirenboard + Home Assistant

Привет, читатель! В этой публикации хочу рассказать, как я запилил себе интерфейс управления кондиционером в Home Assistant.

Недавно приобрёл себе кондиционер Gree Lomo Inverter R32, сразу заказал под него модуль от ONOKOM для интеграции с Wirenboard. 

Кондиционер в интерьере выглядит достаточно аккуратно.

А вот как выглядит модуль

Нюансы подключения модуля

Модуль подключил с помощью видеоинструкции вместо Wi-Fi модуля.  

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

Процесс перепрошивки не сложный, правда софт там под Windows, но, несмотря на то, что у меня OS X, перепрошить можно через VirtualBox. 

У меня при первой попытке прокинуть устройство возникла ошибка Can't attach USB device

Это лечится при помощи запуска VirtualBox через sudo, чтобы появилась возможность прокинуть устройство модуля в виртуалку. Да, и там важно, чтобы USB Type-C кабель был с поддержкой передачи данных, мой штатный зарядный кабель не обнаружил устройство.        

Вот скриншот, где прокидывается устройство.

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

И еще один нюанс, до подключения модуля к шине, позаботьтесь о смене Modbus Slave ID (при необходимости), так как по умолчанию у модуля Slave ID === 1. Сделать это можно при помощи утилиты modbus_client (внимательно читаем документацию!)

После подключения модуля к контроллеру и установки шаблона в Wirenboard (процесс описан в видеоинструкции от ONOKOM, ссылка выше. Мы получаем вот такой базовый интерфейс

Создание виртуального устройства

В штатном функционале есть некоторые неудобства:

  • Основной режим работы задаётся в виде integer-значения, это не очень удобно, т.к. надо запоминать соответствие цифры режиму.
  • То же самое касается режимов работы горизонтальных и вертикальных шторок.

В таком виде интегрироваться с Home Assistant неудобно, поэтому я решил воспользоваться виртуальным устройством Wirenboard, где поведение можно поправить "под себя". 

Для удобства пользования в скрипте wb-rules все значения хранятся в переменных. Логику работы функций описывать не буду, чтобы не засорять эфис, она там не сложная. Оставил все необходимые комментарии. Код скрипта разместил в конце публикации.

Экспорт виртуального устройства

Для экспорта виртуального устройства и всех его контролов в HA использовал wb-engine. Там ставится deb-пакет, а дальше заходим в настройки

Прокликиваем все галки виртуального устройства.

После сохранения у вас должно появиться виртуальное устройство.

Убеждаемся что оно прокинулось в Home Assistant.

Настройка Home Assistant

Я у себя на дашбордах не использую какие-то дополнительные интерфейсы, один раз поставил Lovelace и у меня после ребута всё отвалилось, с тех пор решил пользоваться только штатным функционалом.

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

  • для управления скоростями вентилятора нужно провалиться в настройки карточки
  • для управления пресетами надо провалиться в настройки карточки
  • для управления шторками предусмотрено всего два режима - выключено и автоматически (нет возможности управлять вручную)
  • нет управления отдельно горизонтальными/вертикальными шторками, не знаю насколько это важно для вас, но я хотел реализовать весь функционал

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

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

У меня есть панель для отображения всего, что касается климата. На панели размещены 4 (по количеству помещений) карточки типа "вертикальный стек". Одним из элементов карточки, относящейся к помещению, где установлен кондиционер, является карточка типа "сетка", количество столбцов - 1, отключил опцию "квадратные карточки". Каждый элемент сетки - горизонтальный стек, элементами горизонтального стека являются контролы. Выглядит это вот так:

В принципе, получилось удобно, хотелось бы, конечно, крутилку, как в термостате, но уже привык к такому варианту. Желаю всем удачи! Отвечу на вопросы в комментариях.

Код скрипта

Выкладываю код скрипта на wb-rules

javascript
Копировать
var realDeviceName = "ONOKOM-AIR-GR-1-MB-B_10";
var virtDeviceName = "virtual-conditioner-livingroom"

var targetTempMin = 16
var targetTempMax = 30
var targetTempDefault = 20

var fanSpeedMin = 0
var fanSpeedMax = 3
var fanSpeedAuto = 0
var fanSpeed1 = 1
var fanSpeed2 = 2
var fanSpeed3 = 3
var fanSpeedDefault = fanSpeedAuto

var modeAuto = 3
var modeCool = 2
var modeDry = 4
var modeFan = 5
var modeHeat = 1
var modeMaxVal = 5
var modeMinVal = 1
var modeDefault = modeCool

var horizontalVanesMin = 0
var horizontalVanesMax = 6
var horizontalVanesDisable = 0
var horizontalVanesAuto = 1
var horizontalVanesExtremeDown = 2
var horizontalVanesExtremeUp = 6
var horizontalVanesMiddle = 4
var horizontalVanesDefault = horizontalVanesMiddle

var verticalVanesMin = 0
var verticalVanesMax = 6
var verticalVanesDisable = 0
var verticalVanesAuto = 1
var verticalVanesExtremeLeft = 2
var verticalVanesExtremeRight = 6
var verticalVanesMiddle = 4
var verticalVanesDefault = verticalVanesMiddle


defineVirtualDevice(virtDeviceName, {
    title: virtDeviceName,
    cells: {
      //зеркалирование топика Active
      enabled: {
        type: "switch",
        value: false,
      },
      //зеркалирование топика Screen light
      light: {
        type: "switch",
        value: false,
      },      
      //зеркалирование топика Quiet mode
      quietMode: {
        type: "switch",
        value: false,
      },  
      //зеркалирование топика Turbo mode
      turboMode: {
        type: "switch",
        value: false,
      },  
      //зеркалирование топика Eco mode
      ecoMode: {
        type: "switch",
        value: false,
      },   
      //зеркалирование топика Sleep mode
      sleepMode: {
        type: "switch",
        value: false,
      },   
      //зеркалирование топика Ionization
      ionization: {
        type: "switch",
        value: false,
      },      
      //зеркалирование топика Target temperature
      targetTemp: {
        type : "range",
        value : targetTempDefault,
        max : targetTempMax,
        min : targetTempMin,
      },
      //устанавливает температуру на 16
      targetTemp16: {
       type: "pushbutton"         
      },  
      //устанавливает температуру на 18
      targetTemp18: {
       type: "pushbutton"         
      },      
      //устанавливает температуру на 20
      targetTemp20: {
       type: "pushbutton"         
      },  
      //устанавливает температуру на 21
      targetTemp21: {
        type: "pushbutton"       
      },       
      //устанавливает температуру на 22
      targetTemp22: {
       type: "pushbutton"        
      },        
      //зеркалирует топик Fan speed
      fanSpeed: {
        type : "range",
        value : fanSpeedDefault,
        max : fanSpeedMax,
        min : fanSpeedMin,
      },  
      //устанавливает скорость вентилятора в авто-режим
      fanSpeedAuto: {
        type: "switch",
        value: false,
      },
      //устанавливает скорость вентилятора на 1
      fanSpeed1: {
        type: "switch",
        value: false,
      },
      //устанавливает скорость вентилятора на 2
      fanSpeed2: {
        type: "switch",
        value: false,
      },
      //устанавливает скорость вентилятора на 3
      fanSpeed3: {
        type: "switch",
        value: false,
      },      
      //зеркалирует топик Mode
      mode: {
        type : "range",
        value : modeDefault,
        max : modeMaxVal,
        min : modeMinVal,
      },      
      //устанавливает режим кондиционера на авто
      modeAuto: {
        type: "switch",
        value: false,
      },  
      //устанавливает режим кондиционера на охлаждение
      modeCool: {
        type: "switch",
        value: false,
      },    
      //устанавливает режим кондиционера на осушение
      modeDry: {
        type: "switch",
        value: false,
      },    
      //устанавливает режим кондиционера на вентилирование
      modeFan: {
        type: "switch",
        value: false,
      },  
      //устанавливает режим кондиционера на нагрев
      modeHeat: {
        type: "switch",
        value: false,
      },   
      //зеркалирование топика Horizontal vanes
      horizontalVanes: {
        type : "range",
        value : horizontalVanesDefault,
        max : horizontalVanesMax,
        min : horizontalVanesMin,
      },
      //устанавливает режим горизонтальных шторок в режим свинга
      horizontalAuto: {
        type: "switch",
        value: false,
      },
      //выключаем режим свинга на горизонтальных шторках
      horizontalDisable: {
        type: "switch",
        value: false,
      },      
      //подвинуть горизонтальные шторки вверх на 1 деление, если был включен режим свинга или выключены - подвинет в крайнее верхнее положение
      horizontalMoveUp: {
        type: "pushbutton",
      },
      //подвинуть горизонтальные шторки вниз на 1 деление, если был включен режим свинга или выключены - подвинет в крайнее нижнее положение
      horizontalMoveDown: {
        type: "pushbutton",
      },     
      //зеркалирование топика Vertical vanes
      verticalVanes: {
        type : "range",
        value : verticalVanesDefault,
        max : verticalVanesMax,
        min : verticalVanesMin,
      },  
      //устанавливает режим вертикальных шторок в режим свинга
      verticalAuto: {
        type: "switch",
        value: false,
      },
      //выключаем режим свинга на вертикальных шторках
      verticalDisable: {
        type: "switch",
        value: false,
      },      
      //подвинуть вертикальных шторки вправо на 1 деление, если был включен режим свинга или выключены - подвинет в крайнее правое положение
      verticalMoveRight: {
        type: "pushbutton",
      },
      //подвинуть вертикальных шторки влево на 1 деление, если был включен режим свинга или выключены - подвинет в крайнее левое положение
      verticalMoveLeft: {
        type: "pushbutton",
      },        
    }
})

/*
ВКЛЮЧЕНИЕ/ВЫКЛЮЧЕНИЕ + ПОДСВЕТКА
*/
defineRule("{}/enabled".format(virtDeviceName), {
  whenChanged: "{}/enabled".format(virtDeviceName),
  then: function (newValue, devName, cellName) {
    dev[realDeviceName]["Active"] = newValue
  }
})
defineRule("{}/light".format(virtDeviceName), {
  whenChanged: "{}/light".format(virtDeviceName),
  then: function (newValue, devName, cellName) {
    //конвертируем булевое значение свитча в 1/0
    if(newValue){
      dev[realDeviceName]["Screen light"] = 1
    } else {
      dev[realDeviceName]["Screen light"] = 0
    }
  }
})

/*
УПРАВЛЕНИЕ ТЕМПЕРАТУРОЙ
*/
defineRule("{}/targetTemp".format(virtDeviceName), {
  whenChanged: "{}/targetTemp".format(virtDeviceName),
  then: function (newValue, devName, cellName) {
    dev[realDeviceName]["Target temperature"] = newValue
  }
})

defineRule("{}/targetTemp16".format(virtDeviceName), {
  whenChanged: "{}/targetTemp16".format(virtDeviceName),
  then: function (newValue, devName, cellName) {
    dev[virtDeviceName]['targetTemp'] = 16
  }
})

defineRule("{}/targetTemp18".format(virtDeviceName), {
  whenChanged: "{}/targetTemp18".format(virtDeviceName),
  then: function (newValue, devName, cellName) {
    dev[virtDeviceName]['targetTemp'] = 18
  }
})

defineRule("{}/targetTemp20".format(virtDeviceName), {
  whenChanged: "{}/targetTemp20".format(virtDeviceName),
  then: function (newValue, devName, cellName) {
    dev[virtDeviceName]['targetTemp'] = 20
  }
})

defineRule("{}/targetTemp21".format(virtDeviceName), {
  whenChanged: "{}/targetTemp21".format(virtDeviceName),
  then: function (newValue, devName, cellName) {
    dev[virtDeviceName]['targetTemp'] = 21
  }
})

defineRule("{}/targetTemp22".format(virtDeviceName), {
  whenChanged: "{}/targetTemp22".format(virtDeviceName),
  then: function (newValue, devName, cellName) {
    dev[virtDeviceName]['targetTemp'] = 22
  }
})


/*
ОСНОВНЫЕ РЕЖИМЫ (взаимоисключаемые)
*/
defineRule("{}/mode".format(virtDeviceName), {
  whenChanged: "{}/mode".format(virtDeviceName),
  then: function (newValue, devName, cellName) {
    dev[realDeviceName]["Mode"] = newValue
    if(newValue === modeAuto && !dev[virtDeviceName]["modeAuto"]) {
      dev[virtDeviceName]["modeAuto"] = true
    }
    if(newValue === modeCool && !dev[virtDeviceName]["modeCool"]) {
      dev[virtDeviceName]["modeCool"] = true
    }
    if(newValue === modeDry && !dev[virtDeviceName]["modeDry"]) {
      dev[virtDeviceName]["modeDry"] = true
    }
    if(newValue === modeFan && !dev[virtDeviceName]["modeFan"]) {
      dev[virtDeviceName]["modeFan"] = true
    }
    if(newValue === modeHeat && !dev[virtDeviceName]["modeHeat"]) {
      dev[virtDeviceName]["modeHeat"] = true
    }
  }
})

var checkModes = function() {
  if(!dev[virtDeviceName]["modeAuto"] && !dev[virtDeviceName]["modeCool"] && !dev[virtDeviceName]["modeDry"] && !dev[virtDeviceName]["modeFan"] && !dev[virtDeviceName]["modeHeat"]) {
    dev[virtDeviceName]["enabled"] = false
  }
if(dev[virtDeviceName]["modeAuto"] || dev[virtDeviceName]["modeCool"] || dev[virtDeviceName]["modeDry"] || dev[virtDeviceName]["modeFan"] || dev[virtDeviceName]["modeHeat"]) { dev[virtDeviceName]["enabled"] = true } } defineRule("{}/modeAuto".format(virtDeviceName), { whenChanged: "{}/modeAuto".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue){ dev[virtDeviceName]["mode"] = modeAuto dev[virtDeviceName]["modeCool"] = false dev[virtDeviceName]["modeDry"] = false dev[virtDeviceName]["modeFan"] = false dev[virtDeviceName]["modeHeat"] = false } checkModes() } }) defineRule("{}/modeCool".format(virtDeviceName), { whenChanged: "{}/modeCool".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue){ dev[virtDeviceName]["mode"] = modeCool dev[virtDeviceName]["modeAuto"] = false dev[virtDeviceName]["modeDry"] = false dev[virtDeviceName]["modeFan"] = false dev[virtDeviceName]["modeHeat"] = false } checkModes() } }) defineRule("{}/modeDry".format(virtDeviceName), { whenChanged: "{}/modeDry".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue){ dev[virtDeviceName]["mode"] = modeDry dev[virtDeviceName]["modeAuto"] = false dev[virtDeviceName]["modeCool"] = false dev[virtDeviceName]["modeFan"] = false dev[virtDeviceName]["modeHeat"] = false } checkModes() } }) defineRule("{}/modeFan".format(virtDeviceName), { whenChanged: "{}/modeFan".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue){ dev[virtDeviceName]["mode"] = modeFan dev[virtDeviceName]["modeAuto"] = false dev[virtDeviceName]["modeCool"] = false dev[virtDeviceName]["modeDry"] = false dev[virtDeviceName]["modeHeat"] = false } checkModes() } }) defineRule("{}/modeHeat".format(virtDeviceName), { whenChanged: "{}/modeHeat".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue){ dev[virtDeviceName]["mode"] = modeHeat dev[virtDeviceName]["modeAuto"] = false dev[virtDeviceName]["modeCool"] = false dev[virtDeviceName]["modeDry"] = false dev[virtDeviceName]["modeFan"] = false } checkModes() } }) /* ПРЕСЕТЫ */ defineRule("{}/quietMode".format(virtDeviceName), { whenChanged: "{}/quietMode".format(virtDeviceName), then: function (newValue, devName, cellName) { dev[realDeviceName]["Quiet mode"] = newValue //взаимоисключаемость (повторяем реализацию из шаблона) if(newValue){ dev[virtDeviceName]['turboMode'] = false } } }) defineRule("{}/turboMode".format(virtDeviceName), { whenChanged: "{}/turboMode".format(virtDeviceName), then: function (newValue, devName, cellName) { dev[realDeviceName]["Turbo mode"] = newValue //взаимоисключаемость (повторяем реализацию из шаблона) if(newValue){ dev[virtDeviceName]['quietMode'] = false } } }) defineRule("{}/ecoMode".format(virtDeviceName), { whenChanged: "{}/ecoMode".format(virtDeviceName), then: function (newValue, devName, cellName) { dev[realDeviceName]["Eco mode"] = newValue } }) defineRule("{}/sleepMode".format(virtDeviceName), { whenChanged: "{}/sleepMode".format(virtDeviceName), then: function (newValue, devName, cellName) { dev[realDeviceName]["Sleep mode"] = newValue } }) defineRule("{}/ionization".format(virtDeviceName), { whenChanged: "{}/ionization".format(virtDeviceName), then: function (newValue, devName, cellName) { dev[realDeviceName]["Ionization"] = newValue } }) /* УПРАВЛЕНИЕ ВЕНТИЛЯТОРОМ */ defineRule("{}/fanSpeed".format(virtDeviceName), { whenChanged: "{}/fanSpeed".format(virtDeviceName), then: function (newValue, devName, cellName) { dev[realDeviceName]["Fan speed"] = newValue if(newValue === fanSpeedAuto && !dev[virtDeviceName]['fanSpeedAuto']){ dev[virtDeviceName]['fanSpeedAuto'] = true } if(newValue === fanSpeed1 && !dev[virtDeviceName]['fanSpeed1']){ dev[virtDeviceName]['fanSpeed1'] = true } if(newValue === fanSpeed2 && !dev[virtDeviceName]['fanSpeed2']){ dev[virtDeviceName]['fanSpeed2'] = true } if(newValue === fanSpeed3 && !dev[virtDeviceName]['fanSpeed3']){ dev[virtDeviceName]['fanSpeed3'] = true } } }) var checkFan = function() { if(!dev[virtDeviceName]["fanSpeed1"] && !dev[virtDeviceName]["fanSpeed2"] && !dev[virtDeviceName]["fanSpeed3"]) { dev[virtDeviceName]["fanSpeedAuto"] = true } } defineRule("{}/fanSpeedAuto".format(virtDeviceName), { whenChanged: "{}/fanSpeedAuto".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue){ if(dev[virtDeviceName]["fanSpeed"] !== fanSpeedAuto ){ dev[virtDeviceName]["fanSpeed"] = fanSpeedAuto } dev[virtDeviceName]["fanSpeed1"] = false dev[virtDeviceName]["fanSpeed2"] = false dev[virtDeviceName]["fanSpeed3"] = false } checkFan() } }) defineRule("{}/fanSpeed1".format(virtDeviceName), { whenChanged: "{}/fanSpeed1".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue){ if(dev[virtDeviceName]["fanSpeed"] !== fanSpeed1 ){ dev[virtDeviceName]["fanSpeed"] = fanSpeed1 } dev[virtDeviceName]["fanSpeedAuto"] = false dev[virtDeviceName]["fanSpeed2"] = false dev[virtDeviceName]["fanSpeed3"] = false } checkFan() } }) defineRule("{}/fanSpeed2".format(virtDeviceName), { whenChanged: "{}/fanSpeed2".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue){ if(dev[virtDeviceName]["fanSpeed"] !== fanSpeed2 ){ dev[virtDeviceName]["fanSpeed"] = fanSpeed2 } dev[virtDeviceName]["fanSpeedAuto"] = false dev[virtDeviceName]["fanSpeed1"] = false dev[virtDeviceName]["fanSpeed3"] = false } checkFan() } }) defineRule("{}/fanSpeed3".format(virtDeviceName), { whenChanged: "{}/fanSpeed3".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue){ if(dev[virtDeviceName]["fanSpeed"] !== fanSpeed3 ){ dev[virtDeviceName]["fanSpeed"] = fanSpeed3 } dev[virtDeviceName]["fanSpeedAuto"] = false dev[virtDeviceName]["fanSpeed1"] = false dev[virtDeviceName]["fanSpeed2"] = false } checkFan() } }) /* ГОРИЗОНТАЛЬНЫЕ ШТОРКИ (вверх-вниз) */ var checkHorizontalVanes = function() { if(!dev[virtDeviceName]['horizontalVanesDisable'] && !dev[virtDeviceName]['horizontalVanesAuto'] && dev[virtDeviceName]['horizontalVanes'] < horizontalVanesExtremeDown){ dev[virtDeviceName]['horizontalVanes'] = horizontalVanesMiddle } } defineRule("{}/horizontalVanes".format(virtDeviceName), { whenChanged: "{}/horizontalVanes".format(virtDeviceName), then: function (newValue, devName, cellName) { dev[realDeviceName]['Horizontal vanes'] = newValue if(newValue === horizontalVanesDisable) { dev[virtDeviceName]['horizontalVanesDisable'] = true } if(newValue === horizontalVanesAuto) { dev[virtDeviceName]['horizontalVanesAuto'] = true } } }) defineRule("{}/horizontalDisable".format(virtDeviceName), { whenChanged: "{}/horizontalDisable".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue) { dev[virtDeviceName]['horizontalAuto'] = false dev[virtDeviceName]['horizontalVanes'] = horizontalVanesDisable } else { checkHorizontalVanes() } } }) defineRule("{}/horizontalAuto".format(virtDeviceName), { whenChanged: "{}/horizontalAuto".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue) { dev[virtDeviceName]['horizontalDisable'] = false dev[virtDeviceName]['horizontalVanes'] = horizontalVanesAuto } else { checkHorizontalVanes() } } }) defineRule("{}/horizontalMoveUp".format(virtDeviceName), { whenChanged: "{}/horizontalMoveUp".format(virtDeviceName), then: function (newValue, devName, cellName) { //если режим шторок "выключен" или авто if(dev[virtDeviceName]['horizontalVanes'] === horizontalVanesDisable || dev[virtDeviceName]['horizontalVanes'] === horizontalVanesAuto) { //тогда ставим в крайнее верхнее положение dev[virtDeviceName]['horizontalVanes'] = horizontalVanesExtremeUp //иначе проверяем что мы не вышли за границу } else if(dev[virtDeviceName]['horizontalVanes'] < horizontalVanesMax) { //и тогда двигаем шторки вверх dev[virtDeviceName]['horizontalVanes']++ } dev[virtDeviceName]['horizontalAuto'] = false dev[virtDeviceName]['horizontalDisable'] = false } }) defineRule("{}/horizontalMoveDown".format(virtDeviceName), { whenChanged: "{}/horizontalMoveDown".format(virtDeviceName), then: function (newValue, devName, cellName) { //если режим шторок "выключен" или авто if(dev[virtDeviceName]['horizontalVanes'] === verticalVanesDisable || dev[virtDeviceName]['horizontalVanes'] === verticalVanesAuto) { //тогда ставим в крайнее нижнее положение dev[virtDeviceName]['horizontalVanes'] = horizontalVanesExtremeDown //иначе проверяем что мы не вышли за границу } else if(dev[virtDeviceName]['horizontalVanes'] > horizontalVanesExtremeDown) { //и тогда двигаем шторки вниз dev[virtDeviceName]['horizontalVanes']-- } } }) /* ВЕРТИКАЛЬНЫЕ ШТОРКИ (вправо-влево) */ var checkVerticalVanes = function() { if(!dev[virtDeviceName]['verticalVanesDisable'] && !dev[virtDeviceName]['verticalVanesAuto'] && dev[virtDeviceName]['verticalVanes'] < verticalVanesExtremeLeft){ dev[virtDeviceName]['verticalVanes'] = verticalVanesMiddle } } defineRule("{}/verticalVanes".format(virtDeviceName), { whenChanged: "{}/verticalVanes".format(virtDeviceName), then: function (newValue, devName, cellName) { dev[realDeviceName]['Vertical vanes'] = newValue if(newValue === verticalVanesDisable) { dev[virtDeviceName]['verticalVanesDisable'] = true } if(newValue === verticalVanesAuto) { dev[virtDeviceName]['verticalVanesAuto'] = true } } }) defineRule("{}/verticalDisable".format(virtDeviceName), { whenChanged: "{}/verticalDisable".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue){ dev[virtDeviceName]['verticalAuto'] = false dev[virtDeviceName]['verticalVanes'] = verticalVanesDisable } else { checkVerticalVanes() } } }) defineRule("{}/verticalAuto".format(virtDeviceName), { whenChanged: "{}/verticalAuto".format(virtDeviceName), then: function (newValue, devName, cellName) { if(newValue) { dev[virtDeviceName]['verticalDisable'] = false dev[virtDeviceName]['verticalVanes'] = verticalVanesAuto } else { checkVerticalVanes() } } }) defineRule("{}/verticalMoveRight".format(virtDeviceName), { whenChanged: "{}/verticalMoveRight".format(virtDeviceName), then: function (newValue, devName, cellName) { //если режим шторок "выключен" или авто if(dev[virtDeviceName]['verticalVanes'] === verticalVanesDisable || dev[virtDeviceName]['verticalVanes'] === verticalVanesAuto) { //тогда ставим в крайнее правое положение dev[virtDeviceName]['verticalVanes'] = verticalVanesExtremeRight //иначе проверяем что мы не вышли за границу } else if(dev[virtDeviceName]['verticalVanes'] < verticalVanesMax) { //и тогда двигаем шторки вправо dev[virtDeviceName]['verticalVanes']++ } } }) defineRule("{}/verticalMoveLeft".format(virtDeviceName), { whenChanged: "{}/verticalMoveLeft".format(virtDeviceName), then: function (newValue, devName, cellName) { //если режим шторок "выключен" или авто if(dev[virtDeviceName]['verticalVanes'] === verticalVanesDisable || dev[virtDeviceName]['verticalVanes'] === verticalVanesAuto) { //тогда ставим в крайнее левое положение dev[virtDeviceName]['verticalVanes'] = verticalVanesExtremeLeft } else if(dev[virtDeviceName]['verticalVanes'] > verticalVanesExtremeLeft) { dev[virtDeviceName]['verticalVanes']-- } dev[virtDeviceName]['verticalAuto'] = false dev[virtDeviceName]['verticalDisable'] = false } })

Конфиг карточки

Выкладываю конфиг карточки home assistant.

yaml
Копировать
type: vertical-stack
cards:
  - square: false
    type: grid
    cards:
      - type: horizontal-stack
        cards:
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_enabled
            icon: mdi:air-conditioner
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_light
            icon: mdi:lightbulb-on
      - type: horizontal-stack
        cards:
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_modeauto
            name: авто
            icon: mdi:auto-mode
            show_state: false
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_modecool
            name: охлаждение
            icon: mdi:snowflake
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_modedry
            icon: mdi:water
            name: осушение
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_modefan
            icon: mdi:fan
            name: вентиляция
            show_state: false
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_modeheat
            name: обогрев
            icon: mdi:weather-sunny
            show_state: false
      - type: horizontal-stack
        cards:
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_fanspeedauto
            icon: mdi:fan-auto
            show_state: false
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_fanspeed1
            icon: mdi:fan-speed-1
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_fanspeed2
            icon: mdi:fan-speed-2
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_fanspeed3
            icon: mdi:fan-speed-3
      - type: horizontal-stack
        cards:
          - type: horizontal-stack
            cards:
              - show_name: true
                show_icon: true
                type: button
                tap_action:
                  action: toggle
                entity: switch.virtual_conditioner_livingroom_quietmode
                icon: mdi:ear-hearing-off
                name: тихо
              - show_name: true
                show_icon: true
                type: button
                tap_action:
                  action: toggle
                entity: switch.virtual_conditioner_livingroom_ecomode
                icon: mdi:charity
                name: эко
              - show_name: true
                show_icon: true
                type: button
                tap_action:
                  action: toggle
                entity: switch.virtual_conditioner_livingroom_turbomode
                icon: mdi:wind-power
                name: турбо
              - show_name: true
                show_icon: true
                type: button
                tap_action:
                  action: toggle
                entity: switch.virtual_conditioner_livingroom_sleepmode
                icon: mdi:power-sleep
                name: '  сон'
              - show_name: true
                show_icon: true
                type: button
                tap_action:
                  action: toggle
                entity: switch.virtual_conditioner_livingroom_ionization
                icon: mdi:home-heart
                name: ион
      - type: horizontal-stack
        cards:
          - show_name: true
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: button.virtual_conditioner_livingroom_targettemp16
            name: '16'
            icon: mdi:temperature-celsius
          - show_name: true
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: button.virtual_conditioner_livingroom_targettemp18
            name: '18'
            icon: mdi:temperature-celsius
          - show_name: true
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: button.virtual_conditioner_livingroom_targettemp20
            name: '20'
            icon: mdi:temperature-celsius
          - show_name: true
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: button.virtual_conditioner_livingroom_targettemp21
            name: '21'
            icon: mdi:temperature-celsius
          - show_name: true
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: button.virtual_conditioner_livingroom_targettemp22
            name: '22'
            icon: mdi:temperature-celsius
      - type: entities
        entities:
          - entity: number.virtual_conditioner_livingroom_targettemp
            icon: mdi:temperature-celsius
            secondary_info: none
            name: температура
        state_color: false
        show_header_toggle: false
      - type: horizontal-stack
        cards:
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_horizontaldisable
            icon: mdi:close-box
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_horizontalauto
            icon: mdi:arrow-up-down-bold
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: button.virtual_conditioner_livingroom_horizontalmoveup
            icon: mdi:arrow-up-bold
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: button.virtual_conditioner_livingroom_horizontalmovedown
            icon: mdi:arrow-down-bold
      - type: horizontal-stack
        cards:
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_verticaldisable
            icon: mdi:close-box
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.virtual_conditioner_livingroom_verticalauto
            icon: mdi:arrow-left-right-bold
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: button.virtual_conditioner_livingroom_verticalmoveleft
            icon: mdi:arrow-left-bold
          - show_name: false
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: button.virtual_conditioner_livingroom_verticalmoveright
            icon: mdi:arrow-right-bold
    columns: 1
title: кондиционер

Конфиг термостата

Как и обещал, выкладываю конфиг для термостата, я отказался от этой идеи, но вдруг кто-то решил докрутить эту тему под себя.

yaml
Копировать
mqtt:
  climate:
    - name: кондиционер в гостинной
      unique_id: virtual_conditioner_livingroom
      modes:
        - "auto"
        - "cool"
        - "dry"
        - "fan_only"
        - "heat"
        - "off"
      max_temp: 30
      min_temp: 16
      fan_modes:
        - "auto"
        - "high"
        - "medium"
        - "low"
      fan_mode_command_topic: "/devices/virtual-conditioner-livingroom/controls/fanSpeed"
      fan_mode_command_template: >-
        {% if value == 'auto' %}
          0
        {% elif value == 'low' %}
          1
        {% elif value == 'medium' %}
          2
        {% else %}
          3
        {% endif %}
      fan_mode_state_topic: "/devices/virtual-conditioner-livingroom/controls/fanSpeed"
      fan_mode_state_template: >-
        {% if value == '0' %}
          auto
        {% elif value == '1' %}
          low
        {% elif value == '2' %}
          medium
        {% else %}
          high
        {% endif %}
      mode_command_topic: '/devices/virtual-conditioner-livingroom/controls/mode'
      mode_command_template: >-
        {% if value == 'off' %}
          0
        {% elif value == 'cool' %}
          1
        {% elif value == 'auto' %}
          2
        {% elif value == 'dry' %}
          3
        {% elif value == 'fan_only' %}
          4
        {% else %}
          5
        {% endif %}
      mode_state_topic: '/devices/virtual-conditioner-livingroom/controls/mode'
      mode_state_template: >-
        {% if value == '0' %}
          off
        {% elif value == '1' %}
          cool
        {% elif value == '2' %}
          auto
        {% elif value == '3' %}
          dry
        {% elif value == '4' %}
          fan_only
        {% else %}
          heat
        {% endif %}
      temperature_command_topic: '/devices/virtual-conditioner-livingroom/controls/targetTemp'
      temperature_state_topic: '/devices/virtual-conditioner-livingroom/controls/targetTemp'
      current_temperature_topic: '/devices/ONOKOM-AIR-GR-1-MB-B_10/controls/Indoor air temperature'
      temp_step: 1
      temperature_unit: C

Большая, сочная, и подробная статья, лайк однозначно
Странная фраза:

"Я у себя на дашбордах не использую какие-то дополнительные интерфейсы, один раз поставил Lovelace и у меня после ребута всё отвалилось, с тех пор решил пользоваться только штатным функционалом."

Lovelace и есть штатный функционал. Его не надо ставить. Хотя, вроде его не так давно переименовали просто в Dashboard.
Ааа, значит не разобрался. Я там ставил какой-то дополнительный ui, почему-то отложилось в памяти что он назывался lovelace
У меня потом после перезагрузки малины у меня не стартанул докер-контейнер с HA, пришлось вручную выпиливать этот ui, решил больше судьбу не испытывать 😌

Вернуться назад
Вернуться назад