Світло для ліжка

Вже давно мені не подобалась наявність тільки одного джерела світла у кімнаті. Через це треба було постійно вставати з ліжка щоб його вимкнути чи увімкнути, та й у більшості випадків треба було світло не у кімнаті, а локальне світло біля ліжка. Просто придбати маленький світильник це дуже просто та й займає він додаткове місце. Окрім цього вже багато років після ремонту ліжко чекало виготовлення узголів’я. Час прийшов, було вирішено зробити узголів’я та вмонтувати світло у нього. Основні вимоги до світла:

  • плавне вмикання/вимикання
  • наявність нічника
  • можливість керувати з будь-якої сторони ліжка

Схематично це повинно виглядати так

Створюємо креслення узголів’я, та віддаємо у меблеву майстерню на виготовлення, а тим часом переходимо до створення електроніки та програмного забезпечення.

Апаратна частина

У якості плати керування беремо саморобну плату на ESP32 котра має живлення через мережу Ethernet та перетворювач на 12В потужністю 13 Вт.

Схожу плату за $20 можна знайти у китайців – LILYGO® TTGO T Internet POE ESP32 WROOM LAN8720A Chip Ethernet Adapter And Downloader Expansion Board Programmable Hardware|Circuits| – AliExpress  Різниця між китайською та моєю платою полягає у тому, що моя плата має 2 напруги – 3.3В для живлення контролера плати та 12В для живлення у даному випадку джерела світла.

Плата розрахована для розміщення у корпусі D3MG та має тільки 2 входи для перемикачів, але ліжко має 5 перемикачів. Тому довелось додати додаткову плату захисту. Також ця плата не має місця для PWM на 12 В. Довелось це зробити напівнавісним монтажем на місці де повинні бути розташовані MOC3063 та сімістори. Як наслідок вийшов такий бутерброд:

У результаті маємо:

  • 2 PWM виходи на 12В що дають можливість плавно регулювати світло
  • 6 входів для перемикачів з захистом портів мікроконтролера
  • живлення плати та світла від мережі Ethernet та відсутність додаткових блоків живлення
  • можливість отримання команд та передача статусу за допомогою протоколу MQTT

Як бонус – маємо світло котре продовжує працювати, якщо немає електроенергії, якщо мережевий комутатор має блок безперебійного живлення.

Програмне забезпечення

Для керування просто створимо конфігурацію для вже існуючої системи керування на базі OpeHAB (кілька днів тому вийшла 3-тя версія, але стоїть поки 2.5)

Створення items

Створюємо файл room.items

Створюємо групи

Group ROOM "Room"
Group:Switch RM_BED_IOT1 "IOT2 Switches"
Group:String RM_BED_IOT1_SW_STATUS "IOT2 Switch status"
Group:Number RM_BED_IOT1_DIM_STATUS "IOT2 Dimmer status"

Головним чином групи треба для зменшення кількості правил при оновленні статусу чи керування коли один перемикач керує тільки одним виходом.

Створюємо перемикачі:

Switch RM_BED_IOT1_SW_35 "SW 35" (ROOM) { mqtt="<[broker:/room-iot-bad-1/gpio/state/35:state:MAP(onoff.map)]"}
String RM_BED_IOT1_SW_35_STATUS "SW 35 STATUS" (ROOM, RM_BED_IOT1_SW_STATUS) { mqtt="<[broker:/room-iot-bad-1/module/state:state:JSONPATH($.gpio[?(@.pin35)].pin35.state)]"}
Switch RM_BED_IOT1_SW_34 "SW 34" (ROOM) { mqtt="<[broker:/room-iot-bad-1/gpio/state/34:state:MAP(onoff.map)]"}
String RM_BED_IOT1_SW_34_STATUS "SW 34 STATUS" (ROOM, RM_BED_IOT1_SW_STATUS) { mqtt="<[broker:/room-iot-bad-1/module/state:state:JSONPATH($.gpio[?(@.pin34)].pin34.state)]"}
Switch RM_BED_IOT1_SW_36 "SW 36" (ROOM) { mqtt="<[broker:/room-iot-bad-1/gpio/state/36:state:MAP(onoff.map)]"}
String RM_BED_IOT1_SW_36_STATUS "SW 36 STATUS" (ROOM) { mqtt="<[broker:/room-iot-bad-1/module/state:state:JSONPATH($.gpio[?(@.pin36)].pin36.state)]"}
Switch RM_BED_IOT1_SW_39 "SW 39" (ROOM) { mqtt="<[broker:/room-iot-bad-1/gpio/state/39:state:MAP(onoff.map)]"}
String RM_BED_IOT1_SW_39_STATUS "SW 39 STATUS" (ROOM, RM_BED_IOT1_SW_STATUS) { mqtt="<[broker:/room-iot-bad-1/module/state:state:JSONPATH($.gpio[?(@.pin39)].pin39.state)]"}

У даному випадку кожен перемикач має окремо конфігурацію для обробки зміни положення перемикача та оновлення стану перемикача коли блок керування надсилає свій стан.

{ mqtt=”<[broker:/room-iot-bad-1/gpio/state/35:state:MAP(onoff.map)]”} – конвертує значення 1 чи 0 у ON чи OFF

{ mqtt=”<[broker:/room-iot-bad-1/module/state:state:JSONPATH($.gpio[?(@.pin35)].pin35.state)]”} – оброблює json зі статусом. JSON має наступний формат:

{
  "status": 207816,
  "gpio": [
    {
      "pin23": {
        "state": 1023,
        "mode": 4
      }
    },
    {
      "pin32": {
        "state": 1023,
        "mode": 4
      }
    },
    {
      "pin34": {
        "state": 0,
        "mode": 1
      }
    },
    {
      "pin35": {
        "state": 0,
        "mode": 1
      }
    },
    {
      "pin36": {
        "state": 0,
        "mode": 1
      }
    },
    {
      "pin39": {
        "state": 0,
        "mode": 1
      }
    }
  ]
}

Створюємо конфігурацію для світла:

Dimmer RM_BED_IOT1_PWM_23 "Ліжко, світло [%d %%]" (ROOM)
Number RM_BED_IOT1_PWM_23_RAW_STATE "PWM 23 STATE[%s]" (ROOM,RM_BED_IOT1_DIM_STATUS) { mqtt="<[broker:/room-iot-bad-1/module/state:state:JSONPATH($.gpio[?(@.pin23)].pin23.state)]"}
Dimmer RM_BED_IOT1_PWM_32 "Нічник [%d %%]" (ROOM)
Number RM_BED_IOT1_PWM_32_RAW_STATE "PWM 32 STATE[%s]" (ROOM,RM_BED_IOT1_DIM_STATUS) { mqtt="<[broker:/room-iot-bad-1/module/state:state:JSONPATH($.gpio[?(@.pin32)].pin32.state)]"}

Тут усе просто – Dimmer треба щоб мати можливість керувати з UI, статус – щоб оновлювати статус на UI якщо поточне значення не відповідає тому що у блоці керування (наприклад після рестарту блоку керування).

Створюємо конфігурацію для надсилання команд блоку керування

String RM_BED_IOT1_CMD "IOT ROOM CMD" (ROOM) {mqtt=">[broker:/room-iot-bad-1/gpio/command:command:*:default]"}

Незалежно від того що ми бажаємо зробити, цей блок керування очікує усі команди у єдиному топіку.

Створення правил (rules)

Правила для оновлення статусу

import org.eclipse.smarthome.model.script.ScriptServiceUtil
rule "IOT Switch state updated"
when
    Member of RM_BED_IOT1_SW_STATUS received update
then
    val swName = triggeringItem.name.split("_").get(4)
    val swItem = ScriptServiceUtil.getItemRegistry.getItem("RM_BED_IOT1_SW_" + swName)
    swItem.postUpdate(transform("MAP", "onoff.map", triggeringItem.state.toString))
end

rule "IOT Dimmer state updated"
when
    Member of RM_BED_IOT1_DIM_STATUS received update
then
    val dimName = triggeringItem.name.split("_").get(4)
    val dimItem = ScriptServiceUtil.getItemRegistry.getItem("RM_BED_IOT1_PWM_" + dimName)
    val rawVal = triggeringItem.state as Number
    var newState = (1023-rawVal)/1024*100
    dimItem.postUpdate(newState)
end

Правила для керування світлом

rule "Room bad LED strip light brightness"
when
    Item RM_BED_IOT1_PWM_23 changed
then
    var jsonString = "{\"pin\":23,\"state\":" + (1023 - (triggeringItem.state as DecimalType).floatValue() /100 * 1023) + "}"
    RM_BED_IOT1_CMD.sendCommand(jsonString)
end

rule "Room bad LED strip light brightness"
when
    Item RM_BED_IOT1_PWM_32 changed
then
    var jsonString = "{\"pin\":32,\"state\":" + (1023 - (triggeringItem.state as DecimalType).floatValue() /100 * 1023) + "}"
    RM_BED_IOT1_CMD.sendCommand(jsonString)
end

Dimmer має значення 0-100%, блок керування 0-1023, правило перетворює проценти у значення та інвертує значення.

Приклад правила для керування світлом за допомогою перемикача


rule "LED Light 800"
when
    Item RM_BED_IOT1_SW_34 changed to ON
then
    {
      var jsonString = "{\"pin\":23,\"state\":800}"
      RM_BED_IOT1_CMD.sendCommand(jsonString)
    }
end

Кілька правил для вмикання вимикання нічника при сході за заході сонця:

rule "Light off on sunrise"
when
    Channel "astro:sun:home:rise#event" triggered START
then
    var jsonString = "{\"pin\":32,\"state\":1023}"
    RM_BED_IOT1_CMD.sendCommand(jsonString)
end

rule "Light on on sunset"
when
    Channel "astro:sun:home:civilDusk#event" triggered END
then
   var jsonString = "{\"pin\":32,\"state\":0}"
   RM_BED_IOT1_CMD.sendCommand(jsonString)
end

rule "Light on on nights start"
when
    Time cron "0 0 22 * * ? *"
then
   var jsonString = "{\"pin\":32,\"state\":787}"
   RM_BED_IOT1_CMD.sendCommand(jsonString)
end

Для роботи цих правил потрібно встановити таки плагіни:

  • JsonPath Transformation
  • Map Transformation
  • Astro Binding

Програмне забезпечення для блока керування можна знайти за посиланням

Результат виглядає так: