Статья

Умное WI-FI присутствие с помощью роутера Keenetic и Node-Red

Предисловие или зачем мне это нужно

Понимание того, кто конкретно в квартире помогает подумать о безопасности и о персональных сценариях работы. Долго не будем задерживаться на этом - на выходе у вас будет сенсор, показывающий подключен ли кто-то из близких к WI-FI или нет.

Что нужно для того, чтобы все получилось?

  1. Роутер Keenetic с возможностью установки OPKG пакетов
  2. NodeRed

Инструкция по установке:

Первым делом нам нужно получить возможность установки OPGK пакетов на ваш роутер, для этого воспользуемся данной инструкцией:

Вы можете использовать внутреннюю память роутера для установки.

Затем вам необходимо подключиться к вашему роутеру по SSH  и установить mosquitto-client на ваш роутер.

bash
Копировать
opkg install mosquitto-client-nossl jq

Не разрываем SSH подключение и закидываем исполняемый скрипт 010-neighbour.sh в папку /storage/etc/ndm/neighbour.d, если такого каталога нет - создать с правами на выполнение.

Скрипт предварительно редактируем под себя, изменяя несколько харрактеристик :
bash
Копировать
#!/bin/sh

DEV1="e8:78:65:d7:61:bf" #тут мы вписываем мак адрес первого устройства
DEV2="74:42:8b:1b:bd:73" #тут мы вписываем мак адрес второго устройства

replacement() {
    case "$1" in
        *$DEV1* ) echo "Name dev1";; # тут вы можете изменить имя первого девайса
        *$DEV2* ) echo "Name dev2";; # тут вы можете изменить имя второго девайса
    esac
}

if [[ "$address" = "ipv4" && "$update" = "none" ]]; then
  #получаем mac устройства по ID - информация сугубо информационная
  mac=$(wget -qO - http://127.0.0.1:79/rci/show/ip/neighbour | jq --arg num "$id" '.[$num].mac')
  #проверяем mac на принадлежность нужному нам хосту - информация сугубо информационная
  who=$(replacement "$mac")
  [ -z "$who" ] && exit 0
  #публикуем mqtt сообщение типа "Device 1 new" и "Device 1 del" в топик zyxel - информация сугубо информационная
  mosquitto_pub -d -t zyxel -m "$who $action" -h 10.10.10.112 -p 1883 -u login -P password > /dev/null # вот на этой строчке я заменить брокер на броке малины с NR
fi

Теперь незабываем сделать скрипт исполняемым, снова возвращаемся к SSH и вписываем:

bash
Копировать
chmod +x 010-neighbour.sh

Теперь вы можете зайти на брокер и проверить со своим устройством публикуемый топик zyxel

Скрипт исполнения для Node Red

Упростил свой скрипт до базовой логики и очистил от своих допов для вас. В целом тут все очень просто - кто-то подключился к WI-FI, было ли действие с входной дверью в квартиру? Кто-то отключился от WI-FI - закрывали ли вы дверь? И так далее. Все завязывается на входной двери, которая как раз и помогает улучшить алгоритм присутствия в квартире. В случае, если вы находитесь к квартире и отключитесь от WI-FI - дом будет думать, что вы дома, ведь вы не выходили на улицу и не взаимодействовали с входной дверью.

Не забудьте установить node-red-contrib-controltimer

Флоу ниже
json
Копировать
[
    {
        "id": "681003fedcd65918",
        "type": "tab",
        "label": "Тест",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "86ad78107440c330",
        "type": "spruthub-in",
        "z": "681003fedcd65918",
        "name": "Входная дверь",
        "server": "6caeb7a3.0661a8",
        "friendly_name": "Входная дверь NEW  (Спальня) : Состояние датчика касания",
        "uid": [
            "17_10"
        ],
        "cid": "12",
        "ctype": "ContactSensorState",
        "showHidden": false,
        "enableMultiple": false,
        "outputAtStartup": false,
        "x": 340,
        "y": 600,
        "wires": [
            [
                "b76836241d69fe51"
            ]
        ]
    },
    {
        "id": "b76836241d69fe51",
        "type": "switch",
        "z": "681003fedcd65918",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "cont",
                "v": "1",
                "vt": "str"
            },
            {
                "t": "cont",
                "v": "0",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 455,
        "y": 600,
        "wires": [
            [
                "51ebab837f3544eb"
            ],
            [
                "78e9c94c91a1a926",
                "0a94843d3af37ba3"
            ]
        ],
        "l": false
    },
    {
        "id": "78e9c94c91a1a926",
        "type": "change",
        "z": "681003fedcd65918",
        "name": "Запоминаем на 3 минуты закрытие двери",
        "rules": [
            {
                "t": "set",
                "p": "doorcheckclosed",
                "pt": "global",
                "to": "true",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 990,
        "y": 640,
        "wires": [
            [
                "bf1942c171f26fcb"
            ]
        ]
    },
    {
        "id": "410148b7bc5b5cf9",
        "type": "change",
        "z": "681003fedcd65918",
        "name": "Возвращаем на место",
        "rules": [
            {
                "t": "set",
                "p": "doorcheckclosed",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 990,
        "y": 680,
        "wires": [
            []
        ]
    },
    {
        "id": "bf1942c171f26fcb",
        "type": "trigger",
        "z": "681003fedcd65918",
        "name": "",
        "op1": "",
        "op2": "test",
        "op1type": "nul",
        "op2type": "str",
        "duration": "3",
        "extend": true,
        "overrideDelay": false,
        "units": "min",
        "reset": "",
        "bytopic": "all",
        "topic": "topic",
        "outputs": 1,
        "x": 835,
        "y": 680,
        "wires": [
            [
                "410148b7bc5b5cf9"
            ]
        ],
        "l": false
    },
    {
        "id": "51ebab837f3544eb",
        "type": "change",
        "z": "681003fedcd65918",
        "name": "Запоминаем на три минуты открытие двери",
        "rules": [
            {
                "t": "set",
                "p": "doorcheckopen",
                "pt": "global",
                "to": "true",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 1000,
        "y": 560,
        "wires": [
            [
                "38a23d229a6722d8"
            ]
        ]
    },
    {
        "id": "38a23d229a6722d8",
        "type": "trigger",
        "z": "681003fedcd65918",
        "name": "",
        "op1": "",
        "op2": "test",
        "op1type": "nul",
        "op2type": "str",
        "duration": "3",
        "extend": true,
        "overrideDelay": false,
        "units": "min",
        "reset": "",
        "bytopic": "all",
        "topic": "topic",
        "outputs": 1,
        "x": 845,
        "y": 520,
        "wires": [
            [
                "aa63519fe4edcfe0"
            ]
        ],
        "l": false
    },
    {
        "id": "aa63519fe4edcfe0",
        "type": "change",
        "z": "681003fedcd65918",
        "name": "Возвращаем на место",
        "rules": [
            {
                "t": "set",
                "p": "doorcheckopen",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 990,
        "y": 520,
        "wires": [
            []
        ]
    },
    {
        "id": "210c12c1d77775bf",
        "type": "mqtt in",
        "z": "681003fedcd65918",
        "name": "",
        "topic": "zyxel",
        "qos": "2",
        "datatype": "auto",
        "broker": "ba753d3ede0325ae",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 270,
        "y": 280,
        "wires": [
            [
                "cf8db90eca5beae3",
                "95f9831b18db0cf2"
            ]
        ]
    },
    {
        "id": "bdbd7a9664ee5bd3",
        "type": "switch",
        "z": "681003fedcd65918",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "cont",
                "v": "Nikita del",
                "vt": "str"
            },
            {
                "t": "cont",
                "v": "Mari del",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 725,
        "y": 300,
        "wires": [
            [
                "78c880393a46688e"
            ],
            [
                "908405dba26c8e38"
            ]
        ],
        "l": false
    },
    {
        "id": "345f82920d9c0ed5",
        "type": "spruthub-out",
        "z": "681003fedcd65918",
        "name": "Маша",
        "server": "6caeb7a3.0661a8",
        "friendly_name": "Маша  (Вспомогательные) : Обнаружено присутствие",
        "uid": [
            "155_19"
        ],
        "cid": "21",
        "showHidden": false,
        "enableMultiple": false,
        "payload": "payload",
        "payloadType": "msg",
        "rbe": true,
        "x": 970,
        "y": 380,
        "wires": []
    },
    {
        "id": "7c7cfb8a0d11fc21",
        "type": "change",
        "z": "681003fedcd65918",
        "name": "1",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "1",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 795,
        "y": 420,
        "wires": [
            [
                "345f82920d9c0ed5"
            ]
        ],
        "l": false
    },
    {
        "id": "908405dba26c8e38",
        "type": "change",
        "z": "681003fedcd65918",
        "name": "0",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "0",
                "tot": "num"
            },
            {
                "t": "set",
                "p": "mariwifi",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 795,
        "y": 320,
        "wires": [
            [
                "345f82920d9c0ed5"
            ]
        ],
        "l": false
    },
    {
        "id": "db53559fc473c5b4",
        "type": "change",
        "z": "681003fedcd65918",
        "name": "1",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "1",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 795,
        "y": 380,
        "wires": [
            [
                "ab3751299733538a"
            ]
        ],
        "l": false
    },
    {
        "id": "78c880393a46688e",
        "type": "change",
        "z": "681003fedcd65918",
        "name": "0",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "0",
                "tot": "num"
            },
            {
                "t": "set",
                "p": "nikitawifi",
                "pt": "global",
                "to": "false",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 795,
        "y": 280,
        "wires": [
            [
                "ab3751299733538a"
            ]
        ],
        "l": false
    },
    {
        "id": "ab3751299733538a",
        "type": "spruthub-out",
        "z": "681003fedcd65918",
        "name": "Никита",
        "server": "6caeb7a3.0661a8",
        "friendly_name": "Никита  (Вспомогательные) : Обнаружено присутствие",
        "uid": [
            "155_15"
        ],
        "cid": "17",
        "showHidden": false,
        "enableMultiple": false,
        "payload": "payload",
        "payloadType": "msg",
        "rbe": true,
        "x": 980,
        "y": 320,
        "wires": []
    },
    {
        "id": "cf8db90eca5beae3",
        "type": "switch",
        "z": "681003fedcd65918",
        "name": "",
        "property": "doorcheckclosed",
        "propertyType": "global",
        "rules": [
            {
                "t": "true"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 495,
        "y": 260,
        "wires": [
            [
                "bdbd7a9664ee5bd3"
            ]
        ],
        "l": false
    },
    {
        "id": "95f9831b18db0cf2",
        "type": "switch",
        "z": "681003fedcd65918",
        "name": "",
        "property": "payload",
        "propertyType": "msg",
        "rules": [
            {
                "t": "cont",
                "v": "Nikita new",
                "vt": "str"
            },
            {
                "t": "cont",
                "v": "Mari new",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 365,
        "y": 420,
        "wires": [
            [
                "d0e6592679582701"
            ],
            [
                "bb169d458486ccad"
            ]
        ],
        "l": false
    },
    {
        "id": "d0e6592679582701",
        "type": "change",
        "z": "681003fedcd65918",
        "name": "Start",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "START",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "nikitawifi",
                "pt": "global",
                "to": "true",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 505,
        "y": 360,
        "wires": [
            [
                "dc24bafce89d6a3a"
            ]
        ],
        "l": false
    },
    {
        "id": "dc24bafce89d6a3a",
        "type": "controltimer",
        "z": "681003fedcd65918",
        "name": "",
        "timerType": "delay",
        "timerDurationUnit": "minute",
        "timerDurationType": "num",
        "timerDuration": 1,
        "timerLoopTimeoutUnit": "second",
        "timerLoopTimeoutType": "num",
        "timerLoopTimeout": 0,
        "loopTimeoutMessageType": "str",
        "loopTimeoutMessage": "LOOP_TIMEOUT",
        "timerMaxLoopIterationsType": "num",
        "timerMaxLoopIterations": 0,
        "loopMaxIterationsMessageType": "str",
        "loopMaxIterationsMessage": "MAX_LOOP_ITERATIONS",
        "isConsecutiveStartActionTimerResetAllowed": false,
        "isRunningTimerProgressVisible": true,
        "outputReceivedMessageOnTimerTrigger": true,
        "outputReceivedMessageOnTimerHalt": true,
        "startTimerOnReceivalOfUnknownMessage": false,
        "resetTimerOnReceivalOfUnknownMessage": false,
        "isDebugModeEnabled": false,
        "timerTriggeredMessageType": "str",
        "timerTriggeredMessage": "TIMER_TRIGGERED",
        "timerHaltedMessageType": "str",
        "timerHaltedMessage": "TIMER_HALTED",
        "isStartActionEnabled": true,
        "isStopActionEnabled": true,
        "isResetActionEnabled": true,
        "isPauseActionEnabled": true,
        "isContinueActionEnabled": true,
        "actionPropertyNameType": "str",
        "actionPropertyName": "payload",
        "startActionNameType": "str",
        "startActionName": "START",
        "stopActionNameType": "str",
        "stopActionName": "STOP",
        "resetActionNameType": "str",
        "resetActionName": "RESET",
        "pauseActionNameType": "str",
        "pauseActionName": "PAUSE",
        "continueActionNameType": "str",
        "continueActionName": "CONTINUE",
        "x": 665,
        "y": 360,
        "wires": [
            [
                "69fcab2491cbef8f"
            ],
            [
                "69fcab2491cbef8f"
            ]
        ],
        "l": false
    },
    {
        "id": "69fcab2491cbef8f",
        "type": "switch",
        "z": "681003fedcd65918",
        "name": "",
        "property": "nikitawifi",
        "propertyType": "global",
        "rules": [
            {
                "t": "true"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 735,
        "y": 380,
        "wires": [
            [
                "db53559fc473c5b4"
            ]
        ],
        "l": false
    },
    {
        "id": "bb169d458486ccad",
        "type": "change",
        "z": "681003fedcd65918",
        "name": "Start",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "START",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "mariwifi",
                "pt": "global",
                "to": "true",
                "tot": "bool"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 505,
        "y": 440,
        "wires": [
            [
                "f52b5651afc33c5f"
            ]
        ],
        "l": false
    },
    {
        "id": "0a94843d3af37ba3",
        "type": "change",
        "z": "681003fedcd65918",
        "name": "Stop",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "STOP",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 585,
        "y": 400,
        "wires": [
            [
                "f52b5651afc33c5f",
                "dc24bafce89d6a3a"
            ]
        ],
        "l": false
    },
    {
        "id": "f52b5651afc33c5f",
        "type": "controltimer",
        "z": "681003fedcd65918",
        "name": "",
        "timerType": "delay",
        "timerDurationUnit": "minute",
        "timerDurationType": "num",
        "timerDuration": 1,
        "timerLoopTimeoutUnit": "second",
        "timerLoopTimeoutType": "num",
        "timerLoopTimeout": 0,
        "loopTimeoutMessageType": "str",
        "loopTimeoutMessage": "LOOP_TIMEOUT",
        "timerMaxLoopIterationsType": "num",
        "timerMaxLoopIterations": 0,
        "loopMaxIterationsMessageType": "str",
        "loopMaxIterationsMessage": "MAX_LOOP_ITERATIONS",
        "isConsecutiveStartActionTimerResetAllowed": false,
        "isRunningTimerProgressVisible": true,
        "outputReceivedMessageOnTimerTrigger": true,
        "outputReceivedMessageOnTimerHalt": true,
        "startTimerOnReceivalOfUnknownMessage": false,
        "resetTimerOnReceivalOfUnknownMessage": false,
        "isDebugModeEnabled": false,
        "timerTriggeredMessageType": "str",
        "timerTriggeredMessage": "TIMER_TRIGGERED",
        "timerHaltedMessageType": "str",
        "timerHaltedMessage": "TIMER_HALTED",
        "isStartActionEnabled": true,
        "isStopActionEnabled": true,
        "isResetActionEnabled": true,
        "isPauseActionEnabled": true,
        "isContinueActionEnabled": true,
        "actionPropertyNameType": "str",
        "actionPropertyName": "payload",
        "startActionNameType": "str",
        "startActionName": "START",
        "stopActionNameType": "str",
        "stopActionName": "STOP",
        "resetActionNameType": "str",
        "resetActionName": "RESET",
        "pauseActionNameType": "str",
        "pauseActionName": "PAUSE",
        "continueActionNameType": "str",
        "continueActionName": "CONTINUE",
        "x": 665,
        "y": 440,
        "wires": [
            [
                "37758122dd96c4bf"
            ],
            [
                "37758122dd96c4bf"
            ]
        ],
        "l": false
    },
    {
        "id": "37758122dd96c4bf",
        "type": "switch",
        "z": "681003fedcd65918",
        "name": "",
        "property": "mariwifi",
        "propertyType": "global",
        "rules": [
            {
                "t": "true"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 735,
        "y": 420,
        "wires": [
            [
                "7c7cfb8a0d11fc21"
            ]
        ],
        "l": false
    },
    {
        "id": "383f892d07f27238",
        "type": "comment",
        "z": "681003fedcd65918",
        "name": "Убираем присутствие из дома только если за последние 3 минуты была закрыта дверь",
        "info": "",
        "x": 540,
        "y": 220,
        "wires": []
    },
    {
        "id": "63ef86619be27df4",
        "type": "comment",
        "z": "681003fedcd65918",
        "name": "",
        "info": "Тут при подключениии срабатывает минутный таймер ожидающий закрытия двери. Если таймер истечет , дом еще раз проверит подключены ли вы к WIFI и если да оставит ваше присутствие дома.\n\nМне это нужно было, чтобы Алиса четко после закрытия двери запускала приветствие и все рабочие вещи по подсветке и тд, а не когда я еще на подходе к квартире.",
        "x": 592.8571243286133,
        "y": 325.7142696380615,
        "wires": []
    },
    {
        "id": "6caeb7a3.0661a8",
        "type": "spruthub-server",
        "name": "Мой Спрутхабушка",
        "host": "10.10.10.41",
        "api_port": "80"
    },
    {
        "id": "ba753d3ede0325ae",
        "type": "mqtt-broker",
        "name": "",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "sessionExpiry": ""
    }
]

Крутая статья, примеров не хватает и где взять пример flow для node red, статья очень интересная, но повторить ее новичку невозможно
Отлично, ждём аналогичное решение для СХ.
Ну и флоу всё-таки надо добавить.
Вы изобрели велосипед.
С кинетика можно получить данные по API или SNMP. Или уж хотя бы по ARP вычислить устройства в сети, даже плагин для нодред есть.
Это не изобретение велосипеда, это получение данных с роутера без лишних тонн запросов от сервера. Роутер сам присылает по MQTT статус. А все эти арп грузят сервера и кладут сеть:)
Ну вы хоть немного матчасть изучите прежде чем такое писать
Матчасть моя это боль без этого скрипта пол года:) Если удобно, Николай юзать или есть другая инструкция - скиньте сюда ее. Я больше не смог найти ничего внятного, ну и спрашивал много раз в чатах)
Давайте просто изложу минусы вашего решения:
1. Вы установили на роутер OPKG и mosquitto-client. Само по себе это норм, но нагрузки добавит.
2. В скрипте вы все равно ходите по http на роутер на http://127.0.0.1:79/rci/show/ip/neighbour - а чего внутри то не смогли получить? Скрипт же уже на роутере.
3. В скрипте захардкожены логины, пароли, мак адреса.

Как можно было сделать:
1. Если есть ХА - там уже есть интеграция кинетика.
2. Если только нодред - есть node-red-contrib-arp.
3. Можно из нодред обращаться к апи кинетика, плагин я не нашел, но на гитхабе есть примеры.
Первый и третий согласен, второй пункт про arp - кучу отзывов о том, что этот плагин вешает НР - я и сам видел излишнюю нагрузку при его использовании. Пингование роутера с малины мне не интересно, хочу лучше нагрузить роутер, чтобы он присылал изменение статуса о себе ко мне. Ни разу еще он не нагрузился у меня, к слову:)
А в целом спасибо за комментарий! Если расковыряете api для удобной работы, честь вам и хвала. Пока я не видел подобных статей на спруте, которые бы агрегировали эту кучку инфы в одном месте. Конечно же это не мой скрипт на кинетеке, моя лишь реализация в НР с алгоритмом.
Как сложно. Я в нодеред просто пингую телефон по ipадресу раз в 10 сек. Если 15 минут нет ответа. Значит ушёл.
Не получается подключить с СХ брокеру. Ругается на Broker doesn't permit MQTT empty client ID. Username: login, channel: [id: 0xa85d76ed, L:/192.168.1.35:44444 - R:/192.168.1.1:54278]
Не могу понять где взять client ID
Скинь в личку телеги свой конфиг
Тому кто захочет повторить это все и тоже надевать штаны через голову - попробуйте сначала встроенную интеграцию в Home Assistant для keenetic + добавить "человеку" в качестве устройства для отслеживания device_tracker.кто-он-там-на-роутере, это прекрасно работает.
Наверняка в ваших статьях раскрыты данные темы, поделитесь ссылкой?
Да вот же она, выше вся, в полном объеме. Представляете, насколько все просто можно сделать? :)))
Наверняка ХА потрясающая система - очень надежная и безотказно работающая после любых обновлений, а так же с очень удобна с точки зрения отказоустойчивости на SD флешки.
Неразумно использовать "SD флешки" для систем, на которых происходит постоянная запись на диск, это давно известно и это никак не относится к недостаткам HomeAssistant. Для дома сейчас минимальный и классный вариант сервера, это JetHome JetHub H1, там применяется eMMC.

За последние пару лет HA сильно изменился. Умным домом для домохозяек он конечно не стал и не будет наверное никогда, но практически всё (или всё) перенесли из yaml в графический интерфейс. Не помню о проблемах при обновлениях. Наверное когда то это было, но давно :)

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