npmjs-package/src/door.js
author Luke Hoersten <luke@hoersten.org>
Mon, 16 Nov 2020 13:35:10 -0600
changeset 30 14c6ce90a512
parent 29 04bc5820407b
permissions -rw-r--r--
Added network dep to systemd.

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");