Расстаемся с облаками на примере Sonoff

vwtzyhijxaerhgrtjf8iq2nm_94.jpeg

Привет всем!

В своем «умном доме» я решил, что все устройства должны быть завязаны на локальном сервере, чтобы без интернета он не превратился в тыкву.

Но почти всё так или иначе привязано к облаку.
Этот пост о том, как отвязать устройства Sonoff от облаков.

Да, я знаю, что таких постов очень много и уже все рассказали, как прошить Sonoff.
Но я хочу показать как можно отвязать от облака Sonoff DW2, не прошивая его.
Прошить его в любом случае не получится, ведь там не ESP8266, а другая микросхема.

Что понадобится для освобождения от облаков:
Sonoff Basic, Sonoff 4CH — понадобится паяльник и USB-TTL преобразователь;
Sonoff DW2 — собственный сервер.

В посте я использую ESPHome + Home Assistant + Docker + PHP, вы можете использовать любое другое ПО, тут главное принцип работы.

Начнем с простого.

Sonoff Basic R2

fs7gjk_xkvbzjra54saamrnnpzc.jpeg
Хорошая игрушка для умного дома, если вам требуется поуправлять отдельным светильником и не хочется менять всю проводку

Первым делом подготавливаем конфиг нужной нам прошивки (актуальная версия находится тут):

Конфиг 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 пины, возможно, ваш программатор не учитывает такие моменты.

yj1xw6wttn9-sw4secsj2vgabj0.jpeg

Сама инструкция по прошивке, скорее всего, вам уже известна, но для тех, кто делает это впервые, рекомендую ознакомиться с сайтом esphome.io

На этом наша работа с R2 окончена, теперь это ESPHome Sonoff R2.

Переходим к следующему пациенту.

Sonoff 4CH (Pro)

lqo7p84p6ul_mjukemicu8ravdc.jpeg
Цифра 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) Max Super Digital.

Настало время самого сложного для меня пациента.

Sonoff DW2

zqut2ajpummbcwodgyuyoroudfy.jpeg

Это единственное устройство у которого был доступ в интернет до сегодняшнего дня, у всех остальных «умных устройств» интернет отключен для безопасности.

В интернете я не нашел, как локально работать с датчиком двери, поэтому пришлось разбираться самому.

Начнем с принципа работы: DW2 подключается к вашей Wi-Fi сети и ждет прерывания от «открытия» или «закрытия» двери, в этот момент отправляется запрос на сервер eu-api.coolkit.cc, а уже с него вы получаете всю информацию.

Довольно сложная схема для простого датчика, подумал я и полез в Wireshark.
Подменив DNS запись для домена eu-api.coolkit.cc на свой IP, я заметил обращение на 8080 порт, ура, они используют HTTP — значит, это победа! Нет, они используют HTTPS

Запустив 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 скрипт
 [
        '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

© Habrahabr.ru