Еще летом, когда светит яркое солнце мне надоело крутить ручку закрытия/открытия жалюзи в офисе как на этой гифке и пришла идея их автоматизировать.
До покупки жалюзи из Леруа Мерлен у меня была идея поставить шторы с электроприводом, но цена на них несколько лет назад, когда делал выбор, была довольно кусачая. К тому же, из-за высоты и ширины окна размер штор получался нестандартный, что тоже увеличило стоимость.
Результат работы моего проекта по автоматизации жалюзи из Leroy Merlin
После этого прошло пару лет и после того, как количество “умных” вещей в офисе на базе Home Assistant начало разрастаться я вернулся к идее автоматизации жалюзи.
Мониторинг вариантов особенно ничего не дал. Все проекты автоматизации жалюзи которые я видел были для двухдюймовых жалюзи, в то время как практически все жалюзи которые продаются в РФ шириной в один дюйм.
Автоматизированные жалюзи на окне офиса
1. Выбор двигателя и управляющего микроконтроллера
Сначала непонятно было с чего вообще начинать. Для автоматизации, в других проектах часто использовали шаговый двигатель 28BYJ-48 за примерно 130 руб за штуку (в Китае). С управляющим контроллером у меня вопрос не стоял, поскольку практически везде использую LOLIN (WEMOS) D1 mini.
Переделанные и стандартные жалюзи: вид сверху
2. Прошивка для микроконтроллера ESP8266 китайского производителя Espressif Systems
На следующем шаге — прошивке мне не хотелось заморачиваться со сложным кодингом, а привычная Tasmota не выдавала готовых вариантов. Тогда я познакомился с ESPHome — прошивкой которая нативно и без MQTT поддерживается Home Assistant.
Переделанные и стандартные жалюзи: вид сбоку
Приятным бонусом ESPHome было, что она имеет компонент работы с шаговыми двигателями, который в свою очередь поддерживает работу с микросхемой ULN2003, которая может быть применена для управления нагрузкой значительной мощности, включая электромагнитные реле, двигатели постоянного тока, электромагнитные клапаны, в схемах управления различными шаговыми двигателями.
Переделанные жалюзи: вид сбоку
Поскольку я использую Hass.io, то для компиляции прошивок использовал самый простой для этого вариант — ESPHome Hass.io Add-On.
На окне три жалюзи и получилось три микроконтроллера. Вот получившиеся прошивки:
После тестов обнаружил что, для корректного открытия/закрытия жалюзи необходимо задавать разное число шагов для каждой жалюзи.
substitutions:
devicename: window_1
upper_devicename: Window 1
esphome:
name: $devicename
platform: ESP8266
board: d1_mini
wifi:
ssid: "xxx"
password: "xxx"
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Window 1 Fallback Hotspot"
password: "xxx"
captive_portal:
# web_server:
# port: 80
# css_url: http://192.168.15.10:8123/local/webserver-v1.min.css
# js_url: http://192.168.15.10:8123/local/webserver-v1.min.js
# Enable Home Assistant API
api:
services:
- service: control_stepper
variables:
target: int
then:
- stepper.set_target:
id: my_stepper
target: !lambda 'return target;'
# Enable OTA Access
ota:
# Enable verbose logging over serial
logger:
# physical connection
stepper:
- platform: uln2003
id: my_stepper
pin_a: D0
pin_b: D5
pin_c: D6
pin_d: D7
max_speed: 250 steps/s
sleep_when_done: true
acceleration: inf
deceleration: inf
i2c:
cover:
- platform: template
name: "Zhaliuzi 1"
id: window1
device_class: blind
open_action:
- stepper.report_position:
id: my_stepper
position: 0
- stepper.set_target:
id: my_stepper
target: -1550
close_action:
- stepper.report_position:
id: my_stepper
position: 0
- stepper.set_target:
id: my_stepper
target: 1550
stop_action:
- stepper.set_target:
id: my_stepper
target: !lambda return id(my_stepper).current_position;
optimistic: true
assumed_state: true
sensor:
- platform: bh1750
name: "Osveshchennost u okna"
address: 0x23
update_interval: 40s
# General device data
- platform: uptime
id: uptime_sec
- platform: wifi_signal
name: ${upper_devicename} WiFi Signal
id: wifis_signal
update_interval: 900s
text_sensor:
- platform: template
name: ${upper_devicename} Uptime
lambda: |-
int seconds = (id(uptime_sec).state);
int days = seconds / (24 * 3600);
seconds = seconds % (24 * 3600);
int hours = seconds / 3600;
seconds = seconds % 3600;
int minutes = seconds / 60;
seconds = seconds % 60;
return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
icon: mdi:clock-start
update_interval: 113s
- platform: template
name: ${upper_devicename} Wifi Strength
icon: "mdi:wifi"
lambda: |-
if (id(wifis_signal).state > -50 ) {
return {"Excellent"};
} else if (id(wifis_signal).state > -60) {
return {"Good"};
} else if (id(wifis_signal).state > -70) {
return {"Fair"};
} else if (id(wifis_signal).state < -70) {
return {"Weak"};
} else {
return {"None"};
}
update_interval: 900s
- platform: version
name: ${upper_devicename} Version
- platform: template
name: ${upper_devicename} MAC Address
lambda: 'return {WiFi.macAddress().c_str()};'
icon: mdi:fingerprint
update_interval: 1d
switch:
- platform: restart
name: ${upper_devicename} Restart
substitutions:
devicename: window_2
upper_devicename: Window 2
esphome:
name: $devicename
platform: ESP8266
board: d1_mini
wifi:
ssid: "xxx"
password: "xxx"
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Window 2 Fallback Hotspot"
password: "xxx"
captive_portal:
# web_server:
# port: 80
# css_url: http://192.168.15.10:8123/local/webserver-v1.min.css
# js_url: http://192.168.15.10:8123/local/webserver-v1.min.js
# Enable Home Assistant API
api:
services:
- service: control_stepper
variables:
target: int
then:
- stepper.set_target:
id: my_stepper
target: !lambda 'return target;'
# Enable OTA Access
ota:
# Enable verbose logging over serial
logger:
# physical connection
stepper:
- platform: uln2003
id: my_stepper
pin_a: D0
pin_b: D5
pin_c: D6
pin_d: D7
max_speed: 250 steps/s
sleep_when_done: true
acceleration: inf
deceleration: inf
cover:
- platform: template
name: "Zhaliuzi 2"
id: window2
device_class: blind
open_action:
- stepper.report_position:
id: my_stepper
position: 0
- stepper.set_target:
id: my_stepper
target: -1800
close_action:
- stepper.report_position:
id: my_stepper
position: 0
- stepper.set_target:
id: my_stepper
target: 1800
stop_action:
- stepper.set_target:
id: my_stepper
target: !lambda return id(my_stepper).current_position;
optimistic: true
assumed_state: true
# General device data
sensor:
- platform: uptime
id: uptime_sec
- platform: wifi_signal
name: ${upper_devicename} WiFi Signal
id: wifis_signal
update_interval: 900s
text_sensor:
- platform: template
name: ${upper_devicename} Uptime
lambda: |-
int seconds = (id(uptime_sec).state);
int days = seconds / (24 * 3600);
seconds = seconds % (24 * 3600);
int hours = seconds / 3600;
seconds = seconds % 3600;
int minutes = seconds / 60;
seconds = seconds % 60;
return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
icon: mdi:clock-start
update_interval: 113s
- platform: template
name: ${upper_devicename} Wifi Strength
icon: "mdi:wifi"
lambda: |-
if (id(wifis_signal).state > -50 ) {
return {"Excellent"};
} else if (id(wifis_signal).state > -60) {
return {"Good"};
} else if (id(wifis_signal).state > -70) {
return {"Fair"};
} else if (id(wifis_signal).state < -70) {
return {"Weak"};
} else {
return {"None"};
}
update_interval: 900s
- platform: version
name: ${upper_devicename} Version
- platform: template
name: ${upper_devicename} MAC Address
lambda: 'return {WiFi.macAddress().c_str()};'
icon: mdi:fingerprint
update_interval: 1d
switch:
- platform: restart
name: ${upper_devicename} Restart
substitutions:
devicename: window_3
upper_devicename: Window 3
esphome:
name: $devicename
platform: ESP8266
board: d1_mini
wifi:
ssid: "xxx"
password: "xxx"
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Window 3 Fallback Hotspot"
password: "xxx"
captive_portal:
# web_server:
# port: 80
# css_url: http://192.168.15.10:8123/local/webserver-v1.min.css
# js_url: http://192.168.15.10:8123/local/webserver-v1.min.js
# Enable Home Assistant API
api:
services:
- service: control_stepper
variables:
target: int
then:
- stepper.set_target:
id: my_stepper
target: !lambda 'return target;'
# Enable OTA Access
ota:
# Enable verbose logging over serial
logger:
# physical connection
stepper:
- platform: uln2003
id: my_stepper
pin_a: D0
pin_b: D5
pin_c: D6
pin_d: D7
max_speed: 250 steps/s
sleep_when_done: true
acceleration: inf
deceleration: inf
cover:
- platform: template
name: "Zhaliuzi 3"
id: window3
device_class: blind
open_action:
- stepper.report_position:
id: my_stepper
position: 0
- stepper.set_target:
id: my_stepper
target: -1600
close_action:
- stepper.report_position:
id: my_stepper
position: 0
- stepper.set_target:
id: my_stepper
target: 1600
stop_action:
- stepper.set_target:
id: my_stepper
target: !lambda return id(my_stepper).current_position;
optimistic: true
assumed_state: true
# General device data
sensor:
- platform: uptime
id: uptime_sec
- platform: wifi_signal
name: ${upper_devicename} WiFi Signal
id: wifis_signal
update_interval: 900s
text_sensor:
- platform: template
name: ${upper_devicename} Uptime
lambda: |-
int seconds = (id(uptime_sec).state);
int days = seconds / (24 * 3600);
seconds = seconds % (24 * 3600);
int hours = seconds / 3600;
seconds = seconds % 3600;
int minutes = seconds / 60;
seconds = seconds % 60;
return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() };
icon: mdi:clock-start
update_interval: 113s
- platform: template
name: ${upper_devicename} Wifi Strength
icon: "mdi:wifi"
lambda: |-
if (id(wifis_signal).state > -50 ) {
return {"Excellent"};
} else if (id(wifis_signal).state > -60) {
return {"Good"};
} else if (id(wifis_signal).state > -70) {
return {"Fair"};
} else if (id(wifis_signal).state < -70) {
return {"Weak"};
} else {
return {"None"};
}
update_interval: 900s
- platform: version
name: ${upper_devicename} Version
- platform: template
name: ${upper_devicename} MAC Address
lambda: 'return {WiFi.macAddress().c_str()};'
icon: mdi:fingerprint
update_interval: 1d
switch:
- platform: restart
name: ${upper_devicename} Restart
3. Установка привода в жалюзи
Шаговый двигатель не помещался полностью в жалюзи, но поскольку в моем случае они не были прижаты к стене, то была возможность вынести часть двигателя в прорезь с задней стороны жалюзи.
Установленные на окне жалюзи из Леруа Мерлен с электроприводом
4. Установка конструкции на окне
Поскольку жалюзи уже были установлены строителями мне оставалось только установить коробку с микроконтроллерами и блоком питания рядом с окном. Блок питания не самый мощный, поскольку шаговые двигатели включаются последовательно — сначала первый, потом второй, потом третий. Общее время работы около 20 секунд.
Коробка с тремя ESP8266 и блоком питания на стене офиса
5. Правила для автоматизации закрытия жалюзи из Home Assistant
При превышении определенного порога жалюзи поворачиваются на 90 градусов и потом соответственно обратно .
- alias: Covers OPEN
trigger:
- platform: numeric_state
entity_id: sensor.osveshchennost_u_okna
above: 0
below: 2500
condition:
- condition: state
entity_id: cover.zhaliuzi_3
state: 'closed'
- condition: state
entity_id: cover.zhaliuzi_2
state: 'closed'
- condition: state
entity_id: cover.zhaliuzi_1
state: 'closed'
action:
- service: cover.open_cover
data:
entity_id: cover.zhaliuzi_3
- delay: '00:00:06'
- service: cover.open_cover
data:
entity_id: cover.zhaliuzi_2
- delay: '00:00:06'
- service: cover.open_cover
data:
entity_id: cover.zhaliuzi_1
- alias: Covers CLOSE
trigger:
- platform: numeric_state
entity_id: sensor.osveshchennost_u_okna
above: 2501
below: 50000
condition:
- condition: state
entity_id: cover.zhaliuzi_3
state: 'open'
- condition: state
entity_id: cover.zhaliuzi_2
state: 'open'
- condition: state
entity_id: cover.zhaliuzi_1
state: 'open'
action:
- service: cover.close_cover
data:
entity_id: cover.zhaliuzi_3
- delay: '00:00:06'
- service: cover.close_cover
data:
entity_id: cover.zhaliuzi_2
- delay: '00:00:06'
- service: cover.close_cover
data:
entity_id: cover.zhaliuzi_1
Итог
Перед вами проект автоматизации жалюзи, который требует только временных затрат, но сами компоненты недороги. У проекта есть определенные достоинства. Самое главное достоинство: дешевизна.
Но есть и недостатки — ESP8266 никогда не знает текущего положения жалюзи. Иногда, когда например вал прокручивается, приходится вручную подгонять под начальное положение нажатием кнопки в интерфейсе Home Assistant.
P.S. Уже после окончания работы мне подсказали, что есть специальные соединительные втулки, которые позволят жестко соединить вал двигателя и вал жалюзи. Это позволит избежать прокручивания, которое может возникнуть в моем текущем случае из-за недостаточного закрепления соединительной трубки.
Дополнительные подробности можно найти на GitHub.
Автор: Михаил Шардин,
17 декабря 2019 г.