diff --git a/.github/workflows/test-and-release.yml b/.github/workflows/test-and-release.yml index f9c7690b..45b19567 100644 --- a/.github/workflows/test-and-release.yml +++ b/.github/workflows/test-and-release.yml @@ -22,8 +22,7 @@ jobs: strategy: matrix: - node-version: [16.x] - + node-version: [18.x] steps: - name: Checkout code uses: actions/checkout@v3 @@ -50,7 +49,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - node-version: [16, 18] + node-version: [18] os: [ubuntu-latest, windows-latest, macos-latest] steps: diff --git a/README.md b/README.md index 4d77441e..652f63be 100644 --- a/README.md +++ b/README.md @@ -200,8 +200,12 @@ otherwise it is more complex and individually to be parametrized. * blind alert state -> decode bit array ## Changelog +### 2.5.0 +* getbasicdevicestats for powermeter (voltage, power, energy) +* derived values from energy stats -> year to date, month to date, last 12 month, last 31 days, todays accumulation + ### 2.4.1 -* correctionts reported by adapter-checker +* corrections reported by adapter-checker ### 2.4.0 * new function for routines which activatetrigger diff --git a/io-package.json b/io-package.json index 2a4a4ef4..d873164c 100644 --- a/io-package.json +++ b/io-package.json @@ -1,116 +1,140 @@ { - "common": { - "name": "fritzdect", - "version": "2.4.1", - "news": { - "2.4.1": { - "en": "correctionts reported by adapter-checker", - "de": "von adapter-checker gemeldete korrekturen", - "ru": "коррекции, представленные адаптером-чеком", - "pt": "correções relatadas pelo verificador do adaptador", - "nl": "vertaling:", - "fr": "corrections signalées par le contrôleur de l'adaptateur", - "it": "correzioni segnalate da adattatore-checker", - "es": "correcciones notificadas por adaptador", - "pl": "poprawki doniesione przez adjuster-checker", - "uk": "виправлень, які повідомляються перехідником", - "zh-cn": "适应者报告的管教" - }, - "2.4.0": { - "en": "new function for routines, correction for templates", - "de": "neue funktion für routinen, korrektur für vorlagen", - "ru": "новая функция для рутин, коррекция для шаблонов", - "pt": "nova função para rotinas, correção para modelos", - "nl": "nieuwe functie voor routines, correctie voor templates", - "fr": "nouvelle fonction pour les routines, correction pour les modèles", - "it": "nuova funzione per le routine, correzione per i modelli", - "es": "nueva función para rutinas, corrección para plantillas", - "pl": "nowe funkcje dla rutynów, poprawki do szablonów", - "uk": "нова функція для рутин, виправлення шаблонів", - "zh-cn": "例行的新职能,模板的更正" - }, - "2.3.1": { - "en": "fault correction on xml2json conversion at templates from newer FB firmware", - "de": "Fehlerkorrektur bei xml2json Konvertierung bei Vorlagen aus neuerer FB Firmware", - "ru": "коррекция неисправности на конверсии xml2json в шаблонах от новой прошивки FB", - "pt": "correção de falha na conversão xml2json em modelos de firmware FB mais recente", - "nl": "vertaling:", - "fr": "correction de la faute sur la conversion xml2json à des modèles du nouveau firmware FB", - "it": "correzione guasto sulla conversione xml2json a modelli dal nuovo firmware FB", - "es": "corrección de fallas en la conversión de xml2json en plantillas de firmware FB más reciente", - "pl": "poprawka w błędzie na konwersji xml2json w szablonach od nowszego FB", - "uk": "виправлення несправностей на xml2json перетворення на шаблони з нової прошивки FB", - "zh-cn": "fB公司新软件的模板对×ml2json转换的错误" - } - }, - "title": "fritzbox DECT", - "titleLang": { - "en": "fritzbox dect ", - "de": "fritzbox dect", - "ru": "fritzbox дект", - "pt": "fritzbox dect", - "nl": "flitzbox dect", - "fr": "fritzbox dect", - "it": "fritzbox dect", - "es": "fritzbox dect", - "pl": "fritzbox dect", - "uk": "фріцбокс дект", - "zh-cn": "摩茨箱" - }, - "desc": { - "en": "connects DECT ULE devices (fritzdect) to ioBroker", - "de": "verbindet DECT ULE Geräte (fritzdect) mit ioBroker", - "ru": "подключает устройства DECT ULE (fritzdect) к ioBroker", - "pt": "conecta dispositivos DECT ULE (fritzdect) ao ioBroker", - "nl": "verbindt DECT ULE apparaten met ioBroker", - "fr": "connecte les appareils DECT ULE (fritzdect) à ioBroker", - "it": "collega i dispositivi DECT ULE (fritzdect) a ioBroker", - "es": "conecta dispositivos DECT ULE (fritzdect) a ioBroker", - "pl": "łączy DECT ULE (fritzdect) z ioBrokerem", - "uk": "з'єднання пристроїв DECT ULE (фрицдект) до ioBroker", - "zh-cn": "broker的连接" - }, - "authors": [ "foxthefox " ], - "docs": { - "de": "docs/de/install.md" - }, - "keywords": [ "DECT ULE", "lighting", "heating", "switching", "fritzbox", "AVM" ], - "license": "MIT", - "platform": "Javascript/Node.js", - "main": "main.js", - "icon": "fritzdect_logo.png", - "enabled": false, - "extIcon": "https://raw.githubusercontent.com/foxthefox/ioBroker.fritzdect/master/admin/fritzdect_logo.png", - "readme": "https://github.com/foxthefox/ioBroker.fritzdect/blob/master/README.md", - "loglevel": "info", - "restartAdapters": [ "vis" ], - "mode": "daemon", - "type": "hardware", - "compact": true, - "messagebox": true, - "connectionType": "local", - "dataSource": "poll", - "materialize": true, - "supportCustoms": false, - "dependencies": [ - { - "js-controller": ">=2.0.0" - } - ] - }, - "native": { - "fritz_ip": "http://192.168.x.x", - "fritz_user": "admin", - "fritz_pw": "pw", - "fritz_interval": 300, - "fritz_options": null, - "fritz_writeonhyst": false, - "fritz_analogchange": true, - "fritz_hysteresis": 1, - "fritz_boosttime": 5, - "fritz_windowtime": 5, - "fritz_tsolldefault": 23 - }, - "objects": [], - "instanceObjects": [] + "common": { + "name": "fritzdect", + "version": "2.5.0", + "news": { + "2.5.0": { + "en": "getbasicdevicestats for powermeter (voltage, power, energy)", + "de": "getbasicdevicestats für Leistungsmesser (Spannung, Leistung, Energie)", + "ru": "getbasicdevicestats для измерителя мощности (напряжение, мощность, энергия)", + "pt": "getbasicdevicestats para medidor de energia (tensão, potência, energia)", + "nl": "krijg basisapparaatstatistieken voor vermogensmeter (spanning, vermogen, energie)", + "fr": "getbasicdevicestats pour le powermeter (tension, puissance, énergie)", + "it": "getbasicdevicestats per misuratore di potenza (tensione, potenza, energia)", + "es": "getbasicdevicestats para medidor de potencia (voltaje, potencia, energía)", + "pl": "getbasicdevicestats dla miernika mocy (napięcie, moc, energia)", + "uk": "getbasicdevicestats для вимірювача потужності (напруга, потужність, енергія)", + "zh-cn": "getbasicdevicestats 功率计(电压、功率、能量)" + }, + "2.4.1": { + "en": "correctionts reported by adapter-checker", + "de": "von adapter-checker gemeldete korrekturen", + "ru": "коррекции, представленные адаптером-чеком", + "pt": "correções relatadas pelo verificador do adaptador", + "nl": "vertaling:", + "fr": "corrections signalées par le contrôleur de l'adaptateur", + "it": "correzioni segnalate da adattatore-checker", + "es": "correcciones notificadas por adaptador", + "pl": "poprawki doniesione przez adjuster-checker", + "uk": "виправлень, які повідомляються перехідником", + "zh-cn": "适应者报告的管教" + }, + "2.4.0": { + "en": "new function for routines, correction for templates", + "de": "neue funktion für routinen, korrektur für vorlagen", + "ru": "новая функция для рутин, коррекция для шаблонов", + "pt": "nova função para rotinas, correção para modelos", + "nl": "nieuwe functie voor routines, correctie voor templates", + "fr": "nouvelle fonction pour les routines, correction pour les modèles", + "it": "nuova funzione per le routine, correzione per i modelli", + "es": "nueva función para rutinas, corrección para plantillas", + "pl": "nowe funkcje dla rutynów, poprawki do szablonów", + "uk": "нова функція для рутин, виправлення шаблонів", + "zh-cn": "例行的新职能,模板的更正" + }, + "2.3.1": { + "en": "fault correction on xml2json conversion at templates from newer FB firmware", + "de": "Fehlerkorrektur bei xml2json Konvertierung bei Vorlagen aus neuerer FB Firmware", + "ru": "коррекция неисправности на конверсии xml2json в шаблонах от новой прошивки FB", + "pt": "correção de falha na conversão xml2json em modelos de firmware FB mais recente", + "nl": "vertaling:", + "fr": "correction de la faute sur la conversion xml2json à des modèles du nouveau firmware FB", + "it": "correzione guasto sulla conversione xml2json a modelli dal nuovo firmware FB", + "es": "corrección de fallas en la conversión de xml2json en plantillas de firmware FB más reciente", + "pl": "poprawka w błędzie na konwersji xml2json w szablonach od nowszego FB", + "uk": "виправлення несправностей на xml2json перетворення на шаблони з нової прошивки FB", + "zh-cn": "fB公司新软件的模板对×ml2json转换的错误" + } + }, + "title": "fritzbox DECT", + "titleLang": { + "en": "fritzbox dect ", + "de": "fritzbox dect", + "ru": "fritzbox дект", + "pt": "fritzbox dect", + "nl": "flitzbox dect", + "fr": "fritzbox dect", + "it": "fritzbox dect", + "es": "fritzbox dect", + "pl": "fritzbox dect", + "uk": "фріцбокс дект", + "zh-cn": "摩茨箱" + }, + "desc": { + "en": "connects DECT ULE devices (fritzdect) to ioBroker", + "de": "verbindet DECT ULE Geräte (fritzdect) mit ioBroker", + "ru": "подключает устройства DECT ULE (fritzdect) к ioBroker", + "pt": "conecta dispositivos DECT ULE (fritzdect) ao ioBroker", + "nl": "verbindt DECT ULE apparaten met ioBroker", + "fr": "connecte les appareils DECT ULE (fritzdect) à ioBroker", + "it": "collega i dispositivi DECT ULE (fritzdect) a ioBroker", + "es": "conecta dispositivos DECT ULE (fritzdect) a ioBroker", + "pl": "łączy DECT ULE (fritzdect) z ioBrokerem", + "uk": "з'єднання пристроїв DECT ULE (фрицдект) до ioBroker", + "zh-cn": "broker的连接" + }, + "authors": [ + "foxthefox " + ], + "docs": { + "de": "docs/de/install.md" + }, + "keywords": [ + "DECT ULE", + "lighting", + "heating", + "switching", + "fritzbox", + "AVM" + ], + "license": "MIT", + "platform": "Javascript/Node.js", + "main": "main.js", + "icon": "fritzdect_logo.png", + "enabled": false, + "extIcon": "https://raw.githubusercontent.com/foxthefox/ioBroker.fritzdect/master/admin/fritzdect_logo.png", + "readme": "https://github.com/foxthefox/ioBroker.fritzdect/blob/master/README.md", + "loglevel": "info", + "restartAdapters": [ + "vis" + ], + "mode": "daemon", + "type": "hardware", + "compact": true, + "messagebox": true, + "connectionType": "local", + "dataSource": "poll", + "materialize": true, + "supportCustoms": false, + "dependencies": [ + { + "js-controller": ">=2.0.0" + } + ] + }, + "native": { + "fritz_ip": "http://192.168.x.x", + "fritz_user": "admin", + "fritz_pw": "pw", + "fritz_interval": 300, + "fritz_options": null, + "fritz_writeonhyst": false, + "fritz_analogchange": true, + "fritz_hysteresis": 1, + "fritz_boosttime": 5, + "fritz_windowtime": 5, + "fritz_tsolldefault": 23 + }, + "objects": [], + "instanceObjects": [] } diff --git a/main.js b/main.js index ca24a93d..7d6ed684 100644 --- a/main.js +++ b/main.js @@ -209,6 +209,20 @@ class Fritzdect extends utils.Adapter { this.log.debug('polling! fritzdect is alive'); await this.updateDevices(this.fritz).catch((e) => this.errorHandlerAdapter(e)); await this.updateRoutines(this.fritz).catch((e) => this.errorHandlerAdapter(e)); + const deviceswithstat = await this.getStateAsync( + 'global.statdevices' + ).catch((e) => { + this.log.warn('problem getting statdevices ' + e); + }); + + if (deviceswithstat) { + let devstat = []; + devstat.push(deviceswithstat); + for (let i = 0; i < devstat.length; i++) { + this.log.debug('updating device ' + devstat[i]); + await this.updateStats(devstat[i]); + } + } } catch (e) { this.log.warn(`[Polling] <== ${e}`); } @@ -2117,6 +2131,23 @@ class Fritzdect extends utils.Adapter { ack: true }); } + // always the + await this.createObject('', 'global', 'channel', 'list'); + await this.setObjectNotExistsAsync('global.statdevices', { + type: 'state', + common: { + name: 'list of devices with stats', + type: 'array', + read: true, + write: false, + role: 'list', + desc: 'list of devices with stats' + }, + native: {} + }); + let devarr = []; + await this.setStateAsync('global.statdevices', { val: devarr, ack: true }); + //always ID await this.createInfoState(identifier, 'id', 'Device ID'); //etsideviceid im gleichen Object @@ -2357,21 +2388,35 @@ class Fritzdect extends utils.Adapter { // powermeter if (device.powermeter) { this.log.debug('setting up powermeter '); + await Promise.all( Object.keys(device.powermeter).map(async (key) => { //await this.asyncForEach(Object.keys(device.powermeter), async (key) => { + let oldarr = await this.getStateAsync('global.statsdevices').catch((e) => { + this.log.warn('problem getting statdevices ' + e); + }); + var newarray = []; + if (oldarr) { + newarray.push(oldarr); + await this.setStateAsync('global.statsdevices', { + val: newarray.push(identifier), + ack: true + }); + } if (key === 'power') { await this.createValueState(identifier, 'power', 'actual Power', 0, 4000, 'W'); await this.setStateAsync('DECT_' + identifier + '.power', { val: parseFloat(device.powermeter.power) / 1000, ack: true }); + await this.createStats(identifier, 'power'); } else if (key === 'voltage') { await this.createValueState(identifier, 'voltage', 'actual Voltage', 0, 255, 'V'); await this.setStateAsync('DECT_' + identifier + '.voltage', { val: parseFloat(device.powermeter.voltage) / 1000, ack: true }); + await this.createStats(identifier, 'voltage'); } else if (key === 'energy') { await this.createValueState( identifier, @@ -2385,6 +2430,7 @@ class Fritzdect extends utils.Adapter { val: parseInt(device.powermeter.energy), ack: true }); + await this.createStats(identifier, 'energy'); } else { this.log.warn(' new datapoint in API detected -> ' + key); } @@ -3041,6 +3087,22 @@ class Fritzdect extends utils.Adapter { }); return; } + async createList(newId, datapoint, name) { + this.log.debug('create list' + newId + ' with ' + datapoint); + await this.setObjectNotExistsAsync('DECT_' + newId + '.' + datapoint, { + type: 'state', + common: { + name: name, + type: 'array', + read: true, + write: false, + role: 'list', + desc: name + }, + native: {} + }); + return; + } async createTemplateResponse() { this.log.debug('create template.lasttemplate for response '); await this.setObjectNotExistsAsync('template', { @@ -3321,6 +3383,209 @@ class Fritzdect extends utils.Adapter { }); return; } + async createStats(identifier, type) { + this.log.debug('create Stats objects '); + await this.setObjectNotExistsAsync('DECT_' + identifier + '.' + type + '_stats', { + type: 'channel', + common: { + name: type + '_stats' + }, + native: {} + }); + + if (type == 'energy') { + await this.createValueState( + identifier, + type + '_stats' + '.countm', + 'stats count of months', + 0, + 12, + 'months' + ); + await this.createValueState(identifier, type + '_stats.gridm', 'grid of months', 0, 2678400, 's'); + await this.setObjectNotExistsAsync('DECT_' + identifier + '.' + type + '_stats.datatimem', { + type: 'state', + common: { + name: 'time of stats of months', + type: 'number', + min: 0, + max: 2147483648, + read: true, + write: false, + role: 'date', + desc: 'time of stats of months' + }, + native: {} + }); + await this.createValueState(identifier, type + '_stats.countd', 'stats countof days', 0, 31, 'days'); + await this.createValueState(identifier, type + '_stats.gridd', 'grid of days', 0, 86400, 's'); + await this.setObjectNotExistsAsync('DECT_' + identifier + '.' + type + '_stats.datatimed', { + type: 'state', + common: { + name: 'time of stats of days', + type: 'number', + min: 0, + max: 2147483648, + read: true, + write: false, + role: 'date', + desc: 'time of stats of days' + }, + native: {} + }); + await this.createValueState( + identifier, + type + '_stats.energy_ytd', + 'energy year to date', + 0, + 30000000, + 'Wh' + ); + await this.createValueState( + identifier, + type + '_stats.energy_last12m', + 'energy last 12 months', + 0, + 30000000, + 'Wh' + ); + await this.createValueState( + identifier, + type + '_stats.energy_mtd', + 'energy month to date', + 0, + 2500000, + 'Wh' + ); + await this.createValueState( + identifier, + type + '_stats.energy_last31d', + 'energy last 31 days', + 0, + 2500000, + 'Wh' + ); + await this.createValueState(identifier, type + '_stats.energy_dtd', 'energy day to date', 0, 87000, 'Wh'); + await this.createList(identifier, type + '_stats.stats_months', 'energy monthly stats array'); + await this.createList(identifier, type + '_stats.stats_days', 'energy dayly stats array'); + } else { + await this.createValueState(identifier, type + '_stats.count', 'stats count', 0, 360, 'counts'); + await this.createValueState(identifier, type + '_stats.grid', 'grid', 0, 2678400, 's'); + await this.setObjectNotExistsAsync('DECT_' + identifier + '.' + type + '_stats.datatime', { + type: 'state', + common: { + name: 'time of stats', + type: 'number', + min: 0, + max: 2147483648, + read: true, + write: false, + role: 'date', + desc: 'time of stats' + }, + native: {} + }); + await this.createList(identifier, type + '_stats.stats', 'stats array'); + } + + return; + } + async updateStats(identifier) { + this.log.debug('update Stats objects ' + identifier); + let devstat = await this.fritz.getBasicDeviceStats().catch((e) => this.errorHandlerApi(e)); + let statsobj = parser.xml2json(devstat); + await Promise.all( + Object.entries(statsobj.devicetstats).map(async ([ key, obj ]) => { + if (key !== 'temperature') { + if (key == 'energy') { + //months + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.countm', { + val: parseInt(obj['stats'][0]['count']), + ack: true + }); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.gridm', { + val: parseInt(obj['stats'][0]['grid']), + ack: true + }); + let datatimem = parseInt(obj['stats'][0]['datatime']); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.datatimem', { + val: datatimem, + ack: true + }); + let montharr = obj['stats'][0]['_@attribute'].split(',').map(Number); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.stats_months', { + val: montharr, + ack: true + }); + let last12m = montharr.reduce((pv, cv) => pv + cv, 0); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.energy_last12m', { + val: last12m, + ack: true + }); + let monthnum = parseInt(new Date(datatimem * 1000).toISOString().slice(6, 8)); + let ytd = montharr.splice(monthnum, 12 - monthnum).reduce((pv, cv) => pv + cv, 0); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.energy_ytd', { + val: ytd, + ack: true + }); + // days + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.countd', { + val: obj['stats'][1]['count'], + ack: true + }); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.gridd', { + val: obj['stats'][1]['grid'], + ack: true + }); + let datatimed = parseInt(obj['stats'][0]['datatime']); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.datetimed', { + val: datatimed, + ack: true + }); + let dayarr = obj['stats'][1]['_@attribute'].split(',').map(Number); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.stats_days', { + val: dayarr, + ack: true + }); + let last31d = dayarr.reduce((pv, cv) => pv + cv, 0); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.energy_last31d', { + val: last31d, + ack: true + }); + let daynum = parseInt(new Date(datatimed * 1000).toISOString().slice(8, 10)); + let mtd = dayarr.splice(daynum, 31 - daynum).reduce((pv, cv) => pv + cv, 0); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.energy_mtd', { + val: mtd, + ack: true + }); + let dtd = obj[1]['stats']['_@attribute'].split(',').map(Number)[0]; + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.energy_dtd', { + val: dtd, + ack: true + }); + } else { + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.count', { + val: obj['stats']['count'], + ack: true + }); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.grid', { + val: obj['stats']['grid'], + ack: true + }); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.datatime', { + val: obj['stats']['datatime'], + ack: true + }); + await this.setStateAsync('DECT_' + identifier + '.' + key + '_stats.stats', { + val: obj['stats']['_@attribute'], + ack: true + }); + } + } + }) + ); + return; + } } // @ts-ignore parent is a valid property on module diff --git a/package.json b/package.json index 3f203581..d9384874 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iobroker.fritzdect", - "version": "2.4.1", + "version": "2.5.0", "description": "ioBroker fritzbox DECT Adapter", "author": { "name": "foxthefox",