const hap = require("hap-nodejs"); const pysh = require("python-shell"); const Accessory = hap.Accessory; const Characteristic = hap.Characteristic; const CharacteristicEventTypes = hap.CharacteristicEventTypes; const AccessoryEventTypes = hap.AccessoryEventTypes; const Service = hap.Service; const uuid = hap.uuid; const accessoryUuid = hap.uuid.generate("hap.accessories.doorbell-lock"); const accessory = new Accessory("Doorbell and Lock", accessoryUuid); const doorbellService = new Service.Doorbell("Doorbell"); const lockService = new Service.LockMechanism("Lock"); const lockTargetStateCharacteristic = lockService.getCharacteristic(Characteristic.LockTargetState); const lockCurrentStateCharacteristic = lockService.getCharacteristic(Characteristic.LockCurrentState); const doorbellSwitchCharacteristic = doorbellService.getCharacteristic(Characteristic.ProgrammableSwitchEvent); function Door() { this.lockTimeout = 10000; // milliseconds - 10 sec this.pyshell = new pysh.PythonShell("doord.py", { mode: "text", pythonPath: "/usr/bin/python3", pythonOptions: ["-u"], scriptPath: "python/" }); this.lock = () => { console.log("locking door"); this.pyshell.send("lock"); }; this.unlock = () => { console.log("unlocking door"); this.pyshell.send("unlock"); }; this.identify = () => { console.log("identify door"); }; this.listenDoorbell = (doorbellOnCallback, doorbellOffCallback) => { this.pyshell.on("message", (message) => { console.log(message); switch(message) { case "doorbell on": doorbellOnCallback(); break; case "doorbell off": doorbellOffCallback(); break; } }); }; } const door = new Door(); function setDoorTargetState(value) { switch(value) { case Characteristic.LockTargetState.UNSECURED: unlockDoor(); break; case Characteristic.LockTargetState.SECURED: lockDoor(); break; } } function unlockDoor() { door.unlock(); lockCurrentStateCharacteristic.setValue(Characteristic.LockCurrentState.UNSECURED); scheduleUnlockTimeout(); } function lockDoor() { door.lock(); lockCurrentStateCharacteristic.setValue(Characteristic.LockCurrentState.SECURED); } function scheduleUnlockTimeout() { setTimeout(() => { console.log("unlock timeout door"); lockTargetStateCharacteristic.setValue(Characteristic.LockTargetState.SECURED); }, door.lockTimeout); } door.listenDoorbell( () => { doorbellSwitchCharacteristic.setValue(Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); }, () => {} ); // set initial state lockTargetStateCharacteristic.setValue(Characteristic.LockTargetState.SECURED); lockCurrentStateCharacteristic.setValue(Characteristic.LockCurrentState.SECURED); lockTargetStateCharacteristic.on(CharacteristicEventTypes.SET, (value, callback) => { setDoorTargetState(value); callback(); }); accessory.on(AccessoryEventTypes.IDENTIFY, (paired, callback) => { door.identify(); callback(); }); accessory.addService(doorbellService); accessory.addService(lockService); // pseudo-services accessory.addService(Service.CameraRTPStreamManagement, "Pseudo-Camera"); accessory.addService(Service.Speaker, "Pseudo-Speaker"); accessory.addService(Service.Microphone, "Pseudo-Microphone"); accessory.getService(Service.AccessoryInformation) .setCharacteristic(Characteristic.Manufacturer, "Raspberry Pi") .setCharacteristic(Characteristic.Model, "Zero W") .setCharacteristic(Characteristic.SerialNumber, "A1S2NASF88EW"); accessory.publish({ username: "C1:5D:3A:EA:54:AB", pincode: "031-45-154", category: hap.Categories.DOOR_LOCK }); console.log("Accessory initialized");