homebridge-blynk
Version:
Homebridge plugin for the Blynk platform
240 lines (216 loc) • 8.33 kB
text/typescript
// Copyright 2018 ilcato
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Blynk Platform plugin for HomeBridge
//
// Remember to add platform to config.json. Example:
// "platforms": [
// {
// "platform": "Blynk",
// "name": "Blynk",
// "serverurl": "PUT THE URL OF THE BLYNK SERVER HERE, e.g.: http://10.0.0.102:8080",
// "token" : "PUT YOUR PROJECT AUTHORIZATION TOKEN HERE",
// "pollerperiod": "PUT 0 FOR DISABLING POLLING, 1 - 100 INTERVAL IN SECONDS. 1 SECONDS IS THE DEFAULT",
// "accessories": [
// {
// "name": "Switch1",
// "widget": "Switch",
// "mode": "SWITCH",
// "caption": "Main Lamp",
// "pin": "D3"
// },
// {
// "name": "ContactSensor1",
// "widget": "ContactSensor",
// "caption": "Main Door",
// "pin": "D2"
// },
// {
// "name": "TemperatureSensor1",
// "widget": "TemperatureSensor",
// "caption": "Kitchen Temperature",
// "pin": "A17"
// }
// ]
// }
// ],
//
// When you attempt to add a device, it will ask for a "PIN code".
// The default code for all HomeBridge accessories is 031-45-154.
'use strict'
import request = require("request");
import {
pluginName,
platformName,
BlynkAccessory
} from './blynkaccessory'
import {
Poller,
getBlynkvalue
} from './pollerupdate'
const defaultPollerPeriod = 1;
let Accessory,
Service,
Characteristic,
UUIDGen;
export = function (homebridge) {
Accessory = homebridge.platformAccessory
Service = homebridge.hap.Service
Characteristic = homebridge.hap.Characteristic
UUIDGen = homebridge.hap.uuid
homebridge.registerPlatform(pluginName, platformName, Blynk, true)
}
class Config {
name: string;
serverurl: string;
token: string;
pollerperiod?: string;
accessories: string;
constructor() {
this.name = "";
this.serverurl = "";
this.token = "";
this.pollerperiod = "";
this.accessories = "";
}
}
class Blynk {
log: (format: string, message: any) => void;
config: Config;
api: any;
accessories: Map<string, any>;
poller: Poller;
updateSubscriptions: Array<Object>;
constructor (log: (format: string, message: any) => void, config: Config, api: any) {
this.log = log;
this.api = api;
this.accessories = new Map();
this.updateSubscriptions = new Array();
this.config = config;
let pollerPeriod = this.config.pollerperiod ? parseInt(this.config.pollerperiod) : defaultPollerPeriod;
if (isNaN(pollerPeriod) || pollerPeriod < 1 || pollerPeriod > 100)
pollerPeriod = defaultPollerPeriod;
this.poller = new Poller(this, pollerPeriod, Service, Characteristic);
this.api.on('didFinishLaunching', this.didFinishLaunching.bind(this));
}
didFinishLaunching () {
this.log('didFinishLaunching.', '')
this.LoadAccessories(this.config.accessories);
}
configureAccessory (accessory) {
this.log("Configured Accessory: ", accessory.displayName);
for (let s = 0; s < accessory.services.length; s++) {
let service = accessory.services[s];
for (let i = 0; i < service.characteristics.length; i++) {
let characteristic = service.characteristics[i];
if (characteristic.props.needsBinding)
this.bindCharacteristicEvents(characteristic, service);
}
}
this.accessories.set(accessory.context.uniqueSeed, accessory);
accessory.reachable = true;
}
LoadAccessories(accessories) {
this.log('Loading accessories', '');
if (accessories == null || accessories.length == 0) {
return;
}
accessories.map((s, i, a) => {
this.addAccessory(BlynkAccessory.createBlynkAccessory(s, Accessory, Service, Characteristic, this));
});
// Remove not reviewd accessories: cached accessories no more present in config.json
let aa = this.accessories.values() // Iterator for accessories, key is the uniqueseed
for (let a of aa) {
if (!a.reviewed) {
this.removeAccessory(a);
}
}
// Start the poller update mechanism
this.poller.poll();
}
addAccessory (blynkAccessory) {
if (blynkAccessory == undefined)
return;
let uniqueSeed = blynkAccessory.name;
let isNewAccessory = false;
let a:any = this.accessories.get(uniqueSeed);
if (a == null) {
isNewAccessory = true;
let uuid = UUIDGen.generate(uniqueSeed);
a = new Accessory(blynkAccessory.name, uuid); // Create the HAP accessory
a.context.uniqueSeed = uniqueSeed;
this.accessories.set(uniqueSeed, a);
}
blynkAccessory.setAccessory(a);
// init accessory
blynkAccessory.initAccessory();
// Remove services existing in HomeKit, device no more present in Blynk
blynkAccessory.removeNoMoreExistingServices();
// Add services present in Blynk and not existing in Homekit accessory
blynkAccessory.addNewServices(this);
// Register or update platform accessory
blynkAccessory.registerUpdateAccessory(isNewAccessory, this.api);
this.log("Added/changed accessory: ", blynkAccessory.name);
}
removeAccessory (accessory) {
this.log('Remove accessory', accessory.displayName);
this.api.unregisterPlatformAccessories(pluginName, platformName, [accessory]);
this.accessories.delete(accessory.context.uniqueSeed);
}
bindCharacteristicEvents(characteristic, service) {
characteristic.on('set', (value, callback, context) => {
this.setCharacteristicValue(value, callback, context, characteristic, service);
});
characteristic.on('get', (callback) => {
this.getCharacteristicValue(callback, characteristic, service);
});
this.subscribeUpdate(service, characteristic);
}
subscribeUpdate(service, characteristic) {
this.updateSubscriptions.push({'service': service, 'characteristic': characteristic});
}
setCharacteristicValue(value, callback, context, characteristic, service) {
if( context !== 'fromPoller' && context !== 'fromSetValue') {
let params = service.subtype.split("-"); // params[0]: name, params[1]: widget, params[2]: pin, params[3]: mode
let name = params[0];
let widget = params[1];
let pinString = params[2];
let mode = params[3];
this.log("Setting value to device: ", `${name}, parameter: ${characteristic.displayName}, value: ${value}`);
switch (widget) {
case "Switch":
let v = value ? "1" : "0";
request(this.config.serverurl + '/' + this.config.token + '/update/' + pinString + "?value=" + v, function (error, response, body) {
if (!error && mode == "PUSH") {
// In order to behave like a push button reset the status to off
setTimeout( function(){
characteristic.setValue(false, undefined, 'fromSetValue');
}, 100 );
}
})
break;
default:
break
}
}
callback();
}
getCharacteristicValue(callback, characteristic, service) {
this.log("Getting value from device: ", `parameter: ${characteristic.displayName}`);
let params = service.subtype.split("-"); // params[0]: name, params[1]: widget, params[2]: pin, params[3]: mode
let name = params[0];
let widget = params[1];
let pinString = params[2];
getBlynkvalue(name, widget, pinString, callback, characteristic, Characteristic, this);
}
}