update CardLChart_Influx2.ts

- codeanpassungen
This commit is contained in:
Thomas
2024-02-06 15:04:13 +01:00
parent 467a1d92bb
commit d983c44db7

View File

@@ -1,109 +1,130 @@
const Debug = false; /**
* Dieses Script fragt eine influxDb ab, um Daten für die cardLcart (Liniendiagramm) zuberechnen und im richtigen Format bereitzustellen.
* Es erstellt automatisch einen Datenpunkt.
* Die Abfrage muss ggf. angepasst werden. Aktuell ermittelt sie Werte der letzten 24h, zu Stundenwerten zusammengefasst.
*/
const NSPanel_Path = '0_userdata.0.NSPanel.1.'; const Debug = false; // true für erweiterte Ausgaben im Log
const NSPanel_Path = '0_userdata.0.NSPanel.';
const Path = NSPanel_Path + 'Influx2NSPanel.cardLChart.'; const Path = NSPanel_Path + 'Influx2NSPanel.cardLChart.';
const InfluxInstance = 'influxdb.1'; const InfluxInstance = 'influxdb.0';
const influxDbBucket = 'iobroker'; const influxDbBucket = 'storage_short';
const numberOfHoursAgo = 24; const numberOfHoursAgo = 24;
const xAxisTicksEveryM = 60; const xAxisTicksEveryM = 60;
const xAxisLabelEveryM = 240; const xAxisLabelEveryM = 240;
//
// this records holds all sensors and their corresponding states which act as the data source for the charts const sensors : Record<string, Record <string, string>> = {};
// add all sensors which are to be displayed in this script, there is no need to use multiple scripts /**
const sensors : Record<string, string> = {}; * Hier werden die Sensoren festgelegt nach flogendem Schema
/* ↓ Id of the sensor ↓ Id of the data source for the charts */ *
sensors['deconz.0.Sensors.65.temperature'] = Path + 'buero_temperature.ACTUAL'; * sensors[Datenpunkt(kompletter Pfad) des Messwertes'] = {'taget': 'Name des Datenpunkt für die Chartwerte', 'measurement': 'genutzter Alias in der Influxdb für den Messwert'};
sensors['deconz.0.Sensors.65.humidity'] = Path + 'buero_luftfeuchte.ACTUAL'; *
* Wenn der Wert in der Datenbank keinen Alias hat bleibt der Wert 'measurement': weg.
* Jeder Messwert bekommt einen eigenen sensors[...] = {'target':....}
*/
sensors['netatmo-crawler.0.stationData.1.temperature'] = {'target':'AussenTemp', 'measurement':'wetter.temperatur'};
// ##### ab hier keine Änderungen mehr nötig #####
// create data source for NsPanel on script startup // create data source for NsPanel on script startup
Object.keys(sensors).forEach(async x => { Object.keys(sensors).forEach(async id => {
await generateDateAsync(x, sensors[x]); await generateDateAsync(id);
}); });
// then listen to the sensors and update the data source states accordingly // then listen to the sensors and update the data source states accordingly
on({ id: Object.keys(sensors), change: 'any' }, async function (obj) { on({ id: Object.keys(sensors), change: 'any' }, async function (obj) {
if (!obj.id) { if (!obj.id) {
return; return;
} }
await generateDateAsync(obj.id);
await generateDateAsync(obj.id, sensors[obj.id]);
}); });
async function generateDateAsync(sensorId: string, dataPointId: string) { //__________________________
const query =[ // Beschreibe diese Funktion: Daten generieren
'from(bucket: "' + influxDbBucket + '")', async function generateDateAsync(sensorId: string) {
'|> range(start: -' + numberOfHoursAgo + 'h)', let idMeasurement = sensors[sensorId].measurement;
'|> filter(fn: (r) => r["_measurement"] == "' + sensorId + '")', if (idMeasurement =='' ||idMeasurement == undefined) {idMeasurement = sensorId};
'|> filter(fn: (r) => r["_field"] == "value")', const dataPointId:string = Path + sensors[sensorId].target +'.ACTUAL';
'|> drop(columns: ["from", "ack", "q"])', if (Debug) log(`(f) generateDateAsync: ${sensorId} ${dataPointId} > ${idMeasurement}`);
'|> aggregateWindow(every: 1h, fn: last, createEmpty: false)',
'|> map(fn: (r) => ({ r with _rtime: int(v: r._time) - int(v: r._start)}))',
'|> yield(name: "_result")'].join('');
if (Debug) console.log('Query: ' + query); const query =[
'from(bucket: "' + influxDbBucket + '")',
'|> range(start: -' + numberOfHoursAgo + 'h)',
'|> filter(fn: (r) => r["_measurement"] == "' + idMeasurement + '")',
'|> filter(fn: (r) => r["_field"] == "value")',
'|> drop(columns: ["from", "ack", "q"])',
'|> aggregateWindow(every: 1h, fn: last, createEmpty: false)',
'|> map(fn: (r) => ({ r with _rtime: int(v: r._time) - int(v: r._start)}))',
'|> yield(name: "_result")'].join('');
const result : any = await sendToAsync(InfluxInstance, 'query', query); if (Debug) console.log('Query: ' + query);
if (result.error) {
console.error(result.error);
return;
}
if (Debug) console.log(result); const result : any = await sendToAsync(InfluxInstance, 'query', query);
const numResults = result.result.length; if (result.error) {
let coordinates : string = ''; console.error(result.error);
for (let r = 0; r < numResults; r++) return;
{ }
const list : string[] = [] if (Debug) console.log(JSON.stringify(result));
const numValues = result.result[r].length; const numResults = result.result.length;
let coordinates : string = '';
for (let r = 0; r < numResults; r++)
{
const list : string[] = [];
const numValues = result.result[r].length;
for (let i = 0; i < numValues; i++) for (let i = 0; i < numValues; i++)
{ {
const time = Math.round(result.result[r][i]._rtime/1000/1000/1000/60); const time = Math.round(result.result[r][i]._rtime/1000/1000/1000/60);
const value = Math.round(result.result[r][i]._value * 10); const value = Math.round(result.result[r][i]._value * 10);
list.push(time + ":" + value); list.push(time + ":" + value);
} }
coordinates = list.join("~"); coordinates = list.join("~");
if (Debug) console.log(coordinates);
}
if (Debug) console.log(coordinates); const ticksAndLabelsList : string[] = []
} const date = new Date();
date.setMinutes(0, 0, 0);
const ts = Math.round(date.getTime() / 1000);
const tsYesterday = ts - (numberOfHoursAgo * 3600);
if (Debug) console.log('Iterate from ' + tsYesterday + ' to ' + ts + ' stepsize=' + (xAxisTicksEveryM * 60));
for (let x = tsYesterday, i = 0; x < ts; x += (xAxisTicksEveryM * 60), i += xAxisTicksEveryM)
{
if ((i % xAxisLabelEveryM))
ticksAndLabelsList.push('' + i);
else
{
const currentDate = new Date(x * 1000);
// Hours part from the timestamp
const hours = "0" + String(currentDate.getHours());
// Minutes part from the timestamp
const minutes = "0" + String(currentDate.getMinutes());
const formattedTime = hours.slice(-2) + ':' + minutes.slice(-2);
const ticksAndLabelsList : string[] = [] ticksAndLabelsList.push(String(i) + "^" + formattedTime);
const date = new Date(); }
date.setMinutes(0, 0, 0); }
const ts = Math.round(date.getTime() / 1000); if (Debug) console.log('Ticks & Label: ' + ticksAndLabelsList);
const tsYesterday = ts - (numberOfHoursAgo * 3600); if (Debug) console.log('Coordinates: ' + coordinates);
if (Debug) console.log('Iterate from ' + tsYesterday + ' to ' + ts + ' stepsize=' + (xAxisTicksEveryM * 60)); await setOrCreate(dataPointId, ticksAndLabelsList.join("+") + '~' + coordinates, true);
for (let x = tsYesterday, i = 0; x < ts; x += (xAxisTicksEveryM * 60), i += xAxisTicksEveryM)
{
if ((i % xAxisLabelEveryM))
ticksAndLabelsList.push('' + i);
else
{
const currentDate = new Date(x * 1000);
// Hours part from the timestamp
const hours = "0" + String(currentDate.getHours());
// Minutes part from the timestamp
const minutes = "0" + String(currentDate.getMinutes());
const formattedTime = hours.slice(-2) + ':' + minutes.slice(-2);
ticksAndLabelsList.push(String(i) + "^" + formattedTime);
}
}
if (Debug) console.log('Ticks & Label: ' + ticksAndLabelsList);
if (Debug) console.log('Coordinates: ' + coordinates);
await setOrCreate(dataPointId, ticksAndLabelsList.join("+") + '~' + coordinates, true);
} }
//__________________________
// Beschreibe diese Funktion: Datenpunkte anlegen bzw. schreiben
async function setOrCreate(id : string, value : any, ack : boolean) { async function setOrCreate(id : string, value : any, ack : boolean) {
if (!(await existsStateAsync(id))) { if (!(await existsStateAsync(id))) {
await createStateAsync(id, value, { await createStateAsync(id, value, {
name: id.split('.').reverse()[0], name: id.split('.').reverse()[0],
desc: 'Sensor Values [~<time>:<value>]*', desc: 'Sensor Values [~<time>:<value>]*',
type: 'string', type: 'string',
role: 'value', role: 'value',
}); });
} else { } else {
await setStateAsync(id, value, ack); await setStateAsync(id, value, ack);
} }
} }