Виртуальное устройство Hikvision ISAPI

Здравствуйте!
Помогите пожалуйста с вопросом интеграции событий домофона Hikvision в контролер Wirenboard с помощью команд описанных в документации ISAPI Hikvision Video Intercom.
ISAPI - Video Intercom.pdf (716,8 КБ)

Можно ли это сделать с помощью виртуального устройства?
Задача такая: получить событие входящего звонка домофона и включить с помощью релейного модуля установленного в контроллер физический звонок в квартире.
Нашел похожий функционал в Home Assistant через аддон и трансляцию в MQTT но хотелось бы настроить прямое управление реле без прокладок стороннего софта.

Здравствуйте.

Работать с url запросами и json ответами можно из python.

Для работы с MQTT из Python есть библиотека paho-mqtt-python.

Есть какие-либо дополнительные вопросы?

ДА, больше чем ответов.
Спасибо)

А какие именно? давайте разберемся.

Добрый день!
Я надеялся на то что это возможно решить более упрощенными стандартными средствами ПО без углубленного программирования (не очень в этом понимаю) :slight_smile:

Действительно, нативных и конфигурируемых способов такой интеграции нет - нужно задействовать разработку.

Автор [аддона для HA] решил, что одними isapi rest запросами сделать не получится, и поэтому надо использовать sdk от hikvision (набор наТивных библиотек), и поэтому его интеграция требует аддон. Действительно, на внутренней станции (экранчике) нет веб-интерфейса, а якобы снять и положить “трубку” при звонке с домофона можно только с внутренней станции, то есть управлять ей можно только с sdk.

Но я планирую попробовать без аддона и чисто на wb. Правда, я не знаю, получится ли и сколько времени это займет

Но если найдете готовое решение, пишите пожалуйста

а открывать замок можно прям щас есичо

defineVirtualDevice('out_frontwicket_doorbell', {
    title: "Front Wicket Doorbell",
    cells: {
        open: {
            title: "Open Door",
            type: "pushbutton",
            value: false
        }
    }

});

defineRule({
    whenChanged: "out_frontwicket_doorbell/open",
    then: function () {
        runShellCommand("curl --digest -u " + user + ":" + password +
                " -X PUT -d '<RemoteControlDoor><cmd>open</cmd></RemoteControlDoor>' http://" +
                host + "/ISAPI/AccessControl/RemoteControl/door/1");

    }
});

3 лайка

О! Спасибо большое. Это уже половина того что мне нужно было)))
В правиле описывается создание виртуальной кнопки и функция - при изменении её value - выполнить runShellCommand("curl…
А мне необходимо создать виртуальное устройство и в первую очередь получить его value с помощью runShellCommand("curl…, а уже потом выполнить функцию активации реле дополнительного модуля контроллера.

пока не умею получать данные с домофона, все время возвращает какую-то ерунду (в том числе статус звонка у меня все время “idle”, даже если кто-то звонит). может, вообще не получится

вот так опрашивал раз в секунду, отчего у меня домофон уходил в кому, ломался веб-интерфейс и приходилось удалять куки, чтобы он ожил:

function checkStatusRecur() {
    runShellCommand('curl --digest -u ' + user + ':' + password + ' http://' + host + '/ISAPI/VideoIntercom/callStatus?format=json', {
        captureOutput: true,
        captureErrorOutput: true,
        exitCallback: function (exitCode, capturedOutput, capturedError) {
            if (exitCode === 0) {
                var input = JSON.parse(capturedOutput);
                dev['out_frontwicket_doorbell/status'] = input.CallStatus.status;
            } else {
                log.warning("Wicket check status error: " + capturedError);
            }
            lastRan = Date.now();
            setTimeout(function () {
                checkStatusRecur();
            }, 1000);
        }
    });
}

setInterval(function () {

    if (Date.now() - lastRan > 1000 * 60) {
        log.warning("Wicket check status ran too long ago, reinstating");
        //checkStatusRecur();
    }

}, 1000 * 60); // every minute

клалося сюда в виртуальное устройство:

    cells: {
        status: {
            title: "Call Status",
            type: "text",
            value: "onCall",
            readonly: true,
            enum: {
                idle: {en: 'Idle', ru: 'Ничего'},
                ring: {en: 'Ringing', ru: 'Звонок'},
                onCall: {en: 'Talking', ru: 'Разговор'}
            }
        },

короче непросто это. не работает так.

В общем один очень хороший человек написал полностью код по задаче:
теперь при звонке в домофон задействуется реле на контроллере для физического звонка в квартире, а после открытия двери (событии от геркона двери) - отбой звонка и выключение реле.

var HIK_HOST = “192.168.1.ххх”;
var HIK_USER = “admin”;
var HIK_PASSWORD = “ххххххххххх”;

var DOOR_SENSOR_DEVICE = “wb-gpio”;
var DOOR_SENSOR_CONTROL = “W1_IN”;

var BELL_RELAY_DEVICE = “wb-gpio”;
var BELL_RELAY_CONTROL = “MOD1_OUT1”;

var VIRTUAL_DEVICE_ID = “hikvision_intercom”;
var POLLING_INTERVAL_SEC = 2;

var DOOR_SENSOR_TOPIC = DOOR_SENSOR_DEVICE + “/” + DOOR_SENSOR_CONTROL;
var BELL_RELAY_TOPIC = BELL_RELAY_DEVICE + “/” + BELL_RELAY_CONTROL;

defineVirtualDevice(VIRTUAL_DEVICE_ID, {
title: “Домофон Hikvision”,
cells: {
callStatus: {
title: “Статус вызова”,
type: “text”,
value: “idle”,
readonly: true
},
openDoor: {
title: “Открыть дверь”,
type: “pushbutton”
},
rejectCall: {
title: “Сбросить вызов”,
type: “pushbutton”
}
}
});

function callHikvisionOpenDoor() {
log(“Hikvision: Отправка команды ‘Открыть дверь’ через VideoIntercom API”);
var xmlPayload = ‘unlock’;
var command = “curl --silent --digest -u " + HIK_USER + “:” + HIK_PASSWORD +
" -X PUT -d '” + xmlPayload + “'” +
" http://" + HIK_HOST + “/ISAPI/VideoIntercom/remoteOpenDoor”;
runShellCommand(command, {
exitCallback: function(exitCode, output) {
if (exitCode !== 0) {
log.error("Hikvision: Ошибка при открытии двери! Код: " + exitCode + " Ответ: " + output);
}
}
});
}

function callHikvisionReject() {
log(“Hikvision: Отправка команды ‘Сбросить вызов’”);
var command = “curl --silent --digest -u " + HIK_USER + “:” + HIK_PASSWORD +
" -X PUT -H ‘Content-Type: application/json’ -d ‘{"CallSignal":{"cmdType":"reject"}}’” +
" http://" + HIK_HOST + “/ISAPI/VideoIntercom/callSignal?format=json”;
runShellCommand(command);
}

defineRule(“poll_hikvision_status”, {
when: cron(“*/” + POLLING_INTERVAL_SEC + " * * * * *"),
then: function () {
var command = “curl --silent --digest -u " + HIK_USER + “:” + HIK_PASSWORD +
" http://” + HIK_HOST + “/ISAPI/VideoIntercom/callStatus?format=json”;
runShellCommand(command, {
captureOutput: true,
exitCallback: function (exitCode, output) {
if (exitCode === 0 && output) {
try {
var response = JSON.parse(output);
if (response && response.CallStatus && response.CallStatus.status) {
var currentStatus = response.CallStatus.status;
if (dev[VIRTUAL_DEVICE_ID + “/callStatus”] !== currentStatus) {
dev[VIRTUAL_DEVICE_ID + “/callStatus”] = currentStatus;
}
}
} catch (e) {
log.error("Hikvision: Не удалось разобрать ответ JSON: " + output);
}
} else {
log.error("Hikvision: Ошибка опроса статуса. Код выхода: " + exitCode + ", Ответ: " + output);
if (dev[VIRTUAL_DEVICE_ID + “/callStatus”] !== “error”) {
dev[VIRTUAL_DEVICE_ID + “/callStatus”] = “error”;
}
}
}
});
}
});

defineRule(“handle_doorbell_logic”, {
whenChanged: [VIRTUAL_DEVICE_ID + “/callStatus”, DOOR_SENSOR_TOPIC],
then: function (newValue, deviceName, cellName) {
var isRinging = (dev[VIRTUAL_DEVICE_ID + “/callStatus”] === “ring”);
// !!! ВОТ ОНО, ИСПРАВЛЕНИЕ !!!
var isDoorOpen = (dev[DOOR_SENSOR_TOPIC] == false);
var isBellRelayOn = (dev[BELL_RELAY_TOPIC] == true);

    log("Логика звонка: Статус=" + dev[VIRTUAL_DEVICE_ID + "/callStatus"] + ", Дверь=" + (isDoorOpen ? "Открыта" : "Закрыта") + ", Реле=" + (isBellRelayOn ? "Вкл" : "Выкл"));
    
    if (isRinging && !isDoorOpen && !isBellRelayOn) {
        log("-> Сценарий 1: Входящий вызов. Включаю реле звонка.");
        dev[BELL_RELAY_TOPIC] = true;
        return;
    }
    if (isDoorOpen && isBellRelayOn) {
        log("-> Сценарий 2: Дверь открыта во время звонка. Выключаю реле и сбрасываю вызов.");
        dev[BELL_RELAY_TOPIC] = false;
        callHikvisionReject();
        return;
    }
    if (!isRinging && isBellRelayOn) {
        log("-> Сценарий 3: Вызов завершен. Выключаю реле звонка.");
        dev[BELL_RELAY_TOPIC] = false;
    }
}

});

defineRule(“manual_intercom_control”, {
whenChanged: [
VIRTUAL_DEVICE_ID + “/openDoor”,
VIRTUAL_DEVICE_ID + “/rejectCall”
],
then: function (newValue, devName, cellName) {
switch(cellName) {
case “openDoor”:
callHikvisionOpenDoor();
break;
case “rejectCall”:
callHikvisionReject();
break;
}
}
});