From e94ab483f2bc569d4850a17a9bf2acad7b30731f Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 12 Apr 2020 07:13:51 +0100 Subject: [PATCH] New release Support for Tasmota devices. Homekit accessories converted to TypeScript Support for Tasmota devices Central heating interface added to web app GUI Installer updated to work with latest HAP-NodeJS (TypeScript version) Exported Homekit accessory files updated from JavaScript to TypeScript Improved functionality of Homekit radiators Silent Samba install - no more anoying dialog GUI selection of animated backgrounds --- ConfigFiles/Generic_ContactSensor.js | 81 ------ ConfigFiles/Generic_ContactSensor.ts | 61 +++++ ConfigFiles/Generic_Fan.js | 94 ------- ConfigFiles/Generic_Fan.ts | 102 ++++++++ ConfigFiles/Generic_Heating.ts | 93 +++++++ ConfigFiles/Generic_Light.js | 91 ------- ConfigFiles/Generic_Light.ts | 99 ++++++++ ConfigFiles/Generic_Outlet.js | 94 ------- ConfigFiles/Generic_Outlet.ts | 102 ++++++++ ConfigFiles/Generic_Radiator.js | 154 ------------ ConfigFiles/Generic_Radiator.ts | 220 +++++++++++++++++ ConfigFiles/Hub_accessory.js | 121 --------- ConfigFiles/Office radiator_accessory.sj | 152 ------------ ConfigFiles/types.js | 93 ------- ConfigFiles/types.ts | 91 +++++++ ConfigFiles/user.txt | 9 - README.md | 40 +-- Scripts/alarm.sh | 291 +++++++++++++++------- WebPage/HeatPageAjaxCall.php | 95 +++++-- WebPage/PowerPageAjaxCall.php | 63 +++-- WebPage/SettingsPageAjaxCall.php | 40 ++- WebPage/TaskPageAjaxCall.php | 6 +- WebPage/background_9.inc | 301 +++++++++++++++++++++++ WebPage/factory.txt | 31 +-- WebPage/hols_default.txt | 48 ++++ WebPage/index.php | 45 +++- WebPage/js/index.js | 97 ++++++-- WebPage/readvars.php | 3 +- WebPage/themes/css/system.css | 2 +- WebPage/themes/css/system.scss | 21 +- WebPage/work_default.txt | 48 ++++ install.sh | 14 +- 32 files changed, 1713 insertions(+), 1089 deletions(-) delete mode 100644 ConfigFiles/Generic_ContactSensor.js create mode 100644 ConfigFiles/Generic_ContactSensor.ts delete mode 100644 ConfigFiles/Generic_Fan.js create mode 100644 ConfigFiles/Generic_Fan.ts create mode 100644 ConfigFiles/Generic_Heating.ts delete mode 100644 ConfigFiles/Generic_Light.js create mode 100644 ConfigFiles/Generic_Light.ts delete mode 100644 ConfigFiles/Generic_Outlet.js create mode 100644 ConfigFiles/Generic_Outlet.ts delete mode 100644 ConfigFiles/Generic_Radiator.js create mode 100644 ConfigFiles/Generic_Radiator.ts delete mode 100644 ConfigFiles/Hub_accessory.js delete mode 100644 ConfigFiles/Office radiator_accessory.sj delete mode 100644 ConfigFiles/types.js create mode 100644 ConfigFiles/types.ts delete mode 100644 ConfigFiles/user.txt create mode 100644 WebPage/background_9.inc create mode 100644 WebPage/hols_default.txt create mode 100644 WebPage/work_default.txt diff --git a/ConfigFiles/Generic_ContactSensor.js b/ConfigFiles/Generic_ContactSensor.js deleted file mode 100644 index 0b4e583..0000000 --- a/ConfigFiles/Generic_ContactSensor.js +++ /dev/null @@ -1,81 +0,0 @@ -// First parameter = Parm1 = Accessory name -// Second parameter = Parm2 = MAC address - -var Accessory = require('../').Accessory; -var Service = require('../').Service; -var Characteristic = require('../').Characteristic; -var uuid = require('../').uuid; -var fs = require('fs'); - -var lastState=0; -// here's a door device that we'll expose to HomeKit -var DOOR = { - sensor: 0, - outputLogs: false, //output logs - read: function(){ - var err = null; // in case there were any problems - fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { - if(err) { return console.log(err); } - var lines = data.split('\n'); - for(var i = 0; i < lines.length; i++){ - if ((lines[i].indexOf("zcon") !=-1) && (lines[i].indexOf("Parm1") !=-1)) { - var svalues = lines[i].split(':'); - if (svalues[8].toString().trim() === '0') { - if(this.outputLogs) console.log('Parm1 open') - DOOR.open = true; - } else { - if(this.outputLogs) console.log('Parm1 closed') - DOOR.open = false; - } - } - } - }); - }, - identify: function() { - console.log("Identify the Parm1"); - } -} - -// Generate a consistent UUID for our Lock Accessory that will remain the same even when -// restarting our server. We use the `uuid.generate` helper function to create a deterministic -// UUID based on an arbitrary "namespace" and the word "door". -var doorUUID = uuid.generate('hap-nodejs:accessories:Parm1'); - -// This is the Accessory that we'll return to HAP-NodeJS that represents our door -var door = exports.accessory = new Accessory('Parm1', doorUUID); - -// Add properties for publishing (in case we're using Core.js and not BridgedCore.js) -door.username = "Parm2"; -door.pincode = "031-45-154"; - -// set some basic properties (these values are arbitrary and setting them is optional) -door - .getService(Service.AccessoryInformation) - .setCharacteristic(Characteristic.Manufacturer, "oddwires.co.uk") - .setCharacteristic(Characteristic.Model, "v1.0") - .setCharacteristic(Characteristic.SerialNumber, "A12S345KGB"); - -// listen for the "identify" event for this Accessory -door.on('identify', function(paired, callback) { - DOOR.identify(); - callback(); // success -}); - -door - .addService(Service.ContactSensor,"Parm1") - .getCharacteristic(Characteristic.ContactSensorState) - .on('get', function(callback) { - DOOR.read(); - // return our current value - callback(null, DOOR.open); -}); - -setInterval(function() { - DOOR .read() - - // update the characteristic value so interested iOS devices can get notified - door - .getService(Service.ContactSensor) - .getCharacteristic(Characteristic.ContactSensorState, DOOR.open) - .setValue(DOOR.open); -}, 1000); \ No newline at end of file diff --git a/ConfigFiles/Generic_ContactSensor.ts b/ConfigFiles/Generic_ContactSensor.ts new file mode 100644 index 0000000..1f5a0ac --- /dev/null +++ b/ConfigFiles/Generic_ContactSensor.ts @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// First parameter (Accessory name) : Parm1 +// Second parameter (MAC address) : Parm2 +/////////////////////////////////////////////////////////////////////////////////////////////////// + +import { Accessory, AccessoryEventTypes, Categories, Characteristic, CharacteristicEventTypes, CharacteristicSetCallback, + CharacteristicValue, NodeCallback, Service, uuid, VoidCallback,} from '..'; +import fs from 'fs'; + +class DoorSensorClass { + name: CharacteristicValue = "Parm1"; + pincode: CharacteristicValue = "031-45-154"; + username: CharacteristicValue = "Parm2"; + manufacturer: CharacteristicValue = "oddwires.co.uk"; + model: CharacteristicValue = "v1.0"; + serialNumber: CharacteristicValue = "12345"; + SensorOpen: CharacteristicValue = true; + outputLogs = true; + + identify() { + if(this.outputLogs) console.log("Identify the '%s'", this.name); + } +} + +const DoorSensor = new DoorSensorClass(); +var lightUUID = uuid.generate('hap-nodejs:accessories:light' + DoorSensor.name); +var DoorSensorAccessory = exports.accessory = new Accessory(DoorSensor.name as string, lightUUID); + +DoorSensorAccessory + .getService(Service.AccessoryInformation)! + .setCharacteristic(Characteristic.Manufacturer, DoorSensor.manufacturer) + .setCharacteristic(Characteristic.Model, DoorSensor.model) + .setCharacteristic(Characteristic.SerialNumber, DoorSensor.serialNumber); + +DoorSensorAccessory.on(AccessoryEventTypes.IDENTIFY, (paired: boolean, callback: VoidCallback) => { + DoorSensor.identify(); + callback(); +}); + +DoorSensorAccessory + .addService(Service.ContactSensor,"Parm1")! + .getCharacteristic(Characteristic.ContactSensorState)! + .on(CharacteristicEventTypes.GET, (callback: CharacteristicSetCallback) => { + fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { + if(err) { return console.log(err); } + var lines = data.split('\n'); + for(var i = 0; i < lines.length; i++){ + if ((lines[i].indexOf("zcon") !=-1) && (lines[i].indexOf("Parm1") !=-1)) { + var values = lines[i].split(':'); + if (values[8].toString().trim() === '0') { + if(DoorSensor.outputLogs) console.log('Parm1: Open'); + DoorSensor.SensorOpen = true; + } else { + if(DoorSensor.outputLogs) console.log('Parm1: Closed'); + DoorSensor.SensorOpen = false; + } + callback(null, DoorSensor.SensorOpen); + } + } + }); + }); \ No newline at end of file diff --git a/ConfigFiles/Generic_Fan.js b/ConfigFiles/Generic_Fan.js deleted file mode 100644 index 25f6dd0..0000000 --- a/ConfigFiles/Generic_Fan.js +++ /dev/null @@ -1,94 +0,0 @@ -// First parameter = Parm1 = Accessory name -// Second parameter = Parm2 = MAC address -// Third parameter = Parm3 = Command string to switch accessory on -// Fourth parameter = Parm4 = Command string to switch accessory off -// Fifth parameter = Parm5 = Address and Channel info - -var Accessory = require('../').Accessory; -var Service = require('../').Service; -var Characteristic = require('../').Characteristic; -var uuid = require('../').uuid; -var fs = require('fs'); - -// here's a GENERIC fan device that we'll expose to HomeKit -var GENERIC_Fan01 = { - powerOn: false, - setPowerOn: function(on) { - console.log("Turning the Parm1 fan %s", on ? "on" : "off"); - if (on) { - fs.appendFile("/var/www/data/input.txt", "Parm3", function(err) { - if(err) { return console.log(err); } - console.log("Parm1 fan on Success"); - }); - } else { - fs.appendFile("/var/www/data/input.txt", "Parm4", function(err) { - if(err) { return console.log(err); } - console.log("Parm1 fan off Success"); - }); - } - }, -} - -// Generate a consistent UUID for our light Accessory that will remain the same even when -// restarting our server. We use the `uuid.generate` helper function to create a deterministic -// UUID based on an arbitrary "namespace" and the word "light". -var fanUUID01 = uuid.generate('hap-nodejs:accessories:fan01'); - -// This is the Accessory that we'll return to HAP-NodeJS that represents our GENERIC light. -var fan01 = exports.accessory = new Accessory('Parm1', fanUUID01); - -// Add properties for publishing (in case we're using Core.js and not BridgedCore.js) -fan01.username = "Parm2"; -fan01.pincode = "031-45-154"; -fan01.displayName = "Parm1"; - -// set some basic properties (these values are arbitrary and setting them is optional) -fan01 - .getService(Service.AccessoryInformation) - .setCharacteristic(Characteristic.Manufacturer, "oddwires.co.uk") - .setCharacteristic(Characteristic.Model, "Ver 1.0") - .setCharacteristic(Characteristic.SerialNumber, "Parm5"); - -// Add the actual outlet Service and listen for change events from iOS. -// We can see the complete list of Services and Characteristics in `lib/gen/HomeKitTypes.js` -fan01 - .addService(Service.Fan, "Parm1") // services exposed to the user should have "names" like "GENERIC Fan" for us - .getCharacteristic(Characteristic.On) - .on('set', function(value, callback) { - GENERIC_Fan01.setPowerOn(value); - callback(); // Our GENERIC Light is synchronous - this value has been successfully set - }); - -// We want to intercept requests for our current power state so we can query the hardware itself instead of -// allowing HAP-NodeJS to return the cached Characteristic.value. -fan01 - .getService(Service.Fan) - .getCharacteristic(Characteristic.On) - .on('get', function(callback) { - // this event is emitted when you ask Siri directly whether your fan is on or not. you might query - // the fan hardware itself to find this out, then call the callback. But if you take longer than a - // few seconds to respond, Siri will give up. - var err = null; // in case there were any problems - fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { - if(err) { return console.log(err); } - var lines = data.split('\n'); - for(var i = 0; i < lines.length; i++){ - if ((lines[i].indexOf("rcon") !=-1) && (lines[i].indexOf("Parm1") !=-1)) { - // falls through here when we have found the line for this device in the status file - // console.log(lines[i]); - var values = lines[i].split(':'); - // read the actual status of the accessory from the file - console.log(values[5]); - if (values[5].indexOf("On") !=-1) { - console.log("Current status: on"); - GENERIC_Fan01.powerOn = true; - callback(err, true); - } else { - console.log("Current status: off"); - GENERIC_Fan01.powerOn = false; - callback(err, false); - } - } - } - }); - }); \ No newline at end of file diff --git a/ConfigFiles/Generic_Fan.ts b/ConfigFiles/Generic_Fan.ts new file mode 100644 index 0000000..2e2a2ae --- /dev/null +++ b/ConfigFiles/Generic_Fan.ts @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// First parameter (Accessory name) : Parm1 +// Second parameter (MAC address) : Parm2 +// Third parameter (command string to switch accessory on) : Parm3 +// Fourth parameter (command string to switch accessory off) : Parm4 +// Fifth parameter (Address and Channel info) : Parm5 +/////////////////////////////////////////////////////////////////////////////////////////////////// + +import { + Accessory, + AccessoryEventTypes, + Categories, + Characteristic, + CharacteristicEventTypes, CharacteristicSetCallback, + CharacteristicValue, + NodeCallback, + Service, + uuid, + VoidCallback, +} from '..'; +import fs from 'fs'; + +class FanControllerClass { + name: CharacteristicValue = "Parm1"; + pincode: CharacteristicValue = "031-45-154"; + username: CharacteristicValue = "Parm2"; + manufacturer: CharacteristicValue = "oddwires.co.uk"; + model: CharacteristicValue = "v1.0"; + serialNumber: CharacteristicValue = "Parm5"; + + power: CharacteristicValue = false; + outputLogs = true; + + setPower(status: CharacteristicValue) { + if(this.outputLogs) console.log("%s: switching fan %s", this.name, status ? "on" : "off"); + if (status) { + fs.appendFile("/var/www/data/input.txt", "Parm3", function(err) { + if(err) { return console.log(err); } + }); + } else { + fs.appendFile("/var/www/data/input.txt", "Parm4", function(err) { + if(err) { return console.log(err); } + }); + } + } + +identify() { + if(this.outputLogs) console.log("Identify the '%s'", this.name); + } +} + +const FanController = new FanControllerClass(); +var FanUUID = uuid.generate('hap-nodejs:accessories:Fan' + FanController.name); +var FanAccessory = exports.accessory = new Accessory(FanController.name as string, FanUUID); + +// @ts-ignore +FanAccessory.username = FanController.username; +// @ts-ignore +FanAccessory.pincode = FanController.pincode; +// @ts-ignore +FanAccessory.category = Categories.Fan; + +FanAccessory + .getService(Service.AccessoryInformation)! + .setCharacteristic(Characteristic.Manufacturer, FanController.manufacturer) + .setCharacteristic(Characteristic.Model, FanController.model) + .setCharacteristic(Characteristic.SerialNumber, FanController.serialNumber); + +FanAccessory.on(AccessoryEventTypes.IDENTIFY, (paired: boolean, callback: VoidCallback) => { + FanController.identify(); + callback(); +}); + +FanAccessory + .addService(Service.Fan, FanController.name) + .getCharacteristic(Characteristic.On)! + .on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => { + FanController.setPower(value); + callback(); + }); + +FanAccessory + .getService(Service.Fan)! + .getCharacteristic(Characteristic.On)! + .on(CharacteristicEventTypes.GET, (callback: CharacteristicSetCallback) => { + fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { + if(err) { return console.log(err); } + var lines = data.split('\n'); + for(var i = 0; i < lines.length; i++){ + if ((lines[i].indexOf("rcon") !=-1) && (lines[i].indexOf("Parm1") !=-1)) { + var values = lines[i].split(':'); + if (values[6].indexOf("On") !=-1) { + console.log("Parm1: Get status: On"); + callback(err, true); + } else { + console.log("Parm1: Get status: Off"); + callback(err, false); + } + } + } + }); + }) \ No newline at end of file diff --git a/ConfigFiles/Generic_Heating.ts b/ConfigFiles/Generic_Heating.ts new file mode 100644 index 0000000..b3c0aa6 --- /dev/null +++ b/ConfigFiles/Generic_Heating.ts @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// First parameter (Accessory name) : Parm1 +// Second parameter (MAC address) : Parm2 +// Third parameter (command string to switch accessory on) : Parm3 +// Fourth parameter (command string to switch accessory off) : Parm4 +// Fifth parameter (Address and Channel info) : Parm5 +// Sixth parameter (Status string) : Parm6 +/////////////////////////////////////////////////////////////////////////////////////////////////// + +import { Accessory, AccessoryEventTypes, Categories, Characteristic, CharacteristicEventTypes, + CharacteristicSetCallback, CharacteristicValue, NodeCallback, Service, uuid, VoidCallback,} from '..'; +import fs from 'fs'; + +class OutletControllerClass { + name: CharacteristicValue = "Parm1"; + pincode: CharacteristicValue = "031-45-154"; + username: CharacteristicValue = "Parm2"; + manufacturer: CharacteristicValue = "oddwires.co.uk"; + model: CharacteristicValue = "v1.0"; + serialNumber: CharacteristicValue = "Parm5"; + + power: CharacteristicValue = false; + outputLogs = true; + + setPower(status: CharacteristicValue) { + if(this.outputLogs) console.log("%s: switching outlet %s", this.name, status ? "on" : "off"); + if (status) { + fs.appendFile("/var/www/data/input.txt", "Parm3", function(err) { + if(err) { return console.log(err); } + }); + } else { + fs.appendFile("/var/www/data/input.txt", "Parm4", function(err) { + if(err) { return console.log(err); } + }); + } + } + +identify() { + if(this.outputLogs) console.log("Identify the '%s'", this.name); + } +} + +const OutletController = new OutletControllerClass(); +var OutletUUID = uuid.generate('hap-nodejs:accessories:Outlet' + OutletController.name); +var OutletAccessory = exports.accessory = new Accessory(OutletController.name as string, OutletUUID); + +// @ts-ignore +//OutletAccessory.username = OutletController.username; +// @ts-ignore +//OutletAccessory.pincode = OutletController.pincode; +// @ts-ignore +//OutletAccessory.category = Categories.Outlet; + +OutletAccessory + .getService(Service.AccessoryInformation)! + .setCharacteristic(Characteristic.Manufacturer, OutletController.manufacturer) + .setCharacteristic(Characteristic.Model, OutletController.model) + .setCharacteristic(Characteristic.SerialNumber, OutletController.serialNumber); + +OutletAccessory.on(AccessoryEventTypes.IDENTIFY, (paired: boolean, callback: VoidCallback) => { + OutletController.identify(); + callback(); +}); + +OutletAccessory + .addService(Service.Outlet, OutletController.name) + .getCharacteristic(Characteristic.On)! + .on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => { + OutletController.setPower(value); + callback(); + }); + +OutletAccessory + .getService(Service.Outlet)! + .getCharacteristic(Characteristic.On)! + .on(CharacteristicEventTypes.GET, (callback: CharacteristicSetCallback) => { + fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { + if(err) { return console.log(err); } + var lines = data.split('\n'); + for(var i = 0; i < lines.length; i++){ + if ((lines[i].indexOf("heat") !=-1) && (lines[i].indexOf("mode") !=-1)) { + var values = lines[i].split(':'); + if (values[2].indexOf("Parm6") !=-1) { + console.log("Boiler: Get status: On"); + callback(err, true); + } else { + console.log("Boiler: Get status: Off"); + callback(err, false); + } + } + } + }); + }) \ No newline at end of file diff --git a/ConfigFiles/Generic_Light.js b/ConfigFiles/Generic_Light.js deleted file mode 100644 index a8b8ed3..0000000 --- a/ConfigFiles/Generic_Light.js +++ /dev/null @@ -1,91 +0,0 @@ -// First parameter = Parm1 = Accessory name -// Second parameter = Parm2 = MAC address -// Third parameter = Parm3 = Command string to switch accessory on -// Fourth parameter = Parm4 = Command string to switch accessory off -// Fifth parameter = Parm5 = Address and Channel info - -var Accessory = require('../').Accessory; -var Service = require('../').Service; -var Characteristic = require('../').Characteristic; -var uuid = require('../').uuid; -var fs = require('fs'); - -// here's a GENERIC light device that we'll expose to HomeKit -var GENERIC_LIGHT01 = { - powerOn: false, - setPowerOn: function(on) { - console.log("Set Parm1 light: %s", on ? "on" : "off"); - if (on) { - fs.appendFile("/var/www/data/input.txt", "Parm3", function(err) { - if(err) { return console.log(err); } - }); - } else { - fs.appendFile("/var/www/data/input.txt", "Parm4", function(err) { - if(err) { return console.log(err); } - }); - } - }, -} - -// Generate a consistent UUID for our light Accessory that will remain the same even when -// restarting our server. We use the `uuid.generate` helper function to create a deterministic -// UUID based on an arbitrary "namespace" and the word "light". -var lightUUID01 = uuid.generate('hap-nodejs:accessories:Parm1'); - -// This is the Accessory that we'll return to HAP-NodeJS that represents our GENERIC light. -var light01 = exports.accessory = new Accessory('Parm1', lightUUID01); - -// Add properties for publishing (in case we're using Core.js and not BridgedCore.js) -light01.username = "Parm2"; -light01.pincode = "031-45-154"; -light01.displayName = "Parm1 Light"; - -// set some basic properties (these values are arbitrary and setting them is optional) -light01 - .getService(Service.AccessoryInformation) - .setCharacteristic(Characteristic.Manufacturer, "oddwires.co.uk") - .setCharacteristic(Characteristic.Model, "Ver 1.0") - .setCharacteristic(Characteristic.SerialNumber, "Parm5"); - -// Add the actual Lightbulb Service and listen for change events from iOS. -// We can see the complete list of Services and Characteristics in `lib/gen/HomeKitTypes.js` -light01 - .addService(Service.Lightbulb, "Parm1") // services exposed to the user should have "names" like "GENERIC Light" for us - .getCharacteristic(Characteristic.On) - .on('set', function(value, callback) { - GENERIC_LIGHT01.setPowerOn(value); - callback(); // Our GENERIC Light is synchronous - this value has been successfully set - }); - -// We want to intercept requests for our current power state so we can query the hardware itself instead of -// allowing HAP-NodeJS to return the cached Characteristic.value. -light01 - .getService(Service.Lightbulb) - .getCharacteristic(Characteristic.On) - .on('get', function(callback) { - // this event is emitted when you ask Siri directly whether your light is on or not. you might query - // the light hardware itself to find this out, then call the callback. But if you take longer than a - // few seconds to respond, Siri will give up. - var err = null; // in case there were any problems - fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { - if(err) { return console.log(err); } - var lines = data.split('\n'); - for(var i = 0; i < lines.length; i++){ - if ((lines[i].indexOf("rcon") !=-1) && (lines[i].indexOf("Parm1") !=-1)) { - // falls through here when we have found the line for this device in the status file - // console.log(lines[i]); - var values = lines[i].split(':'); - // read the actual status of the accessory from the file - if (values[5].indexOf("On") !=-1) { - console.log("Get Parm1 light status: on"); - GENERIC_LIGHT01.powerOn = true; - callback(err, true); - } else { - console.log("Get Parm1 light status: off"); - GENERIC_LIGHT01.powerOn = false; - callback(err, false); - } - } - } - }); - }); \ No newline at end of file diff --git a/ConfigFiles/Generic_Light.ts b/ConfigFiles/Generic_Light.ts new file mode 100644 index 0000000..a575bb0 --- /dev/null +++ b/ConfigFiles/Generic_Light.ts @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// First parameter (Accessory name) : Parm1 +// Second parameter (MAC address) : Parm2 +// Third parameter (command string to switch accessory on) : Parm3 +// Fourth parameter (command string to switch accessory off) : Parm4 +// Fifth parameter (Address and Channel info) : Parm5 +/////////////////////////////////////////////////////////////////////////////////////////////////// + +import { + Accessory, + AccessoryEventTypes, + Categories, + Characteristic, + CharacteristicEventTypes, CharacteristicSetCallback, + CharacteristicValue, + NodeCallback, + Service, + uuid, + VoidCallback, +} from '..'; +import fs from 'fs'; + +class LightControllerClass { + name: CharacteristicValue = "Parm1"; + pincode: CharacteristicValue = "031-45-154"; + username: CharacteristicValue = "Parm2"; + manufacturer: CharacteristicValue = "oddwires.co.uk"; + model: CharacteristicValue = "v1.0"; + serialNumber: CharacteristicValue = "Parm5"; + power: CharacteristicValue = false; + outputLogs = true; + + SetPower(status: CharacteristicValue) { + if(this.outputLogs) console.log("%s: switching light %s", this.name, status ? "on" : "off"); + if (status) { + fs.appendFile("/var/www/data/input.txt", "Parm3", function(err) { + if(err) { return console.log(err); } + }); + } else { + fs.appendFile("/var/www/data/input.txt", "Parm4", function(err) { + if(err) { return console.log(err); } + }); + } + } + + GetPower(status: CharacteristicValue) { // ( this function not currently used ) + if(this.outputLogs) console.log("Get the '%s' status.", this.name); + } + + identify() { + if(this.outputLogs) console.log("Identify the '%s'", this.name); + } +} + +const LightController = new LightControllerClass(); +var lightUUID = uuid.generate('hap-nodejs:accessories:light' + LightController.name); +var lightAccessory = exports.accessory = new Accessory(LightController.name as string, lightUUID); + +lightAccessory + .getService(Service.AccessoryInformation)! + .setCharacteristic(Characteristic.Manufacturer, LightController.manufacturer) + .setCharacteristic(Characteristic.Model, LightController.model) + .setCharacteristic(Characteristic.SerialNumber, LightController.serialNumber); + +lightAccessory.on(AccessoryEventTypes.IDENTIFY, (paired: boolean, callback: VoidCallback) => { + LightController.identify(); + callback(); +}); + +lightAccessory + .addService(Service.Lightbulb, LightController.name) + .getCharacteristic(Characteristic.On)! + .on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => { + LightController.SetPower(value); + callback(); + }); + + lightAccessory + .getService(Service.Lightbulb)! + .getCharacteristic(Characteristic.On)! + .on(CharacteristicEventTypes.GET, (callback: CharacteristicSetCallback) => { + fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { + if(err) { return console.log(err); } + var lines = data.split('\n'); + for(var i = 0; i < lines.length; i++){ + if ((lines[i].indexOf("rcon") !=-1) && (lines[i].indexOf("Parm1") !=-1)) { + var values = lines[i].split(':'); + if (values[6].indexOf("On") !=-1) { + console.log("Parm1: Get status: On"); + callback(err, true); + } else { + console.log("Parm1: Get status: Off"); + callback(err, false); + } + } + } + }); + }) + \ No newline at end of file diff --git a/ConfigFiles/Generic_Outlet.js b/ConfigFiles/Generic_Outlet.js deleted file mode 100644 index 95ffa5d..0000000 --- a/ConfigFiles/Generic_Outlet.js +++ /dev/null @@ -1,94 +0,0 @@ -// First parameter = Parm1 = Accessory name -// Second parameter = Parm2 = MAC address -// Third parameter = Parm3 = Command string to switch accessory on -// Fourth parameter = Parm4 = Command string to switch accessory off -// Fifth parameter = Parm5 = Address and Channel info - -var Accessory = require('../').Accessory; -var Service = require('../').Service; -var Characteristic = require('../').Characteristic; -var uuid = require('../').uuid; -var fs = require('fs'); - -// here's a GENERIC outlet device that we'll expose to HomeKit -var GENERIC_OUTLET01 = { - powerOn: false, - setPowerOn: function(on) { - console.log("Turning the Parm1 outlet %s", on ? "on" : "off"); - if (on) { - fs.appendFile("/var/www/data/input.txt", "Parm3", function(err) { - if(err) { return console.log(err); } - console.log("Parm1 outlet on Success"); - }); - } else { - fs.appendFile("/var/www/data/input.txt", "Parm4", function(err) { - if(err) { return console.log(err); } - console.log("Parm1 outlet off Success"); - }); - } - }, -} - -// Generate a consistent UUID for our light Accessory that will remain the same even when -// restarting our server. We use the `uuid.generate` helper function to create a deterministic -// UUID based on an arbitrary "namespace" and the word "light". -var outletUUID01 = uuid.generate('hap-nodejs:accessories:Parm1'); - -// This is the Accessory that we'll return to HAP-NodeJS that represents our GENERIC light. -var outlet01 = exports.accessory = new Accessory('Parm1', outletUUID01); - -// Add properties for publishing (in case we're using Core.js and not BridgedCore.js) -outlet01.username = "Parm2"; -outlet01.pincode = "031-45-154"; -outlet01.displayName = "Parm1"; - -// set some basic properties (these values are arbitrary and setting them is optional) -outlet01 - .getService(Service.AccessoryInformation) - .setCharacteristic(Characteristic.Manufacturer, "oddwires.co.uk") - .setCharacteristic(Characteristic.Model, "Ver 1.0") - .setCharacteristic(Characteristic.SerialNumber, "Parm5"); - -// Add the actual outlet Service and listen for change events from iOS. -// We can see the complete list of Services and Characteristics in `lib/gen/HomeKitTypes.js` -outlet01 - .addService(Service.Outlet, "Parm1") // services exposed to the user should have "names" like "GENERIC Light" for us - .getCharacteristic(Characteristic.On) - .on('set', function(value, callback) { - GENERIC_OUTLET01.setPowerOn(value); - callback(); // Our GENERIC Light is synchronous - this value has been successfully set - }); - -// We want to intercept requests for our current power state so we can query the hardware itself instead of -// allowing HAP-NodeJS to return the cached Characteristic.value. -outlet01 - .getService(Service.Outlet) - .getCharacteristic(Characteristic.On) - .on('get', function(callback) { - // this event is emitted when you ask Siri directly whether your outlet is on or not. you might query - // the outlet hardware itself to find this out, then call the callback. But if you take longer than a - // few seconds to respond, Siri will give up. - var err = null; // in case there were any problems - fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { - if(err) { return console.log(err); } - var lines = data.split('\n'); - for(var i = 0; i < lines.length; i++){ - if ((lines[i].indexOf("rcon") !=-1) && (lines[i].indexOf("Parm1") !=-1)) { - // falls through here when we have found the line for this device in the status file - // console.log(lines[i]); - var values = lines[i].split(':'); - // read the actual status of the accessory from the file - console.log(values[5]); - if (values[5].indexOf("On") !=-1) { - console.log("Current status: on"); - GENERIC_OUTLET01.powerOn = true; - callback(err, true); - } else { - console.log("Current status: off"); - GENERIC_OUTLET01.powerOn = false; - callback(err, false); - } - } - } - }); - }); \ No newline at end of file diff --git a/ConfigFiles/Generic_Outlet.ts b/ConfigFiles/Generic_Outlet.ts new file mode 100644 index 0000000..cd080c2 --- /dev/null +++ b/ConfigFiles/Generic_Outlet.ts @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// First parameter (Accessory name) : Parm1 +// Second parameter (MAC address) : Parm2 +// Third parameter (command string to switch accessory on) : Parm3 +// Fourth parameter (command string to switch accessory off) : Parm4 +// Fifth parameter (Address and Channel info) : Parm5 +/////////////////////////////////////////////////////////////////////////////////////////////////// + +import { + Accessory, + AccessoryEventTypes, + Categories, + Characteristic, + CharacteristicEventTypes, CharacteristicSetCallback, + CharacteristicValue, + NodeCallback, + Service, + uuid, + VoidCallback, +} from '..'; +import fs from 'fs'; + +class OutletControllerClass { + name: CharacteristicValue = "Parm1"; + pincode: CharacteristicValue = "031-45-154"; + username: CharacteristicValue = "Parm2"; + manufacturer: CharacteristicValue = "oddwires.co.uk"; + model: CharacteristicValue = "v1.0"; + serialNumber: CharacteristicValue = "Parm5"; + + power: CharacteristicValue = false; + outputLogs = true; + + setPower(status: CharacteristicValue) { + if(this.outputLogs) console.log("%s: switching outlet %s", this.name, status ? "on" : "off"); + if (status) { + fs.appendFile("/var/www/data/input.txt", "Parm3", function(err) { + if(err) { return console.log(err); } + }); + } else { + fs.appendFile("/var/www/data/input.txt", "Parm4", function(err) { + if(err) { return console.log(err); } + }); + } + } + +identify() { + if(this.outputLogs) console.log("Identify the '%s'", this.name); + } +} + +const OutletController = new OutletControllerClass(); +var OutletUUID = uuid.generate('hap-nodejs:accessories:Outlet' + OutletController.name); +var OutletAccessory = exports.accessory = new Accessory(OutletController.name as string, OutletUUID); + +// @ts-ignore +OutletAccessory.username = OutletController.username; +// @ts-ignore +OutletAccessory.pincode = OutletController.pincode; +// @ts-ignore +OutletAccessory.category = Categories.Outlet; + +OutletAccessory + .getService(Service.AccessoryInformation)! + .setCharacteristic(Characteristic.Manufacturer, OutletController.manufacturer) + .setCharacteristic(Characteristic.Model, OutletController.model) + .setCharacteristic(Characteristic.SerialNumber, OutletController.serialNumber); + +OutletAccessory.on(AccessoryEventTypes.IDENTIFY, (paired: boolean, callback: VoidCallback) => { + OutletController.identify(); + callback(); +}); + +OutletAccessory + .addService(Service.Outlet, OutletController.name) + .getCharacteristic(Characteristic.On)! + .on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => { + OutletController.setPower(value); + callback(); + }); + +OutletAccessory + .getService(Service.Outlet)! + .getCharacteristic(Characteristic.On)! + .on(CharacteristicEventTypes.GET, (callback: CharacteristicSetCallback) => { + fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { + if(err) { return console.log(err); } + var lines = data.split('\n'); + for(var i = 0; i < lines.length; i++){ + if ((lines[i].indexOf("rcon") !=-1) && (lines[i].indexOf("Parm1") !=-1)) { + var values = lines[i].split(':'); + if (values[6].indexOf("On") !=-1) { + console.log("Parm1: Get status: On"); + callback(err, true); + } else { + console.log("Parm1: Get status: Off"); + callback(err, false); + } + } + } + }); + }) \ No newline at end of file diff --git a/ConfigFiles/Generic_Radiator.js b/ConfigFiles/Generic_Radiator.js deleted file mode 100644 index b2ac00b..0000000 --- a/ConfigFiles/Generic_Radiator.js +++ /dev/null @@ -1,154 +0,0 @@ -// First parameter = Parm1 = Radiator name -// Second parameter = Parm2 = MAC address -// Third parameter = Parm3 = Command string to switch radiator on -// Fourth parameter = Parm4 = Command string to switch radiator off -// Fifth parameter = Parm5 = Maximum temperature -// Sixth parameter = Parm6 = Minimum temperature - -// HomeKit types required -var types = require("./types.js") -var exports = module.exports = {}; -var fs = require('fs'); -var Accessory = require('../').Accessory; -var Service = require('../').Service; -var Characteristic = require('../').Characteristic; -var uuid = require('../').uuid; -var radiatorNAME = 'Parm1'; //the temperature sensor's name - this is what Siri responds to -var uuidNAME = 'hap-nodejs:accessories:Parm1'; //UUID name - -// Generic radiator valve that we'll expose to HomeKit -var Generic_valve = { - heatOn: false, - - setHeatOn: function(on) { - console.log("Set Parm1 radiator: %s", on ? "on" : "off"); - if (on) { Generic_sensor.TargetHeatingCoolingState = 0; - fs.appendFile("/var/www/data/input.txt", "Parm3", function(err) { - if (err) { return console.log(err); } - }); - } else { Generic_sensor.TargetHeatingCoolingState = 1; - fs.appendFile("/var/www/data/input.txt", "Parm4", function(err) { - if (err) { return console.log(err); } - }); - } - }, -} - -// HomeKit doesn't support radiators, so this accessory is a modified temperature sensor. -// here's the temperature sensor device that we'll expose to HomeKit... -var Generic_sensor = { - currentTemperature: 20, - getTemperature: function() { - console.log("Getting the current temperature."); - return Generic_sensor.currentTemperature; - }, - incTemperature: function() { - if (Generic_sensor.currentTemperature >= 25) { step = -1; } - if (Generic_sensor.currentTemperature <= 20) { step = 1; } -// if (Generic_sensor.targetTemperature > Generic_sensor.currentTemperature) - if (Generic_sensor.currentTemperature > 23) - { Generic_sensor.TargetHeatingCoolingState = 1; -// console.log("Greater than 23."); - } - else - { Generic_sensor.TargetHeatingCoolingState = 0; -// console.log("Less than 23."); - } - Generic_sensor.currentTemperature = Generic_sensor.currentTemperature + step; - }, getTemperature: function() { - fs.readFile("/var/data/app-sensor/current_values.txt", 'utf-8', function(err, data) { - if (err) { return console.log(err); } - var json = JSON.parse(data); // convert JSON data to array -// console.log(json["Parm1"]); // Debug - Generic_sensor.currentTemperature = (json["Parm1"]); - }); - }, -} - -// Generate a consistent UUID for our Temperature Sensor Accessory that will remain the same -// even when restarting our server. We use the `uuid.generate` helper function to create -// a deterministic UUID based on an arbitrary "namespace" and the string "temperature-sensor". -var sensorUUID = uuid.generate(uuidNAME); -var sensor = exports.accessory = new Accessory(radiatorNAME, sensorUUID); -sensor.username = "Parm2"; -sensor.pincode = "031-45-154"; -sensor.displayname = "Parm1"; - -// set some basic properties (these values are arbitrary and setting them is optional) -sensor - .getService(Service.AccessoryInformation) - .setCharacteristic(Characteristic.Manufacturer, "Smartwares") - .setCharacteristic(Characteristic.Model, "SHS-5300") - .setCharacteristic(Characteristic.SerialNumber, "4500176458"); - -// Add the actual TemperatureSensor Service. -// Full list of Services and Characteristics in `lib/gen/HomeKitTypes.js` -sensor - .addService(Service.Thermostat) - -sensor - .getService(Service.Thermostat) - .getCharacteristic(Characteristic.TargetHeatingCoolingState) - .setProps({ -// validValues: [0, 1, 2, 3] // Off, Heat, Cool & Auto - validValues: [0, 1, 3] // Off, Heat & Auto - }) - .on('set', function(value, callback) { - Generic_valve.setHeatOn(value); - callback(); - }); -sensor - .getService(Service.Thermostat) - .getCharacteristic(Characteristic.CurrentHeatingCoolingState) - - .on('get', function(callback) { - var err = null; // in case there are any problems - fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { - if (err) { return console.log(err); } - var lines = data.split('\n'); - for(var i = 0; i < lines.length; i++){ - if ((lines[i].indexOf("rdtr") !=-1) && (lines[i].indexOf("Parm1") !=-1)) { - // falls through here when we have found the line for this device in the status file - // console.log(lines[i]); // debug - var values = lines[i].split(':'); - // read the actual status of the accessory from the file - if (values[4].indexOf("On") !=-1) { - console.log("Get Parm1 radiator status: On"); - Generic_valve.heatOn = true; - Generic_sensor.CurrentHeatingCoolingState = 1; - callback(err, true); - return; - } else { - console.log("Get Parm1 radiator status: Off"); - Generic_valve.heatOn = false; - Generic_sensor.CurrentHeatingCoolingState = 0; - callback(err, false); - return; - } - } - } - }); - }); - -sensor - .getService(Service.Thermostat) - .getCharacteristic(Characteristic.TargetTemperature) - .setProps({ - maxValue: 28, - minValue: 5 - }) - .on('set', function(value, callback) { - console.log("Parm1 radiator target temp:",value); - callback(); - }); - - -// cycle the temperature reading -setInterval(function() { -//Generic_sensor.incTemperature(); - Generic_sensor.getTemperature(); - // update the characteristic value so interested iOS devices can get notified - sensor - .getService(Service.Thermostat) - .setCharacteristic(Characteristic.CurrentTemperature, Generic_sensor.currentTemperature); -}, 3000); \ No newline at end of file diff --git a/ConfigFiles/Generic_Radiator.ts b/ConfigFiles/Generic_Radiator.ts new file mode 100644 index 0000000..45a3bc4 --- /dev/null +++ b/ConfigFiles/Generic_Radiator.ts @@ -0,0 +1,220 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// First parameter (Radiator name) : Parm1 +// Second parameter (MAC address) : Parm2 +// Third parameter (command string to switch accessory on) : Parm3 +// Fourth parameter (command string to switch accessory off) : Parm4 +// Fifth parameter (Maximum temperature) : Parm5 +// Sixth parameter (Minimum temperature) : Parm6 +/////////////////////////////////////////////////////////////////////////////////////////////////// + +import {Accessory, AccessoryEventTypes, Categories, Characteristic, CharacteristicEventTypes, CharacteristicSetCallback, +CharacteristicValue, NodeCallback, Service, uuid, VoidCallback } from '..'; +import fs from 'fs'; + +class RadiatorControllerClass { + name: CharacteristicValue = "Parm1"; + displayName: CharacteristicValue = "Parm1"; + pincode: CharacteristicValue = "031-45-154"; + username: CharacteristicValue = "CC:22:3D:B3:CE:00"; + HeatingState: CharacteristicValue = 0; + TargetHeatingState: CharacteristicValue = 0; + TargetTemperature: CharacteristicValue = 0; + DummyCurrentTemp = 25; + RealCurrentTemp = 25; + step = 1; + + outputLogs = true; + + SetHeatingOn(status: CharacteristicValue) { + if(this.outputLogs) console.log("Parm1: Set heating state: %s", status); + if(this.outputLogs) { // convert status to format used in the accessory interface... + if ( status == 0 ) { console.log("Parm1: Set heating state: Off"); } + if ( status == 1 ) { console.log("Parm1: Set heating state: Heat"); } + if ( status == 3 ) { console.log("Parm1: Set heating state: Auto"); } + }; + this.TargetHeatingState = status; + // following code ensures we only send commands to the radiator valve when a change of state is required + // ( otherwise they would be beeping all the time ) + if ( this.TargetHeatingState != this.HeatingState ) { + if (( this.TargetHeatingState == 0 ) && ( this.HeatingState !=0 )) { + // 0 indicates 'off'. If it isn't already off, switch it off. + this.HeatingState = 0; + fs.appendFile("/var/www/data/input.txt", "Parm4", function(err) { + if(err) { return console.log(err); } + }); + } + if (( this.TargetHeatingState == 1 ) && ( this.HeatingState !=1 )) { + // 1 indicates 'on'. If it isn't already on, switch it on. + this.HeatingState = 1; + fs.appendFile("/var/www/data/input.txt", "Parm3", function(err) { + if(err) { return console.log(err); } + }); + } + } // NOTE: HeatingState = 3 indicates 'auto', and is implemented in the DummyTemp / RealTemp function. + } + +// Function to get (real) temperature data from ESP8266 based sensor... + RealTemp() { + let that = this; + fs.readFile("/var/data/app-sensor/current_values.txt", 'utf-8', function(err, data) { + if (err) { return console.log(err); } + const json = JSON.parse(data); // NOTE: if object isn't found, returns the string 'undefined' + that.RealCurrentTemp = +json[ 'Parm1' ]; // convert string to numeric + }); + if ( this.TargetHeatingState == 3 ) { // Heating state = auto + if ( this.RealCurrentTemp >= this.TargetTemperature ) { // Too hot ! + if ( this.HeatingState !=0 ) { // If radiator isn't already off, switch it off. + if(this.outputLogs) console.log("%s: Currently %sC - Too hot. Turning the radiator off.", this.name, this.RealCurrentTemp ); + this.HeatingState = 0; + fs.appendFile("/var/www/data/input.txt", "Parm4", function(err) { // send command to alarm service + if(err) { return console.log(err); } + }); + } + } + if ( this.RealCurrentTemp < this.TargetTemperature ) { // Too cold ! + if ( this.HeatingState !=1 ) { // If radiator isn't already on, switch it on. + if(this.outputLogs) console.log("%s: Currently %sC - Too cold. Turning the radaitor on.", this.name, this.RealCurrentTemp ); + this.HeatingState = 1; + fs.appendFile("/var/www/data/input.txt", "Parm3", function(err) { // send command to alarm service + if(err) { return console.log(err); } + }); + } + } + } + return this.RealCurrentTemp + } + +// Function to create dummy temperature data... + DummyTemp() { + if (this.DummyCurrentTemp >= 25) this.step = -1; // Count down + if (this.DummyCurrentTemp <= 20) this.step = 1; // Count up + this.DummyCurrentTemp = this.DummyCurrentTemp + this.step; + + if ( this.TargetHeatingState == 3 ) { // Heating state = auto + if ( this.DummyCurrentTemp >= this.TargetTemperature ) { // Too hot ! + if ( this.HeatingState !=0 ) { // If radiator isn't already off, switch it off. + if(this.outputLogs) console.log("%s: Currently %sC - Too hot. Turning the radiator off.", this.name, this.DummyCurrentTemp ); + this.HeatingState = 0; + fs.appendFile("/var/www/data/input.txt", "Parm4", function(err) { // send command to alarm service + if(err) { return console.log(err); } + }); + } + } + if ( this.DummyCurrentTemp < this.TargetTemperature ) { // Too cold ! + if ( this.HeatingState !=1 ) { // If radiator isn't already on, switch it on. + if(this.outputLogs) console.log("%s: Currently %sC - Too cold. Turning the radaitor on.", this.name, this.DummyCurrentTemp ); + this.HeatingState = 1; + fs.appendFile("/var/www/data/input.txt", "Parm3", function(err) { // send command to alarm service + if(err) { return console.log(err); } + }); + } + } + return this.DummyCurrentTemp + } + } + + SetTargetTemp(value: CharacteristicValue) { this.TargetTemperature = value; } + + Identify() { if(this.outputLogs) console.log("Identify the '%s'", this.name); } +} +const Radiator = new RadiatorControllerClass(); +var RadiatorUUID = uuid.generate('hap-nodejs:accessories:Radiator' + Radiator.name); +var RadiatorAccessory = exports.accessory = new Accessory(Radiator.name as string, RadiatorUUID); + +RadiatorAccessory + .getService(Service.AccessoryInformation)! + .setCharacteristic(Characteristic.Manufacturer, "Smartwares") + .setCharacteristic(Characteristic.Model, "SHS-5300") + .setCharacteristic(Characteristic.SerialNumber, "4500176458"); + +RadiatorAccessory.on(AccessoryEventTypes.IDENTIFY, (paired: boolean, callback: VoidCallback) => { + Radiator.Identify(); + callback(); +}); + +RadiatorAccessory! + .addService(Service.Thermostat, Radiator.name)! + .getCharacteristic(Characteristic.TargetHeatingCoolingState)! + .setProps({ + validValues: [0, 1, 3] // Off, Heat & Auto + }) + .on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => { + Radiator.SetHeatingOn(value); + callback(); + }) + + RadiatorAccessory! + .getService(Service.Thermostat)! + .getCharacteristic(Characteristic.TargetTemperature)! + .setProps({ +// maxValue: Parm5, // If max and min are set too close, the accessory will cause Homebridge to crash. +// minValue: Parm6 // So hard coding these values as a workaround... + maxValue: 30, + minValue: 10 + }) + .on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => { + console.log("Parm1: Set target temp:",value); + Radiator.SetTargetTemp(value); + callback(); + }) + +////////////////////// + RadiatorAccessory! + .addService(Service.TemperatureSensor)! + .getCharacteristic(Characteristic.CurrentTemperature)! + .on(CharacteristicEventTypes.GET, (callback: NodeCallback) => { + callback(null, Radiator.RealTemp()); +}); + +// This should be handled through the function in the class, only I can't get the callback to work. + // .on(CharacteristicEventTypes.GET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => { + // Radiator.GetHeatingOn(value); + // callback(); + // }) +// So using an external function as a workaround... + + RadiatorAccessory! + .getService(Service.Thermostat)! + .getCharacteristic(Characteristic.CurrentHeatingCoolingState)! + .on(CharacteristicEventTypes.GET, (callback: CharacteristicSetCallback) => { + fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { + if (err) { return console.log(err); } + var lines = data.split('\n'); + for(var i = 0; i < lines.length; i++){ + if ((lines[i].indexOf("rdtr") !=-1) && (lines[i].indexOf("Parm1") !=-1)) { + // falls through here when we have found the line for this device in the status file + // console.log(lines[i]); // debug + var values = lines[i].split(':'); + if (values[4].indexOf("On") !=-1) { + console.log("Parm1 radiator: Get status: On"); + Radiator.HeatingState = 1; + callback(err, true); + return; + } else { + console.log("Parm1 radiator: Get status: Off"); + Radiator.HeatingState = 0; + callback(err, false); + return; + } + } + } + }); + }); + +// Use this section if you don't have a temperature sensor to provide data... +// Cycle the temperature reading... +setInterval(function() { + Radiator.DummyTemp(); + RadiatorAccessory + .getService(Service.Thermostat)! + .setCharacteristic(Characteristic.CurrentTemperature, Radiator.DummyCurrentTemp); +}, 3000); + +// Use this section if you do have a temperature sensor to provide real data... +// cycle the temperature reading... +//setInterval(function() { +// Radiator.RealTemp(); +// RadiatorAccessory +// .getService(Service.Thermostat)! +// .setCharacteristic(Characteristic.CurrentTemperature, Radiator.RealCurrentTemp); +//}, 3000); \ No newline at end of file diff --git a/ConfigFiles/Hub_accessory.js b/ConfigFiles/Hub_accessory.js deleted file mode 100644 index e7ced0d..0000000 --- a/ConfigFiles/Hub_accessory.js +++ /dev/null @@ -1,121 +0,0 @@ -// HomeKit types required -var fs = require('fs'); -var types = require("./types.js") -var exports = module.exports = {}; - -var exec = require('child_process').exec; - -var execute = function(accessory,characteristic,value){ - console.log("executed accessory: " + accessory + ", and characteristic: " + characteristic + ", with value: " + value + "."); -} - -exports.accessory = { - displayName: "Hub", - username: "1A:2B:3C:4D:5E:FE", - pincode: "031-45-154", - services: [{ - sType: types.ACCESSORY_INFORMATION_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Hub", - supportEvents: false, - supportBonjour: false, - manfDescription: "Bla", - designedMaxLength: 255 - },{ - cType: types.MANUFACTURER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "oddwires.co.uk", - supportEvents: false, - supportBonjour: false, - manfDescription: "Bla", - designedMaxLength: 255 - },{ - cType: types.MODEL_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Rev-1", - supportEvents: false, - supportBonjour: false, - manfDescription: "Bla", - designedMaxLength: 255 - },{ - cType: types.SERIAL_NUMBER_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "A1S2NASF88EW", - supportEvents: false, - supportBonjour: false, - manfDescription: "Bla", - designedMaxLength: 255 - },{ - cType: types.IDENTIFY_CTYPE, - onUpdate: null, - perms: ["pw"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Identify Accessory", - designedMaxLength: 1 - }] - },{ - sType: types.OUTLET_STYPE, - characteristics: [{ - cType: types.NAME_CTYPE, - onUpdate: null, - perms: ["pr"], - format: "string", - initialValue: "Hub Service", - supportEvents: false, - supportBonjour: false, - manfDescription: "Bla", - designedMaxLength: 255 - },{ - cType: types.POWER_STATE_CTYPE, - onUpdate: function(value) - { - console.log("Change:",value); - if (value) { - fs.writeFile("/var/www/data/input.txt", ":192.168.1.7:rcon swtch:7:on\n", function(err) { - if(err) { - return console.log(err); - } - console.log("On Success"); - }); - } else { - fs.writeFile("/var/www/data/input.txt", ":192.168.1.7:rcon swtch:7:off\n", function(err) { - if(err) { - return console.log(err); - } - console.log("Off Success"); - }); - } - }, - perms: ["pw","pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Turn On the Light", - designedMaxLength: 1 - },{ - cType: types.OUTLET_IN_USE_CTYPE, - onUpdate: function(value) { console.log("Change:",value); execute("Test Accessory 1", "light service", value); }, - perms: ["pr","ev"], - format: "bool", - initialValue: false, - supportEvents: false, - supportBonjour: false, - manfDescription: "Turn On the Light", - designedMaxLength: 1 - }] - }] -} \ No newline at end of file diff --git a/ConfigFiles/Office radiator_accessory.sj b/ConfigFiles/Office radiator_accessory.sj deleted file mode 100644 index fdfc1bf..0000000 --- a/ConfigFiles/Office radiator_accessory.sj +++ /dev/null @@ -1,152 +0,0 @@ -// First parameter = Office = Radiator name -// Second parameter = fa:3c:ed:5a:1a:27 = MAC address -// Third parameter = Siri:iPhone:rdtr swtch:7:On\n = Command string to switch radiator on -// Fourth parameter = Siri:iPhone:rdtr swtch:7:Off\n = Command string to switch radiator off -// Fifth parameter = 26 = Maximum temperature -// Sixth parameter = 5 = Minimum temperature - -// HomeKit types required -var types = require("./types.js") -var exports = module.exports = {}; -var fs = require('fs'); -var Accessory = require('../').Accessory; -var Service = require('../').Service; -var Characteristic = require('../').Characteristic; -var uuid = require('../').uuid; -var radiatorNAME = 'Office radiator'; //the temperature sensor's name - this is what Siri responds to -var uuidNAME = 'hap-nodejs:accessories:Office'; //UUID name - -// Generic radiator valve that we'll expose to HomeKit -var Generic_valve = { - heatOn: false, - - setHeatOn: function(on) { - console.log("Set Office radiator: %s", on ? "on" : "off"); - if (on) { Generic_sensor.TargetHeatingCoolingState = 0; - fs.appendFile("/var/www/data/input.txt", "Siri:iPhone:rdtr swtch:7:On\n", function(err) { - if (err) { return console.log(err); } - }); - } else { Generic_sensor.TargetHeatingCoolingState = 1; - fs.appendFile("/var/www/data/input.txt", "Siri:iPhone:rdtr swtch:7:Off\n", function(err) { - if (err) { return console.log(err); } - }); - } - }, -} - -// HomeKit doesn't support radiators, so this accessory is a modified temperature sensor. -// here's the temperature sensor device that we'll expose to HomeKit... -var Generic_sensor = { - currentTemperature: 20, - getTemperature: function() { - console.log("Getting the current temperature."); - return Generic_sensor.currentTemperature; - }, - incTemperature: function() { - if (Generic_sensor.currentTemperature >= 25) { step = -1; } - if (Generic_sensor.currentTemperature <= 20) { step = 1; } -// if (Generic_sensor.targetTemperature > Generic_sensor.currentTemperature) - if (Generic_sensor.currentTemperature > 23) - { Generic_sensor.TargetHeatingCoolingState = 1; -// console.log("Greater than 23."); - } - else - { Generic_sensor.TargetHeatingCoolingState = 0; -// console.log("Less than 23."); - } - Generic_sensor.currentTemperature = Generic_sensor.currentTemperature + step; - }, getTemperature: function() { - fs.readFile("/var/www/logs/serialized.txt", 'utf-8', function(err, data) { - if (err) { return console.log(err); } - var json = JSON.parse(data); // convert JSON data to array -// console.log(json["Office"]); // Debug - Generic_sensor.currentTemperature = (json["Office"]); - }); - }, -} - -// Generate a consistent UUID for our Temperature Sensor Accessory that will remain the same -// even when restarting our server. We use the `uuid.generate` helper function to create -// a deterministic UUID based on an arbitrary "namespace" and the string "temperature-sensor". -var sensorUUID = uuid.generate(uuidNAME); -var sensor = exports.accessory = new Accessory(radiatorNAME, sensorUUID); -sensor.username = "fa:3c:ed:5a:1a:27"; -sensor.pincode = "031-45-154"; -sensor.displayname = "Office"; - -// set some basic properties (these values are arbitrary and setting them is optional) -sensor - .getService(Service.AccessoryInformation) - .setCharacteristic(Characteristic.Manufacturer, "Smartwares") - .setCharacteristic(Characteristic.Model, "SHS-5300") - .setCharacteristic(Characteristic.SerialNumber, "4500176458"); - -// Add the actual TemperatureSensor Service. -// Full list of Services and Characteristics in `lib/gen/HomeKitTypes.js` -sensor - .addService(Service.Thermostat) - -sensor - .getService(Service.Thermostat) - .getCharacteristic(Characteristic.TargetHeatingCoolingState) - .setProps({ -// validValues: [0, 1, 2, 3] // Off, Heat, Cool & Auto - validValues: [0, 1, 3] // Off, Heat & Auto - }) - .on('set', function(value, callback) { - Generic_valve.setHeatOn(value); - callback(); - }); -sensor - .getService(Service.Thermostat) - .getCharacteristic(Characteristic.CurrentHeatingCoolingState) - - .on('get', function(callback) { - var err = null; // in case there are any problems - fs.readFile("/var/www/data/status.txt", 'utf8', function(err, data) { - if (err) { return console.log(err); } - var lines = data.split('\n'); - for(var i = 0; i < lines.length; i++){ - if ((lines[i].indexOf("rdtr") !=-1) && (lines[i].indexOf("Office") !=-1)) { - // falls through here when we have found the line for this device in the status file - // console.log(lines[i]); // debug - var values = lines[i].split(':'); - // read the actual status of the accessory from the file - if (values[4].indexOf("On") !=-1) { - console.log("Get Office radiator status: On"); - Generic_valve.heatOn = true; - Generic_sensor.CurrentHeatingCoolingState = 1; - callback(err, true); - } else { - console.log("Get Office radiator status: Off"); - Generic_valve.heatOn = false; - Generic_sensor.CurrentHeatingCoolingState = 0; - callback(err, false); - } - } - } - }); - }); - -sensor - .getService(Service.Thermostat) - .getCharacteristic(Characteristic.TargetTemperature) - .setProps({ - maxValue: 28, - minValue: 5 - }) - .on('set', function(value, callback) { - console.log("Office radiator target temp:",value); - callback(); - }); - - -// cycle the temperature reading -setInterval(function() { -//Generic_sensor.incTemperature(); - Generic_sensor.getTemperature(); - // update the characteristic value so interested iOS devices can get notified - sensor - .getService(Service.Thermostat) - .setCharacteristic(Characteristic.CurrentTemperature, Generic_sensor.currentTemperature); -}, 3000); \ No newline at end of file diff --git a/ConfigFiles/types.js b/ConfigFiles/types.js deleted file mode 100644 index b399763..0000000 --- a/ConfigFiles/types.js +++ /dev/null @@ -1,93 +0,0 @@ -var exports = module.exports = {}; - -//HomeKit Types UUID's - -var stPre = "000000"; -var stPost = "-0000-1000-8000-0026BB765291"; - - -//HomeKitTransportCategoryTypes -exports.OTHER_TCTYPE = 1; -exports.FAN_TCTYPE = 3; -exports.GARAGE_DOOR_OPENER_TCTYPE = 4; -exports.LIGHTBULB_TCTYPE = 5; -exports.DOOR_LOCK_TCTYPE = 6; -exports.OUTLET_TCTYPE = 7; -exports.SWITCH_TCTYPE = 8; -exports.THERMOSTAT_TCTYPE = 9; -exports.SENSOR_TCTYPE = 10; -exports.ALARM_SYSTEM_TCTYPE = 11; -exports.DOOR_TCTYPE = 12; -exports.WINDOW_TCTYPE = 13; -exports.WINDOW_COVERING_TCTYPE = 14; -exports.PROGRAMMABLE_SWITCH_TCTYPE = 15; - -//HomeKitServiceTypes - -exports.LIGHTBULB_STYPE = stPre + "43" + stPost; -exports.SWITCH_STYPE = stPre + "49" + stPost; -exports.THERMOSTAT_STYPE = stPre + "4A" + stPost; -exports.GARAGE_DOOR_OPENER_STYPE = stPre + "41" + stPost; -exports.ACCESSORY_INFORMATION_STYPE = stPre + "3E" + stPost; -exports.FAN_STYPE = stPre + "40" + stPost; -exports.OUTLET_STYPE = stPre + "47" + stPost; -exports.LOCK_MECHANISM_STYPE = stPre + "45" + stPost; -exports.LOCK_MANAGEMENT_STYPE = stPre + "44" + stPost; -exports.ALARM_STYPE = stPre + "7E" + stPost; -exports.WINDOW_COVERING_STYPE = stPre + "8C" + stPost; -exports.OCCUPANCY_SENSOR_STYPE = stPre + "86" + stPost; -exports.CONTACT_SENSOR_STYPE = stPre + "80" + stPost; -exports.MOTION_SENSOR_STYPE = stPre + "85" + stPost; -exports.HUMIDITY_SENSOR_STYPE = stPre + "82" + stPost; -exports.TEMPERATURE_SENSOR_STYPE = stPre + "8A" + stPost; - -//HomeKitCharacteristicsTypes - - -exports.ALARM_CURRENT_STATE_CTYPE = stPre + "66" + stPost; -exports.ALARM_TARGET_STATE_CTYPE = stPre + "67" + stPost; -exports.ADMIN_ONLY_ACCESS_CTYPE = stPre + "01" + stPost; -exports.AUDIO_FEEDBACK_CTYPE = stPre + "05" + stPost; -exports.BRIGHTNESS_CTYPE = stPre + "08" + stPost; -exports.BATTERY_LEVEL_CTYPE = stPre + "68" + stPost; -exports.COOLING_THRESHOLD_CTYPE = stPre + "0D" + stPost; -exports.CONTACT_SENSOR_STATE_CTYPE = stPre + "6A" + stPost; -exports.CURRENT_DOOR_STATE_CTYPE = stPre + "0E" + stPost; -exports.CURRENT_LOCK_MECHANISM_STATE_CTYPE = stPre + "1D" + stPost; -exports.CURRENT_RELATIVE_HUMIDITY_CTYPE = stPre + "10" + stPost; -exports.CURRENT_TEMPERATURE_CTYPE = stPre + "11" + stPost; -exports.HEATING_THRESHOLD_CTYPE = stPre + "12" + stPost; -exports.HUE_CTYPE = stPre + "13" + stPost; -exports.IDENTIFY_CTYPE = stPre + "14" + stPost; -exports.LOCK_MANAGEMENT_AUTO_SECURE_TIMEOUT_CTYPE = stPre + "1A" + stPost; -exports.LOCK_MANAGEMENT_CONTROL_POINT_CTYPE = stPre + "19" + stPost; -exports.LOCK_MECHANISM_LAST_KNOWN_ACTION_CTYPE = stPre + "1C" + stPost; -exports.LOGS_CTYPE = stPre + "1F" + stPost; -exports.MANUFACTURER_CTYPE = stPre + "20" + stPost; -exports.MODEL_CTYPE = stPre + "21" + stPost; -exports.MOTION_DETECTED_CTYPE = stPre + "22" + stPost; -exports.NAME_CTYPE = stPre + "23" + stPost; -exports.OBSTRUCTION_DETECTED_CTYPE = stPre + "24" + stPost; -exports.OUTLET_IN_USE_CTYPE = stPre + "26" + stPost; -exports.OCCUPANCY_DETECTED_CTYPE = stPre + "71" + stPost; -exports.POWER_STATE_CTYPE = stPre + "25" + stPost; -exports.PROGRAMMABLE_SWITCH_SWITCH_EVENT_CTYPE = stPre + "73" + stPost; -exports.PROGRAMMABLE_SWITCH_OUTPUT_STATE_CTYPE = stPre + "74" + stPost; -exports.ROTATION_DIRECTION_CTYPE = stPre + "28" + stPost; -exports.ROTATION_SPEED_CTYPE = stPre + "29" + stPost; -exports.SATURATION_CTYPE = stPre + "2F" + stPost; -exports.SERIAL_NUMBER_CTYPE = stPre + "30" + stPost; -exports.STATUS_LOW_BATTERY_CTYPE = stPre + "79" + stPost; -exports.STATUS_FAULT_CTYPE = stPre + "77" + stPost; -exports.TARGET_DOORSTATE_CTYPE = stPre + "32" + stPost; -exports.TARGET_LOCK_MECHANISM_STATE_CTYPE = stPre + "1E" + stPost; -exports.TARGET_RELATIVE_HUMIDITY_CTYPE = stPre + "34" + stPost; -exports.TARGET_TEMPERATURE_CTYPE = stPre + "35" + stPost; -exports.TEMPERATURE_UNITS_CTYPE = stPre + "36" + stPost; -exports.VERSION_CTYPE = stPre + "37" + stPost; -exports.WINDOW_COVERING_TARGET_POSITION_CTYPE = stPre + "7C" + stPost; -exports.WINDOW_COVERING_CURRENT_POSITION_CTYPE = stPre + "6D" + stPost; -exports.WINDOW_COVERING_OPERATION_STATE_CTYPE = stPre + "72" + stPost; -exports.CURRENTHEATINGCOOLING_CTYPE = stPre + "0F" + stPost; -exports.TARGETHEATINGCOOLING_CTYPE = stPre + "33" + stPost; - diff --git a/ConfigFiles/types.ts b/ConfigFiles/types.ts new file mode 100644 index 0000000..91f5f7e --- /dev/null +++ b/ConfigFiles/types.ts @@ -0,0 +1,91 @@ +//HomeKit Types UUID's + +const stPre = "000000"; +const stPost = "-0000-1000-8000-0026BB765291"; + + +//HomeKitTransportCategoryTypes +export const OTHER_TCTYPE = 1; +export const FAN_TCTYPE = 3; +export const GARAGE_DOOR_OPENER_TCTYPE = 4; +export const LIGHTBULB_TCTYPE = 5; +export const DOOR_LOCK_TCTYPE = 6; +export const OUTLET_TCTYPE = 7; +export const SWITCH_TCTYPE = 8; +export const THERMOSTAT_TCTYPE = 9; +export const SENSOR_TCTYPE = 10; +export const ALARM_SYSTEM_TCTYPE = 11; +export const DOOR_TCTYPE = 12; +export const WINDOW_TCTYPE = 13; +export const WINDOW_COVERING_TCTYPE = 14; +export const PROGRAMMABLE_SWITCH_TCTYPE = 15; + +//HomeKitServiceTypes + +export const LIGHTBULB_STYPE = stPre + "43" + stPost; +export const SWITCH_STYPE = stPre + "49" + stPost; +export const THERMOSTAT_STYPE = stPre + "4A" + stPost; +export const GARAGE_DOOR_OPENER_STYPE = stPre + "41" + stPost; +export const ACCESSORY_INFORMATION_STYPE = stPre + "3E" + stPost; +export const FAN_STYPE = stPre + "40" + stPost; +export const OUTLET_STYPE = stPre + "47" + stPost; +export const LOCK_MECHANISM_STYPE = stPre + "45" + stPost; +export const LOCK_MANAGEMENT_STYPE = stPre + "44" + stPost; +export const ALARM_STYPE = stPre + "7E" + stPost; +export const WINDOW_COVERING_STYPE = stPre + "8C" + stPost; +export const OCCUPANCY_SENSOR_STYPE = stPre + "86" + stPost; +export const CONTACT_SENSOR_STYPE = stPre + "80" + stPost; +export const MOTION_SENSOR_STYPE = stPre + "85" + stPost; +export const HUMIDITY_SENSOR_STYPE = stPre + "82" + stPost; +export const TEMPERATURE_SENSOR_STYPE = stPre + "8A" + stPost; + +//HomeKitCharacteristicsTypes + + +export const ALARM_CURRENT_STATE_CTYPE = stPre + "66" + stPost; +export const ALARM_TARGET_STATE_CTYPE = stPre + "67" + stPost; +export const ADMIN_ONLY_ACCESS_CTYPE = stPre + "01" + stPost; +export const AUDIO_FEEDBACK_CTYPE = stPre + "05" + stPost; +export const BRIGHTNESS_CTYPE = stPre + "08" + stPost; +export const BATTERY_LEVEL_CTYPE = stPre + "68" + stPost; +export const COOLING_THRESHOLD_CTYPE = stPre + "0D" + stPost; +export const CONTACT_SENSOR_STATE_CTYPE = stPre + "6A" + stPost; +export const CURRENT_DOOR_STATE_CTYPE = stPre + "0E" + stPost; +export const CURRENT_LOCK_MECHANISM_STATE_CTYPE = stPre + "1D" + stPost; +export const CURRENT_RELATIVE_HUMIDITY_CTYPE = stPre + "10" + stPost; +export const CURRENT_TEMPERATURE_CTYPE = stPre + "11" + stPost; +export const HEATING_THRESHOLD_CTYPE = stPre + "12" + stPost; +export const HUE_CTYPE = stPre + "13" + stPost; +export const IDENTIFY_CTYPE = stPre + "14" + stPost; +export const LOCK_MANAGEMENT_AUTO_SECURE_TIMEOUT_CTYPE = stPre + "1A" + stPost; +export const LOCK_MANAGEMENT_CONTROL_POINT_CTYPE = stPre + "19" + stPost; +export const LOCK_MECHANISM_LAST_KNOWN_ACTION_CTYPE = stPre + "1C" + stPost; +export const LOGS_CTYPE = stPre + "1F" + stPost; +export const MANUFACTURER_CTYPE = stPre + "20" + stPost; +export const MODEL_CTYPE = stPre + "21" + stPost; +export const MOTION_DETECTED_CTYPE = stPre + "22" + stPost; +export const NAME_CTYPE = stPre + "23" + stPost; +export const OBSTRUCTION_DETECTED_CTYPE = stPre + "24" + stPost; +export const OUTLET_IN_USE_CTYPE = stPre + "26" + stPost; +export const OCCUPANCY_DETECTED_CTYPE = stPre + "71" + stPost; +export const POWER_STATE_CTYPE = stPre + "25" + stPost; +export const PROGRAMMABLE_SWITCH_SWITCH_EVENT_CTYPE = stPre + "73" + stPost; +export const PROGRAMMABLE_SWITCH_OUTPUT_STATE_CTYPE = stPre + "74" + stPost; +export const ROTATION_DIRECTION_CTYPE = stPre + "28" + stPost; +export const ROTATION_SPEED_CTYPE = stPre + "29" + stPost; +export const SATURATION_CTYPE = stPre + "2F" + stPost; +export const SERIAL_NUMBER_CTYPE = stPre + "30" + stPost; +export const FIRMWARE_REVISION_CTYPE = stPre + "52" + stPost; +export const STATUS_LOW_BATTERY_CTYPE = stPre + "79" + stPost; +export const STATUS_FAULT_CTYPE = stPre + "77" + stPost; +export const TARGET_DOORSTATE_CTYPE = stPre + "32" + stPost; +export const TARGET_LOCK_MECHANISM_STATE_CTYPE = stPre + "1E" + stPost; +export const TARGET_RELATIVE_HUMIDITY_CTYPE = stPre + "34" + stPost; +export const TARGET_TEMPERATURE_CTYPE = stPre + "35" + stPost; +export const TEMPERATURE_UNITS_CTYPE = stPre + "36" + stPost; +export const VERSION_CTYPE = stPre + "37" + stPost; +export const WINDOW_COVERING_TARGET_POSITION_CTYPE = stPre + "7C" + stPost; +export const WINDOW_COVERING_CURRENT_POSITION_CTYPE = stPre + "6D" + stPost; +export const WINDOW_COVERING_OPERATION_STATE_CTYPE = stPre + "72" + stPost; +export const CURRENTHEATINGCOOLING_CTYPE = stPre + "0F" + stPost; +export const TARGETHEATINGCOOLING_CTYPE = stPre + "33" + stPost; diff --git a/ConfigFiles/user.txt b/ConfigFiles/user.txt deleted file mode 100644 index cdaae44..0000000 --- a/ConfigFiles/user.txt +++ /dev/null @@ -1,9 +0,0 @@ -email:::- -admin:$2y$07$2c0bfALZ4WxenSybt2ZobO76Hyc5ZVH82DVMdl8GNp5WCljh/iT7G::blah -Pugh:$2y$07$2c0bfALZ4WxenSybt2xobO76Hyc5ZVH82DVMdl8GNp5WCljh/iT7G::blah -Pugh:$2y$07$2c0bfALZ4WxenSybt2xobO76Hyc5ZVH82DVMdl8GNp5WCljh/iT7G::blah -Barney:$2y$07$2c0bfALZ4WxenSybx2ZobO76Hyc5ZVH82DVMdl8GNp5WCljh/iT7G::blah -McGrew:$2y$07$2c0bfALZ4WxenSybx2ZobO76Hyc5ZVH82DVMdl8GNp5WCljh/iT7G::blah -Cuthbert:$2y$07$2c0bfALZ4WxenSxbt2ZobO76Hyc5ZVH82DVMdl8GNp5WCljh/iT7G::blah -Dibble:$2y$07$2c0bfALZ4WxenSybx2ZobO76Hyc5ZVH82DVMdl8GNp5WCljh/iT7G::blah -Grubb:$2y$07$2c0bfALZ4WxenSybtxZobO76Hyc5ZVH82DVMdl8GNp5WCljh/iT7G::blah diff --git a/README.md b/README.md index b205632..f2c9f4a 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,40 @@ -

Home automation system with HomeKit bridge and Security system.

- -A dogs dinner of technology resulting in a system that provides home security and automation. +

A combined Home automation and Security system with a Homekit Bridge.

-There are some pictures, and interactive sample screens on my web page here. +

( * = custom hardware required )

+

There are some pictures, and interactive sample screens on my web page here.

-Key components:- +Key components:- Installation:- -Full installation details are in the Wiki +Full installation details are in the Wiki Latest features:- + -TBD:- +tbd:- diff --git a/Scripts/alarm.sh b/Scripts/alarm.sh index ae054e8..4eb0ee4 100644 --- a/Scripts/alarm.sh +++ b/Scripts/alarm.sh @@ -14,6 +14,7 @@ # Up to 160 configurable automation channels (radio controlled). # # Up to 9 configurable thermostatic radiator valves (radio controlled). # # Industry standard 12 volt interface to alarm sensors, bell boxes and strobe. # +# Completely non-standard Central Heating controller (Y-Plan config) # # Internet remote control using iPhone SE web app interface. # # Animated page transitions and user controls - look and feel of a native app. # # eMail alerts for alarm events. # @@ -38,11 +39,11 @@ DefaultPassword='$2y$07$2c0bfALZ4WxenSybt2ZobO76Hyc5ZVH82DVMdl8GNp5WCljh/iT7G' # Define dynamic data arrays. These will hold the data that ultimately gets displayed on the web page. # NOTE: Multi dimensional arrays aren't available in BASH, so arrays use chunks of multiple elements to simulate multi dimensional arrays. -# Array to store Remote Control data. Two dimensional array, 7 x N. -declare -a rcon=() -rcon_header=0; rcon_name=1; rcon_address=2; rcon_channel=3; rcon_status=4 rcon_alrm_action=5; rcon_HK_type=6 +# Array to store Remote Control data. Two dimensional array, 8 x N. +declare -a rcon=(); rcon_count=8; +rcon_header=0; rcon_name=1; rcon_type=2; rcon_address=3; rcon_channel=4; rcon_status=5; rcon_alrm_action=6; rcon_HK_type=7; -# Array to store Alarm zone configuration. 9 elements per record defined as follows... +# Array to store Alarm zone configuration. 9 elements per record defined as follows... Type=0 # Tamper / Alarm Name=1 # Zone name DayMode=2 # on / off @@ -107,6 +108,9 @@ EMAIL_server=""; EMAIL_port=""; EMAIL_sender=""; EMAIL_password="" alarm="Set" ; mode="Standby" # Note: ${alarm} has 3 states: 'Set' 'Active !' and 'Timeout !' changed="" # flag to indicate either the state of an alarm circuit, or the configuration of an alarm # zone has changed. Either can cause an alarm zone to trigger and the alarm to activate. +# GLOBAL variables used by heat... +heatmode='Heat and Water' # Default value. NOTE: Hardware powers up in this state, so init the variable to match. + # start defining functions ... Homebridge_Export() @@ -116,13 +120,18 @@ Homebridge_Export() # # ################################################################################################################################# { -# Types_File="/home/pi/Downloads/alarm-system/ConfigFiles/types.js" PathToHAPNodeJS=$(sudo find / -type d -name "HAP-NodeJS") # don't really know where the install has PathToConfigFiles=$(sudo find / -type d -name "ConfigFiles") # been run from so find one of the source files AccessoriesPath=$PathToHAPNodeJS"/src/accessories/" + # extract MAC address details for the Homekit bridge... + BridgeMAC=$(sudo cat $PathToHAPNodeJS/src/BridgedCore.ts | grep -n'username:' | awk -F'"' '{print $2}') + # extract first 5 digits from the bridge MAC address. This will be used as the base MAC for all our accessories... + BaseMAC=${BridgeMAC::-2} + echo HAP-NodeJS install found at: $PathToHAPNodeJS echo Source config files found at: $PathToConfigFiles echo Path to accessory files: $AccessoriesPath + i=0 ; MAC_Count=0 printf "Removing existing accessories...\n" @@ -131,6 +140,11 @@ Homebridge_Export() printf "Removing existing device pairing...\n" PersistPath=$PathToHAPNodeJS/persist/ find "$PersistPath" -name * -type f -delete + + printf "Creating Homekit accessories...\n\n" + printf "Accessory | Type | Name | MAC address\n" + printf "==========|================|===========================|===================\n" + printf " %-7s | %-14s | %-25s | %s\n" $MAC_Count "Bridge" "Node Bridge" "$BridgeMAC" # create an accessory file and customise the parameters for all defined alarm zones... maxval=${#zcon[@]} ; (( maxval-- )) ; i=0 # bump down because the array starts at zero @@ -138,11 +152,10 @@ Homebridge_Export() ZoneName="${zcon[$i+Name]}" ZoneName="${ZoneName//[^[:ascii:]]/}" # stripping out any emojis ZoneName="${ZoneName%"${ZoneName##*[![:space:]]}"}" # strip any trailing spaces left by removing emojis - MAC_address=$(printf "fa:3c:ed:5a:1a:%02x\n" ${MAC_Count}) -# echo $MAC_address # Diagnostic - FileName=$AccessoriesPath"${ZoneName}_accessory.js" - printf "Creating Homekit Contact sensor: %s\n" "${ZoneName}" - cp $PathToConfigFiles"/Generic_ContactSensor.js" "${FileName}" + MAC_address=$(printf "%s%02x\n" $BaseMAC ${MAC_Count}) + FileName=$AccessoriesPath"${ZoneName}_accessory.ts" + printf " %-7s | %-14s | %-25s | %s\n" $(( $MAC_Count + 1 )) "Sensor" "${ZoneName}" "$MAC_address" + cp $PathToConfigFiles"/Generic_ContactSensor.ts" "${FileName}" oldstring='Parm1' # need to replace this string... newstring=${ZoneName} # ... with the zone name sed -i -e "s@$oldstring@$newstring@g" "$FileName" # do it. @@ -157,24 +170,24 @@ Homebridge_Export() # create an accessory file and customise the parameters for all defined power outlets... maxval=${#rcon[@]} ; (( maxval-- )) ; Count=0 ; i=0 # bump down because the array starts at zero while [ $i -le "$maxval" ]; do -# printf "rcon:%s:%s:%s:%s:%s:%s:%s\n" "${rcon[$i+rcon_header]}" "${rcon[$i+rcon_name]}" "${rcon[$i+rcon_address]}" \ +# printf "rcon:%s:%s:%s:%s:%s:%s:%s:%s\n" "${rcon[$i+rcon_header]}" "${rcon[$i+rcon_name]}" "${rcon[$i+rcon_type]}" "${rcon[$i+rcon_address]}" \ # "${rcon[$i+rcon_channel]}" "${rcon[$i+rcon_status]}" "${rcon[$i+rcon_alrm_action]}" "${rcon[$i+rcon_HK_type]}" AccName="${rcon[$i+rcon_name]}" AccName="${AccName//[^[:ascii:]]/}" # stripping out any emojis AccName="${AccName%"${AccName##*[![:space:]]}"}" # strip any trailing spaces left by removing emojis - FileName=$AccessoriesPath"${AccName}_accessory.js" + FileName=$AccessoriesPath"${AccName}_accessory.ts" - MAC_address=$(printf "fa:3c:ed:5a:1a:%02x\n" ${MAC_Count}) - case "${rcon[$i+rcon_HK_type]}" in # Create default accessory file + MAC_address=$(printf "%s%02x\n" $BaseMAC ${MAC_Count}) + case "${rcon[$i+rcon_HK_type]}" in # Create default accessory files... "Light") - cp $PathToConfigFiles"/Generic_Light.js" "$FileName";; + cp $PathToConfigFiles"/Generic_Light.ts" "$FileName";; "Outlet") - cp $PathToConfigFiles"/Generic_Outlet.js" "$FileName";; + cp $PathToConfigFiles"/Generic_Outlet.ts" "$FileName";; "Fan") - cp $PathToConfigFiles"/Generic_Fan.js" "$FileName";; + cp $PathToConfigFiles"/Generic_Fan.ts" "$FileName";; esac if [ "${rcon[$i+rcon_HK_type]}" != "None" ]; then - printf "Creating Homekit power outlet: %s\n" "$AccName" + printf " %-7s | %-14s | %-25s | %s\n" $(( $MAC_Count + 1 )) "${rcon[$i+rcon_HK_type]}" "$AccName" "$MAC_address" # if we have copied a file we need to customise it... # Function strings to pass to alarm service oldstring='Parm1' # need to replace this string... @@ -190,13 +203,17 @@ Homebridge_Export() newstring='Siri:iPhone:rcon swtch:'$Count':Off\\n' # command to switch accessory off sed -i -e "s@$oldstring@$newstring@g" "$FileName" # do it. oldstring='Parm5' # need to replace this string... - newstring=$(printf 'Handset %02d, Button %01d\n' "${rcon[i+rcon_address]}" \ - "${rcon[$i+rcon_channel]}") # configuration details + if [ "${rcon[$i+rcon_type]}" == "RF" ]; then + newstring=$(printf 'Handset %02d, Button %01d\n' "${rcon[i+rcon_address]}" \ + "${rcon[$i+rcon_channel]}") # RF configuration details + else + newstring=$(printf 'IP address 192.168.1.%02d\n' "${rcon[i+rcon_channel]}") # Tasmota configuration details + fi sed -i -e "s@$oldstring@$newstring@g" "$FileName" # do it. fi Count=$(( Count + 1 )) (( MAC_Count++ )) # Bump the loop and MAC counters - i=$(( i + 7 )) + i=$(( i + $rcon_count )) done # create an accessory file and customise the parameters for all defined radiator... @@ -204,11 +221,11 @@ Homebridge_Export() while [ $i -le "$maxval" ]; do # printf "rdtr:%s:%s:%s:%s:%s:%s\n" "${rdtr[$i+rdtr_header]}" "${rdtr[$i+rdtr_name]}" "${rdtr[$i+rdtr_address]}" \ # "${rdtr[$i+rdtr_status]}" "${rdtr[$i+rdtr_hi]}" "${rdtr[$i+rdtr_lo]}" - FileName=$AccessoriesPath"${rdtr[$i+rdtr_name]}_radiator_accessory.js" - cp $PathToConfigFiles"/Generic_Radiator.js" "$FileName" - MAC_address=$(printf "fa:3c:ed:5a:1a:%02x\n" ${MAC_Count}) -# if [ "${rdtr[$i+4]}" != "None" ]; then # Oops ! - think I've lost some functionality - printf "Creating Homekit radiator: %s radiator_accessory.js\n" "${rdtr[$i+rdtr_name]}" # visual progress indicator + FileName=$AccessoriesPath"${rdtr[$i+rdtr_name]}_radiator_accessory.ts" + cp $PathToConfigFiles"/Generic_Radiator.ts" "$FileName" + MAC_address=$(printf "%s%02x\n" $BaseMAC ${MAC_Count}) + if [ "${rdtr[$i+4]}" != "None" ]; then + printf " %-7s | %-14s | %-25s | %s\n" $(( $MAC_Count + 1 )) "Radiator" "${rdtr[$i+rdtr_name]}" "$MAC_address" # if we have copied a file we need to customise it... # Function strings to pass to alarm service oldstring='Parm1' # need to replace this string... @@ -229,18 +246,71 @@ Homebridge_Export() oldstring='Parm6' # need to replace Min temp... newstring="${rdtr[$i+rdtr_lo]}" # configuration details sed -i -e "s@$oldstring@$newstring@g" "$FileName" # do it. -# fi + fi Count=$(( Count + 1 )) (( MAC_Count++ )) # Bump the loop and MAC counters i=$(( i + 6 )) done + # Add additional outlet for the Boiler... + MAC_address=$(printf "%s%02x\n" $BaseMAC ${MAC_Count}) + printf " %-7s | %-14s | %-25s | %s\n" $(( $MAC_Count + 1 )) "Heating" "Heat" "$MAC_address" + FileName=$AccessoriesPath"/Boiler_accessory.ts" + cp $PathToConfigFiles"/Generic_Heating.ts" $FileName + oldstring='Parm1' # need to replace this string... + newstring="Boiler" # ... with the zone name + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + oldstring='Parm2' # need to replace this string... + newstring=${MAC_address} + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + oldstring='Parm3' # need to replace this string... + newstring='Siri:iPhone:Heat mode:Heat and Water\\n' # command to switch accessory on + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + oldstring='Parm4' # need to replace this string... + newstring='Siri:iPhone:Heat mode:Heat off\\n' # command to switch accessory off + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + oldstring='Parm5' # need to replace this string... + newstring='Handset 31, Button ??' + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + oldstring='Parm6' # need to replace this string... + newstring='Heat and Water' + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + +# # Add additional outlet for the Water... + (( MAC_Count++ )) # Bump the MAC counters + MAC_address=$(printf "%s%02x\n" $BaseMAC ${MAC_Count}) + printf " %-7s | %-14s | %-25s | %s\n" $(( $MAC_Count + 1 )) "Heating" "Water" "$MAC_address" + FileName=$AccessoriesPath"/Water_accessory.ts" + cp $PathToConfigFiles"/Generic_Heating.ts" $FileName + oldstring='Parm1' # need to replace this string... + newstring="Water" # ... with the zone name + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + oldstring='Parm2' # need to replace this string... + newstring=${MAC_address} + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + oldstring='Parm3' # need to replace this string... + newstring='Siri:iPhone:Heat mode:Water only\\n' # command to switch accessory on + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + oldstring='Parm4' # need to replace this string... + newstring='Siri:iPhone:Heat mode:Heat off\\n' # command to switch accessory off + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + oldstring='Parm5' # need to replace this string... + newstring='Handset 31, Button ??' + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + oldstring='Parm6' # need to replace this string... + newstring='Water only' + sed -i -e "s@$oldstring@$newstring@g" $FileName # do it. + + cp $PathToConfigFiles"/types.ts" $AccessoriesPath"/types.ts" + printf "==========|================|===========================|===================\n" + # accessories have been created by root, so change owner to pi... sudo chown -R pi $AccessoriesPath sudo chgrp -R pi $AccessoriesPath - + + printf "\nExport complete\nRestarting HAP-NodeJS\n" sudo killall node # ensure we get a clean start... - printf "Export complete\nRestarting HAP-NodeJS\n" +# printf "(Last assigned acessory MAC address: "$MAC_address")\n" sudo service homebridge restart # restart Homebridge to pick up new accessories } @@ -267,13 +337,13 @@ CreateTaskList() # # # Function to create a list of tasks currently defined on the device. # # This is required to provide accurate descriptions on events in the log files and on the console. # -# The first four tasks are static as they allow the status of the alarm to be controlled. # +# The first seven tasks are static as they allow the status of the alarm to be controlled. # # The remaining tasks are variable based on the Remote Control and Radiator channels currently defined. # # Each Remote Control channel and radiator requires an 'on' task and an 'off' task associated with it. # # # ################################################################################################################################# -{ cmnd+=('mode:Day mode' 'mode:Night mode' 'mode:Standby' 'Check ip' ) - maxval=$(( ${#rcon[@]} / 7 )) ; (( maxval-- )) # bump down because the array starts at zero +{ cmnd+=('mode:Day mode' 'mode:Night mode' 'mode:Standby' 'Check ip' 'Heat mode:Heat and Water' 'Heat mode:Water only' 'Heat mode:Off' ) + maxval=$(( ${#rcon[@]} / $rcon_count )) ; (( maxval-- )) # bump down because the array starts at zero i=0 while [ $i -le "$maxval" ]; do cmnd+=("rcon swtch:$i") # add the Remote Control channel name @@ -315,8 +385,13 @@ WriteCronJobs() printf "%s %s %s %s %s /var/www/Scripts/CheckIP.sh\n" \ "${cron[$i+cron_mins]}" "${cron[$i+cron_hours]}" "${cron[$i+cron_dom]}" "${cron[$i+cron_mnth]}" \ "${cron[$i+cron_wday]}" >>/var/www/cron.txt + elif ((DeviceNum<7)); then + # Next three tasks have static names and don't need to specify on/off. + printf "%s %s %s %s %s echo \"task:RasPi:%s\" >>/var/www/data/input.txt\n" \ + "${cron[$i+cron_mins]}" "${cron[$i+cron_hours]}" "${cron[$i+cron_dom]}" "${cron[$i+cron_mnth]}" "${cron[$i+cron_wday]}" \ + "${cmnd[$DeviceNum]}" >>/var/www/cron.txt else - # Tasks > 3 are for devices. These have variable names and need the additional on/off parameter + # Tasks >= 7 are for devices. These have variable names and need the additional on/off parameter printf "%s %s %s %s %s echo \"task:RasPi:%s:%s\" >>/var/www/data/input.txt\n" \ "${cron[$i+cron_mins]}" "${cron[$i+cron_hours]}" "${cron[$i+cron_dom]}" "${cron[$i+cron_mnth]}" "${cron[$i+cron_wday]}" \ "${cmnd[$DeviceNum]}" "${cron[$i+cron_status]}" >>/var/www/cron.txt @@ -590,7 +665,8 @@ load_status_file() rcon[${rconindex}]=${info_array[4]} ; (( rconindex++ )) rcon[${rconindex}]=${info_array[5]} ; (( rconindex++ )) rcon[${rconindex}]=${info_array[6]} ; (( rconindex++ )) - rcon[${rconindex}]=${info_array[7]} ; (( rconindex++ )) ;; + rcon[${rconindex}]=${info_array[7]} ; (( rconindex++ )) + rcon[${rconindex}]=${info_array[8]} ; (( rconindex++ )) ;; "zcon") # Load zone data... zcon[${zconindex}]=${info_array[1]} ; (( zconindex++ )) # Type zcon[${zconindex}]=${info_array[2]} ; (( zconindex++ )) # Name @@ -637,6 +713,11 @@ load_status_file() "port") EMAIL_port=${info_array[2]};; esac;; + "heat") + case "${info_array[1]}" in + "mode") + heatmode=${info_array[2]};; + esac;; esac done <$1 # file name passed as parameter. } @@ -673,6 +754,7 @@ write_status_file() echo "email:server:"${EMAIL_server} >>/var/www/temp1.txt echo "email:port:"${EMAIL_port} >>/var/www/temp1.txt echo "email:sender:"${EMAIL_sender} >>/var/www/temp1.txt + echo "heat:mode:"${heatmode} >>/var/www/temp1.txt # Write the Alarm Zone configuration... maxval=${#zcon[@]} ; (( maxval-- )) ; i=0 # bump down because the array starts at zero @@ -686,10 +768,10 @@ write_status_file() # Write the Radio Control configuration... maxval=${#rcon[@]} ; (( maxval-- )) ; i=0 # bump down because the array starts at zero while [ $i -le "$maxval" ]; do - printf "rcon:%s:%s:%s:%s:%s:%s:%s\n" "${rcon[$i+rcon_header]}" "${rcon[$i+rcon_name]}" "${rcon[$i+rcon_address]}" \ - "${rcon[$i+rcon_channel]}" "${rcon[$i+rcon_status]}" "${rcon[$i+rcon_alrm_action]}" \ - "${rcon[$i+rcon_HK_type]}" >>/var/www/temp1.txt - i=$(( i + 7 )) + printf "rcon:%s:%s:%s:%s:%s:%s:%s:%s\n" "${rcon[$i+rcon_header]}" "${rcon[$i+rcon_name]}" "${rcon[$i+rcon_type]}" "${rcon[$i+rcon_address]}" \ + "${rcon[$i+rcon_channel]}" "${rcon[$i+rcon_status]}" "${rcon[$i+rcon_alrm_action]}" \ + "${rcon[$i+rcon_HK_type]}" >>/var/www/temp1.txt + i=$(( i + $rcon_count )) done # Write the radiator configuration... @@ -750,13 +832,13 @@ alm_on() while [ $i -le "$maxval" ]; do # convert to upper case if [[ ${rcon[$i+3]^^} == "ON" ]]; then # if channel is configured to switch on during an alarm... - echo 'alarm:raspi:rcon swtch:'$(( $i/7 ))':on' >> /var/www/data/input.txt + echo 'alarm:raspi:rcon swtch:'$(( $i/$rcon_count ))':on' >> /var/www/data/input.txt fi if [[ ${rcon[$i+3]^^} == "OFF" ]]; then # if channel is configured to switch off during an alarm... - echo 'alarm:raspi:rcon swtch:'$(( $i/7 ))':off' >> /var/www/data/input.txt + echo 'alarm:raspi:rcon swtch:'$(( $i/$rcon_count ))':off' >> /var/www/data/input.txt fi - i=$(( i + 7 )) + i=$(( i + $rcon_count )) done } @@ -892,27 +974,27 @@ crontab_diag() rcon_diag() ################################################################################################################################# # # -# Radio Control diagnostic routine. Prints rcon data array to console. # +# Remote Control diagnostic routine. Prints rcon data array to console. # # # # ** Development use only ** # # This routine slows down the scan of the alarm inputs, so should never be left running on a live system. # # # ################################################################################################################################# { clear - printf %s"------|-----------|----------------|---------|---------|--------|--------------|---------|\n" - printf "Array | Header | Name | Address | Channel | Status | Alarm action | HB type |\n" - printf %s"------|-----------|----------------|---------|---------|--------|--------------|---------|\n" + printf %s"------|-----------|----------------|------|---------|---------|--------|--------------|---------|\n" + printf "Array | Header | Name | Type | Address | Channel | Status | Alarm action | HB type |\n" + printf %s"------|-----------|----------------|------|---------|---------|--------|--------------|---------|\n" maxval=${#rcon[@]} # number of defined Radio Control circuits (( maxval-- )) # bump down because the array starts at zero i=0 # array index while [ $i -le "$maxval" ]; do # print all configured Radio Control circuits - printf "%-6s|%-11s|%-16s| %-5s| %-5s| %-6s| %-12s| %-8s|\n" "$i" "${rcon[$i+rcon_header]}" \ - "${rcon[$i+rcon_name]}" "${rcon[$i+rcon_address]}" "${rcon[$i+rcon_channel]}" "${rcon[$i+rcon_status]}" \ - "${rcon[$i+rcon_alrm_action]}" "${rcon[$i+rcon_HK_type]}" - i=$(( i + 7 )) + printf "%-6s|%-11s|%-16s| %-5s| %-5s| %-5s| %-6s| %-12s| %-8s|\n" "$i" "${rcon[$i+rcon_header]}" \ + "${rcon[$i+rcon_name]}" "${rcon[$i+rcon_type]}" "${rcon[$i+rcon_address]}" "${rcon[$i+rcon_channel]}" \ + "${rcon[$i+rcon_status]}" "${rcon[$i+rcon_alrm_action]}" "${rcon[$i+rcon_HK_type]}" + i=$(( i + $rcon_count )) done - printf %s"------|-----------|----------------|---------|---------|--------|--------------|---------|\n" + printf %s"------|-----------|----------------|------|---------|---------|--------|--------------|---------|\n" sleep 0.5s } @@ -1327,52 +1409,66 @@ LOGFILE="/var/www/logs/"`date +%d-%m-%Y`".csv" # nam ################################################################################################################################# "rcon swtch") - tmp=${CURRTIME}","${PARAMS[0]}","${PARAMS[1]}","${rcon[${PARAMS[3]}*7+rcon_name]}","${PARAMS[4]} - echo $tmp >> $LOGFILE # log the event - echo $tmp # tell the user - rcon[${PARAMS[3]}*7+rcon_status]="${PARAMS[4]}" # set array element to new string value - if [ "${PARAMS[4]}" == "On" ]; then - # format the command string for 'on'... - if [ "I2C_bus" == "0" ]; then - # RasPi model 1 uses i2C bus 0... - printf -v tmp ' -y 0 0x08 0x01 0x%02X 0x%02X 0x01 i \n' ${rcon[${PARAMS[3]}*7+rcon_address]} ${rcon[${PARAMS[3]}*7+rcon_channel]} - else - # RasPi model 2 and 3 uses i2C bus 1... - printf -v tmp ' -y 1 0x08 0x01 0x%02X 0x%02X 0x01 i \n' ${rcon[${PARAMS[3]}*7+rcon_address]} ${rcon[${PARAMS[3]}*7+rcon_channel]} + tmp=${CURRTIME}","${PARAMS[0]}","${PARAMS[1]}","${rcon[${PARAMS[3]}*$rcon_count+rcon_name]}","${PARAMS[4]} + echo $tmp >> $LOGFILE # log the event + echo $tmp # tell the user + rcon[${PARAMS[3]}*$rcon_count+rcon_status]="${PARAMS[4]}" # set array element to new string value + if [ "${rcon[$((${PARAMS[3]}*$rcon_count+rcon_type))]}" == "WiFi" ]; then + # Falls through here for WiFi switch... + if [ "${PARAMS[4]}" == "On" ]; then + printf -v tmp 'http://192.168.1.%d/cm?cmnd=Power%%20On' ${rcon[${PARAMS[3]}*$rcon_count+rcon_channel]} + else + printf -v tmp 'http://192.168.1.%d/cm?cmnd=Power%%20Off' ${rcon[${PARAMS[3]}*$rcon_count+rcon_channel]} fi - else - # format the command string for 'off'... - if [ "I2C_bus" == "0" ]; then - # RasPi model 1 uses i2C bus 0... - printf -v tmp ' -y 0 0x08 0x01 0x%02X 0x%02X 0x00 i \n' ${rcon[${PARAMS[3]}*7+rcon_address]} ${rcon[${PARAMS[3]}*7+rcon_channel]} - else - # RasPi model 2 and 3 uses i2C bus 1... - printf -v tmp ' -y 1 0x08 0x01 0x%02X 0x%02X 0x00 i \n' ${rcon[${PARAMS[3]}*7+rcon_address]} ${rcon[${PARAMS[3]}*7+rcon_channel]} - fi - fi -# echo $tmp # DEBUG - view the I2C command - if [ ${running_on_RasPi} == "true" ]; then - # Only send I2C commands if we are on a pi. This prevents non pi platforms from flooding the console with errors. - i2cset $tmp # send I2C command to PIC chip - sleep 0.3s # give the PIC time to complete the transmission - fi;; + curl -s "${tmp}" > /dev/null + sleep 0.3s # pause for effect !!! + else + # Falls through here for RF switch... + if [ "${PARAMS[4]}" == "On" ]; then + # format the command string for 'on'... + if [ "I2C_bus" == "0" ]; then + # RasPi model 1 uses i2C bus 0... + printf -v tmp ' -y 0 0x08 0x01 0x%02X 0x%02X 0x01 i \n' ${rcon[${PARAMS[3]}*$rcon_count+rcon_address]} ${rcon[${PARAMS[3]}*$rcon_count+rcon_channel]} + else + # RasPi model 2 and 3 uses i2C bus 1... + printf -v tmp ' -y 1 0x08 0x01 0x%02X 0x%02X 0x01 i \n' ${rcon[${PARAMS[3]}*$rcon_count+rcon_address]} ${rcon[${PARAMS[3]}*$rcon_count+rcon_channel]} + fi + else + # format the command string for 'off'... + if [ "I2C_bus" == "0" ]; then + # RasPi model 1 uses i2C bus 0... + printf -v tmp ' -y 0 0x08 0x01 0x%02X 0x%02X 0x00 i \n' ${rcon[${PARAMS[3]}*$rcon_count+rcon_address]} ${rcon[${PARAMS[3]}*$rcon_count+rcon_channel]} + else + # RasPi model 2 and 3 uses i2C bus 1... + printf -v tmp ' -y 1 0x08 0x01 0x%02X 0x%02X 0x00 i \n' ${rcon[${PARAMS[3]}*$rcon_count+rcon_address]} ${rcon[${PARAMS[3]}*$rcon_count+rcon_channel]} + fi + fi + if [ ${running_on_RasPi} == "true" ]; then + # Only send I2C commands if we are on a pi. This prevents non pi platforms from flooding the console with errors. + i2cset $tmp # send I2C command to PIC chip + sleep 0.3s # give the PIC time to complete the transmission + fi + fi +# echo $tmp # DEBUG - view the I2C / WiFi command + ;; "rcon del") tmp=${CURRTIME}","${PARAMS[0]}","${PARAMS[1]}","${PARAMS[2]}","${PARAMS[3]} echo $tmp >> $LOGFILE # log the event echo $tmp # tell the user - rcon=("${rcon[@]:0:$((${PARAMS[3]}*7))}" "${rcon[@]:$(($((${PARAMS[3]}*7)) + 7))}") + rcon=("${rcon[@]:0:$((${PARAMS[3]}*$rcon_count))}" "${rcon[@]:$(($((${PARAMS[3]}*$rcon_count)) + $rcon_count))}") CreateTaskList;; # assemble the list of all the task strings "rcon cfg") tmp=${CURRTIME}","${PARAMS[0]}","${PARAMS[1]}",remote config,"${PARAMS[3]}","${PARAMS[4]}","${PARAMS[5]}","${PARAMS[6]}","${PARAMS[7]}","${PARAMS[9]} echo $tmp >> $LOGFILE # log the event echo $tmp # tell the user - rcon[${PARAMS[3]}*7+rcon_header]="${PARAMS[4]}" - rcon[${PARAMS[3]}*7+rcon_name]="${PARAMS[5]}" - rcon[${PARAMS[3]}*7+rcon_address]="${PARAMS[6]}" - rcon[${PARAMS[3]}*7+rcon_channel]="${PARAMS[7]}" - rcon[${PARAMS[3]}*7+rcon_status]="Off" # default state for new device - rcon[${PARAMS[3]}*7+rcon_alrm_action]="${PARAMS[8]}" - rcon[${PARAMS[3]}*7+rcon_HK_type]="${PARAMS[9]}" + rcon[${PARAMS[3]}*$rcon_count+rcon_header]="${PARAMS[4]}" + rcon[${PARAMS[3]}*$rcon_count+rcon_name]="${PARAMS[5]}" + rcon[${PARAMS[3]}*$rcon_count+rcon_type]="${PARAMS[6]}" + rcon[${PARAMS[3]}*$rcon_count+rcon_address]="${PARAMS[7]}" + rcon[${PARAMS[3]}*$rcon_count+rcon_channel]="${PARAMS[8]}" + rcon[${PARAMS[3]}*$rcon_count+rcon_status]="Off" # default state for new device + rcon[${PARAMS[3]}*$rcon_count+rcon_alrm_action]="${PARAMS[9]}" + rcon[${PARAMS[3]}*$rcon_count+rcon_HK_type]="${PARAMS[10]}" CreateTaskList;; # assemble the list of all the task strings "zcon cfg") tmp=${CURRTIME}","${PARAMS[0]}","${PARAMS[1]}","${PARAMS[2]}","${PARAMS[3]}"," @@ -1414,6 +1510,29 @@ LOGFILE="/var/www/logs/"`date +%d-%m-%Y`".csv" # nam # Handle commands passed from the Radiator web page. # ################################################################################################################################# + + "Heat mode") + tmp=${CURRTIME}","${PARAMS[0]}","${PARAMS[1]}","${PARAMS[2]}","${PARAMS[3]} + echo $tmp >> $LOGFILE # log the event + echo $tmp # tell the user + # falls through here if we need to change the heat mode... + heatmode=${PARAMS[3]} # set new mode + if [[ ${PARAMS[3]} == "Heat and Water" ]]; then + printf -v tmp ' -y 1 0x08 0x03 0xFF 0x00 0x00 i' + fi + if [[ ${PARAMS[3]} == "Water only" ]]; then + printf -v tmp ' -y 1 0x08 0x03 0xFF 0x02 0x00 i' + fi + if [[ ${PARAMS[3]} == "Heat off" ]]; then + printf -v tmp ' -y 1 0x08 0x03 0xFF 0x03 0x00 i' + fi +# echo $tmp # DEBUG - view the I2C command +# echo $heatmode + if [ ${running_on_RasPi} == "true" ]; then + # Only send I2C commands if we are on a pi. This prevents non pi platforms from flooding the console with errors. + i2cset $tmp # send I2C command to PIC chip + sleep 0.6s # give the PIC time to complete the transmission + fi;; # Note: Heat switch take twice as long as switches to complete. "rdtr swtch") tmp=${CURRTIME}","${PARAMS[0]}","${PARAMS[1]}","${rdtr[${PARAMS[3]}*6+rdtr_name]}" \ "radiator","${PARAMS[4]} diff --git a/WebPage/HeatPageAjaxCall.php b/WebPage/HeatPageAjaxCall.php index ccf9831..a628639 100644 --- a/WebPage/HeatPageAjaxCall.php +++ b/WebPage/HeatPageAjaxCall.php @@ -1,5 +1,24 @@ +"; + $temperatures=(json_decode($json, TRUE)); +// var_dump($temperatures); +// if (array_key_exists("Bathroom",$temperatures)) { +// echo $temperatures["Bathroom"]."
"; +// $tmp = number_format(floatval($temperatures["Bathroom"]), 1); +// } + } + } ?> +
@@ -22,21 +41,32 @@
  • - - -
    - - value=> -
    - - - - - - + + + +
    "; + echo $rdtr[$row][2]; + echo " ".number_format(floatval($temperatures[$rdtr[$row][2]]), 1); } + else { ?> + + ".$rdtr[$row][2]; + } + ?> + + + value=> + + + + + + + +
  • @@ -62,6 +92,41 @@ class="ui-btn floaty radiator button ui-shadow ui-btn-icon-right ui-icon-carat-r

    +
    +
    + +
    +
    + + + +
    diff --git a/WebPage/PowerPageAjaxCall.php b/WebPage/PowerPageAjaxCall.php index 624b9dd..9602240 100644 --- a/WebPage/PowerPageAjaxCall.php +++ b/WebPage/PowerPageAjaxCall.php @@ -14,9 +14,9 @@

    - - + also acts as a marker for the scroll to top button. +Make all task configuration data available on the page just in case we want to drill down and start editing +Note: added a spare entry at the end in case we want to create a new switch -->