From c335045aa3e751482592644144baaa87160deb38 Mon Sep 17 00:00:00 2001 From: foxthefox <16841643+foxthefox@users.noreply.github.com> Date: Fri, 31 Mar 2023 08:18:23 +0200 Subject: [PATCH 1/2] 2.1.0 --- README.md | 49 +++- lib/fritz_ahaapi.js | 141 ++++++++--- lib/fritz_mockserver.js | 340 ++++++++++++++++++--------- lib/mjs_version/start_mockserver.mjs | 16 ++ package.json | 2 +- test/integration.js | 291 ++++++++++++++++++++--- 6 files changed, 665 insertions(+), 174 deletions(-) create mode 100644 lib/mjs_version/start_mockserver.mjs diff --git a/README.md b/README.md index f86b225..ae887b0 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,56 @@ const logout = await fritz.logout_SID(); see the example.js. ## API Calls -* todo for 1.0.3 +### reading FB + +|API-call|implements FB call| +|:----|:---| +|getDeviceListInfos()| getdevicelistinfos| +|getTemplateListInfos()| gettemplatelistinfos| +|getTriggerListInfos()| gettriggerlistinfos| +|getColorDefaults()| getcolordefaults| +|getDeviceInfos(ain)| getdeviceinfos| +|getBasicDeviceStats(ain)| getbasicdevicestats| +|getSwitchList()| getswitchlist| +|getSwitchState(ain)| getswitchstate| +|getSwitchPresent(ain)| getswitchpresent| +|getSwitchPower(ain)| getswitchpower| +|getSwitchEnergy(ain)| getswitchenergy| +|getSwitchName(ain)| getswitchname| +|getTemperature(ain)| gettemperature| +|getHkrTsoll(ain)| gethkrtsoll| +|getHkrKomfort(ain)| gethkrkomfort| +|getHkrAbsenk(ain)| gethkrabsenk| +|getUserPermissions()| not a FB call| + +### commands to FB + +|API-call|implements FB call| +|:----|:---| +|applyTemplate(ain)| applytemplate| +|setSwitchOn(ain)| setswitchon| +|setSwitchOff(ain)| setswitchoff| +|setSwitchToggle(ain)| setswitchtoggle| +|setSimpleOn(ain)| setsimpleonoff&onoff=1| +|setSimpleOff(ain)| setsimpleonoff&onoff=0| +|setSimpleToggle(ain)| setsimpletoggle| +|setTempTarget(ain, temp)| sethkrtsoll| +|setHkrBoost(ain, time)| sethkrboost| +|setWindowOpen(ain, time)| sethkrwindowopen| +|setBlind(ain, target)| setblind| +|setLevel(ain, level)| setlevel| +|setColorTemperature(ain, temp)| setcolortemperature| +|setColor(ain, saturation, hue)| setcolor| +|setUnmappedColor(ain, saturation, hue)| setunmappedcolor| +|setTriggerActive(ain, active)| settriggeractive| ## Changelog +### 2.1.0 +* (foxthefox) new functions setSimpleToggle, setLevelPercentage, setUnmappedColor, setTriggerActive +* (foxthefox) remove spaces in ain if part of the function call +* (foxthefox) extended testing +* (foxthefox) debug-flag as new parameter during instantiation + ### 2.0.1 * (foxthefox) forgotten "this." at apiresponse diff --git a/lib/fritz_ahaapi.js b/lib/fritz_ahaapi.js index ca6bee7..276d3ae 100644 --- a/lib/fritz_ahaapi.js +++ b/lib/fritz_ahaapi.js @@ -33,13 +33,13 @@ class Fritz { * @param {string} uri * @param {object} options */ - constructor(username, password, uri, options) { + constructor(username, password, uri, debug, options) { this.sid = null; this.username = username; this.password = password; this.url = { url: uri || 'http://fritz.box' }; this.options = options; - this.debug = false; + this.debug = debug; this.newVersion = null; } @@ -84,6 +84,7 @@ class Fritz { */ async getBasicDeviceStats(ain) { try { + ain = ain.replace(/\s/g, ''); const body = this.executeCommand2('getbasicdevicestats', ain, 1); return Promise.resolve(body); } catch (error) { @@ -117,8 +118,10 @@ class Fritz { */ async setSwitchOn(ain) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2('setswitchon', ain, 1); - return Promise.resolve(/^1/.test(body)); + //return Promise.resolve(/^1/.test(body)); + return Promise.resolve(body); } catch (error) { return Promise.reject(error); } @@ -130,8 +133,10 @@ class Fritz { */ async setSwitchOff(ain) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2('setswitchoff', ain, 1); - return Promise.resolve(/^1/.test(body)); + //return Promise.resolve(/^1/.test(body)); + return Promise.resolve(body); } catch (error) { return Promise.reject(error); } @@ -139,8 +144,10 @@ class Fritz { //set switchtoggle async setSwitchToggle(ain) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2('setswitchtoggle', ain, 1); - return Promise.resolve(/^1/.test(body)); + //return Promise.resolve(/^1/.test(body)); + return Promise.resolve(body); } catch (error) { return Promise.reject(error); } @@ -152,8 +159,9 @@ class Fritz { */ async setSimpleOn(ain) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2('setsimpleonoff&onoff=1', ain, 1); - return Promise.resolve('OK'); + return Promise.resolve(body); } catch (error) { return Promise.reject(error); } @@ -165,13 +173,27 @@ class Fritz { */ async setSimpleOff(ain) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2('setsimpleonoff&onoff=0', ain, 1); - return Promise.resolve('OK'); + return Promise.resolve(body); } catch (error) { return Promise.reject(error); } } + // toggle an device. returns the state the outlet was set to + /** + * @param {string} ain + */ + async setSimpleToggle(ain) { + try { + ain = ain.replace(/\s/g, ''); + const body = await this.executeCommand2('setsimpleonoff&onoff=2', ain, 1); + return Promise.resolve(body); + } catch (error) { + return Promise.reject(error); + } + } // set target temperature (Solltemperatur) /** * @param {string} ain @@ -179,8 +201,9 @@ class Fritz { */ async setTempTarget(ain, temp) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2('sethkrtsoll¶m=' + Fritz.temp2api(temp), ain, 1); - return Promise.resolve(temp); + return Promise.resolve(body); } catch (error) { return Promise.reject(error); } @@ -193,6 +216,7 @@ class Fritz { */ async setHkrBoost(ain, time) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2('sethkrboost&endtimestamp=' + time, ain, 1); return Promise.resolve(body); } catch (error) { @@ -207,6 +231,7 @@ class Fritz { */ async setWindowOpen(ain, time) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2('sethkrwindowopen&endtimestamp=' + time, ain, 1); return Promise.resolve(body); } catch (error) { @@ -221,27 +246,43 @@ class Fritz { */ async setBlind(ain, target) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2('setblind&target=' + target, ain, 1); - return Promise.resolve(target); + return Promise.resolve(body); } catch (error) { return Promise.reject(error); } } - // set level (dimmer etc.) + // set level (dimmer etc.) 0...255 /** * @param {string} ain * @param {string | number | boolean} level */ async setLevel(ain, level) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2('setlevel&level=' + level, ain, 1); - return Promise.resolve(level); + return Promise.resolve(body); } catch (error) { return Promise.reject(error); } } + // set level in percent (dimmer etc.) 0...199% + /** + * @param {string} ain + * @param {string | number | boolean} level + */ + async setLevelPercentage(ain, level) { + try { + ain = ain.replace(/\s/g, ''); + const body = await this.executeCommand2('setlevelpercentage&level=' + level, ain, 1); + return Promise.resolve(body); + } catch (error) { + return Promise.reject(error); + } + } // set color temperature /** * @param {string} ain @@ -249,12 +290,13 @@ class Fritz { */ async setColorTemperature(ain, temp) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2( 'setcolortemperature&temperature=' + Fritz.colortemp2api(temp) + '&duration=0', ain, 1 ); - return Promise.resolve(temp); + return Promise.resolve(body); } catch (error) { return Promise.reject(error); } @@ -269,6 +311,7 @@ class Fritz { */ async setColor(ain, saturation, hue) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2( 'setcolor&saturation=' + saturation + '&hue=' + hue + '&duration=0', ain, @@ -289,6 +332,7 @@ class Fritz { */ async setUnmappedColor(ain, saturation, hue) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2( 'setunmappedcolor&saturation=' + saturation + '&hue=' + hue + '&duration=0', ain, @@ -307,9 +351,10 @@ class Fritz { */ async setTriggerActive(ain, active) { try { + ain = ain.replace(/\s/g, ''); const val = active === 1 || active === '1' || active === true ? '1' : '0'; - const body = await this.executeCommand2('settriggeractive¶m=' + val, ain, 1); - return Promise.resolve(active); + const body = await this.executeCommand2('settriggeractive&active=' + val, ain, 1); + return Promise.resolve(body); } catch (error) { return Promise.reject(error); } @@ -320,6 +365,7 @@ class Fritz { */ async applyTemplate(ain) { try { + ain = ain.replace(/\s/g, ''); const body = await this.executeCommand2('applytemplate', ain, 1); return Promise.resolve(body); } catch (error) { @@ -340,6 +386,7 @@ class Fritz { // getswitchstate async getSwitchState(ain) { try { + ain = ain.replace(/\s/g, ''); const body = this.executeCommand2('getswitchstate', ain, 1); return Promise.resolve(body); } catch (error) { @@ -358,6 +405,7 @@ class Fritz { // getswitchpower async getSwitchPower(ain) { try { + ain = ain.replace(/\s/g, ''); const body = this.executeCommand2('getswitchpower', ain, 1); return Promise.resolve(body); } catch (error) { @@ -367,6 +415,7 @@ class Fritz { // getswitchenergy async getSwitchEnergy(ain) { try { + ain = ain.replace(/\s/g, ''); const body = this.executeCommand2('getswitchenergy', ain, 1); return Promise.resolve(body); } catch (error) { @@ -376,6 +425,7 @@ class Fritz { // getswitchname async getSwitchName(ain) { try { + ain = ain.replace(/\s/g, ''); const body = this.executeCommand2('getswitchname', ain, 1); return Promise.resolve(body); } catch (error) { @@ -385,6 +435,7 @@ class Fritz { // gettemperature async getTemperature(ain) { try { + ain = ain.replace(/\s/g, ''); const body = this.executeCommand2('gettemperature', ain, 1); return Promise.resolve(body); } catch (error) { @@ -394,6 +445,7 @@ class Fritz { // gethkrtsoll async getHkrTsoll(ain) { try { + ain = ain.replace(/\s/g, ''); const body = this.executeCommand2('gethkrtsoll', ain, 1); return Promise.resolve(body); } catch (error) { @@ -403,6 +455,7 @@ class Fritz { // gethkrkomfort async getHkrKomfort(ain) { try { + ain = ain.replace(/\s/g, ''); const body = this.executeCommand2('gethkrkomfort', ain, 1); return Promise.resolve(body); } catch (error) { @@ -412,6 +465,7 @@ class Fritz { // gethkrabsenk async getHkrAbsenk(ain) { try { + ain = ain.replace(/\s/g, ''); const body = this.executeCommand2('gethkrabsenk', ain, 1); return Promise.resolve(body); } catch (error) { @@ -421,7 +475,8 @@ class Fritz { // getdeviceinfos async getDeviceInfos(ain) { try { - const body = this.executeCommand2('getdevicelistinfos', ain, 1); + ain = ain.replace(/\s/g, ''); + const body = this.executeCommand2('getdeviceinfos', ain, 1); return Promise.resolve(body); } catch (error) { return Promise.reject(error); @@ -446,7 +501,9 @@ class Fritz { try { const state = await this.get_login_state(box_url); //Aufrufe von statischen methoden aus anderen statischen methoden der gleichen Klasse mit this let challenge_response = null; - //console.log('state ', state); + if (this.debug) { + console.log('get_login_state ', state); + } if (state.pbkf2 === true) { if (this.debug) console.log('PBKDF2 supported'); this.newVersion = true; @@ -477,11 +534,15 @@ class Fritz { }; } else { if (sidORrights === true) { - //console.log('SID ', sid.sessionID); + if (this.debug) { + console.log('SID ', sid.sessionID); + } this.sid = sid.sessionID; return sid.sessionID; } else { - //console.log('rights ', sid.rights); + if (this.debug) { + console.log('rights ', sid.rights); + } return sid.rights; } } @@ -525,12 +586,12 @@ class Fritz { if (protocol == 'http:') { http = require('http'); defaultport = 80; - if (this.debug) console.log('using http'); + if (this.debug) console.log('get_login_state using http'); } else if (protocol == 'https:') { http = require('https'); defaultport = 443; //http.globalAgent.options.secureProtocol = 'SSLv3_method'; - if (this.debug) console.log('using https'); + if (this.debug) console.log('get_login_state using https'); } else { console.log('invalid protocol'); } @@ -555,8 +616,8 @@ class Fritz { reject({ msg: 'http.request error', function: 'get_login_state', - error: res.statusCode, - response: res + error: res, + status: res.statusCode }); } // cumulate data @@ -571,7 +632,16 @@ class Fritz { const challenge = responseBody.match('(.*?)')[1]; const blocktime = Math.floor(responseBody.match('(.*?)')[1]); const pbkf2 = challenge.startsWith('2$') ? true : false; - //console.log('get login state result: ' + challenge + ' blocktime: ' + blocktime + ' pb: ' + pbkf2); + if (this.debug) { + console.log( + 'get login state result: ' + + challenge + + ' blocktime: ' + + blocktime + + ' pb: ' + + pbkf2 + ); + } resolve({ challenge: challenge, blocktime: blocktime, pbkf2: pbkf2 }); } else { reject({ @@ -661,12 +731,12 @@ class Fritz { if (protocol == 'http:') { http = require('http'); defaultport = 80; - if (this.debug) console.log('using http'); + if (this.debug) console.log('send_response using http'); } else if (protocol == 'https:') { http = require('https'); defaultport = 443; //http.globalAgent.options.secureProtocol = 'SSLv3_method'; - if (this.debug) console.log('using https'); + if (this.debug) console.log('send_response using https'); } else { console.log('invalid protocol'); } @@ -696,7 +766,7 @@ class Fritz { msg: 'http.request error', function: 'send_response', error: res.statusCode, - response: res + status: res.statusCode }); } // cumulate data @@ -795,8 +865,9 @@ class Fritz { // das ist ein wenig tricky, der http.request wirft einen Fehler, also landet man hier // damit ist der erste Aufruf beendet, aber in der catch routine wird ein 2ter Aufruf versucht // dessen erfolgreicher Abschluß führt dann insgesamt zu einem Erfolg - if (this.debug) console.log('ERROR '); - if (count === loop) { + let statusCode = error.error; + if (this.debug) console.log('ERROR executeCommand2 ', error.error); + if (count === loop && error.error !== 400 && error.error !== 500) { const login = await this.login_SID(); if (login === true) { const secondresponse = await this.executeCommand2(command, ain, 2); @@ -813,9 +884,10 @@ class Fritz { } // loop === 2 return Promise.reject({ - msg: 'relogin failed', + msg: 'relogin failed, or status400/500', function: 'executeCommand2', - error: error + error: error, + status: statusCode }); } } @@ -837,12 +909,12 @@ class Fritz { if (protocol == 'http:') { http = require('http'); defaultport = 80; - if (this.debug) console.log('using http'); + if (this.debug) console.log('fritzAHA_Request using http'); } else if (protocol == 'https:') { http = require('https'); defaultport = 443; //http.globalAgent.options.secureProtocol = 'SSLv3_method'; - if (this.debug) console.log('using https'); + if (this.debug) console.log('fritzAHA_Request using https'); } else { console.log('invalid protocol'); } @@ -861,6 +933,7 @@ class Fritz { let p = new Promise((resolve, reject) => { const req = http.request(options, (res) => { res.setEncoding('utf8'); + const statuscode = parseInt(res.statusCode); if (res.statusCode !== 200) { // throw Error(`HTTP request Failed. Status Code: ${res.statusCode}`); console.log(`HTTP request Failed. Status Code: ${res.statusCode}`); @@ -868,7 +941,7 @@ class Fritz { msg: 'AHA request error', function: 'fritzAHA_Request', error: res.statusCode, - response: res + status: statuscode }); } // cumulate data @@ -880,7 +953,7 @@ class Fritz { res.on('end', () => { if (responseBody) { //console.log('statusCode:', response.statusCode); // Print the response status code if a response was received - //console.log('body:', responseBody); // Print the XML answer. + // console.log('body:', responseBody); // Print the XML answer. resolve(responseBody.trim()); } else { reject({ diff --git a/lib/fritz_mockserver.js b/lib/fritz_mockserver.js index 0c168f9..db9b6d0 100644 --- a/lib/fritz_mockserver.js +++ b/lib/fritz_mockserver.js @@ -25,8 +25,8 @@ class FritzEmu { xmlTemplates, xmlTrigger, xmlColors, - xmlTempStat, - xmlPowerStat, + xmlTempStats, + xmlPowerStats, guestWlan, hkr_batt ) { @@ -36,8 +36,8 @@ class FritzEmu { this.xmlTemplates = xmlTemplates; this.xmlTrigger = xmlTrigger; this.xmlColors = xmlColors; - this.xmlTempStat = xmlTempStat; - this.xmlPowerStat = xmlPowerStat; + this.xmlTempStats = xmlTempStats; + this.xmlPowerStats = xmlPowerStats; this.guestWlan = guestWlan; this.hkr_batt = hkr_batt; this.apiresponse = {}; @@ -75,6 +75,8 @@ class FritzEmu { temperature = null, duration = null, target = null, + level = null, + active = null, username = null, version = null, logout = null, @@ -97,16 +99,16 @@ class FritzEmu { ain = commandsplit[1]; console.log('\x1b[36m', '-> ain : ', commandsplit[1]); break; - case 'ain': + case 'onoff': onoff = commandsplit[1]; console.log('\x1b[36m', '-> onoff : ', commandsplit[1]); break; case 'param': - onoff = commandsplit[1]; + param = commandsplit[1]; console.log('\x1b[36m', '-> param : ', commandsplit[1]); break; case 'endtimestamp': - onoff = commandsplit[1]; + endtimestamp = commandsplit[1]; console.log('\x1b[36m', '-> endtimestamp : ', commandsplit[1]); break; case 'hue': @@ -124,7 +126,7 @@ class FritzEmu { console.log('\x1b[36m', '-> temperature : ', commandsplit[1]); break; case 'duration': - temperature = commandsplit[1]; + duration = commandsplit[1]; console.log('\x1b[36m', '-> duration : ', commandsplit[1]); break; case 'target': @@ -143,6 +145,14 @@ class FritzEmu { response = commandsplit[1]; console.log('\x1b[36m', '-> response : ', commandsplit[1]); break; + case 'level': + level = commandsplit[1]; + console.log('\x1b[36m', '-> level : ', commandsplit[1]); + break; + case 'active': + active = commandsplit[1]; + console.log('\x1b[36m', '-> active : ', commandsplit[1]); + break; case 'logout': logout = commandsplit[1]; console.log('\x1b[36m', '-> logout : ', commandsplit[1]); @@ -173,7 +183,9 @@ class FritzEmu { saturation, temperature, duration, - target + target, + level, + active ); } else if (request.url == '/wlan/guest_access.lua?0=0&sid=' + mocksid) { //check the URL of the current request @@ -230,6 +242,7 @@ class FritzEmu { } else { console.log('\x1b[31m', '-> not supported call ' + request.method + ' ' + request.url); response.statusCode = 403; + response.write(String('')); response.end(); } }); @@ -246,7 +259,56 @@ class FritzEmu { callback(); }); } - + JSONtoXML(obj) { + let xml = ''; + for (let prop in obj) { + xml += obj[prop] instanceof Array ? '' : '<' + prop + '>'; + if (obj[prop] instanceof Array) { + for (let array in obj[prop]) { + xml += '\n<' + prop + '>\n'; + xml += JSONtoXML(new Object(obj[prop][array])); + xml += ''; + } + } else if (typeof obj[prop] == 'object') { + xml += JSONtoXML(new Object(obj[prop])); + } else { + xml += obj[prop]; + } + xml += obj[prop] instanceof Array ? '' : '\n'; + } + xml = xml.replace(/<\/?[0-9]{1,}>/g, ''); + return xml; + } + json2xml(o, tab) { + var toXml = function(v, name, ind) { + var xml = ''; + if (v instanceof Array) { + for (var i = 0, n = v.length; i < n; i++) xml += ind + toXml(v[i], name, ind + '\t') + '\n'; + } else if (typeof v == 'object') { + var hasChild = false; + xml += ind + '<' + name; + for (var m in v) { + if (m.charAt(0) == '@') xml += ' ' + m.substr(1) + '="' + v[m].toString() + '"'; + else hasChild = true; + } + xml += hasChild ? '>' : '/>'; + if (hasChild) { + for (var m in v) { + if (m == '#text') xml += v[m]; + else if (m == '#cdata') xml += ''; + else if (m.charAt(0) != '@') xml += toXml(v[m], m, ind + '\t'); + } + xml += (xml.charAt(xml.length - 1) == '\n' ? ind : '') + ''; + } + } else { + xml += ind + '<' + name + '>' + v.toString() + ''; + } + return xml; + }, + xml = ''; + for (var m in o) xml += toXml(o[m], m, ''); + return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, ''); + } // Functions for reply on the requests loginoutAnswerV2(response, sid, method, username, userresponse, request) { if (!sid && method == 'GET') { @@ -361,6 +423,7 @@ class FritzEmu { } errorAnswer(response, code) { response.statusCode = code; + //response.writeHead(code, { 'Content-Type': 'application/x-www-form-urlencoded' }); response.end(); return response; } @@ -376,7 +439,9 @@ class FritzEmu { saturation, temperature, duration, - target + target, + level, + active ) { switch (switchcmd) { case 'getdevicelistinfos': @@ -390,16 +455,17 @@ class FritzEmu { return response; break; case 'getdeviceinfos': + console.log('getdeviceinfos ' + ain); const deviceinfos = this.apiresponse['devicelist']['device'].filter( (device) => device.identifier === ain ); if (deviceinfos) { response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(String(deviceinfos)); + response.write(String(this.json2xml({ device: deviceinfos }))); response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -422,7 +488,7 @@ class FritzEmu { response.end(); } else { console.log('\x1b[31m', ' did not find the ain in templates ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -451,45 +517,47 @@ class FritzEmu { const setgroupstate = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('switch') && group.identifier === ain) .map((group) => group.switch.state); - if (setswitchstate) { + if (setswitchstate.length == 1) { const pos = this.findAin('device', ain); - if ((switchcmd = 'setswitchon')) { + if (switchcmd == 'setswitchon') { this.apiresponse.devicelist.device[pos].switch.state = 1; response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(JSON.stringify([ '1' ])); + response.write(String('1')); response.end(); - } else if ((switchcmd = 'setswitchoff')) { + } else if (switchcmd == 'setswitchoff') { this.apiresponse.devicelist.device[pos].switch.state = 0; response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(JSON.stringify([ '0' ])); + response.write(String('0')); response.end(); - } else if ((switchcmd = 'setswitchtoggle')) { - this.apiresponse.devicelist.device[pos].switch.state = !setswitchstate; + } else if (switchcmd == 'setswitchtoggle') { + let sstate = setswitchstate == '1' ? '0' : '1'; + this.apiresponse.devicelist.device[pos].switch.state = sstate; response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(JSON.stringify([ "'" + !setswitchstate + "'" ])); + response.write(String(sstate)); response.end(); } - } else if (setgroupstate) { + } else if (setgroupstate.length == 1) { const pos = this.findAin('group', ain); - if ((switchcmd = 'setswitchon')) { - this.apiresponse.devicelist.group[pos].switch.state = 0; + if (switchcmd == 'setswitchon') { + this.apiresponse.devicelist.group[pos].switch.state = 1; response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(JSON.stringify([ '1' ])); + response.write(String('1')); response.end(); - } else if ((switchcmd = 'setswitchoff')) { - this.apiresponse.devicelist.group[pos].switch.state = 1; + } else if (switchcmd == 'setswitchoff') { + this.apiresponse.devicelist.group[pos].switch.state = 0; response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(JSON.stringify([ '0' ])); + response.write(String('0')); response.end(); - } else if ((switchcmd = 'setswitchtoggle')) { - this.apiresponse.devicelist.group[pos].switch.state = !setgroupstate; + } else if (switchcmd == 'setswitchtoggle') { + let gstate = setgroupstate == '1' ? '0' : '1'; + this.apiresponse.devicelist.group[pos].switch.state = gstate; response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(JSON.stringify([ "'" + !setgroupstate + "'" ])); + response.write(String(gstate)); response.end(); } } else { console.log(' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -500,40 +568,42 @@ class FritzEmu { const getgrouptemp = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('temperature') && device.identifier === ain) .map((group) => group.temperature.celsius); - if (gettemp) { + if (gettemp.length > 0) { response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); response.write(String(gettemp)); response.end(); - } else if (getgrouptemp) { + } else if (getgrouptemp.length > 0) { response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); response.write(String(getgrouptemp)); response.end(); } else { console.log(' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; case 'getswitchname': case 'getswitchpresent': const item = switchcmd.replace('getswitch', ''); - const switchvalue = this.apiresponse['devicelist']['device'] - .filter((device) => device.hasOwnProperty('switch') && device.identifier === ain) + const devicevalue = this.apiresponse['devicelist']['device'] + .filter((device) => device.hasOwnProperty(item) && device.identifier === ain) .map((device) => device[item]); const groupvalue = this.apiresponse['devicelist']['group'] - .filter((group) => group.hasOwnProperty('switch') && group.identifier === ain) + .filter((group) => group.hasOwnProperty(item) && group.identifier === ain) .map((group) => group[item]); - if (switchvalue) { + if (devicevalue.length > 0) { response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(JSON.stringify([ "'" + switchvalue + "'" ])); + //response.write(JSON.stringify([ "'" + devicevalue + "'" ])); + response.write(String(devicevalue)); response.end(); - } else if (groupvalue) { + } else if (groupvalue.length > 0) { response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(JSON.stringify([ "'" + groupvalue + "'" ])); + //response.write(JSON.stringify([ "'" + groupvalue + "'" ])); + response.write(String(groupvalue)); response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -544,17 +614,19 @@ class FritzEmu { const getgroupstate = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('switch') && group.identifier === ain) .map((group) => group.switch.state); - if (getswitchstate) { + if (getswitchstate.length > 0) { response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(JSON.stringify([ "'" + getswitchstate + "'" ])); + //response.write(JSON.stringify([ "'" + getswitchstate + "'" ])); + response.write(String(getswitchstate)); response.end(); - } else if (groupstate) { + } else if (getgroupstate.length > 0) { response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(JSON.stringify([ "'" + getgroupstate + "'" ])); + //response.write(JSON.stringify([ "'" + getgroupstate + "'" ])); + response.write(String(getgroupstate)); response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -580,11 +652,11 @@ class FritzEmu { } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); //verursacht StatusCode400 - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; - //alles KHR + //alles HKR case 'gethkrtsoll': case 'gethkrkomfort': case 'gethkrabsenk': @@ -595,17 +667,17 @@ class FritzEmu { const getgrouphkrtemp = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('hkr') && group.identifier === ain) .map((group) => group.hkr[item3]); - if (gethkrtemp) { + if (gethkrtemp.length > 0) { response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); response.write(String(gethkrtemp)); response.end(); - } else if (getgrouphkrtemp) { + } else if (getgrouphkrtemp.length > 0) { response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); response.write(String(getgrouphkrtemp)); response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.Answer(response, 400); } return response; break; @@ -617,19 +689,21 @@ class FritzEmu { const setgrouphkrtemp = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('hkr') && group.identifier === ain) .map((group) => group.hkr.tsoll); - if (sethkrtemp) { + if (sethkrtemp.length > 0) { const pos = this.findAin('device', ain); this.apiresponse.devicelist.device[pos].hkr.tsoll = param; response.statusCode = 200; + response.write(String(param)); response.end(); - } else if (setgrouphkrtemp) { + } else if (setgrouphkrtemp.length > 0) { const pos = this.findAin('group', ain); this.apiresponse.devicelist.group[pos].hkr.tsoll = param; response.statusCode = 200; + response.write(String(param)); response.end(); } else { console.log('\x1b[31m', '-> did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -638,29 +712,34 @@ class FritzEmu { // todo prüfen ob gruppen statistik haben können const thermostat = this.apiresponse['devicelist']['device'] .filter((device) => device.hasOwnProperty('hkr') && device.identifier === ain) - .map((device) => device.hkr.tsoll); + .map((device) => device.temperature); const groupthermo = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('hkr') && group.identifier === ain) - .map((group) => group.hkr.tsoll); + .map((group) => group.temperature); const switcher = this.apiresponse['devicelist']['device'] - .filter((device) => device.hasOwnProperty('hkr') && device.identifier === ain) - .map((device) => device.hkr.tsoll); + .filter((device) => device.hasOwnProperty('switch') && device.identifier === ain) + .map((device) => device.switch); const groupswitcher = this.apiresponse['devicelist']['group'] - .filter((group) => group.hasOwnProperty('hkr') && group.identifier === ain) - .map((group) => group.hkr.tsoll); - if (thermostat || groupthermo) { + .filter((group) => group.hasOwnProperty('switch') && group.identifier === ain) + .map((group) => group.switch); + if (thermostat.length > 0) { //check the URL of the current request response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(String(this.xmlTempStat)); + response.write(String(this.xmlTempStats)); response.end(); - } else if (switcher || groupswitcher) { + } else if (switcher.length > 0) { //check the URL of the current request response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); response.write(String(this.xmlPowerStats)); response.end(); + } else if (groupthermo.length > 0 || groupswitcher.length > 0) { + //check the URL of the current request + response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); + response.write(String('')); + response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -674,9 +753,9 @@ class FritzEmu { .map((group) => group.simpleonoff.state); let newstate = null; - if (simplevalue) { + if (simplevalue.length > 0) { if (onoff == 2) { - newstate = !simplevalue; + newstate = simplevalue == '1' ? '0' : '1'; } else { //ohne prüfung ob auch 0 oder 1 geschickt wird newstate = onoff; @@ -684,10 +763,11 @@ class FritzEmu { const pos = this.findAin('device', ain); this.apiresponse.devicelist.device[pos].simpleonoff.state = newstate; response.statusCode = 200; + response.write(String(newstate)); response.end(); - } else if (groupsimplevalue) { + } else if (groupsimplevalue.length > 0) { if (onoff == 2) { - newstate = !groupsimplevalue; + newstate = groupsimplevalue == '1' ? '0' : '1'; } else { //ohne prüfung ob auch 0 oder 1 geschickt wird newstate = onoff; @@ -695,10 +775,11 @@ class FritzEmu { const pos = this.findAin('group', ain); this.apiresponse.devicelist.group[pos].simpleonoff.state = newstate; response.statusCode = 200; + response.write(String(newstate)); response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response); + response = this.errorAnswer(response); } return response; break; @@ -710,25 +791,27 @@ class FritzEmu { const grouplevel = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('levelcontrol') && group.identifier === ain) .map((group) => group.levelcontrol.level); - if (levelvalue) { + if (levelvalue.length > 0) { const pos = this.findAin('device', ain); this.apiresponse.devicelist.device[pos].levelcontrol.level = level; this.apiresponse.devicelist.device[pos].levelcontrol.levelpercentage = Math.floor( Number(level) / 255 * 100 ); + response.write(String(level)); response.statusCode = 200; response.end(); - } else if (grouplevel) { + } else if (grouplevel.length > 0) { const pos = this.findAin('group', ain); this.apiresponse.devicelist.group[pos].levelcontrol.level = level; this.apiresponse.devicelist.device[pos].levelcontrol.levelpercentage = Math.floor( Number(level) / 255 * 100 ); response.statusCode = 200; + response.write(String(level)); response.end(); } else { console.log(' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response); + response = this.errorAnswer(response); } return response; break; @@ -740,21 +823,23 @@ class FritzEmu { const grouplevelperc = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('levelcontrol') && group.identifier === ain) .map((group) => group.levelcontrol.levelpercentage); - if (levelvalueperc) { + if (levelvalueperc.length > 0) { const pos = this.findAin('device', ain); this.apiresponse.devicelist.device[pos].levelcontrol.level = Math.floor(Number(level) / 100 * 255); this.apiresponse.devicelist.device[pos].levelcontrol.levelpercentage = level; response.statusCode = 200; + response.write(String(level)); response.end(); - } else if (grouplevelperc) { + } else if (grouplevelperc.length > 0) { const pos = this.findAin('group', ain); this.apiresponse.devicelist.group[pos].levelcontrol.level = Math.floor(Number(level) / 100 * 255); this.apiresponse.devicelist.device[pos].levelcontrol.levelpercentage = level; response.statusCode = 200; + response.write(String(level)); response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -765,24 +850,28 @@ class FritzEmu { console.log('additional cmd duration' + duration); const colorvalue = this.apiresponse['devicelist']['device'] .filter((device) => device.hasOwnProperty('colorcontrol') && device.identifier === ain) - .map((device) => device.colorontrol[cmd]); + .map((device) => device.colorcontrol[colorcmd]); const groupcolor = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('colorcontrol') && group.identifier === ain) - .map((group) => group.colorcontrol[cmd]); + .map((group) => group.colorcontrol[colorcmd]); const newvalue = hue || saturation; - if (colorvalue) { + if (colorvalue.length > 0) { const pos = this.findAin('device', ain); - this.apiresponse.devicelist.device[pos].colorcontrol[cmd] = newvalue; + this.apiresponse.devicelist.device[pos].colorcontrol[colorcmd] = newvalue; response.statusCode = 200; + //todo was kommt wirklich zurück? + response.write(String(newvalue)); response.end(); - } else if (groupcolor) { + } else if (groupcolor.length > 0) { const pos = this.findAin('group', ain); - this.apiresponse.devicelist.group[pos].colorcontrol[cmd] = newvalue; + this.apiresponse.devicelist.group[pos].colorcontrol[colorcmd] = newvalue; response.statusCode = 200; + //todo was kommt wirklich zurück? + response.write(String(newvalue)); response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -790,23 +879,27 @@ class FritzEmu { console.log('temperature ' + temperature); const settempvalue = this.apiresponse['devicelist']['device'] .filter((device) => device.hasOwnProperty('colorcontrol') && device.identifier === ain) - .map((device) => device.colorontrol.temperature); + .map((device) => device.colorcontrol.temperature); const setgrouptemp = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('colorcontrol') && group.identifier === ain) .map((group) => group.colorcontrol.temperature); - if (settempvalue) { + if (settempvalue.length > 0) { const pos = this.findAin('device', ain); this.apiresponse.devicelist.device[pos].colorcontrol.temperature = temperature; response.statusCode = 200; + //todo was kommt wirklich zurück? + response.write(String(temperature)); response.end(); - } else if (setgrouptemp) { + } else if (setgrouptemp.length > 0) { const pos = this.findAin('group', ain); this.apiresponse.devicelist.group[pos].colorcontrol.temperature = temperature; response.statusCode = 200; + //todo was kommt wirklich zurück? + response.write(String(temperature)); response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -825,16 +918,24 @@ class FritzEmu { const groupboost = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('hkr') && group.identifier === ain) .map((group) => group.hkr.boostactiveendtime); - if (endttimestamp > now) { - endtimepstamp = ''; + const now = new Date(); + /* + // auch in vergangenheit liegende Zeitstempel werden als gültig angesehen + if (parseInt(endtimestamp) < now.getTime()) { + endtimestamp = ''; } - if (hkrboost) { + */ + + if (parseInt(endtimestamp) * 1000 > now.getTime() + 86400) { + console.log('\x1b[31m', ' endtime > now +24h ' + ain); + response = this.errorAnswer(response, 500); + } else if (hkrboost.length > 0) { const pos = this.findAin('device', ain); this.apiresponse.devicelist.device[pos].hkr.boostactiveendtime = endtimestamp; response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); response.write(String(endtimestamp)); response.end(); - } else if (groupboost) { + } else if (groupboost.length > 0) { const pos = this.findAin('group', ain); this.apiresponse.devicelist.group[pos].hkr.boostactiveendtime = endtimestamp; response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); @@ -842,7 +943,7 @@ class FritzEmu { response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -854,16 +955,23 @@ class FritzEmu { const groupwindow = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('hkr') && group.identifier === ain) .map((group) => group.hkr.windowopenactiveendtime); - if (endttimestamp > now) { - endtimepstamp = ''; + const now2 = new Date(); + /* + // auch in vergangenheit liegende Zeitstempel werden als gültig angesehen + if (parseInt(endtimestamp) < now2.getTime()) { + endtimestamp = ''; } - if (hkrwindow) { + */ + if (parseInt(endtimestamp) > now2.getTime() + 86400) { + console.log('\x1b[31m', ' endtime > now +24h ' + ain); + response = this.errorAnswer(response, 500); + } else if (hkrwindow.length > 0) { const pos = this.findAin('device', ain); - this.apiresponse.devicelist.device[pos].hkr.windowopenactiveendtime = endtimestamp; + //this.apiresponse.devicelist.device[pos].hkr.windowopenactiveendtime = endtimestamp; response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); response.write(String(endtimestamp)); response.end(); - } else if (groupwindow) { + } else if (groupwindow.length > 0) { const pos = this.findAin('group', ain); this.apiresponse.devicelist.group[pos].hkr.windowopenactiveendtime = endtimestamp; response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); @@ -871,7 +979,7 @@ class FritzEmu { response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; @@ -883,33 +991,49 @@ class FritzEmu { const groupblind = this.apiresponse['devicelist']['group'] .filter((group) => group.hasOwnProperty('blind') && group.identifier === ain) .map((group) => group.blind.mode); - if (blindvalue) { + if (blindvalue.length > 0) { response.statusCode = 200; + response.write(String(target)); response.end(); - } else if (groupblind) { + } else if (groupblind.length > 0) { response.statusCode = 200; + response.write(String(target)); response.end(); } else { console.log('\x1b[31m', ' did not find the ain in devices/groups ' + ain); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); } return response; break; case 'setname': response.statusCode = 200; + response.write(String(name)); response.end(); return response; break; case 'setmetadata': break; - case 'gettriggerlistinfo': + case 'gettriggerlistinfos': // todo empty templates response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); - response.write(String(xmlTriggerlist)); + response.write(String(this.xmlTrigger)); response.end(); return response; break; case 'settriggeractive': + console.log('active ' + active); + const trigger = this.apiresponse['triggerlist']['trigger'].filter( + (trigger) => trigger.hasOwnProperty('identifier') && trigger.identifier === ain + ); + if (trigger.length > 0) { + response.writeHead(200, { 'xmlDevicesGroups-Type': 'application/json' }); + response.write(String(active)); + response.end(); + } else { + console.log('\x1b[31m', ' did not find the ain in trigger ' + ain); + response = this.errorAnswer(response, 400); + } + return response; break; case 'setmappedcolor': break; @@ -923,7 +1047,7 @@ class FritzEmu { break; default: console.log('\x1b[31m', 'switchcmd no case found ' + switchcmd); - response = errorAnswer(response, 400); + response = this.errorAnswer(response, 400); return response; break; } @@ -939,8 +1063,8 @@ class FritzEmu { } } } else if (type === 'group') { - for (let i = 0; i < this.apiresponse['devicelist']['device'].length; i++) { - if (this.apiresponse['devicelist']['device'][i].identifier === ain) { + for (let i = 0; i < this.apiresponse['devicelist']['group'].length; i++) { + if (this.apiresponse['devicelist']['group'][i].identifier === ain) { position = i; break; } diff --git a/lib/mjs_version/start_mockserver.mjs b/lib/mjs_version/start_mockserver.mjs new file mode 100644 index 0000000..62e8a13 --- /dev/null +++ b/lib/mjs_version/start_mockserver.mjs @@ -0,0 +1,16 @@ +let FritzEmu; +(async () => { + let fb = await import('fritzdect-aha-nodejs'); + FritzEmu = fb.FritzEmu; +})().catch((err) => console.error(err)); + +console.log(FritzEmu); + +function fireUpEmu() { + let testfile = 'testFBall.xml'; + let port = 3333; + const emulation = new FritzEmu(testfile, port, false); + emulation.setupHttpServer(function() {}); +} + +fireUpEmu(); diff --git a/package.json b/package.json index 4df5bcd..f931997 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fritzdect-aha-nodejs", - "version": "2.0.1", + "version": "2.1.0", "description": "NodeJS library using the AHA api of Fritzbox to control DECT smarthome devices.", "main": "index.js", "dependencies": { diff --git a/test/integration.js b/test/integration.js index a0650d9..9ca1127 100644 --- a/test/integration.js +++ b/test/integration.js @@ -13,18 +13,18 @@ const crypto = require('crypto'); const path = require('path'); console.log('PATH ist ' + path.join(__dirname, './data/')); -const xmlDevicesGroups = fs.readFileSync(path.join(__dirname, './data/') + 'test_api_response.xml'); +const xmlDevicesGroups = String(fs.readFileSync(path.join(__dirname, './data/') + 'test_api_response.xml')); //var xmlDevicesGroups = fs.readFileSync('./test.xml'); -const xmlTemplate = fs.readFileSync(path.join(__dirname, './data/') + 'template_answer.xml'); -const xmlTriggerlist = fs.readFileSync(path.join(__dirname, './data/') + 'getriggerlistinfos.xml'); -const xmlTempStat = fs.readFileSync(path.join(__dirname, './data/') + 'devicestat_temp_answer.xml'); -const xmlPowerStats = fs.readFileSync(path.join(__dirname, './data/') + 'devicestat_power_answer.xml'); -const xmlColorDefaults = fs.readFileSync(path.join(__dirname, './data/') + 'color_defaults.xml'); +const xmlTemplate = String(fs.readFileSync(path.join(__dirname, './data/') + 'template_answer.xml')); +const xmlTriggerlist = String(fs.readFileSync(path.join(__dirname, './data/') + 'getriggerlistinfos.xml')); +const xmlTempStats = String(fs.readFileSync(path.join(__dirname, './data/') + 'devicestat_temp_answer.xml')); +const xmlPowerStats = String(fs.readFileSync(path.join(__dirname, './data/') + 'devicestat_power_answer.xml')); +const xmlColorDefaults = String(fs.readFileSync(path.join(__dirname, './data/') + 'color_defaults.xml')); const hkr_batt = fs.readFileSync(path.join(__dirname, './data/') + 'hkr_response.xml'); const guestWlan = fs.readFileSync(path.join(__dirname, './data/') + 'guest_wlan_form.xml'); -const devices2json = parser.xml2json(String(xmlDevicesGroups)); +const devices2json = parser.xml2json(xmlDevicesGroups); let devices = [].concat((devices2json.devicelist || {}).device || []).map((device) => { // remove spaces in AINs device.identifier = device.identifier.replace(/\s/g, ''); @@ -35,10 +35,10 @@ let groups = [].concat((devices2json.devicelist || {}).group || []).map((group) group.identifier = group.identifier.replace(/\s/g, ''); return group; }); -const templates2json = parser.xml2json(String(xmlTemplate)); +const templates2json = parser.xml2json(xmlTemplate); let templates = [].concat((templates2json.templatelist || {}).template || []).map(function(template) { // remove spaces in AINs - // template.identifier = group.identifier.replace(/\s/g, ''); + template.identifier = template.identifier.replace(/\s/g, ''); return template; }); @@ -58,7 +58,7 @@ describe('Test of Fritzdect-AHA-API', () => { xmlTemplate, xmlTriggerlist, xmlColorDefaults, - xmlTempStat, + xmlTempStats, xmlPowerStats, guestWlan, hkr_batt @@ -68,22 +68,157 @@ describe('Test of Fritzdect-AHA-API', () => { var fritz; // if promise is returned = success it('should create a new fritzdect instance', function() { - fritz = new Fritz('admin', 'password', 'http://localhost:3333', null); + fritz = new Fritz('admin', 'password', 'http://localhost:3333', false, null); }); it('login success returns true', async () => { const result = await fritz.login_SID(); //assert.equal(result, true); expect(result).to.equal(true); }); - /* + it('function getUserPermissions()', async () => { + const result = await fritz.getUserPermissions(); + //console.log('getUserPermissions result', result); + const permissions = + 'Dial2App2HomeAuto2BoxAdmin2Phone2NAS2'; + expect(result).to.eql(permissions); + console.log('----------------------'); + }); it('function getdevicelistinfos', async () => { const result = await fritz.getDeviceListInfos(); - //console.log('getdevicelistinfos result', JSON.parse(result)); + const devicesgroups = parser.xml2json(result); + let devices = [].concat((devicesgroups.devicelist || {}).device || []).map((device) => { + // remove spaces in AINs + device.identifier = device.identifier.replace(/\s/g, ''); + return device; + }); + let groups = [].concat((devicesgroups.devicelist || {}).group || []).map((group) => { + // remove spaces in AINs + group.identifier = group.identifier.replace(/\s/g, ''); + return group; + }); const devicelist = apiresponse['devicelist']; - //console.log(switchlist); - expect(parser.xml2json(result).devicelist).to.eql(devicelist); + expect({ version: '1', device: devices, group: groups }).to.eql(devicelist); + }); + it('function gettemplatelistinfos', async () => { + const result = await fritz.getTemplateListInfos(); + //console.log('gettemplatelistinfos result', result); + const templatelist = apiresponse['templatelist']; + expect(parser.xml2json(result).templatelist).to.eql(templatelist); + }); + it('function gettriggerlistinfos', async () => { + const result = await fritz.getTriggerListInfos(); + //console.log('gettriggerlistinfos result', result); + const triggerlist = apiresponse['triggerlist']; + expect(parser.xml2json(result).trigger).to.eql(triggerlist); + }); + it('function getcolordefaults', async () => { + const result = await fritz.getColorDefaults(); + //console.log('getcolordefaults result', result); + const colordefaults = parser.xml2json(xmlColorDefaults); + expect(parser.xml2json(result)).to.eql(colordefaults); + //expect(result).to.eql(xmlColorDefaults); + }); + it('function getdeviceinfos', async () => { + let ain = '087610006161'; + const result = await fritz.getDeviceInfos(ain); + const deviceinfo = apiresponse['devicelist']['device'].filter( + (device) => device.hasOwnProperty('switch') && device.identifier === ain + ); + expect(parser.xml2json(result)).to.eql({ device: deviceinfo[0] }); + }); + it('function getbasicdevicestats Temp', async () => { + let ain = '119600642220'; + const result = await fritz.getBasicDeviceStats(ain); + const basicdevicestats = parser.xml2json(xmlTempStats); + expect(parser.xml2json(result)).to.eql(basicdevicestats); + }); + it('function getbasicdevicestats Power', async () => { + let ain = '087610006161'; + const result = await fritz.getBasicDeviceStats(ain); + const basicdevicestats = parser.xml2json(xmlPowerStats); + expect(parser.xml2json(result)).to.eql(basicdevicestats); + }); + it('function getswitchstate', async () => { + let ain = '087610006161'; + const result = await fritz.getSwitchState(ain); + //console.log('getSwitchState result', result); + const switchstate = apiresponse['devicelist']['device'] + .filter((device) => device.hasOwnProperty('switch') && device.identifier === ain) + .map((device) => device.switch.state); + expect(result).to.equal(switchstate[0]); + }); + it('function getswitchpresent', async () => { + let ain = '087610006161'; + const result = await fritz.getSwitchPresent(ain); + //console.log('getswitchpresent result', result); + const switchpresent = apiresponse['devicelist']['device'] + .filter((device) => device.hasOwnProperty('present') && device.identifier === ain) + .map((device) => device.present); + expect(result).to.equal(switchpresent[0]); + }); + it('function getswitchpower', async () => { + let ain = '087610006161'; + const result = await fritz.getSwitchPower(ain); + //console.log('getswitchpower result', result); + const switchpower = apiresponse['devicelist']['device'] + .filter((device) => device.hasOwnProperty('powermeter') && device.identifier === ain) + .map((device) => device.powermeter.power); + expect(result).to.equal(switchpower[0]); + }); + it('function getswitchenergy', async () => { + let ain = '087610006161'; + const result = await fritz.getSwitchEnergy(ain); + //console.log('getswitchenergy result', result); + const switchenergy = apiresponse['devicelist']['device'] + .filter((device) => device.hasOwnProperty('powermeter') && device.identifier === ain) + .map((device) => device.powermeter.energy); + expect(result).to.equal(switchenergy[0]); + }); + it('function getswitchname', async () => { + let ain = '087610006161'; + const result = await fritz.getSwitchName(ain); + //console.log('getswitchname result', result); + const switchname = apiresponse['devicelist']['device'] + .filter((device) => device.hasOwnProperty('name') && device.identifier === ain) + .map((device) => device.name); + expect(result).to.equal(String(switchname[0])); + }); + it('function gettemperature', async () => { + let ain = '119600642220'; + const result = await fritz.getTemperature(ain); + //console.log('gettemperature result', result); + const temperature = apiresponse['devicelist']['device'] + .filter((device) => device.hasOwnProperty('temperature') && device.identifier === ain) + .map((device) => device.temperature.celsius); + expect(result).to.equal(temperature[0]); + }); + it('function gethkrtsoll', async () => { + let ain = '119600642220'; + const result = await fritz.getHkrTsoll(ain); + //console.log('gethkrtsoll result', result); + const tsoll = apiresponse['devicelist']['device'] + .filter((device) => device.hasOwnProperty('hkr') && device.identifier === ain) + .map((device) => device.hkr.tsoll); + expect(result).to.equal(tsoll[0]); + }); + it('function gethkrkomfort', async () => { + let ain = '119600642220'; + const result = await fritz.getHkrKomfort(ain); + //console.log('gethkrkomfort result', result); + const komfort = apiresponse['devicelist']['device'] + .filter((device) => device.hasOwnProperty('hkr') && device.identifier === ain) + .map((device) => device.hkr.komfort); + expect(result).to.equal(komfort[0]); + }); + it('function gethkrabsenk', async () => { + let ain = '119600642220'; + const result = await fritz.getHkrAbsenk(ain); + //console.log('gethkrabsenk result', result); + const absenk = apiresponse['devicelist']['device'] + .filter((device) => device.hasOwnProperty('hkr') && device.identifier === ain) + .map((device) => device.hkr.absenk); + expect(result).to.equal(absenk[0]); }); - */ it('function getswitchlist', async () => { const result = await fritz.getSwitchList(); //console.log('getswitchlist result', result); @@ -99,6 +234,116 @@ describe('Test of Fritzdect-AHA-API', () => { //switchlist wäre ein array, über String() wird es vergleichbarer Text expect(result).to.eql(String(switchlist)); }); + it('applytemplate', async () => { + let ain = 'tmp6F0093-39091EED0'; + const result = await fritz.applyTemplate(ain); + expect(result).to.equal('60010'); + }); + it('setswitchon', async () => { + let ain = '34:31:C1:AB:68:53'; + const result = await fritz.setSwitchOn(ain); + expect(result).to.equal('1'); + }); + it('setswitchoff', async () => { + let ain = '34:31:C1:AB:68:53'; + const result = await fritz.setSwitchOff(ain); + expect(result).to.equal('0'); + }); + it('setswitchtoggle', async () => { + let ain = 'EF:C4:CC-900'; + const result = await fritz.setSwitchToggle(ain); + expect(result).to.equal('0'); + }); + it('setsimpleonoff&onoff=1', async () => { + let ain = '13077 0018976-1'; + const result = await fritz.setSimpleOn(ain); + expect(result).to.equal('1'); + }); + it('setsimpleonoff&onoff=0', async () => { + let ain = '123456789012-1'; + const result = await fritz.setSimpleOff(ain); + expect(result).to.equal('0'); + }); + it('setsimpleonoff&onoff=2', async () => { + let ain = 'grp9CEFB6-3BDCFFC80'; + const result = await fritz.setSimpleToggle(ain); + expect(result).to.equal('0'); + }); + it('sethkrtsoll', async () => { + let ain = '11960 0642220'; + let temp = 23; + const result = await fritz.setTempTarget(ain, temp); + expect(result).to.equal('46'); + }); + it('sethkrboost', async () => { + let ain = '11795 1033333'; + const now = new Date(); + let time = now.getTime() + 86410; + let result = null; + try { + result = await fritz.setHkrBoost(ain, time); + } catch (error) { + result = error; + } + expect(result.status).to.equal(500); + }); + it('sethkrwindowopen', async () => { + let ain = '11795 1033333'; + let time = 1677538435; + const result = await fritz.setWindowOpen(ain, time); + expect(result).to.equal('1677538435'); + }); + it('setblind', async () => { + let ain = '14276 0470139-1'; + let target = 55; + const result = await fritz.setBlind(ain, target); + expect(result).to.equal('55'); + }); + it('setlevel', async () => { + let ain = '142760470139-1'; + let level = 55; + const result = await fritz.setLevel(ain, level); + expect(result).to.equal('55'); + }); + it('setlevelpercentage', async () => { + let ain = '142760470139-1'; + let level = 23; + const result = await fritz.setLevelPercentage(ain, level); + expect(result).to.equal('23'); + }); + it('setcolortemperature', async () => { + let ain = '13077 0018976-1'; + let temp = 3600; + const result = await fritz.setColorTemperature(ain, temp); + expect(result).to.equal('3400'); + }); + it('setcolor', async () => { + let ain = '13077 0018976-1'; + let saturation = 188; + let hue = 17; + const result = await fritz.setColor(ain, saturation, hue); + expect(result).to.equal('OK'); + }); + /* + it('setunmappedcolor', async () => { + let ain = '13077 0018976-1'; + let saturation = 200; + let hue = 22; + const result = await fritz.setUnmappedColor(ain, saturation, hue); + expect(result).to.equal(false); + }); + */ + it('settriggeractive', async () => { + let ain = 'trg695F2D-3CBF1DC25'; + let active = '1'; + try { + const result = await fritz.setTriggerActive(ain, active); + } catch (error) { + console.log(error); + expect(result).to.equal(active); + } + }); + it('logout success returns true', async () => { const result = await fritz.logout_SID(); expect(result).to.equal(false); @@ -106,17 +351,3 @@ describe('Test of Fritzdect-AHA-API', () => { }); // alte Fb prüfen -// alle exponierten CMDs prüfen -// Inhalte prüfen - -/* -var assert = require('assert'); -describe('login test', () => { - const fritz = new Fritz('admin', 'password', 'http://localhost:3333', null); - it('login success returns true', () => { - return fritz.login_SID().then((result) => { - assert.equal(result, true); - }); - }); -}); -*/ From 0aacfec191caed2389cca79e69882055308b25d9 Mon Sep 17 00:00:00 2001 From: foxthefox <16841643+foxthefox@users.noreply.github.com> Date: Wed, 5 Apr 2023 07:19:11 +0200 Subject: [PATCH 2/2] 2.1.0a --- example.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example.js b/example.js index e9dc371..91e6d76 100644 --- a/example.js +++ b/example.js @@ -2,7 +2,7 @@ const Fritz = require('./index.js').Fritz; -const fritz = new Fritz('admin', 'password', 'http://localhost:3333'); +const fritz = new Fritz('admin', 'password', 'http://localhost:3333', false); async function test() { const login = await fritz.login_SID().catch((e) => {