UNPKG

domoticz-heaters

Version:

Piloting Domoticz heaters with Node.js.

307 lines (244 loc) 8.63 kB
"use strict"; /** Main module. */ // dependencies const fs = require("fs"); const MyDate = require("./class/date"); const domoAPI = require("./class/domoticzAPI"); const Room = require("./class/room"); const gsheetAPI = require("./class/googleSheetAPI"); const domoJS = {}; domoJS.configs = {}; domoJS.state = {}; /** * Some stupid console.log wrapper with prepended time * @param msg */ function say( msg ) { console.log(new MyDate().stringTime5() + " " + msg); } /** * We load the configs from configuration file (URL of Domoticz, user/pwd, ...) * @param path */ domoJS.loadConfigs = function( path ) { domoJS.configs = JSON.parse( fs.readFileSync( path ) ); // give domoticz API access to configs domoAPI.setUrl( domoJS.configs.domoticz ); }; /** * Get the state of the house from json state file : last update, rooms, temperatures, heaters, ... * @param path */ domoJS.loadState = function( path ) { // load state from file domoJS.state = JSON.parse( fs.readFileSync( path ) ); // make it easier to find which file we read the state from domoJS.state.file = path; // make all rooms inherit from Room prototypal object // (I love prototypal inheritance !!!) for (let room in domoJS.state.rooms) domoJS.state.rooms[room].__proto__ = Room; }; /** * Reads the temperatures in Google Calc "proxy" file, and writes them in state.rooms.xxx.wantedTemps */ domoJS.updateWantedTemps = async function() { if (! fs.existsSync(domoJS.configs.root + "wantedTemps.json")) { console.log("could not load " + domoJS.configs.root + "wantedTemps.json from local disk"); return } let wantedTemps = JSON.parse(fs.readFileSync(domoJS.configs.root + "wantedTemps.json")); let now5 = new MyDate().stringTime5(); // from the sheet of wanted temps, we get which temperature we now want for (let room in wantedTemps) { let wantedTemp = 10; for (let keyHours in wantedTemps[room]) if (now5 > keyHours) wantedTemp = wantedTemps[room][keyHours]; domoJS.state.rooms[room].wantedTemp = Number(wantedTemp); // console.log(domoJS.state.rooms[room]); } // say(" Wanted Temperatures:"); console.log(wantedTemps); } domoJS.loadTemperaturesFromDomoticz = async function() { const temps = await domoAPI.getTemperatures(); // console.log(temps); for (const sensor of temps) { // console.log(sensor); // we get the room from the name of the device: tempBed -> Bed let room_name = sensor.Name.substring(4); // update state if (! domoJS.state.rooms[room_name]) { // say("Room " + room_name + " does not exist in domoJS.state"); continue; } domoJS.state.rooms[room_name].setTemperature(sensor.Temp); domoJS.state.rooms[room_name].setHumidity(sensor.Humidity); domoJS.state.rooms[room_name].setLastSensorTime(sensor.LastUpdate); } // console.log(this.state); } /** * Get the wanted temperature * Get the heater switches status * send Domoticz API call * get result as list of switch data * process each switch (heater) in a callback * then count consumption */ domoJS.updateSwitches = async function() { // reset all power of rooms to zero for (const name in domoJS.state.rooms) { let room = domoJS.state.rooms[name]; room.power = 0; } // console.log(domoJS.state); // console.log(domoJS.state); let switches = await domoAPI.getSwitchesInfo(); for (const oneSwitch of switches) { let namebits = oneSwitch.Name.split('-'); // oneSwitch.idx // Heater-Kitchen-Small-dir-36-1000 // deal with the heaters if (namebits[0] == 'Heater') { let current_room = domoJS.state.rooms[namebits[1]]; // now we check if heater and room state are coherent: // room should impose the value of heater. If not coherent, we switch heater if (current_room.state === "On") { if (namebits[3] == 'inv') { if (oneSwitch.Status === "On") { // something is wrong: heater should be aligned with room // say(oneSwitch.Name + " (inverted) is On and should be Off !!!"); await switchHeaterOn(oneSwitch.idx, oneSwitch.Name, true); } else { // resend ? if (current_room.resend) { say(oneSwitch.Name + " resend On command"); await switchHeaterOn(oneSwitch.idx, oneSwitch.Name, true); } } } else { if (oneSwitch.Status === "Off") { // something is wrong: heater should be aligned with room // say(oneSwitch.Name + " is Off and should be On !!!"); await switchHeaterOn(oneSwitch.idx, oneSwitch.Name, false); } else { // resend ? if (current_room.resend) { say(oneSwitch.Name + " resend On command"); await switchHeaterOn(oneSwitch.idx, oneSwitch.Name, false); } } } current_room.power += parseInt(namebits[4]); } else { if (namebits[3] == 'inv') { if (oneSwitch.Status === "Off") { // something is wrong: heater should be aligned with room // say(oneSwitch.Name + " (inverted) is Off and should be On !!!"); await switchHeaterOff(oneSwitch.idx, oneSwitch.Name, true); } else { // resend ? if (current_room.resend) { say(oneSwitch.Name + " resend Off command"); await switchHeaterOff(oneSwitch.idx, oneSwitch.Name, true); } } } else { if (oneSwitch.Status === "On") { // something is wrong: heater should be aligned with room // say(oneSwitch.Name + " is On and should be Off !!!"); await switchHeaterOff(oneSwitch.idx, oneSwitch.Name, false); } else { // resend ? if (current_room.resend) { say(oneSwitch.Name + " resend Off command"); await switchHeaterOff(oneSwitch.idx, oneSwitch.Name, false); } } } } // deal with the VMC } else if (namebits[0] == 'VMC') { if (parseInt(domoJS.state.rooms[namebits[1]].humidity) >= parseInt(namebits[2])) { // too wet if (oneSwitch.Status == 'Off') { await switchHeaterOn(oneSwitch.idx, oneSwitch.Name, false); } } else { // dry enough if (oneSwitch.Status == 'On') { await switchHeaterOff(oneSwitch.idx, oneSwitch.Name, false); } } // Other } else { // console.log(oneSwitch.Name); } } // console.log(switches); } // switching on a heater async function switchHeaterOn (devIdx, name, isInverted) { say( "sending switching ON command to " + name ); // this sends the request if (isInverted) { await domoAPI.switchDevice( devIdx, "Off"); } else { await domoAPI.switchDevice( devIdx, "On"); } }; // switching off a heater async function switchHeaterOff (devIdx, name, isInverted) { say( "sending switching OFF command to " + name ); // this sends the request if (isInverted) { await domoAPI.switchDevice( devIdx, "On"); } else { await domoAPI.switchDevice( devIdx, "Off"); } }; /** * Count each minute each heater has been ON. * Scatter those minutes between High price hours and low price hours. */ function countConsumption() { const now = new MyDate(); // console.log(state); const lastStateUpdate = new MyDate(domoJS.state.lastUpdate); const minSinceLastRun = Math.round( (now - lastStateUpdate) / 1000 / 60 ); const HCorHP = lastStateUpdate.getHCHP(); // we add the number of minutes each heater has been on (in state) for (let room_name in domoJS.state.rooms) { let room = domoJS.state.rooms[room_name]; if (room.state === "On") { room[HCorHP] += minSinceLastRun; // console.log( room.name + " is " + room.state); } } // update last update in the state and save it to disk domoJS.state.lastUpdate = now.toISOString(); // we save state in 5 seconds: we want to be sure that everything has been done setTimeout( writeState, 5000); } /** * Write down state in json file */ domoJS.writeState = async function(filepathname) { // console.log(filepathname); // console.log(JSON.stringify(domoJS.state, null, 4)); fs.writeFileSync( filepathname, JSON.stringify(domoJS.state, null, 4), function(err){ if(err) throw err; } ); } /** * Wrappers around gsheetAPI just so that I don't need to call * gsheetAPI (and have to require it) from the outside world. */ domoJS.getTempsFromGoogleSheet = async function() { await gsheetAPI.getTempsFromGoogleSheet(domoJS.configs); }; domoJS.uploadToGoogleSheet = function() { gsheetAPI.uploadToGoogleSheet(domoJS.configs, domoJS.state); }; module.exports = domoJS;