/** Hikvision with status to Hubitat is inspired on matthewpetro project, * which I modded to deliver HTTP POST command on ISAPI from Hikvision doorbells * * The app needs the IP from intercom and its password to give HTTP POST command. * To deliver lock/unlock state, you can select a Contact Sensor. If you don't need this status, use the lighter drive: * https://github.com/VCTGomes/Hikvision-Door-Control-to-Hubitat */ definition( name: 'Hikvision with Status', namespace: 'vctgomes', author: 'Vitor Gomes', description: 'Combine a contact sensor with your Hikvision Doorbell to unlock your door', category: 'Convenience', importUrl: 'https://raw.githubusercontent.com/VCTGomes/Hikvision-with-Status-to-Hubitat/main/hik-door-app.groovy', iconUrl: '', iconX2Url: '' ) preferences { section('Devices') { input("ipAddress", "text", title: "IP Address", required: true) input("password", "password", title: "Password", required: true) input 'closedContactSensor', 'capability.contactSensor', title: 'Contact sensor that detects lock closed state', required: true } section('Logging') { input('debug', 'bool', title: 'Enable debug logging?', required: true, defaultValue: false) } } def installed() { logDebug 'Installed' updated() def deviceNetworkId = "${app.id}-simulated-lock-device" def lockDevice = addChildDevice('vctgomes', 'Hikvision Locker', deviceNetworkId, null, [label: 'Hikvision Locker']) state.deviceNetworkId = deviceNetworkId subscribe(lockDevice, 'lock', 'lockChangeHandler') } def updated() { logDebug 'Updated' unsubscribe('closedSensorHandler') unsubscribe('openSensorHandler') subscribe(closedContactSensor, 'contact', 'closedSensorHandler') if (null != openContactSensor) { subscribe(openContactSensor, 'contact', 'openSensorHandler') } } def uninstalled() { logDebug 'Uninstalled' unsubscribe(closedContactSensor) if (null != openContactSensor) { unsubscribe(openContactSensor) } deleteChildDevice(getChildDevice(state.deviceNetworkId)) } def lockChangeHandler(event) { logDebug "lockChangeHandler() called: ${event.name} ${event.value}" def actualLockState = actualLockState() logDebug "actualLockState: ${actualLockState}" if (event.value == 'unlocking' && actualLockState == 'unlocked') { activateLock() } else if (event.value == 'locking' && actualLockState == 'locked') { disableLock() } } private syncLockState() { logDebug 'syncLockState()' def lockDevice = getChildDevice(state.deviceNetworkId) def lockDeviceState = lockDevice.currentValue('lock') if (!['locked', 'unlocked'].contains(lockDeviceState)) { def actualLockState = actualLockState() if (['locked', 'unlocked'].contains(actualLockState)) { lockDevice.lockChangeHandler(actualLockState) } } } private actualLockState() { if (null != openContactSensor) { if (openContactSensor.currentValue('contact') == 'closed') { return 'locked' } else if (closedContactSensor.currentValue('contact') == 'closed') { return 'unlocked' } else { return 'unknown' } } else { if (closedContactSensor.currentValue('contact') == 'closed') { return 'unlocked' } else { return 'locked' } } } private activateLock() { logDebug 'activateLock()' sendCommand("open") } private disableLock() { logDebug 'disableLock()' sendCommand("open") } def closedSensorHandler(event) { logDebug "closedSensorHandler() called: ${event.name} ${event.value}" def lockDevice = getChildDevice(state.deviceNetworkId) def lockDeviceState = lockDevice.currentValue('lock') if (event.value == 'open' && lockDeviceState != 'openning') { lockDevice.lockChangeHandler('unlocked') } else if (event.value == 'closed') { lockDevice.lockChangeHandler('locked') } } // HTTP POST on Hikvision Doorbell def sendCommand(String cmd) { def headers = [ 'Content-Type': 'application/xml', ] def data = "<RemoteControlDoor><cmd>${cmd}</cmd></RemoteControlDoor>" def params = [ uri: "http://admin:${settings.password}@${settings.ipAddress}/ISAPI/AccessControl/RemoteControl/door/1", requestContentType: 'application/xml', headers: headers, body: data ] try { httpPut(params) { response -> log.debug "Server response: ${response.status}" if (response.status == 200) { log.debug "Command sent" } else { log.error "Command faild: ${response.status}" } } } catch (e) { log.error "Fail to sent command: ${e}" } } private void logDebug(message) { if (debug) log.debug message }