Предисловие или зачем мне это нужно
Понимание того, кто конкретно в квартире помогает подумать о безопасности и о персональных сценариях работы. Долго не будем задерживаться на этом - на выходе у вас будет сенсор, показывающий подключен ли кто-то из близких к WI-FI или нет.
Что нужно для того, чтобы все получилось?
- Роутер Keenetic с возможностью установки OPKG пакетов
- NodeRed
Инструкция по установке:
Первым делом нам нужно получить возможность установки OPGK пакетов на ваш роутер, для этого воспользуемся данной инструкцией:
Вы можете использовать внутреннюю память роутера для установки.
Затем вам необходимо подключиться к вашему роутеру по SSH и установить mosquitto-client на ваш роутер.
opkg install mosquitto-client-nossl jq
Не разрываем SSH подключение и закидываем исполняемый скрипт 010-neighbour.sh в папку /storage/etc/ndm/neighbour.d, если такого каталога нет - создать с правами на выполнение.
#!/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 и вписываем:
chmod +x 010-neighbour.sh
Теперь вы можете зайти на брокер и проверить со своим устройством публикуемый топик zyxel
Скрипт исполнения для Node Red
Упростил свой скрипт до базовой логики и очистил от своих допов для вас. В целом тут все очень просто - кто-то подключился к WI-FI, было ли действие с входной дверью в квартиру? Кто-то отключился от WI-FI - закрывали ли вы дверь? И так далее. Все завязывается на входной двери, которая как раз и помогает улучшить алгоритм присутствия в квартире. В случае, если вы находитесь к квартире и отключитесь от WI-FI - дом будет думать, что вы дома, ведь вы не выходили на улицу и не взаимодействовали с входной дверью.
Не забудьте установить node-red-contrib-controltimer
[
{
"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": ""
}
]
Ну и флоу всё-таки надо добавить.
С кинетика можно получить данные по API или SNMP. Или уж хотя бы по ARP вычислить устройства в сети, даже плагин для нодред есть.
1. Вы установили на роутер OPKG и mosquitto-client. Само по себе это норм, но нагрузки добавит.
2. В скрипте вы все равно ходите по http на роутер на http://127.0.0.1:79/rci/show/ip/neighbour - а чего внутри то не смогли получить? Скрипт же уже на роутере.
3. В скрипте захардкожены логины, пароли, мак адреса.
Как можно было сделать:
1. Если есть ХА - там уже есть интеграция кинетика.
2. Если только нодред - есть node-red-contrib-arp.
3. Можно из нодред обращаться к апи кинетика, плагин я не нашел, но на гитхабе есть примеры.
А в целом спасибо за комментарий! Если расковыряете api для удобной работы, честь вам и хвала. Пока я не видел подобных статей на спруте, которые бы агрегировали эту кучку инфы в одном месте. Конечно же это не мой скрипт на кинетеке, моя лишь реализация в НР с алгоритмом.
Не могу понять где взять client ID
За последние пару лет HA сильно изменился. Умным домом для домохозяек он конечно не стал и не будет наверное никогда, но практически всё (или всё) перенесли из yaml в графический интерфейс. Не помню о проблемах при обновлениях. Наверное когда то это было, но давно :)
можно добавить красивости отдав в json результат и добавив время последнего действия
mosquitto_pub -d -t MAIN_TOPIC_NAME/"$who" -m "{\"action\": \"$action\",\"time\": \"$(date +%s)\"}" -h HOSTNAME_IP -p 1883 > /dev/null
теперь можно строить автоматизации точно зная как давно человек ушел, например после того как прошел час с момента ухода последнего выключать чайник итп
Настраивал на Sprut Hub, все работает четко и быстро.
Только застрял на пункте:
Теперь незабываем сделать скрипт исполняемым, снова возвращаемся к SSH и вписываем:
chmod +x 010-neighbour.sh
Сначала нужно было выполнить :
cd /opt/etc/ndm/neighbor.d/
А потом:
chmod +x 010-neighbour.sh
Или в последних версиях хаба уже встроен на лор для этих датчиков присутствия?
Я настроил у себя все по инструкции на роутере. Но контроллер mqtt на хабе не видит ни чего.