Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Привет всем!
В своем «умном доме» я решил, что все устройства должны быть завязаны на локальном сервере, чтобы без интернета он не превратился в тыкву.
Но почти всё так или иначе привязано к облаку. Этот пост о том, как отвязать устройства Sonoff от облаков.
Да, я знаю, что таких постов очень много и уже все рассказали, как прошить Sonoff. Но я хочу показать как можно отвязать от облака Sonoff DW2, не прошивая его. Прошить его в любом случае не получится, ведь там не ESP8266, а другая микросхема.
Что понадобится для освобождения от облаков:
- Sonoff Basic, Sonoff 4CH — понадобится паяльник и USB-TTL преобразователь;
- Sonoff DW2 — собственный сервер.
В посте я использую ESPHome + Home Assistant + Docker + PHP, вы можете использовать любое другое ПО, тут главное принцип работы.
Начнем с простого.
Sonoff Basic R2
Хорошая игрушка для умного дома, если вам требуется поуправлять отдельным светильником и не хочется менять всю проводку
Первым делом подготавливаем конфиг нужной нам прошивки (актуальная версия находится тут):
Конфиг ESPHome
Copy
# Basic Config
esphome:
name: sonoff_basic_r2
platform: ESP8266
board: esp8285
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
logger:
api:
ota:
# Device Specific Config
binary_sensor:
- platform: gpio
id: push_button
pin:
number: GPIO0
mode: INPUT_PULLUP
inverted: True
internal: true
on_press:
# Prevents unintended LED lit states.
if:
condition:
- switch.is_off: relay
then:
- switch.turn_on: blue_led
- switch.turn_on: relay
else:
- switch.turn_off: relay
switch:
# The relay switches on the red side of the LED when active.
- platform: gpio
name: "Sonoff Basic Relay"
pin: GPIO12
id: relay
on_turn_off:
if:
condition:
- switch.is_on: blue_led
then:
- switch.turn_off: blue_led
# With this we can control the blue side of the LED.
- platform: gpio
id: blue_led
pin:
number: GPIO13
inverted: True
Теперь переходим к железу.
Для удобства припаиваем гребенку с шагом 2.54 на плату и подключаемся USB-TTL преобразователем, например CH340G, соблюдая пины:
- GND — GND
- TX — TX (возможно RX, зависит от преобразователя)
- RX — RX (возможно TX, зависит от преобразователя)
- 3.3V — 3.3V
Перед подключением Sonoff к ПК зажмите кнопку на плате, это необходимо для перехода в режим прошивки. Бывает такое, что устройство не определяется, поэтому я рекомендую поменять местами TX и RX пины, возможно, ваш программатор не учитывает такие моменты.
Сама инструкция по прошивке, скорее всего, вам уже известна, но для тех, кто делает это впервые, рекомендую ознакомиться с сайтом esphome.io
На этом наша работа с R2 окончена, теперь это ESPHome Sonoff R2.
Переходим к следующему пациенту.
Sonoff 4CH (Pro)
Цифра 5 означает, что это пятый контроллер в моем доме.
Приставка PRO означает, что вам доступны 433 МГц выключатели, но сама микросхема, отвечающая за 433 МГц протокол, распаяна отдельно, и ей управлять не получится, но после прошивки она продолжает работать как раньше.
По моим ощущениям 433 МГц протокол стал работать даже быстрее, чем до прошивки, но это может быть плацебо.
Отлично подходит для управления светом в комнатах. Я использую 3 таких на управлении светом во всем доме и 1 для управления насосами котла отопления.
Принцип прошивки аналогичен Basic R2, отличается лишь конфиг (актуальная версия находится тут):
Конфиг ESPHome
Copy
# Basic Config
esphome:
name: sonoff_4chpror2
platform: ESP8266
board: esp01_1m
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
logger:
api:
ota:
# Device Specific Config
binary_sensor:
- platform: gpio
pin:
number: GPIO0
mode: INPUT_PULLUP
inverted: True
on_press:
- switch.toggle: "relay_1"
- platform: gpio
pin:
number: GPIO9
mode: INPUT_PULLUP
inverted: True
on_press:
- switch.toggle: "relay_2"
- platform: gpio
pin:
number: GPIO10
mode: INPUT_PULLUP
inverted: True
on_press:
- switch.toggle: "relay_3"
- platform: gpio
pin:
number: GPIO14
mode: INPUT_PULLUP
inverted: True
on_press:
- switch.toggle: "relay_4"
- platform: gpio
pin:
number: GPIO0
mode: INPUT_PULLUP
inverted: True
name: "Sonoff 4CH Pro Button 1"
- platform: gpio
pin:
number: GPIO9
mode: INPUT_PULLUP
inverted: True
name: "Sonoff 4CH Pro Button 2"
- platform: gpio
pin:
number: GPIO10
mode: INPUT_PULLUP
inverted: True
name: "Sonoff 4CH Pro Button 3"
- platform: gpio
pin:
number: GPIO14
mode: INPUT_PULLUP
inverted: True
name: "Sonoff 4CH Pro Button 4"
- platform: status
name: "Sonoff 4CH Pro Status"
switch:
- platform: gpio
name: "Relay 1"
pin: GPIO12
id: "relay_1"
- platform: gpio
name: "Relay 2"
pin: GPIO5
id: "relay_2"
- platform: gpio
name: "Relay 3"
pin: GPIO4
id: "relay_3"
- platform: gpio
name: "Relay 4"
pin: GPIO15
id: "relay_4"
Заливаем прошивку и радуемся отвязанному ESPHome Sonoff 4CH (Pro)
Настало время самого сложного для меня пациента.
Sonoff DW2
Это единственное устройство у которого был доступ в интернет до сегодняшнего дня, у всех остальных «умных устройств» интернет отключен для безопасности.
В интернете я не нашел, как локально работать с датчиком двери, поэтому пришлось разбираться самому.
Начнем с принципа работы: DW2 подключается к вашей Wi-Fi сети и ждет прерывания от «открытия» или «закрытия» двери, в этот момент отправляется запрос на сервер eu-api.coolkit.cc, а уже с него вы получаете всю информацию.
Довольно сложная схема для простого датчика, подумал я и полез в Wireshark. Подменив DNS запись для домена eu-api.coolkit.cc на свой IP, я заметил обращение на 8080 порт, ура, они используют HTTP — значит, это победа!
Запустив nginx на своем сервере, я обнаружил странные логи такого вида:
Логи nginx
192.168.65.1 - - [29/Oct/2023:16:36:50 +0000] "\x16\x03\x01\x02\x00\x01\x00\x01\xFC\x03\x03i\xB5\x19G\xB1\xB2X}\xA3L\xCF\x99\x11\x84 \x00\xCE\x88\xB2S\xBC\xFC$\x86bp\xDA\xFA*\xEFl\xC8 \xB5\x12t\xDC\x13\x80W\x8E\xB1\xD5*\x00\x9E\xAF\xABzz\xEFpx\x92\xFCmY0\x03\xB6a\xE9\xF1\xC5\xC5\x00*" 400 157 "-" "-" "-"
192.168.65.1 - - [29/Oct/2023:16:36:53 +0000] "\x16\x03\x01\x02\x00\x01\x00\x01\xFC\x03\x03\x0B;'hR\x90\xAAY\x06Z\x82c6\xEC\x02p1\xF0\xEFF\xF5\xF3j\x99\x08r\xC1\xA2\x0E\xB6n\xD4 \xE5\x90\x1E\xA7N\xDC\xF3\xCA\xEA\x8C\x8Af5\xC4k\x95\x80W\x8A{\x90\x83\xCB\xBDxR\xB5\xF7\xFC\xFF<\x85\x00,\x8A\x8A\x13\x01\x13\x02\x13\x03\xC0,\xC0+\xCC\xA9\xC00\xC0/\xCC\xA8\xC0" 400 157 "-" "-" "-"
192.168.65.1 - - [29/Oct/2023:16:36:53 +0000] "\x16\x03\x01\x00\x97\x01\x00\x00\x93\x03\x01y|q\x17\x1B\xDD\xDB\xC3\xD79\x22\x1C\xA6nf\x0B]\xAC)^5\xFA5p\xF0\x8DS\x93\xF3\xED\x8AJ\x00\x00\x14\xC0" 400 157 "-" "-" "-"
Интересно, но ничего не понятно.
А что если это HTTPS, ведь первые байты у всех запросов одинаковые?
Да, это HTTPS, но на 8080 порту, странно, но ладно.
Конечно, я расстроился, ожидая SSL Pinning или шифрования данных внутри запроса, но нет, никакого шифрования нет и сертификат Sonoff DW2 не проверяет.
Берем любой сертификат, подсовываем его в nginx и запускаем php-fpm для удобства разработки.
И вот мы получаем успешный POST запрос:
Лог nginx
192.168.65.1 - - [29/Oct/2023:19:39:55 +0000] "POST /api/user/device/update HTTP/1.1" 200 5 "-" "-" "-"
Array
(
[deviceid] => **********
[d_seq] => 1546272314
[params] => Array
(
[switch] => off
[battery] => 2.202
[fwVersion] => 1000.2.925
[type] => 3
[chipID] => **********
[mac] => **********
[rssi] => -83
)
)
Отлично, мы получили информацию с датчика. Пишем скрипт-обработчик и добавляем перенаправление DNS запроса на свой сервер. Я использую Adguard Home, тут есть пункт «Перезапись DNS-запросов», в нем добавляем домен eu-api.coolkit.cc с вашим IP. Не забудьте в DHCP указать первым DNS свой Adguard Home.
Далее добавляем в конфиг следующие строки Home Assistant, чтобы появились новые объекты:
configuration.yaml
sensor:
- platform: template
sensors:
entry_door_battery_voltage:
unique_id: 5bc4eb632179f65962327215e2acf9e18d5a83f1
device_class: voltage
unit_of_measurement: 'V'
value_template: ''
entry_door_rssi:
unique_id: 053082ee1f19d9ca6a58a0cdb9061ffd87de2fc4
device_class: signal_strength
unit_of_measurement: 'dBm'
value_template: ''
binary_sensor:
- platform: template
sensors:
entry_door:
unique_id: 130e755dcb5dfa0696f0835fb73f1307c00e6c76
device_class: door
value_template: ''
и получаем долгосрочный токен в HA, его можно выпустить в самом низу страницы /profile
Для удобства и быстрого старта я использую Docker.
Конфигурация nginx
upstream php-sonoff-handler {
server php:9000;
}
server {
listen 8080 ssl;
server_name eu-api.coolkit.cc;
ssl_certificate /etc/nginx/ssl/servercert.pem;
ssl_certificate_key /etc/nginx/ssl/serverkey.pem;
ssl_dhparam /etc/nginx/ssl/dhparams.pem;
location / {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/project/sonoff.php;
fastcgi_pass php-sonoff-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
fastcgi_max_temp_file_size 0;
}
}
Конфигурация Docker Compose
version: '3'
services:
php:
container_name: php
image: "php:8.2-fpm"
volumes:
- ./php:/usr/share/nginx/html/project
restart: unless-stopped
nginx:
container_name: nginx
image: "nginx:latest"
volumes:
- ./nginx:/etc/nginx:ro
restart: unless-stopped
ports:
- "8080:8080"
Добавляем скрипт в /php/sonoff.php.
Скрипт позволяет добавлять несколько датчиков, достаточно указать каждому свой ID.
Вместо ******* укажите deviceid своего устройства. Его можно узнать в приложении ewelink, пункт «ID устройства».
PHP скрипт
<?php
const HA_API_URL = "http://homeassistant:8123/api/states/";
const HA_TOKEN = 'ВАШ ТОКЕН';
$listDevices = [
'**********' => [
'id' => 'entry_door',
'name' => 'Входная дверь',
'v_name' => 'Входная дверь (Напряжение)',
's_name' => 'Входная дверь (Сигнал)',
],
];
$data = json_decode(file_get_contents('php://input'), true);
$currentDevice = $listDevices[$data['deviceid']];
sendData(sprintf('binary_sensor.%s', $currentDevice['id']), [
'state' => $data['params']['switch'],
'attributes' => [
'friendly_name' => $currentDevice['name'],
'device_class' => 'door',
],
]);
sendData(sprintf('sensor.%s_battery_voltage', $currentDevice['id']), [
'state' => $data['params']['battery'],
'attributes' => [
'friendly_name' => $currentDevice['v_name'],
'state_class' => 'measurement',
'unit_of_measurement' => 'V',
'device_class' => 'voltage',
],
]);
sendData(sprintf('sensor.%s_rssi', $currentDevice['id']), [
'state' => $data['params']['rssi'],
'attributes' => [
'friendly_name' => $currentDevice['s_name'],
'state_class' => 'measurement',
'unit_of_measurement' => 'dBm',
'device_class' => 'signal_strength',
],
]);
function sendData($sensor, $postData)
{
$url = HA_API_URL . $sensor;
// for sending data as json type
$fields = json_encode($postData);
$ch = curl_init($url);
curl_setopt(
$ch,
CURLOPT_HTTPHEADER,
[
'Content-Type: application/json', // if the content type is json
'Authorization: Bearer ' . HA_TOKEN, // if you need token in header
]
);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
$result = curl_exec($ch);
curl_close($ch);
}
Запускаем docker и проверяем датчик в панели HA, информация отправляется только при открытии/закрытии двери.
Для удобства я создал репозиторий с примерами кода и конфигурации — GitHub