docs: update TS script for creating chart states

theknut
2024-01-22 23:01:38 +01:00
parent e4348ad208
commit e157159513

@@ -559,7 +559,7 @@ let CardChartExample: PageType =
"items": [{ "items": [{
id: 'alias.0.NSPanel_1.cardChart', id: 'alias.0.NSPanel_1.cardChart',
yAxis: 'Leistung [kW]', yAxis: 'Leistung [kW]',
yAxisTicks: [2,4,6,8,10,2,4,6,8,20,2], yAxisTicks: [2,4,6,8,10,2,4,6,8,20,2], // leave empty for automatic calculation of the y-axis
onColor: Yellow onColor: Yellow
}] }]
}; };
@@ -655,48 +655,54 @@ let CardLChartExample: PageType =
}; };
``` ```
Erklärung zum nachfolgenden Beispiel-JS-Script: Erklärung zum nachfolgenden Beispiel-TS-Script:
> **Wichtiger Hinweis und Voraussetzungen:** > **Wichtiger Hinweis und Voraussetzungen:**
>Für das Beispiel muss der InfluxDB Adapter installiert sein. Ebenfalls sollte über einen Zeitraum X bereits Sensordaten an eine Infux 2.X DB übertragen worden sein, welche jetzt zum Abruf bereit stehen! >Für das Beispiel muss der InfluxDB Adapter installiert sein. Ebenfalls sollte über einen Zeitraum X bereits Sensordaten an eine Infux 2.X DB übertragen worden sein, welche jetzt zum Abruf bereit stehen!
Zu definieren ist der Pfad für den Datenpunkt (im Beispiel 0.userdata.0.NSPanel.Influx2NSPanel.buero_temperature) in den das u.a. JS-Script die aufbereiteten Daten für das NSPanel schreiben kann. Für das Beispiel wurde ein Datenpunkt (deconz.0.Sensors.65.temperature) aus dem DeConz-Adapter mit einem Zigbee-Temperatursensor gewählt. Zu definieren ist der Pfad für den Datenpunkt (im Beispiel 0.userdata.0.NSPanel.Influx2NSPanel.buero_temperature) in den das u.a. TS-Script die aufbereiteten Daten für das NSPanel schreiben kann. Für das Beispiel wurde ein Datenpunkt (deconz.0.Sensors.65.temperature) aus dem DeConz-Adapter mit einem Zigbee-Temperatursensor gewählt.
**Bei Bedarf kann das Query angepasst werden:** **Bei Bedarf kann das Query angepasst werden:**
Es ist darauf zu achten, die Anzahl an Werten aus der Datenbank möglichst gering zu halten. Im nachfolgenden Beispiel wurden diese nochmals aggregiert. Die Summe an Zeichen für das Payload an die HMI des NSPanels ist begrenzt. Falls zu viele Werte verarbeitet werden, wird der Payload von der HMI gekürzt und die folge wäre eine schwarze Seite resultierend aus einem Fehlerzustand. Es ist darauf zu achten, die Anzahl an Werten aus der Datenbank möglichst gering zu halten. Im nachfolgenden Beispiel wurden diese nochmals aggregiert. Die Summe an Zeichen für das Payload an die HMI des NSPanels ist begrenzt. Falls zu viele Werte verarbeitet werden, wird der Payload von der HMI gekürzt und die folge wäre eine schwarze Seite resultierend aus einem Fehlerzustand.
## **Javascript für Influx2** ## **TypeScript für Influx2**
```javascript ```typescript
const Debug = true; const Debug = false;
const NSPanel_Path = '0_userdata.0.NSPanel.'; const NSPanel_Path = '0_userdata.0.NSPanel.1.';
const Path = NSPanel_Path + 'Influx2NSPanel.cardLChart.'; const Path = NSPanel_Path + 'Influx2NSPanel.cardLChart.';
const PathSensor = Path + 'buero_temperature'; const InfluxInstance = 'influxdb.1';
const influxDbBucket = 'iobroker';
const Sensor = 'deconz.0.Sensors.65.temperature';
const numberOfHoursAgo = 24; const numberOfHoursAgo = 24;
const xAxisTicksEveryM = 60; const xAxisTicksEveryM = 60;
const xAxisLabelEveryM = 240; const xAxisLabelEveryM = 240;
const InfluxInstance = 'influxdb.0'; // this records holds all sensors and their corresponding states which act as the data source for the charts
const BucketName: string = 'storage_short'; // 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';
sensors['deconz.0.Sensors.65.humidity'] = Path + 'buero_luftfeuchte';
let coordinates = ''; // create data source for NsPanel on script startup
Object.keys(sensors).forEach(async x => {
await generateDateAsync(x, sensors[x]);
});
createState(PathSensor, 0, { // then listen to the sensors and update the data source states accordingly
name: 'SensorGrid', on({ id: Object.keys(sensors), change: 'any' }, async function (obj) {
desc: 'Sensor Values [~<time>:<value>]*', if (!obj.id) {
type: 'string', return;
role: 'value', }
});
on({ id: Sensor, change: 'any' }, async function (obj) { await generateDateAsync(obj.id, sensors[obj.id]);
});
let query =[ async function generateDateAsync(sensorId: string, dataPointId: string) {
'from(bucket: "' + BucketName + '")', const query =[
'from(bucket: "' + influxDbBucket + '")',
'|> range(start: -' + numberOfHoursAgo + 'h)', '|> range(start: -' + numberOfHoursAgo + 'h)',
'|> filter(fn: (r) => r["_measurement"] == "' + Sensor+ '")', '|> filter(fn: (r) => r["_measurement"] == "' + sensorId + '")',
'|> filter(fn: (r) => r["_field"] == "value")', '|> filter(fn: (r) => r["_field"] == "value")',
'|> drop(columns: ["from", "ack", "q"])', '|> drop(columns: ["from", "ack", "q"])',
'|> aggregateWindow(every: 1h, fn: last, createEmpty: false)', '|> aggregateWindow(every: 1h, fn: last, createEmpty: false)',
@@ -705,63 +711,71 @@ on({ id: Sensor, change: 'any' }, async function (obj) {
if (Debug) console.log('Query: ' + query); if (Debug) console.log('Query: ' + query);
sendTo(InfluxInstance, 'query', query, function (result) { const result : any = await sendToAsync(InfluxInstance, 'query', query);
if (result.error) { if (result.error) {
console.error(result.error); console.error(result.error);
} else { return;
// show result }
if (Debug) console.log(result);
let numResults = result.result.length; if (Debug) console.log(result);
for (let r = 0; r < numResults; r++) const numResults = result.result.length;
{ let coordinates : string = '';
let list = [] for (let r = 0; r < numResults; r++)
let numValues = result.result[r].length; {
const list : string[] = []
const numValues = result.result[r].length;
for (let i = 0; i < numValues; i++) for (let i = 0; i < numValues; i++)
{ {
let 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);
let 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("~");
if (Debug) console.log(coordinates);
}
} }
});
let timeOut = setTimeout ( coordinates = list.join("~");
function () {
let ticksAndLabelsList = []
let date = new Date();
date.setMinutes(0, 0, 0);
let ts = Math.round(date.getTime() / 1000);
let 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
{
let currentDate = new Date(x * 1000);
// Hours part from the timestamp
let hours = "0" + String(currentDate.getHours());
// Minutes part from the timestamp
let minutes = "0" + String(currentDate.getMinutes());
let formattedTime = hours.slice(-2) + ':' + minutes.slice(-2);
ticksAndLabelsList.push(String(i) + "^" + formattedTime); if (Debug) console.log(coordinates);
} }
}
if (Debug) console.log('Ticks & Label: ' + ticksAndLabelsList); const ticksAndLabelsList : string[] = []
if (Debug) console.log('Coordinates: ' + coordinates); const date = new Date();
setState(PathSensor, ticksAndLabelsList.join("+") + '~' + coordinates, true); date.setMinutes(0, 0, 0);
}, const ts = Math.round(date.getTime() / 1000);
1500 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);
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);
}
async function setOrCreate(id : string, value : any, ack : boolean) {
if (!(await existsStateAsync(id))) {
await createStateAsync(id, value, {
name: id.split('.').reverse()[0],
desc: 'Sensor Values [~<time>:<value>]*',
type: 'string',
role: 'value',
});
} else {
await setStateAsync(id, value, ack);
}
}
``` ```
## **Javascript für History adapter** ## **Javascript für History adapter**