UNPKG

iobroker.lovelace

Version:

With this adapter you can build visualization for ioBroker with Home Assistant Lovelace UI

475 lines (443 loc) 20.6 kB
'use strict'; function formatValue(value, decimals, _format) { if (typeof decimals !== 'number') { decimals = 2; _format = decimals; } //format = (_format === undefined) ? (that.isFloatComma) ? ".," : ",." : _format; // does not work... // using default german... const format = _format === undefined || _format === null ? '.,' : _format; if (typeof value !== 'number') { value = parseFloat(value); } return isNaN(value) ? '' : value.toFixed(decimals || 0).replace(format[0], format[1]).replace(/\B(?=(\d{3})+(?!\d))/g, format[0]); } function formatDate(dateObj, isDuration, _format) { // copied from js-controller/lib/adapter.js if ((typeof isDuration === 'string' && isDuration.toLowerCase() === 'duration') || isDuration === true) { isDuration = true; } if (typeof isDuration !== 'boolean') { _format = isDuration; isDuration = false; } if (!dateObj) { return ''; } const type = typeof dateObj; if (type === 'string') dateObj = new Date(dateObj); if (type !== 'object') { const j = parseInt(dateObj, 10); if (j == dateObj) { // may this is interval if (j < 946681200) { isDuration = true; dateObj = new Date(dateObj); } else { // if less 2000.01.01 00:00:00 dateObj = (j < 946681200000) ? new Date(j * 1000) : new Date(j); } } else { dateObj = new Date(dateObj); } } const format = _format || /*this.dateFormat || */'DD.MM.YYYY'; if (isDuration) dateObj.setMilliseconds(dateObj.getMilliseconds() + dateObj.getTimezoneOffset() * 60 * 1000); const validFormatChars = 'YJГMМDTДhSчmмsс'; let s = ''; let result = ''; function put(s) { let v = ''; switch (s) { case 'YYYY': case 'JJJJ': case 'ГГГГ': case 'YY': case 'JJ': case 'ГГ': v = dateObj.getFullYear(); if (s.length === 2) v %= 100; break; case 'MM': case 'M': case 'ММ': case 'М': v = dateObj.getMonth() + 1; if ((v < 10) && (s.length === 2)) v = '0' + v; break; case 'DD': case 'TT': case 'D': case 'T': case 'ДД': case 'Д': v = dateObj.getDate(); if ((v < 10) && (s.length === 2)) v = '0' + v; break; case 'hh': case 'SS': case 'h': case 'S': case 'чч': case 'ч': v = dateObj.getHours(); if ((v < 10) && (s.length === 2)) v = '0' + v; break; case 'mm': case 'm': case 'мм': case 'м': v = dateObj.getMinutes(); if ((v < 10) && (s.length === 2)) v = '0' + v; break; case 'ss': case 's': case 'cc': case 'c': v = dateObj.getSeconds(); if ((v < 10) && (s.length === 2)) v = '0' + v; v = v.toString(); break; case 'sss': case 'ссс': v = dateObj.getMilliseconds(); if (v < 10) { v = '00' + v; } else if (v < 100) { v = '0' + v; } v = v.toString(); } return result += v; } for (let i = 0; i < format.length; i++) { if (validFormatChars.indexOf(format[i]) >= 0) s += format[i]; else { put(s); s = ''; result += format[i]; } } put(s); return result; } function formatBinding(format, states, dateFormat) { const oids = extractBinding(format); if (!oids) { return format; } for (let t = 0; t < oids.length; t++) { let value; if (oids[t].systemOid) { // value = this.getSpecialValues(oids[t].visOid, view, wid, widget); // if (value === undefined || value === null) { value = states[oids[t].systemOid] ? states[oids[t].systemOid].val : ''; // } } if (oids[t].operations) { for (let k = 0; k < oids[t].operations.length; k++) { let string = ''; // '(function() {'; switch (oids[t].operations[k].op) { case 'eval': for (let a = 0; a < oids[t].operations[k].arg.length; a++) { if (!oids[t].operations[k].arg[a].name) continue; // value = this.getSpecialValues(oids[t].operations[k].arg[a].visOid, view, wid, widget); // if (value === undefined || value === null) { value = states[oids[t].operations[k].arg[a].systemOid] ? states[oids[t].operations[k].arg[a].systemOid].val : ''; // } string += 'const ' + oids[t].operations[k].arg[a].name + ' = "' + value + '";'; } /*const formula = oids[t].operations[k].formula; if (formula && formula.indexOf('widget.') !== -1) { string += 'const widget = ' + JSON.stringify(widget) + ';'; }*/ string += 'return ' + oids[t].operations[k].formula + ';'; //string += '}())'; try { value = new Function(string)(); } catch (e) { console.error('Error in eval[value] : ' + format); console.error('Error in eval[script]: ' + string); console.error('Error in eval[error] : ' + e); value = 0; } break; case '*': if (oids[t].operations[k].arg !== undefined && oids[t].operations[k].arg !== null) { value = parseFloat(value) * oids[t].operations[k].arg; } break; case '/': if (oids[t].operations[k].arg !== undefined && oids[t].operations[k].arg !== null) { value = parseFloat(value) / oids[t].operations[k].arg; } break; case '+': if (oids[t].operations[k].arg !== undefined && oids[t].operations[k].arg !== null) { value = parseFloat(value) + oids[t].operations[k].arg; } break; case '-': if (oids[t].operations[k].arg !== undefined && oids[t].operations[k].arg !== null) { value = parseFloat(value) - oids[t].operations[k].arg; } break; case '%': if (oids[t].operations[k].arg !== undefined && oids[t].operations[k].arg !== null) { value = parseFloat(value) % oids[t].operations[k].arg; } break; case 'round': if (oids[t].operations[k].arg === undefined && oids[t].operations[k].arg !== null) { value = Math.round(parseFloat(value)); } else { value = parseFloat(value).toFixed(oids[t].operations[k].arg); } break; case 'pow': if (oids[t].operations[k].arg === undefined && oids[t].operations[k].arg !== null) { value = Math.pow(parseFloat(value), 2); } else { value = Math.pow(parseFloat(value), oids[t].operations[k].arg); } break; case 'sqrt': value = Math.sqrt(parseFloat(value)); break; case 'hex': value = Math.round(parseFloat(value)).toString(16); break; case 'hex2': value = Math.round(parseFloat(value)).toString(16); if (value.length < 2) value = '0' + value; break; case 'HEX': value = Math.round(parseFloat(value)).toString(16).toUpperCase(); break; case 'HEX2': value = Math.round(parseFloat(value)).toString(16).toUpperCase(); if (value.length < 2) value = '0' + value; break; case 'value': value = formatValue(value, parseInt(oids[t].operations[k].arg, 10)); break; case 'array': value = oids[t].operations[k].arg [~~value]; break; case 'date': value = formatDate(value, oids[t].operations[k].arg, dateFormat); break; case 'min': value = parseFloat(value); value = (value < oids[t].operations[k].arg) ? oids[t].operations[k].arg : value; break; case 'max': value = parseFloat(value); value = (value > oids[t].operations[k].arg) ? oids[t].operations[k].arg : value; break; case 'random': if (oids[t].operations[k].arg === undefined && oids[t].operations[k].arg !== null) { value = Math.random(); } else { value = Math.random() * oids[t].operations[k].arg; } break; case 'floor': value = Math.floor(parseFloat(value)); break; case 'ceil': value = Math.ceil(parseFloat(value)); break; } //switch } } //if for format = format.replace(oids[t].token, value); }//for format = format.replace(/{{/g, '{').replace(/}}/g, '}'); return format; } function extractBinding(format) { const oid = format.match(/{(.+?)}/g); let result = null; if (oid) { if (oid.length > 50) { console.warn('Too many bindings in one widget: ' + oid.length + '[max = 50]'); } for (let p = 0; p < oid.length && p < 50; p++) { const _oid = oid[p].substring(1, oid[p].length - 1); if (_oid[0] === '{') { continue; } // If first symbol '"' => it is JSON if (_oid && _oid[0] === '"') { continue; } const parts = _oid.split(';'); result = result || []; let systemOid = parts[0].trim(); let visOid = systemOid; let test1 = visOid.substring(visOid.length - 4); let test2 = visOid.substring(visOid.length - 3); if (visOid && test1 !== '.val' && test2 !== '.ts' && test2 !== '.lc' && test1 !== '.ack') { visOid = visOid + '.val'; } const isSeconds = (test2 === '.ts' || test2 === '.lc'); test1 = systemOid.substring(systemOid.length - 4); test2 = systemOid.substring(systemOid.length - 3); if (test1 === '.val' || test1 === '.ack') { systemOid = systemOid.substring(0, systemOid.length - 4); } else if (test2 === '.lc' || test2 === '.ts') { systemOid = systemOid.substring(0, systemOid.length - 3); } let operations = null; const isEval = visOid.match(/[\d\w_.]+:\s?[-\d\w_.]+/) || (!visOid.length && parts.length > 0);//(visOid.indexOf(':') !== -1) && (visOid.indexOf('::') === -1); if (isEval) { const xx = visOid.split(':', 2); const yy = systemOid.split(':', 2); visOid = xx[1]; systemOid = yy[1]; operations = []; operations.push({ op: 'eval', arg: [{ name: xx[0], visOid: visOid, systemOid: systemOid }] }); } for (let u = 1; u < parts.length; u++) { // eval construction if (isEval) { if (parts[u].trim().match(/^[\d\w_.]+:\s?[-.\d\w_]+$/)) {//parts[u].indexOf(':') !== -1 && parts[u].indexOf('::') === -1) { let _systemOid = parts[u].trim(); let _visOid = _systemOid; test1 = _visOid.substring(_visOid.length - 4); test2 = _visOid.substring(_visOid.length - 3); if (test1 !== '.val' && test2 !== '.ts' && test2 !== '.lc' && test1 !== '.ack') { _visOid = _visOid + '.val'; } test1 = systemOid.substring(_systemOid.length - 4); test2 = systemOid.substring(_systemOid.length - 3); if (test1 === '.val' || test1 === '.ack') { _systemOid = _systemOid.substring(0, _systemOid.length - 4); } else if (test2 === '.lc' || test2 === '.ts') { _systemOid = _systemOid.substring(0, _systemOid.length - 3); } const x1 = _visOid.split(':', 2); const y1 = _systemOid.split(':', 2); operations[0].arg.push({ name: x1[0], visOid: x1[1], systemOid: y1[1] }); } else { operations = operations || []; parts[u] = parts[u].replace(/::/g, ':'); if (operations[0].formula) { const n = JSON.parse(JSON.stringify(operations[0])); n.formula = parts[u]; operations.push(n); } else { operations[0].formula = parts[u]; } } } else { const parse = parts[u].match(/([\w\s/+*-]+)(\(.+\))?/); if (parse && parse[1]) { parse[1] = parse[1].trim(); // operators requires parameter if (parse[1] === '*' || parse[1] === '+' || parse[1] === '-' || parse[1] === '/' || parse[1] === '%' || parse[1] === 'min' || parse[1] === 'max') { if (parse[2] === undefined) { console.log('Invalid format of format string: ' + format); parse[2] = null; } else { parse[2] = (parse[2] || '').trim().replace(',', '.'); parse[2] = parse[2].substring(1, parse[2].length - 1); parse[2] = parseFloat(parse[2].trim()); if (parse[2].toString() === 'NaN') { console.log('Invalid format of format string: ' + format); parse[2] = null; } else { operations = operations || []; operations.push({op: parse[1], arg: parse[2]}); } } } else // date formatting if (parse[1] === 'date') { operations = operations || []; parse[2] = (parse[2] || '').trim(); parse[2] = parse[2].substring(1, parse[2].length - 1); operations.push({op: parse[1], arg: parse[2]}); } else // returns array[value]. e.g.: {id.ack;array(ack is false,ack is true)} if (parse[1] === 'array') { operations = operations || []; let param = (parse[2] || '').trim(); param = param.substring(1, param.length - 1); param = param.split(','); if (Array.isArray(param)) { operations.push ({op: parse[1], arg: param}); //xxx } } else // value formatting if (parse[1] === 'value') { operations = operations || []; let param = (parse[2] === undefined) ? '(2)' : (parse[2] || ''); param = param.trim(); param = param.substring(1, param.length - 1); operations.push({op: parse[1], arg: param}); } else // operators have optional parameter if (parse[1] === 'pow' || parse[1] === 'round' || parse[1] === 'random') { if (parse[2] === undefined) { operations = operations || []; operations.push({op: parse[1]}); } else { parse[2] = (parse[2] || '').trim().replace(',', '.'); parse[2] = parse[2].substring(1, parse[2].length - 1); parse[2] = parseFloat(parse[2].trim()); if (parse[2].toString() === 'NaN') { console.log('Invalid format of format string: ' + format); parse[2] = null; } else { operations = operations || []; operations.push({op: parse[1], arg: parse[2]}); } } } // operators without parameter else { operations = operations || []; operations.push({op: parse[1]}); } } else { console.log('Invalid format ' + format); } } } result.push({ visOid, systemOid, token: oid[p], operations: operations ? operations : undefined, format, isSeconds }); } } return result; } module.exports = { extractBinding, formatBinding };