Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
const fs = require("fs")
const path = require('path')
const utils = require('./lib/utils')
const DeviceDetector = require('./lib/device-detector')

const version = require('./package.json').version;
const model = utils.model()
Expand All @@ -14,13 +15,25 @@ const global = {
// Use the higher resolution video stream
'highResVideo': model !== 'c100x'
}
const doorUnlock = {
// Default behaviour is device ID 20, if you need more, add them to additionalLocks in config.json
openSequence: '*8*19*20##' ,
closeSequence: '*8*20*20##',
};

const additionalLocks = {}
// Expose locks marked as invisible (visible: 0) in mymodules to HomeKit
let exposeInvisibleLocks = false

// Auto-detect locks from mymodules file
const detector = DeviceDetector.create()
const detectedLocks = detector.detectLocks()

const locks = {}
for (const lock of detectedLocks) {
const lockKey = `lock-${lock.deviceId}`
locks[lockKey] = {
openSequence: `*8*19*${lock.deviceId}##`,
closeSequence: `*8*20*${lock.deviceId}##`,
name: `${lock.name} ${lock.buttonId}`,
visible: lock.visible,
deviceId: lock.deviceId
}
}

const mqtt_config = {
// Set to enable to publish events to an external MQTT server
Expand Down Expand Up @@ -87,8 +100,9 @@ if( detectedPath ) {
console.log(`FOUND config.json file at '${detectedPath}' and overriding the values from it.\r\n`)
const config = JSON.parse( fs.readFileSync(detectedPath) )
overrideAndPrintValue( "global", global, config.global)
overrideAndPrintValue( "doorUnlock", doorUnlock, config.doorUnlock)
overrideAndPrintValue( "additionalLocks", additionalLocks, config.additionalLocks)
if (config.exposeInvisibleLocks !== undefined) {
exposeInvisibleLocks = config.exposeInvisibleLocks
}
overrideAndPrintValue( "mqtt_config", mqtt_config, config.mqtt_config)
overrideAndPrintValue( "sip", sip, config.sip)
overrideAndPrintValue( "homeassistant", homeassistant, config.homeassistant)
Expand All @@ -104,9 +118,9 @@ if( global.highResVideo && utils.model() === 'c100x' ) {
}

console.log(`============================== final config =====================================
\x1b[33m${JSON.stringify( { global, doorUnlock, additionalLocks, mqtt_config, sip }, null, 2 )}\x1b[0m
\x1b[33m${JSON.stringify( { global, exposeInvisibleLocks, locks, mqtt_config, sip }, null, 2 )}\x1b[0m
=================================================================================`)

module.exports = {
doorUnlock, additionalLocks, mqtt_config, global, sip, homeassistant, version
locks, mqtt_config, global, exposeInvisibleLocks, sip, homeassistant, version
}
8 changes: 1 addition & 7 deletions config.json.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
{
"ignoredUnknownValue": {},
"doorUnlock": {
"openSequence" : "*8*XX*20##"
},
"additionalLocks": {
"back-door": { "openSequence": "*8*19*21##", "closeSequence": "*8*20*21##" },
"side-door": { "openSequence": "*8*19*22##", "closeSequence": "*8*20*22##" }
},
"exposeInvisibleLocks": false,
"mqtt_config": {
"enabled" : true,
"host": "192.168.0.2"
Expand Down
32 changes: 18 additions & 14 deletions controller-homekit.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,29 +60,33 @@ base.eventbus.on('doorbell:pressed', () => {
base.eventbus.emit('homekit:pressed')
})

const locks = ["default", ...Object.keys(config.additionalLocks)]
for (const lockKey in config.locks) {
const lock = config.locks[lockKey]

if (lock.visible === 0 && !config.exposeInvisibleLocks) {
continue
}

for (const lock of locks) {
const doorHomekitSettings = filestore.read(lock, () => { return { 'displayName': lock, 'hidden': false } })
const doorHomekitSettings = filestore.read(lockKey, () => {
return { 'displayName': lock.name || lockKey, 'hidden': false }
})

if( doorHomekitSettings && doorHomekitSettings.hidden )
if (doorHomekitSettings && doorHomekitSettings.hidden) {
continue

let door = config.additionalLocks[lock];
const { openSequence, closeSequence } = lock === "default" ? { openSequence: config.doorUnlock.openSequence, closeSequence: config.doorUnlock.closeSequence } : { openSequence: door.openSequence, closeSequence: door.closeSequence }
}

const { openSequence, closeSequence } = lock
base.eventbus
.on('lock:unlocked:' + openSequence, () => {
//console.log('received lock:unlocked:' + openSequence)
base.eventbus.emit('homekit:locked:' + lock, false)
base.eventbus.emit('homekit:locked:' + lockKey, false)
}).on('lock:locked:' + closeSequence, () => {
//console.log('received lock:locked:' + closeSequence)
base.eventbus.emit('homekit:locked:' + lock, true)
base.eventbus.emit('homekit:locked:' + lockKey, true)
})

homekitManager.addLock( lock, doorHomekitSettings.displayName )
.unlocked( () => {
homekitManager.addLock(lockKey, doorHomekitSettings.displayName)
.unlocked(() => {
openwebnet.run("doorUnlock", openSequence, closeSequence)
} )
})
}

homekitManager.addSwitch('Muted' )
Expand Down
26 changes: 11 additions & 15 deletions lib/apis/door-unlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,21 @@ module.exports = class Api {

handle(request, response, url, q) {
response.write("<pre>")
response.write("<a href='./unlock?id=default'>Default</a><br/>")
if( config.additionalLocks )
{
for( const lock in config.additionalLocks )
{
response.write("<a href='./unlock?id=" + lock + "'>" + lock + "</a><br/>")
if (config.locks) {
for (const lockKey in config.locks) {
const lock = config.locks[lockKey]
const displayName = lock.name || lockKey
response.write("<a href='./unlock?id=" + lockKey + "'>" + displayName + "</a><br/>")
}
}
response.write("</pre>")
if( q.id ) {
let door = config.additionalLocks[q.id];
if( door ) {
openwebnet.run("doorUnlock", door.openSequence, door.closeSequence )
response.write("Opened lock: " + q.id + "<br/>")
} else if( q.id === "default" ) {
openwebnet.run("doorUnlock", config.doorUnlock.openSequence, config.doorUnlock.closeSequence)
response.write("Opened default lock<br/>")
if (q.id) {
const lock = config.locks[q.id]
if (lock) {
openwebnet.run("doorUnlock", lock.openSequence, lock.closeSequence)
response.write("Opened lock: " + (lock.name || q.id) + "<br/>")
} else {
console.error("Door with id: " + q.id + " not found.")
console.error("Lock with id: " + q.id + " not found.")
}
}
}
Expand Down
101 changes: 101 additions & 0 deletions lib/device-detector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const fs = require("fs")
const filestore = require('../json-store')

const C100X_MODULES = "/home/bticino/cfg/extra/.bt_eliot/mymodules"

class DeviceDetector {
constructor(mymodulesPath = C100X_MODULES) {
this.mymodulesPath = mymodulesPath
this.devices = null
}

static create(mymodulesPath) {
return new DeviceDetector(mymodulesPath)
}

_loadDevices() {
if (this.devices !== null) {
return this.devices
}

if (!fs.existsSync(this.mymodulesPath)) {
console.log(`[DeviceDetector] mymodules file not found at ${this.mymodulesPath}`)
this.devices = []
return this.devices
}

try {
const store = filestore.create(this.mymodulesPath)
this.devices = store.data.modules || []
console.log(`[DeviceDetector] Loaded ${this.devices.length} devices from ${this.mymodulesPath}`)
} catch (e) {
console.error(`[DeviceDetector] Error reading mymodules file: ${e.message}`)
this.devices = []
}

return this.devices
}

detectCameras() {
const devices = this._loadDevices()
const cameras = devices.filter(m =>
m.system === 'videodoorentry' &&
m.deviceType === 'EU' &&
m.privateAddress?.addressValues?.length > 0
).map(m => {
const addressValue = m.privateAddress.addressValues.find(a => a.name === 'address')
return {
id: m.id,
deviceId: addressValue?.value,
name: m.name,
buttonId: m.privateAddress.buttonId,
visible: m.privateAddress.visible
}
}).filter(c => c.deviceId !== undefined)

console.log(`[DeviceDetector] Detected ${cameras.length} camera(s):`, cameras.map(c => `${c.name} (ID: ${c.deviceId})`).join(', '))
return cameras
}

detectLocks() {
const devices = this._loadDevices()
const locks = devices.filter(m =>
m.system === 'automation' &&
m.device === 'lock' &&
m.privateAddress?.addressValues?.length > 0
).map(m => {
const addressValue = m.privateAddress.addressValues.find(a => a.name === 'address')
return {
id: m.id,
deviceId: addressValue?.value,
name: m.name,
buttonId: m.privateAddress.buttonId,
visible: m.privateAddress.visible
}
}).filter(l => l.deviceId !== undefined)

console.log(`[DeviceDetector] Detected ${locks.length} lock(s):`, locks.map(l => `${l.name} (ID: ${l.deviceId})`).join(', '))
return locks
}

detectInternalUnit() {
const devices = this._loadDevices()
const internalUnits = devices.filter(m =>
m.system === 'videodoorentry' &&
m.deviceType === 'IU'
).map(m => {
return {
id: m.id,
deviceId: m.privateAddress?.addressValues?.find(a => a.name === 'address')?.value,
EUaddress: m.EUaddress
}
})

if (internalUnits.length > 0) {
console.log(`[DeviceDetector] Detected ${internalUnits.length} internal unit(s)`)
}
return internalUnits
}
}

module.exports = DeviceDetector
2 changes: 1 addition & 1 deletion lib/handlers/openwebnet-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class OpenwebnetHandler {
}, 100);
break
case msg.startsWith('*8*1#1#4#') ? msg : undefined:
//this.#eventbus.emit('doorbell:pressed', msg)
this.#eventbus.emit('doorbell:pressed', msg)
this.#mqtt.dispatchMessage(msg)
this.#mqtt.dispatchDoorbellEvent(msg)
this.#registry.dispatchEvent('pressed')
Expand Down
12 changes: 11 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.