mirror of
https://github.com/joBr99/nspanel-lovelace-ui.git
synced 2025-12-19 22:24:15 +01:00
update CardLChart_Influx2.ts
- codeanpassungen
This commit is contained in:
@@ -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;
|
||||||
|
//
|
||||||
|
|
||||||
|
const sensors : Record<string, Record <string, string>> = {};
|
||||||
|
/**
|
||||||
|
* Hier werden die Sensoren festgelegt nach flogendem Schema
|
||||||
|
*
|
||||||
|
* sensors[‘Datenpunkt(kompletter Pfad) des Messwertes'] = {'taget': 'Name des Datenpunkt für die Chartwerte', 'measurement': 'genutzter Alias in der Influxdb für den Messwert'};
|
||||||
|
*
|
||||||
|
* Wenn der Wert in der Datenbank keinen Alias hat bleibt der Wert 'measurement': weg.
|
||||||
|
* Jeder Messwert bekommt einen eigenen sensors[...] = {'target':....}
|
||||||
|
*/
|
||||||
|
|
||||||
// this records holds all sensors and their corresponding states which act as the data source for the charts
|
|
||||||
// add all sensors which are to be displayed in this script, there is no need to use multiple scripts
|
|
||||||
const sensors : Record<string, string> = {};
|
|
||||||
/* ↓ Id of the sensor ↓ Id of the data source for the charts */
|
|
||||||
sensors['deconz.0.Sensors.65.temperature'] = Path + 'buero_temperature.ACTUAL';
|
|
||||||
sensors['deconz.0.Sensors.65.humidity'] = Path + 'buero_luftfeuchte.ACTUAL';
|
|
||||||
|
|
||||||
|
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)}))',
|
const query =[
|
||||||
'|> yield(name: "_result")'].join('');
|
'from(bucket: "' + influxDbBucket + '")',
|
||||||
|
'|> range(start: -' + numberOfHoursAgo + 'h)',
|
||||||
if (Debug) console.log('Query: ' + query);
|
'|> filter(fn: (r) => r["_measurement"] == "' + idMeasurement + '")',
|
||||||
|
'|> filter(fn: (r) => r["_field"] == "value")',
|
||||||
const result : any = await sendToAsync(InfluxInstance, 'query', query);
|
'|> drop(columns: ["from", "ack", "q"])',
|
||||||
if (result.error) {
|
'|> aggregateWindow(every: 1h, fn: last, createEmpty: false)',
|
||||||
console.error(result.error);
|
'|> map(fn: (r) => ({ r with _rtime: int(v: r._time) - int(v: r._start)}))',
|
||||||
return;
|
'|> yield(name: "_result")'].join('');
|
||||||
}
|
|
||||||
|
if (Debug) console.log('Query: ' + query);
|
||||||
if (Debug) console.log(result);
|
|
||||||
const numResults = result.result.length;
|
const result : any = await sendToAsync(InfluxInstance, 'query', query);
|
||||||
let coordinates : string = '';
|
if (result.error) {
|
||||||
for (let r = 0; r < numResults; r++)
|
console.error(result.error);
|
||||||
{
|
return;
|
||||||
const list : string[] = []
|
}
|
||||||
const numValues = result.result[r].length;
|
if (Debug) console.log(JSON.stringify(result));
|
||||||
|
const numResults = result.result.length;
|
||||||
for (let i = 0; i < numValues; i++)
|
let coordinates : string = '';
|
||||||
{
|
for (let r = 0; r < numResults; r++)
|
||||||
const time = Math.round(result.result[r][i]._rtime/1000/1000/1000/60);
|
{
|
||||||
const value = Math.round(result.result[r][i]._value * 10);
|
const list : string[] = [];
|
||||||
list.push(time + ":" + value);
|
const numValues = result.result[r].length;
|
||||||
}
|
|
||||||
|
for (let i = 0; i < numValues; i++)
|
||||||
coordinates = list.join("~");
|
{
|
||||||
|
const time = Math.round(result.result[r][i]._rtime/1000/1000/1000/60);
|
||||||
if (Debug) console.log(coordinates);
|
const value = Math.round(result.result[r][i]._value * 10);
|
||||||
}
|
list.push(time + ":" + value);
|
||||||
|
}
|
||||||
const ticksAndLabelsList : string[] = []
|
|
||||||
const date = new Date();
|
coordinates = list.join("~");
|
||||||
date.setMinutes(0, 0, 0);
|
if (Debug) console.log(coordinates);
|
||||||
const ts = Math.round(date.getTime() / 1000);
|
}
|
||||||
const tsYesterday = ts - (numberOfHoursAgo * 3600);
|
|
||||||
if (Debug) console.log('Iterate from ' + tsYesterday + ' to ' + ts + ' stepsize=' + (xAxisTicksEveryM * 60));
|
const ticksAndLabelsList : string[] = []
|
||||||
for (let x = tsYesterday, i = 0; x < ts; x += (xAxisTicksEveryM * 60), i += xAxisTicksEveryM)
|
const date = new Date();
|
||||||
{
|
date.setMinutes(0, 0, 0);
|
||||||
if ((i % xAxisLabelEveryM))
|
const ts = Math.round(date.getTime() / 1000);
|
||||||
ticksAndLabelsList.push('' + i);
|
const tsYesterday = ts - (numberOfHoursAgo * 3600);
|
||||||
else
|
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)
|
||||||
const currentDate = new Date(x * 1000);
|
{
|
||||||
// Hours part from the timestamp
|
if ((i % xAxisLabelEveryM))
|
||||||
const hours = "0" + String(currentDate.getHours());
|
ticksAndLabelsList.push('' + i);
|
||||||
// Minutes part from the timestamp
|
else
|
||||||
const minutes = "0" + String(currentDate.getMinutes());
|
{
|
||||||
const formattedTime = hours.slice(-2) + ':' + minutes.slice(-2);
|
const currentDate = new Date(x * 1000);
|
||||||
|
// Hours part from the timestamp
|
||||||
ticksAndLabelsList.push(String(i) + "^" + formattedTime);
|
const hours = "0" + String(currentDate.getHours());
|
||||||
}
|
// Minutes part from the timestamp
|
||||||
}
|
const minutes = "0" + String(currentDate.getMinutes());
|
||||||
if (Debug) console.log('Ticks & Label: ' + ticksAndLabelsList);
|
const formattedTime = hours.slice(-2) + ':' + minutes.slice(-2);
|
||||||
if (Debug) console.log('Coordinates: ' + coordinates);
|
|
||||||
await setOrCreate(dataPointId, ticksAndLabelsList.join("+") + '~' + coordinates, true);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user