nfarina / homebridge-dummy

Dummy switches for Homebridge: https://github.com/nfarina/homebridge
277 stars 83 forks source link

Big update #84

Open mkz212 opened 1 year ago

mkz212 commented 1 year ago

If You want new functions:

replace config.schema.json to:

{
    "pluginAlias": "DummySwitch",
    "pluginType": "accessory",
    "singular": false,
    "schema": {
        "type": "object",
        "properties": {
            "name": {
                "title": "Name",
                "description": "Select name of the sensor.",
                "type": "string",
                "required": true
            },
            "type": {
                "title": "Type",
                "description": "Select type of the sensor.",
                "type": "string",
                "default": "switch",
                "required": true,
                "oneOf": [
                  { "title": "Switch", "enum": ["switch"] },
                  { "title": "Dimmer", "enum": ["dimmer"] },
                  { "title": "Blind", "enum": ["blind"] },
                  { "title": "Motion", "enum": ["motion"] },
                  { "title": "Lock", "enum": ["lock"] },
                  { "title": "Contact", "enum": ["contact"] },
                  { "title": "Security", "enum": ["security"] },
                  { "title": "Thermostat", "enum": ["thermostat"] }
                ]
            },
            "startup": {
                "title": "After startup",
                "description": "Select sensor state after startup.",
                "type": "string",
                "default": "off",
                "required": true,
                "oneOf": [
                  { "title": "Set Off", "enum": ["off"] },
                  { "title": "Set On", "enum": ["on"] },
                  { "title": "Set Value", "enum": ["setValue"] },
                  { "title": "Last used value", "enum": ["last"] }
                ]
            },      
            "startupValue": {
                "title": "Value",
                "type": "integer", 
                "default": 0,
                "maximum": 100,
                "description": "Override starting value. 0 = disabled.",
                "condition": {
                  "functionBody": "return ((model.type === 'dimmer' || model.type === 'blind') && (model.startup === 'setValue'));"
                }
            },
            "timerEnabled": {
                "title": "Enable timer",
                "type": "boolean",
                "default": true,
                "description": "Disable timer if sensor should remain it's state."
            },
            "resettable": {
                "title": "Resettable",
                "type": "boolean",
                "default": false,
                "description": "Reset timer on each activity.",
                "condition": {
                  "functionBody": "return model.timerEnabled === true;"
                }
            },
            "time": {
                "title": "Time",
                "type": "number",
                "default": 1,
                "description": "Sensor will change state after this time.",
                "condition": {
                  "functionBody": "return model.timerEnabled === true;"
                }
            },
            "timeUnit": {
                "title": "Time Unit",
                "description": "",
                "type": "string",
                "default": "seconds",
                "required": true,
                "oneOf": [
                  { "title": "Miliseconds", "enum": ["miliseconds"] },
                  { "title": "Seconds", "enum": ["seconds"] },
                  { "title": "Minutes", "enum": ["minutes"] },
                  { "title": "Hours", "enum": ["hours"] },
                  { "title": "Days", "enum": ["days"] }
                ],
                "condition": {
                  "functionBody": "return model.timerEnabled === true;"
                }
            },
            "random": {
                "title": "Random",
                "type": "boolean",
                "default": false,
                "description": "Randomize the time.",
                "condition": {
                  "functionBody": "return model.timerEnabled === true;"
                }
            },
            "reverse": {
                "title": "Reverse",
                "type": "boolean",
                "default": false,
                "description": "Reverse sensor operation.",
                "condition": {
                  "functionBody": "return model.timerEnabled === true;"
                }
            },
            "disableLogging": {
                "title": "DisableLogging",
                "type": "boolean",
                "default": false,
                "description": "No state change information (On/Off) will be logged."
            }

        }
    },
    "layout": [     
        {
            "type": "fieldset",
            "title": "General",
            "description": "",
            "expandable": false,
            "expanded": true,
            "items": [ 
                {
                    "type": "flex",
                    "flex-flow": "row wrap",
                    "items": ["name", "type"]
                }

            ]
        },
        {
            "type": "fieldset",
            "title": "After start",
            "description": "",
            "expandable": false,
            "expanded": true,
            "items": [  
                {
                    "type": "flex",
                    "flex-flow": "row wrap",
                    "items": ["startup", "startupValue"]
                }     

            ]
        },
        {
            "type": "fieldset",
            "title": "Timer",
            "description": "",
            "expandable": false,
            "expanded": true,
            "items": [        
                {
                    "type": "flex",
                    "flex-flow": "row wrap",
                    "items": ["timerEnabled", "resettable"]
                },

                {
                    "type": "flex",
                    "flex-flow": "row wrap",
                    "items": ["time", "timeUnit"]
                },

                {
                    "type": "flex",
                    "flex-flow": "row wrap",
                    "items": ["random", "reverse"]
                }     
            ]
        }, 
        {
            "type": "fieldset",
            "title": "Logs",
            "description": "",
            "expandable": false,
            "expanded": true,
            "items": [
                {
                    "type": "flex",
                    "flex-flow": "row wrap",
                    "items": ["disableLogging", ""]
                }
            ]
        }
    ]
}

and replace index.js to:


"use strict";

var Service, Characteristic, HomebridgeAPI;
const { HomebridgeDummyVersion } = require('./package.json');

module.exports = function(homebridge) {
    Service = homebridge.hap.Service;
    Characteristic = homebridge.hap.Characteristic;
    HomebridgeAPI = homebridge;
    homebridge.registerAccessory("homebridge-dummy", "DummySwitch", DummySwitch);
}

function DummySwitch(log, config) {
    this.log = log;
    this.name = config.name;
    this.type = config.type;
    this.startup = config.startup;
    this.startupValue = config.startupValue ? config.startupValue : false;
    this.timerEnabled= config.timerEnabled; 
    this.reverse = config.reverse;
    this.time = config.time ? config.time : 1000;       
    this.timeUnit = config.timeUnit;    
    this.resettable = config.resettable;
    this.timer = null;
    this.random = config.random;
    this.disableLogging = config.disableLogging;

    if (this.type == 'switch') {
        this._service = new Service.Switch(this.name);
        this.modelString = "Dummy Switch";
    }
    else if (this.type == 'dimmer') {
        this._service = new Service.Lightbulb(this.name);
        this.modelString = "Dummy Dimmer";
    } 
    else if (this.type == 'blind') {
        this._service = new Service.WindowCovering(this.name);
        this.modelString = "Dummy Blind";
    } 
    else if (this.type == 'motion') {
        this._service = new Service.MotionSensor(this.name);
        this.modelString = "Dummy Motion";
    } 
    else if (this.type == 'lock') {
        this._service = new Service.LockMechanism(this.name);
        this.modelString = "Dummy Lock";
    } 
    else if (this.type == 'garage') {
        this._service = new Service.GarageDoorOpener(this.name);
        this.modelString = "Dummy Garage";
    } 
    else if (this.type == 'contact') {
        this._service = new Service.ContactSensor(this.name);
        this.modelString = "Dummy Contact";
    } 
    else if (this.type == 'security') {
        this._service = new Service.SecuritySystem(this.name);
        this.modelString = "Dummy Security";
    } 
    else if (this.type == 'thermostat') {
        this._service = new Service.Thermostat(this.name);
        this.modelString = "Dummy Thermostat";
    } 

    this.informationService = new Service.AccessoryInformation();
    this.informationService
        .setCharacteristic(Characteristic.Manufacturer, 'Homebridge')
        .setCharacteristic(Characteristic.Model, this.modelString)
        .setCharacteristic(Characteristic.FirmwareRevision, HomebridgeDummyVersion)
        .setCharacteristic(Characteristic.SerialNumber, 'Dummy-' + this.name.replace(/\s/g, '-'));

    this.cacheDirectory = HomebridgeAPI.user.persistPath();
    this.storage = require('node-persist');
    this.storage.initSync({dir:this.cacheDirectory, forgiveParseErrors: true});

    if (this.type == 'switch') {
        this._service.getCharacteristic(Characteristic.On)
        .on('set', this._setValue.bind(this));
    }
    else if (this.type == 'dimmer') {
        this._service.getCharacteristic(Characteristic.On)
            .on('set', this._setValue.bind(this));

        this._service.getCharacteristic(Characteristic.Brightness)
            .on('set', this._setValue.bind(this));      
    }
    else if (this.type == 'blind') {
        this._service.getCharacteristic(Characteristic.TargetPosition)
            .on('set', this._setValue.bind(this));
    }
    else if (this.type == 'motion') {
        this._service.getCharacteristic(Characteristic.MotionDetected)
            .on('set', this._setValue.bind(this));
    }
    else if (this.type == 'lock') {
        this._service.getCharacteristic(Characteristic.LockTargetState)
            .on('set', this._setValue.bind(this));
    }
    else if (this.type == 'garage') {
        this._service.getCharacteristic(Characteristic.TargetDoorState)
            .on('set', this._setValue.bind(this));
    }
    else if (this.type == 'contact') {
        this._service.getCharacteristic(Characteristic.ContactSensorState)
            .on('set', this._setValue.bind(this));
    }
    else if (this.type == 'security') {
        this._service.getCharacteristic(Characteristic.SecuritySystemTargetState)
            .on('set', this._setValue.bind(this));
    }
    else if (this.type == 'thermostat') {
        this._service.getCharacteristic(Characteristic.TargetHeatingCoolingState)
            .on('set', this._setValue.bind(this));

        this._service.getCharacteristic(Characteristic.TargetTemperature)
            .on('set', this._setValue.bind(this));
    }

    if (this.startup == 'on') {

        if (this.type == 'switch') {
            this._service.setCharacteristic(Characteristic.On, true);
        }
        else if (this.type == 'dimmer') {       
            this._service.setCharacteristic(Characteristic.On, true); 
            this._service.setCharacteristic(Characteristic.Brightness, 100);
        }       
        else if (this.type == 'blind') {
            this._service.setCharacteristic(Characteristic.TargetPosition, 100);
        }
        else if (this.type == 'motion') {
            this._service.setCharacteristic(Characteristic.MotionDetected, 1);
        }
        else if (this.type == 'lock') {
            this._service.setCharacteristic(Characteristic.LockTargetState, 0);
        }
        else if (this.type == 'garage') {
            this._service.setCharacteristic(Characteristic.TargetDoorState, 0);
        }
        else if (this.type == 'contact') {
            this._service.setCharacteristic(Characteristic.ContactSensorState, 'CONTACT_NOT_DETECTED');
        }
        else if (this.type == 'security') {
            this._service.setCharacteristic(Characteristic.SecuritySystemTargetState, 0);   
        }
        else if (this.type == 'thermostat') {
            this._service.setCharacteristic(Characteristic.TargetHeatingCoolingState, 3);
        }

    }

    else if (this.startup == 'setValue' && this.startupValue && (this.type == 'dimmer' || this.type == 'blind')) {

        if (this.type == 'dimmer') {
            this._service.setCharacteristic(Characteristic.On, true);
            this._service.setCharacteristic(Characteristic.Brightness, this.startupValue);
        }
        else if (this.type == 'blind') {
            this._service.setCharacteristic(Characteristic.TargetPosition, this.startupValue);
        }   
    }  

    else if (this.startup == 'last') {

        if (this.type == 'switch') {
            var cachedState = this.storage.getItemSync(this.name + 'on');
            if((cachedState === undefined) || (cachedState === false)) {
                this._service.setCharacteristic(Characteristic.On, false);
            } 
            else {
                this._service.setCharacteristic(Characteristic.On, true);
            }
        }

        else if (this.type == 'dimmer') {
            var cachedValue = this.storage.getItemSync(this.name + 'on');
            if ((cachedValue == undefined) || cachedValue == 0) {               
                this._service.setCharacteristic(Characteristic.On, false);
            } 
            else {              
                this._service.setCharacteristic(Characteristic.On, true);           
            }

            var cachedValue = this.storage.getItemSync(this.name + 'value');
            if ((cachedValue == undefined) || cachedValue == 0) {               
                this._service.setCharacteristic(Characteristic.Brightness, 0);
            } 
            else {              
                this._service.setCharacteristic(Characteristic.Brightness, cachedValue);            
            }

        }

        else if (this.type == 'blind') {
            var cachedValue = this.storage.getItemSync(this.name + 'value');
            if ((cachedValue == undefined) || cachedValue == 0) {               
                this._service.setCharacteristic(Characteristic.TargerPosition, 0);
            } 
            else {
                this._service.setCharacteristic(Characteristic.TargetPosition, cachedValue);
            }
        }

        else if (this.type == 'motion') {
            var cachedValue = this.storage.getItemSync(this.name + 'value');
            if ((cachedValue == undefined) || cachedValue == 0) {               
                this._service.setCharacteristic(Characteristic.MotionDetected, 0);
            } 
            else {
                this._service.setCharacteristic(Characteristic.MotionDetected, cachedValue);
            }
        }

        else if (this.type == 'lock') {
            var cachedValue = this.storage.getItemSync(this.name + 'value');
            if ((cachedValue == undefined) || cachedValue == 0) {               
                this._service.setCharacteristic(Characteristic.LockTargetState, 1);
            } 
            else {
                this._service.setCharacteristic(Characteristic.LockTargetState, cachedValue);
            }
        }

        else if (this.type == 'garage') {
            var cachedValue = this.storage.getItemSync(this.name + 'value');
            if ((cachedValue == undefined) || cachedValue == 0) {               
                this._service.setCharacteristic(Characteristic.TargetDoorState, 1);
            } 
            else {
                this._service.setCharacteristic(Characteristic.TargetDoorState, cachedValue);
            }
        }

        else if (this.type == 'contact') {
            var cachedValue = this.storage.getItemSync(this.name + 'value');
            if ((cachedValue == undefined) || cachedValue == 0) {               
                this._service.setCharacteristic(Characteristic.ContactSensorState, 'CONTACT_DETECTED');
            } 
            else {
                this._service.setCharacteristic(Characteristic.ContactSensorState, cachedValue);
            }
        }

        else if (this.type == 'security') {
            var cachedValue = this.storage.getItemSync(this.name + 'value');
            if ((cachedValue == undefined) || cachedValue == 0) {               
                this._service.setCharacteristic(Characteristic.SecuritySystemTargetState, 0);
            } 
            else {
                this._service.setCharacteristic(Characteristic.SecuritySystemTargetState, cachedValue);
            }
        }

        else if (this.type == 'thermostat') {
            var cachedValue = this.storage.getItemSync(this.name + 'value');
            if ((cachedValue == undefined) || cachedValue == 0) {               
                this._service.setCharacteristic(Characteristic.TargetHeatingCoolingState, 0);
            } 
            else {
                this._service.setCharacteristic(Characteristic.TargetHeatingCoolingState, cachedValue);
            }

        }

    }

    else {

        if (this.type == 'switch') {        
            this._service.setCharacteristic(Characteristic.On, false);
        }       
        else if (this.type == 'motion') {
            this._service.setCharacteristic(Characteristic.MotionDetected, 0);
        }
        else if (this.type == 'lock') {
            this._service.setCharacteristic(Characteristic.LockTargetState, 1);
        }
        else if (this.type == 'garage') {
            this._service.setCharacteristic(Characteristic.TargetDoorState, 1);
        }
        else if (this.type == 'contact') {      
            this._service.setCharacteristic(Characteristic.ContactSensorState, 'CONTACT_DETECTED');
        }
        else if (this.type == 'security') {
            this._service.setCharacteristic(Characteristic.SecuritySystemTargetState, 3);   
        }
        else if (this.type == 'thermostat') {
            this._service.setCharacteristic(Characteristic.TargetHeatingCoolingState, 0);
        }
        else if (this.type == 'dimmer') {       
            this._service.setCharacteristic(Characteristic.On, false); 
            this._service.setCharacteristic(Characteristic.Brightness, 0);
        }
        else if (this.type == 'blind') {
            this._service.setCharacteristic(Characteristic.TargetPosition, 0);
        }

    }

    if ((this.type == 'thermostat') && (this.storage.getItemSync(this.name + 'value2') != undefined)) {

            this._service.setCharacteristic(Characteristic.TargetTemperature, cachedValue);

    }

}

DummySwitch.prototype.getServices = function() {
    return [this.informationService, this._service];
}

function randomize(time) {
    return Math.floor(Math.random() * (time + 1));
}

DummySwitch.prototype._setValue = function(value, callback) {

    if (!this.disableLogging) {

        if (value === true) {
            this.log("ON");
        }

        else if (value === false) {
            this.log("OFF");
        }

        else if ((this.type == 'lock') && (value == 1)) {
            this.log("Lock");
        }

        else if ((this.type == 'lock') && (value == 0)) {
            this.log("Unlock");
        }

        else if ((this.type == 'garage') && (value == 1)) {
            this.log("Open");
        }

        else if ((this.type == 'garage') && (value == 0)) {
            this.log("Close");
        }

        else if ((this.type == 'motion') && (value == 0)) {
            this.log("Motion not detected");
        }

        else if ((this.type == 'motion') && (value == 1)) {
            this.log("Motion detected");
        }

        else if ((this.type == 'contact') && (value == 'CONTACT_DETECTED')) {
            this.log("Contact detected");
        }

        else if ((this.type == 'contact') && (value == 'CONTACT_NOT_DETECTED')) {
            this.log("Contact not detected");
        }

        else if ((this.type == 'security') && (value == 0)) {
            this.log("Stay Arm");
        }

        else if ((this.type == 'security') && (value == 1)) {
            this.log("Away Arm");
        }

        else if ((this.type == 'security') && (value == 2)) {
            this.log("Night Arm");
        }

        else if ((this.type == 'security') && (value == 3)) {
            this.log("Disarm");
        }

        else if ((this.type == 'thermostat') && (value == 0)) {
            this.log("OFF");
        }

        else if ((this.type == 'thermostat') && (value == 1)) {
            this.log("Heat");
        }

        else if ((this.type == 'thermostat') && (value == 2)) {
            this.log("Cool");
        }

        else if ((this.type == 'thermostat') && (value == 3)) {
            this.log("Auto");
        }

        else {
            this.log(value);
        }

    }

    // Calculate Timer

    var delay = this.time;

    if (this.timeUnit == 'seconds') {
        delay = this.time * 1000;
    }
    else if (this.timeUnit == 'minutes') {
        delay = this.time * 60000;
    }
    else if (this.timeUnit == 'hours') {
        delay = this.time * 3600000;
    }
    else if (this.timeUnit == 'days') {
        delay = this.time * 86400000;
    }

    // Randomize Delay

    if (this.random) {
        delay = randomize(delay);
    } 

    // Clear Timer

    if (this.resettable) {
        clearTimeout(this.timer);

        if (!this.disableLogging) {
            this.log("Reset Timer");
        }

    }

    // Target to Curent value

    if (this.type == 'blind') {
        this._service.setCharacteristic(Characteristic.CurrentPosition, value);
    }

    else if (this.type == 'lock') {
        this._service.setCharacteristic(Characteristic.LockCurrentState, value);
    }

    else if (this.type == 'garage') {
        this._service.setCharacteristic(Characteristic.CurrentDoorState, value);
    }

    else if (this.type == 'security') {
        this._service.setCharacteristic(Characteristic.SecuritySystemCurrentState, value);
    }

    else if (this.type == 'thermostat') {

        if (value == 0 || value == 1 || value == 2) { 
            this._service.setCharacteristic(Characteristic.CurrentHeatingCoolingState, value);
        }
        else if (value == 3) {
            this._service.setCharacteristic(Characteristic.CurrentHeatingCoolingState, 1);
        }
        else {
            this._service.setCharacteristic(Characteristic.CurrentTemperature, value);
        }

    }

    // Set Timer
    if (this.timerEnabled) {    

        if (!this.reverse) {

            if ((this.type == 'switch') && (value === true)) {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.On, false);
                }.bind(this), delay);
            }
            else if ((this.type == 'dimmer') && (value === true)) {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.On, false);
                }.bind(this), delay);
            }
            else if ((this.type == 'dimmer') && (value != 0)) {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.Brightness, 0);
                }.bind(this), delay);
            }
            else if ((this.type == 'blind') && (value != 0)) {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.TargetPosition, 0);
                }.bind(this), delay);
            }
            else if ((this.type == 'motion') && (value == 1))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.MotionDetected, 0);
                }.bind(this), delay);
            }
            else if ((this.type == 'lock') && (value == 0))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.LockTargetState, 1);
                }.bind(this), delay);
            }
            else if ((this.type == 'garage') && (value == 0))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.TargetDoorState, 1);
                }.bind(this), delay);
            }
            else if ((this.type == 'contact') && (value == 'CONTACT_NOT_DETECTED'))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.ContactSensorState, 'CONTACT_DETECTED');
                }.bind(this), delay);
            }
            else if ((this.type == 'security') && (value != 3))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.SecuritySystemTargetState, 3);
                }.bind(this), delay);
            }
            else if ((this.type == 'thermostat') && (value != 0))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.TargetHeatingCoolingState, 0);
                }.bind(this), delay);
            }
        }

        else if (this.reverse) {

            if ((this.type == 'switch') && (value === false)) {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.On, true);
                }.bind(this), delay);
            }
            else if ((this.type == 'dimmer') && (value === false)) {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.On, true);
                }.bind(this), delay);
            }
            else if ((this.type == 'dimmer') && (value != 100)) {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.Brightness, 100);
                }.bind(this), delay);
            }
            else if ((this.type == 'blind') && (value != 100))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.TargetPosition, 100);
                }.bind(this), delay);
            }
            else if ((this.type == 'motion') && (value == 0))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.MotionDetected, 1);
                }.bind(this), delay);
            }
            else if ((this.type == 'lock') && (value == 1))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.LockTargetState, 0);
                }.bind(this), delay);
            }
            else if ((this.type == 'garage') && (value == 1))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.TargetDoorState, 0);
                }.bind(this), delay);
            }
            else if ((this.type == 'contact') && (value == 'CONTACT_DETECTED'))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.ContactSensorState, 'CONTACT_NOT_DETECTED');
                }.bind(this), delay);
            }
            else if ((this.type == 'security') && (value == 3))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.SecuritySystemTargetState, 0);
                }.bind(this), delay);
            }
            else if ((this.type == 'thermostat') && (value == 0))  {
                this.timer = setTimeout(function() {
                this._service.setCharacteristic(Characteristic.TargetHeatingCoolingState, 3);
                }.bind(this), delay);
            }
        }

        if ((!this.disableLogging) && (this.timer)) {
            if (!this.random){
                this.log("Set Timer: " + this.time + " " + this.timeUnit );
            }
            else {
                this.log("Setting random Timer: " + delay + " ms");
            }
        }

    }

    if (value === true || value === false) {

        this.storage.setItemSync(this.name + 'on', value);

    }

    else if ((this.type == 'thermostat') && (value > 3)) {

        this.storage.setItemSync(this.name + 'value2', value);

    }

    else {

        this.storage.setItemSync(this.name + 'value', value);

    }

    callback();
}

@nfarina what do you think? This is my suggestion for the next update, to 1.0.0.

dadsalleb commented 1 year ago

I would love to use the dummy sensor. Not sure this patch will be merged into the master repo..

mkz212 commented 1 year ago

It depends on the author of the plugin. I have great hope for that.

nfarina commented 1 year ago

Hello, absentee landlord here :) sorry I'm slow to respond, I don't use this plugin myself anymore. This is a lot of stuff! What is the purpose of making specific dummy accessories like window blinds, etc?

mkz212 commented 1 year ago

I collected different conclusions, from different people, from different threads and somehow it came out. But different types are not the only modification. Yes, I think it's a lot of changes but simple to introduce. Just in time for version 1.0. 😀

mkz212 commented 1 year ago

Different types have their advantages. For example, in the Apple Home application, you can enable notifications for: Lock, Garage, Security. Motion is displayed in status and not in devices. Thermostat and Security are more extensive devices.

In addition to new devices:

nfarina commented 1 year ago

Appreciate the writeup! I'm still struggling a bit though - originally the purpose of the dummy switch was to make a mechanism to trigger multiple scenes in HomeKit, kind of a workaround for missing functionality.

But these other fake accessories, what is their practical purpose?

dadsalleb commented 1 year ago

For the fake sensors, we can get a notification when using the sensor while the standard or dummy switch don't have the option. It is kind of limitation of Apple Home but this can be a good workaround. Not sure about other accessories but @mkz212 may have idea.

mkz212 commented 1 year ago

As @sooyoung0321 writes, such types have more opportunities in Apple Home, especially in iOS 17: notifications and logs etc. My code only adds additional options, you still have the option to choose a regular switch. But with such an ordinary switch, you have additional options to choose what state should be at the start, it is reset with each activity, you still have the ability to set the timer, etc.

All these things are not my invention but the collected wishes of the users of this plugin.

You don't lose anything, you can only gain! Please give it a chance! 😀👍

mkz212 commented 1 year ago

My examples of automation where your plug-in is useful to me, and even it is necessary:

I will not answer you how to use other types of sensors but I know that other users have ideas for it. I know for sure that additional types of sensors are only optional and you do not need to use them. In addition to the sensor types, I added many other things. Code is ready and tested and works well (at least for me).

incline02 commented 1 year ago

@nfarina @mkz212

I used dummies to give my home memory. I'll try to be concise and illustrate with exemples:

When I turn on the PS5 (plugin), a scene turns the living room lights blue and turns on the dummy plugin "PS5" on. If I go to to another room, that room's sensor will turn off the lights in the living room but the dummy ps5 stays on. When I sit on the sofa, motion is detected: homekit will test if dummy PS5 is on, if yes, then lights turn blue again = My house remembers I'm playing ps5 thanks to the dummy and so PS5 scene is persistent. Notice how I could have resume the scene upon motion sensor but rather when I sit on the sofa thanks to a vibration sensor. It feels much more organic and that's thanks to you.

I also have a "Guest mode" dummy where, if on, homekit stops turning off the lights behind me/in the room I just left or increasing the volume of the HomePod because I left to another room.

I like to start an ambiant sound when I enter my bedroom at 50% but It's too loud when I go to sleep. So I have a vibration sensor in my bed that turns down the volume to 5% when I lay down. But if I'm going to the bathroom (right next to the bedroom) at night and come back into the bedroom, it'd go back to 50% right? So I have a dummy "In bed presence" that prevents going back to 50%. If my partner is already in bed and I trigger motion in the bedroom, that dummy covers for that too and volume stays at 5%

Simpler things: automating things only once a day:

@nfarina Nick your plugin literally gave my home persistence and memory. It's very powerful (but does require to create complex if-condition-based automation if you want to do fancier things like I do). But if you're not using it, are you using an alternative that provides dummies too?

mkz212 commented 1 year ago

@incline02 Thank You for your examples. What do you think about my plugin development proposal? Do you have any suggestions what else to add?

incline02 commented 1 year ago

Sure, I'm happy to provide feedback

new types: Blind, Motion, Lock, Garage Door, Contact, Security (4 values switch), Thermostat

I join @nfarina is asking the practical purpose: don't all of the above come down to reporting Virtual On/Off statuses or a given %? It is true that they will unlock different views/properties in HomeKit but nothing actionable as they are virtual. I don't have the blinds or Garage door but I have all the others and I'm not sure why I would need them virtual. I can see a a way to use them but it's rather convoluted: say I have homekit capable blind or door or else but it doesn't advertise as such because the manufacture was lazy or whatever and so they show up as smart plug where OFF means blind/door closed. With your adjustments, I could substitute this plug profile to an actual garage door profile and as such via automations (if the dummy blinds Is set to closes, the real plug turns off. Then in the home app, I'd have to hide it away in favour of the dummy) benefits? Improving Siri/natural language: use "open the curtains" instead of "turn on the curtain". But that's about as useful it get; on top of my mind. I would add, if not too hard to maintain, it's better to have more profiles that not enough. And someone like me, will come up with something creative.

option to set sensor value on startup: on, off, set value (for dimmer or blind) or last used (cache)

Persistence through reboot is great. I need this. If I reboot at night, because of the % dimmer, my systems thinks it's day so I set all motion sensors to reset the % correctly. cumbersome. Last used would be great, set value, would be great.

option to chose timer unit: days, hour, minutes, seconds, miliseconds

I would design the homebridge UI so it offers these, but let it pass the data to homebridge in the unit homebridge prefers, to avoid any bug. But I think it's a great one too.

new settings display, with sections and with showing and hiding fields depending on the need more info in logs small fixes

that's always welcomed. I actually wish homebridge/or this plug could offer Dummy Management because I have around 20 of them and they could easily be sorted by category

Also as we speak, homebridge-Dummy is rendering my entire homebridge non-responsive randomly til I reboot. I could seriously use an update as I realise my dependancy on @nfarina :D ! I'm a little worried Nick isn't using their own plugin anymore :D !

I'm now reverting back to old versions to find out if it'll fix my issues but any fix is highly welcomed.

mkz212 commented 1 year ago

Thank You. My code fix problem with no response. But I'm not the author and only @nfarina can make it public.

incline02 commented 1 year ago

I'm not too familiar with gihub, only use to it open issues or search for fix so pardon my question: can't you create a fork if @nfarina isn't interested in maintenance?

dadsalleb commented 1 year ago

or you can open a separate repository such as dummy-extension, and you can maintain it on it.

mkz212 commented 1 year ago

I still hope @nfarina will use my code and update plugin for all. Unfortunately, I also don't know much about github and I don't know how to make these fork or repository.

incline02 commented 1 year ago

@mkz212 on top of mind, given that it's just a copy paste of a project after which, replacing some files, it can't be complicated !

I found a tutorial that I'm willing to follow but it's your ideas and so you should be the owner of the new copied project. Would you like to give it a try?

https://docs.github.com/en/get-started/quickstart/fork-a-repo

mkz212 commented 1 year ago

Maybe let's ask the author for his opinion? @nfarina What do you think? Are you going to use my code to update the plugin? Are you going to develop plugin or have you completely abandoned the project?

@incline02 If the author will be not interested in updating the plugin, you can use my code.

nfarina commented 1 year ago

Sorry for the very late reply. I don't use this plugin anymore and you can tell I haven't updated this repo in some time.

Expanding this scope of this plugin using your code is something I cannot sign up for right now. But I highly encourage you to fork/copy my code and publish your own repository! It's not too difficult and then you can have complete control.

incline02 commented 1 year ago

solid copy HQ. I'll have a look into it today

@mkz212 the fix for unresponsive device: how did you fix it? fixed it on my own by deleting all cached accessories (assuming I should have deleted only the dummy switches but there was a long list

edit: scratch it, it happened again

incline02 commented 1 year ago

Alright, it's created https://github.com/incline02/homebridge-dummy

and updated with your changes. Only thing is, I can't find a way to "publish it" into homebridge's repo so one can find it and install it via homebridge. I keep looking but, any help?

nfarina commented 1 year ago

Nice! You're welcome to use the homebridge-dummy name of course but you might have more luck picking a new name - that will help folks who are looking for this extended functionality.

To make it installable in Homebridge, you'll need to publish it to npm. Here's their getting started guide.

incline02 commented 1 year ago

thanks for the tip! named changed! I'll publish it today I hope

incline02 commented 1 year ago

Alright, it appears in Homebridge when you search for it. search for "incline_02" to find "homebridge-dummy-incline_02-beta"

Let me know what goes wrong

incline02 commented 1 year ago

HEADS UP It will use your "old" config from nFarina's original project and show them as "not supported" in homekit.

The fix:

  1. In the homebridge interface, go to plugin
  2. find my plugin
  3. click SETTINGS
  4. for each dummy you'll find in SETTINGS, change its type to another one ex Dimmer, and then back to what you had before

I confirm @mkz212 new types of dummies are in there too

And so far, the bug I reported isn't anymore.

Let's keep this open for some after care. please note I'll be attending a private event til Monday morning. Do come with feedback though!

bubffm commented 1 year ago

Appreciate the writeup! I'm still struggling a bit though - originally the purpose of the dummy switch was to make a mechanism to trigger multiple scenes in HomeKit, kind of a workaround for missing functionality.

But these other fake accessories, what is their practical purpose?

Dummy Irrigation would get me the correct icons into Home App for my watering systems which are triggered by switches.

mkz212 commented 1 year ago

@nfarina If you want I can make my code as pull request. Would you then accept the changes?

mkz212 commented 1 year ago

@bubffm I can add Irrigation, but I am not the author of the plugin. It all depends on @nfarina . I just added pull request with all these big changes.

mkz212 commented 1 year ago

@nfarina I added a pull request with all these changes. But I know that it's a lot of changes for one pull request. Perhaps you prefer me to divide it into single changes?

The question is whether you are still going to develop the plugin? If not, maybe you consider pass it to someone who would like to?

dadsalleb commented 1 year ago

Meanwhile, I'd like to test this but don't know how to install it from a separate source. I tried to find a way to install a plugin from the source code but no luck. Could you please point me out how to do it?

mkz212 commented 1 year ago

I've never done it before, but from what I read, in homebridge terminal type: npm install mkz212/homebridge-dummy#big-update --save But I check and for me it doesn't work.

Or connect via FTP and change code in these two files.

5xPa commented 1 year ago

I'm interested in this revision. Especially being able to set security notification. I have interfaced parts of my alarm system (Texecomm) to shelly uni's using mongoose firmware. For example when down stairs alarm is set triggers a shelly uni, which then turns all the downstairs lights off.

My question does this install along side the existing dummy switch. Or do I need to uninstall the original install the revision. Then recreate the dummy switches and timers? Which then require me to rebuild the scenes etc

mkz212 commented 1 year ago

@5xPa I think that it will override plugin. But I changed the code a bit now so that it should work with the current settings - now only miliseconds can be set, and in my version miliseconds are default and you can change it.

mkz212 commented 1 year ago

@5xPa I check this and for me npm install mkz212/homebridge-dummy#big-update --save doesn't work.

5xPa commented 1 year ago

@mkz212

In Homebridge plugins I searched for incline_02, which returned Homebridge Dummy Incline_02 Beta

homebridge-dummy-incline_02-beta v0.9.1 (2023-07-12)

I was not sure if this was the latest version, I do not have it installed.

Hoping someone will comment is this the latest version

On Sat, 28 Oct 2023 at 09:47, mkz212 @.***> wrote:

@5xPa https://github.com/5xPa I check this and for me npm install mkz212/homebridge-dummy#big-update --save doesn't work.

— Reply to this email directly, view it on GitHub https://github.com/nfarina/homebridge-dummy/issues/84#issuecomment-1783750627, or unsubscribe https://github.com/notifications/unsubscribe-auth/ARF3VCADVLLOKQ3TVUX46FLYBTBB3AVCNFSM6AAAAAAXYGWGYKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTOOBTG42TANRSG4 . You are receiving this because you were mentioned.Message ID: @.***>

mkz212 commented 1 year ago

I think you can try. Since then I have only changed the fact that by default the time is set to milliseconds as in the original. But if it installs as an independent plugin, it doesn't matter.

mkz212 commented 1 year ago

Ok now it should work: npm install mkz212/homebridge-dummy#big-update --save .

It will override plugin but you can return to the original code at any time: in the Plugins tab, click the key icon next to the plugin, then: install another version.

dadsalleb commented 1 year ago

I'm not brave enough to update the branch, so I tried to install a separate plugin Incline_02 Beta. But, it looks like the Incline_02 Beta plugin shares the same configuration as this dummy switch plugin. That said, once something is changed in Incline_02 Beta, this dummy plugin is affected. So, I deleted Incline_02 Beta.

mkz212 commented 1 year ago

Writing this code, my idea was for the author of the original plugin to include new additions and corrections in his plugin, not to create a new plugin. That's why I added a pull request as well as the code itself and information on which files to replace. Since this is not a separate plugin but corrections to the current one, it is obvious that it will be the same configuration. But in my opinion the code does not take anything but only corrects and adds.

mkz212 commented 1 year ago

@nfarina

Maybe you could create a new branch and merge PR to it with this big update and release it as a beta? Who will want will install and test it.

Do you have any comments what to improve?

If you do not want to release the entire code as a beta because it is too big change, if I add a small PRs with small changes, is there a chance that you will include them?

If you are busy, maybe you could consider appointing someone as an administrator? This is a great plugin that a lot of people use and it would be nice if it was further supported and developed.

dadsalleb commented 1 year ago

@mkz212 It looks like the original author doesn't want to make any change on this plugin or takeover it. We cannot force him/her to do that. I think you'd better make a separate git and plugin if you want. Once you make your own plugin and publish it, you can advertise it on Reddit. https://www.reddit.com/r/homebridge/ This community is quite active so you can get quick and a lot of feedback on your plugin. You can check a recent post about AppleTV plugin https://www.reddit.com/r/homebridge/comments/17mquj7/apple_tv_enhanced_plugin/ It's been 12 days posted it but there are a lot of comments and the developer attracted a lot of users.

maisun commented 2 months ago

@nfarina

Maybe you could create a new branch and merge PR to it with this big update and release it as a beta? Who will want will install and test it.

Do you have any comments what to improve?

If you do not want to release the entire code as a beta because it is too big change, if I add a small PRs with small changes, is there a chance that you will include them?

If you are busy, maybe you could consider appointing someone as an administrator? This is a great plugin that a lot of people use and it would be nice if it was further supported and developed.

I think it would be great if you can create a separate plugin

spaceg00se-r commented 2 months ago

Hey @mkz212. Thanks a lot for your effort! Its sad to see that the original developer is currently not willing to incorporate your update into the plugin. As the last changes to this plugin are very old I would also opt for you to create a separate plugin where the development can continue.

mkz212 commented 2 months ago

@dadsalleb @maisun @spaceg00se-r I made plugin. But it is not published. You can take it and develop and publish. homebridge-virtual-device available here: https://github.com/homebridge/unmaintained-plugins

spaceg00se-r commented 2 months ago

@mkz212 I'm not sure if I'm the guy to do that but I'll think about it. Do you know if the virtual-device plugin would already be Homebridge 2.0 compatible?

mkz212 commented 2 months ago

@mkz212 I'm not sure if I'm the guy to do that but I'll think about it. Do you know if the virtual-device plugin would already be Homebridge 2.0 compatible?

It should be - I tested it on Homebridge 2.0.0 and works. It is also platform plugin so you could submit it to Verified by Homebridge program.

Plankske commented 2 months ago

Hello, I submitted the project located at https://github.com/Plankske/hb-virtual-switch for verification a few weeks ago. It supports Homebridge 2.0 and includes virtual switches with on/off, stateful/stateless and normally open/closed options. New is that you can also monitor the Homebridge log file for specific keywords or phrases and set a switch that are logged in the Homebridge log file set a switch if they do. Should there be a requirement, this project can be used for further development. I am open to contributing if the current project is not actively maintained any longer.

mkz212 commented 2 months ago

Hello, I submitted the project located at https://github.com/Plankske/hb-virtual-switch for verification a few weeks ago. It supports Homebridge 2.0 and includes virtual switches with on/off, stateful/stateless and normally open/closed options. New is that you can also monitor the Homebridge log file for specific keywords or phrases and set a switch that are logged in the Homebridge log file set a switch if they do. Should there be a requirement, this project can be used for further development. I am open to contributing if the current project is not actively maintained any longer.

Cool. But.. I think that Homebridge plugin name must starts with 'homebridge-'?

spaceg00se-r commented 2 months ago

@Plankske have you also added the timer functionality as well as the option to reset the timer with every activity as @mkz212 did with the code change suggested above?

Plankske commented 2 months ago

Hello, I submitted the project located at https://github.com/Plankske/hb-virtual-switch for verification a few weeks ago. It supports Homebridge 2.0 and includes virtual switches with on/off, stateful/stateless and normally open/closed options. New is that you can also monitor the Homebridge log file for specific keywords or phrases and set a switch that are logged in the Homebridge log file set a switch if they do. Should there be a requirement, this project can be used for further development. I am open to contributing if the current project is not actively maintained any longer.

Cool. But.. I think that Homebridge plugin name must starts with 'homebridge-'?

The name was changed by npm as part of this namespace management because of the name 'virtual switch'.