mirror of
https://github.com/joBr99/nspanel-lovelace-ui.git
synced 2025-12-19 22:24:15 +01:00
Script vom Wiki nach Github schieben
This commit is contained in:
@@ -1,74 +0,0 @@
|
|||||||
const idAbfalliCal = 'ical.1'; // iCal Instanz zum Abfallkalender
|
|
||||||
const idZeichenLoeschen = 14; // x Zeichen links vom String abziehen, wenn vor dem Eventname noch Text steht z.B. Strassenname; Standard = 0
|
|
||||||
const idRestmuellName ='Hausmüll'; // Schwarze Tonne
|
|
||||||
const idWertstoffName = 'Gelber Sack'; // Gelbe Tonne / Sack
|
|
||||||
const idPappePapierName = 'Papier'; // Blaue Tonne
|
|
||||||
const idBioabfaelleName = 'Biomüll'; // Braune Tonne
|
|
||||||
|
|
||||||
|
|
||||||
var i, Muell_JSON, Event2, Color = 0;
|
|
||||||
|
|
||||||
for (i = 1; i <= 4; i++) {
|
|
||||||
if (!existsState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.date')) {
|
|
||||||
log(i + '.date nicht vorhanden, wurde erstellt');
|
|
||||||
createState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.date', '',
|
|
||||||
{
|
|
||||||
name: parseFloat(i) + '.date',
|
|
||||||
role: 'state',
|
|
||||||
type: 'string',
|
|
||||||
read: true,
|
|
||||||
write: true,
|
|
||||||
def: ''
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (!existsState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.event')) {
|
|
||||||
log(i + '.event nicht vorhanden, wurde erstellt');
|
|
||||||
createState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.event', '',
|
|
||||||
{
|
|
||||||
name: parseFloat(i) + '.event',
|
|
||||||
role: 'state',
|
|
||||||
type: 'string',
|
|
||||||
read: true,
|
|
||||||
write: true,
|
|
||||||
def: ''
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (!existsState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.color')) {
|
|
||||||
log(i + '.color nicht vorhanden, wurde erstellt');
|
|
||||||
createState('0_userdata.0.Abfallkalender.' + parseFloat(i) + '.color', 0,
|
|
||||||
{
|
|
||||||
name: parseFloat(i) + '.color',
|
|
||||||
role: 'state',
|
|
||||||
type: 'number',
|
|
||||||
read: true,
|
|
||||||
write: true,
|
|
||||||
def: 0
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function subsequenceFromStartLast(sequence, at1) {
|
|
||||||
var start = at1;
|
|
||||||
var end = sequence.length;
|
|
||||||
return sequence.slice(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
on({ id: idAbfalliCal + '.data.table', change: "ne" }, async function () {
|
|
||||||
|
|
||||||
for (i = 0; i <= 3; i++) {
|
|
||||||
Muell_JSON = getState(idAbfalliCal + '.data.table').val;
|
|
||||||
setStateDelayed((['0_userdata.0.Abfallkalender.', parseFloat(i) + 1, '.date'].join('')), getAttr(Muell_JSON, (String(i) + '.date')), false, parseInt(((0) || "").toString(), 10), false);
|
|
||||||
Event2 = subsequenceFromStartLast(getAttr(Muell_JSON, (String(i) + '.event')), idZeichenLoeschen);
|
|
||||||
setStateDelayed((['0_userdata.0.Abfallkalender.', parseFloat(i) + 1, '.event'].join('')), Event2, false, parseInt(((0) || "").toString(), 10), false);
|
|
||||||
if (Event2 == idRestmuellName) {
|
|
||||||
Color = 33840;
|
|
||||||
} else if (Event2 == idBioabfaelleName) {
|
|
||||||
Color = 2016;
|
|
||||||
} else if (Event2 == idPappePapierName) {
|
|
||||||
Color = 31;
|
|
||||||
} else if (Event2 == idWertstoffName) {
|
|
||||||
Color = 65504;
|
|
||||||
}
|
|
||||||
setStateDelayed((['0_userdata.0.Abfallkalender.', parseFloat(i) + 1, '.color'].join('')), Color, false, parseInt(((0) || "").toString(), 10), false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
224
ioBroker/Blockly/Abfallkalender.ts
Normal file
224
ioBroker/Blockly/Abfallkalender.ts
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* @author 2023 @tt-tom
|
||||||
|
*
|
||||||
|
* Version 5.1.1
|
||||||
|
*
|
||||||
|
* Das Script erstellt die Datenpunkte und Alias für den Abfallkalender im Sonoff NSPanel
|
||||||
|
* Es wird der iCal Adapter benötigt und eine URL mit Terminen vom Entsorger bzw. eine .ics-Datei mit den Terminen.
|
||||||
|
* Das Script triggert auf dem bereitgestellten JSON im iCal adapter und füllt die 0_userdata.0 Datenpunkte
|
||||||
|
* Weitere Informationen findest du in der FAQ auf Github https://github.com/joBr99/nspanel-lovelace-ui/wiki
|
||||||
|
*
|
||||||
|
* changelog
|
||||||
|
* - 06.12.2023 - v5.0.2 add custom name for trashtype
|
||||||
|
* - 06.12.2023 - v5.1.0 Refactoring
|
||||||
|
* - 22.01.2024 - v5.1.1 Add tow Events more
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const idTrashData: string = 'ical.0.data.table'; // Datenpunkt mit Daten im JSON Format
|
||||||
|
const idUserdataAbfallVerzeichnis: string = '0_userdata.0.Abfallkalender'; // Name des Datenpunktverzeichnis unter 0_userdata.0 -> Strandard = 0_userdata.0.Abfallkalender
|
||||||
|
const idAliasPanelVerzeichnis: string = 'alias.0.NSPanel.allgemein'; //Name PanelVerzeichnis unter alias.0. Standard = alias.0.NSPanel.1
|
||||||
|
const idAliasAbfallVerzeichnis: string = 'Abfall'; //Name Verzeichnis unterhalb der idPanelverzeichnis Standard = Abfall
|
||||||
|
|
||||||
|
const anzahlZeichenLoeschen: number = 14; // x Zeichen links vom String abziehen, wenn vor dem Eventname noch Text steht z.B. Strassenname; Standard = 0
|
||||||
|
const jsonEventName1: string = 'Hausmüll'; // Vergleichstring für Schwarze Tonne
|
||||||
|
const customEventName1: string = ''; // benutzerdefinierter Text für schwarze Tonne
|
||||||
|
const jsonEventName2: string = 'Gelber Sack'; // Vergleichstring für Gelbe Tonne / Sack
|
||||||
|
const customEventName2: string = ''; // benutzerdefinierter Text für gelbe Tonne
|
||||||
|
const jsonEventName3: string = 'Papier'; // Vergleichstring für Blaue Tonne
|
||||||
|
const customEventName3: string = ''; // benutzerdefinierter Text für blaue Tonne
|
||||||
|
const jsonEventName4: string = 'Biomüll'; // Vergleichstring für Braune Tonne
|
||||||
|
const customEventName4: string = ''; // benutzerdefinierter Text für braune Tonne
|
||||||
|
const jsonEventName5: string = 'Treppe'; // Vergleichstring für Event 5
|
||||||
|
const customEventName5: string = 'Besen schwingen'; // benutzerdefinierter Text für Event 5
|
||||||
|
const jsonEventName6: string = ''; // Vergleichstring für Event 6
|
||||||
|
const customEventName6: string = ''; // benutzerdefinierter Text für Event 6
|
||||||
|
|
||||||
|
const Debug: boolean = false;
|
||||||
|
|
||||||
|
// ------------------------- Trigger zum füllen der 0_userdata Datenpunkte aus dem json vom ical Adapter -------------------------------
|
||||||
|
|
||||||
|
// Trigger auf JSON Datenpunkt
|
||||||
|
on({ id: idTrashData, change: 'ne' }, async function () {
|
||||||
|
JSON_auswerten();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ------------------------------------- Ende Trigger ------------------------------------
|
||||||
|
|
||||||
|
// ------------------------------------- Funktion JSON auswerten und DP füllen -------------------------------
|
||||||
|
async function JSON_auswerten() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
let trashJSON: any;
|
||||||
|
let instanzName: any;
|
||||||
|
let eventName: string;
|
||||||
|
let eventDatum: string;
|
||||||
|
let eventStartdatum: string;
|
||||||
|
let farbNummer: number = 0;
|
||||||
|
let farbString: string;
|
||||||
|
let abfallNummer: number = 1;
|
||||||
|
|
||||||
|
trashJSON = getState(idTrashData).val;
|
||||||
|
instanzName = idTrashData.split('.');
|
||||||
|
|
||||||
|
if (Debug) log('Rohdaten von Instanz ' + instanzName[0] + ': ' + JSON.stringify(trashJSON), 'info')
|
||||||
|
|
||||||
|
|
||||||
|
if (Debug) log('Anzahl Trash - Daten: ' + trashJSON.length, 'info');
|
||||||
|
|
||||||
|
for (let i = 0; i < trashJSON.length; i++) {
|
||||||
|
if (abfallNummer === 7) {
|
||||||
|
if (Debug) log('Alle Abfall-Datenpunkte gefüllt', 'warn');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
log('Daten vom ical Adapter werden ausgewertet', 'info');
|
||||||
|
eventName = getAttr(trashJSON, (String(i) + '.event')).slice(anzahlZeichenLoeschen, getAttr(trashJSON, (String(i) + '.event')).length);
|
||||||
|
// Leerzeichen vorne und hinten löschen
|
||||||
|
eventName = eventName.trimEnd();
|
||||||
|
eventName = eventName.trimStart();
|
||||||
|
eventDatum = getAttr(trashJSON, (String(i) + '.date'));
|
||||||
|
eventStartdatum = getAttr(trashJSON, (String(i) + '._date'));
|
||||||
|
|
||||||
|
let d: Date = currentDate();
|
||||||
|
let d1: Date = new Date(eventStartdatum);
|
||||||
|
|
||||||
|
if (Debug) log('--------- Nächster Termin wird geprüft ---------', 'info');
|
||||||
|
//if (Debug) log(d + ' ' + d1, 'info');
|
||||||
|
if (Debug) log('Startdatum UTC: ' + eventStartdatum, 'info');
|
||||||
|
if (Debug) log('Datum: ' + eventDatum, 'info');
|
||||||
|
if (Debug) log('Event: ' + eventName, 'info');
|
||||||
|
if (Debug) log('Kontrolle Leerzeichen %' + eventName + '%', 'info');
|
||||||
|
|
||||||
|
if (d.getTime() <= d1.getTime()) {
|
||||||
|
if ((eventName == jsonEventName1) || (eventName == jsonEventName2) || (eventName == jsonEventName3) || (eventName == jsonEventName4) || (eventName == jsonEventName5) || (eventName == jsonEventName6)) {
|
||||||
|
|
||||||
|
switch (eventName) {
|
||||||
|
case jsonEventName1:
|
||||||
|
farbNummer = 33840;
|
||||||
|
if (customEventName1 != '') {
|
||||||
|
eventName = customEventName1;
|
||||||
|
if (Debug) log('Event customName: ' + eventName, 'info');
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case jsonEventName2:
|
||||||
|
farbNummer = 65504;
|
||||||
|
if (customEventName2 != '') {
|
||||||
|
eventName = customEventName2;
|
||||||
|
if (Debug) log('Event customName: ' + eventName, 'info');
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case jsonEventName3:
|
||||||
|
farbNummer = 31;
|
||||||
|
if (customEventName3 != '') {
|
||||||
|
eventName = customEventName3
|
||||||
|
if (Debug) log('Event customName: ' + eventName, 'info');
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case jsonEventName4:
|
||||||
|
farbNummer = 2016;
|
||||||
|
if (customEventName4 != '') {
|
||||||
|
eventName = customEventName4;
|
||||||
|
if (Debug) log('Event customName: ' + eventName, 'info');
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case jsonEventName5:
|
||||||
|
farbNummer = 2016;
|
||||||
|
if (customEventName5 != '') {
|
||||||
|
eventName = customEventName5;
|
||||||
|
if (Debug) log('Event customName: ' + eventName, 'info');
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case jsonEventName6:
|
||||||
|
farbNummer = 2016;
|
||||||
|
if (customEventName6 != '') {
|
||||||
|
eventName = customEventName6
|
||||||
|
if (Debug) log('Event customName: ' + eventName, 'info');
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (farbString != undefined) farbNummer = rgb_dec565(hex_rgb(farbString));
|
||||||
|
|
||||||
|
|
||||||
|
setState(idUserdataAbfallVerzeichnis + '.' + String(abfallNummer) + '.date', eventDatum);
|
||||||
|
setState(idUserdataAbfallVerzeichnis + '.' + String(abfallNummer) + '.event', eventName);
|
||||||
|
setState(idUserdataAbfallVerzeichnis + '.' + String(abfallNummer) + '.color', farbNummer);
|
||||||
|
|
||||||
|
|
||||||
|
//if (Debug) log('farbString: ' + farbString + ' farbNummer: ' + farbNummer, 'info');
|
||||||
|
if (Debug) log('Abfallnummer: ' + abfallNummer, 'info');
|
||||||
|
|
||||||
|
abfallNummer += 1
|
||||||
|
} else {
|
||||||
|
if (Debug) log('Kein Abfalltermin => Event passt mit keinem Abfallnamen überein.', 'warn');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (Debug) log('Termin liegt vor dem heutigen Tag', 'warn');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
log('error at subscrption: ' + err.message, 'warn');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ------------------------------------- Ende Funktion JSON ------------------------------
|
||||||
|
|
||||||
|
// ------------------------------------- Funktion zur Prüfung und Erstellung der Datenpunkte in 0_userdata.0 und alias.0 -----------------------
|
||||||
|
|
||||||
|
async function Init_Datenpunkte() {
|
||||||
|
try {
|
||||||
|
for (let i = 1; i <= 6; i++) {
|
||||||
|
if (existsObject(idUserdataAbfallVerzeichnis + '.' + String(i)) == false) {
|
||||||
|
log('Datenpunkt ' + idUserdataAbfallVerzeichnis + '.' + String(i) + ' werden angelegt', 'info')
|
||||||
|
await createStateAsync(idUserdataAbfallVerzeichnis + '.' + String(i) + '.date', '', { type: 'string' });
|
||||||
|
await createStateAsync(idUserdataAbfallVerzeichnis + '.' + String(i) + '.event', '', { type: 'string' });
|
||||||
|
await createStateAsync(idUserdataAbfallVerzeichnis + '.' + String(i) + '.color', 0, { type: 'number' });
|
||||||
|
setObject(idAliasPanelVerzeichnis + '.' + idAliasAbfallVerzeichnis, { type: 'device', common: { name: { de: 'Abfall', en: 'Trash' } }, native: {} });
|
||||||
|
setObject(idAliasPanelVerzeichnis + '.' + idAliasAbfallVerzeichnis + '.event' + String(i), { type: 'channel', common: { role: 'warning', name: { de: 'Ereignis ' + String(i), en: 'Event' + String(i) } }, native: {} });
|
||||||
|
await createAliasAsync(idAliasPanelVerzeichnis + '.' + idAliasAbfallVerzeichnis + '.event' + String(i) + '.TITLE', idUserdataAbfallVerzeichnis + '.' + String(i) + '.event', true, <iobJS.StateCommon>{ type: 'string', role: 'weather.title.short', name: { de: 'TITEL', en: 'TITLE' } });
|
||||||
|
await createAliasAsync(idAliasPanelVerzeichnis + '.' + idAliasAbfallVerzeichnis + '.event' + String(i) + '.LEVEL', idUserdataAbfallVerzeichnis + '.' + String(i) + '.color', true, <iobJS.StateCommon>{ type: 'number', role: 'value.warning', name: { de: 'LEVEL', en: 'LEVEL' } });
|
||||||
|
await createAliasAsync(idAliasPanelVerzeichnis + '.' + idAliasAbfallVerzeichnis + '.event' + String(i) + '.INFO', idUserdataAbfallVerzeichnis + '.' + String(i) + '.date', true, <iobJS.StateCommon>{ type: 'string', role: 'weather.title', name: { de: 'INFO', en: 'INFO' } });
|
||||||
|
log('Fertig', 'info')
|
||||||
|
} else {
|
||||||
|
log('Datenpunkt ' + idUserdataAbfallVerzeichnis + '.' + String(i) + ' vorhanden', 'info')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log('Startabfrage der Daten', 'info');
|
||||||
|
JSON_auswerten();
|
||||||
|
} catch (err) {
|
||||||
|
log('error at function Init_Datenpunkte: ' + err.message, 'warn');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Init_Datenpunkte();
|
||||||
|
|
||||||
|
// --------------------------- Ende Funktion Datenpunkte ------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------- Zusatzfuktionen -------------------------------------------------------------
|
||||||
|
function currentDate() {
|
||||||
|
let d: Date = new Date();
|
||||||
|
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
function rgb_dec565(rgb: RGB): number {
|
||||||
|
//return ((Math.floor(rgb.red / 255 * 31) << 11) | (Math.floor(rgb.green / 255 * 63) << 5) | (Math.floor(rgb.blue / 255 * 31)));
|
||||||
|
return ((rgb.red >> 3) << 11) | ((rgb.green >> 2)) << 5 | ((rgb.blue) >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hex_rgb(colorhex: string): RGB {
|
||||||
|
let r = parseInt(colorhex.substring(1, 3), 16);
|
||||||
|
let g = parseInt(colorhex.substring(3, 5), 16);
|
||||||
|
let b = parseInt(colorhex.substring(5, 7), 16);
|
||||||
|
return { red: r, green: g, blue: b };
|
||||||
|
}
|
||||||
|
|
||||||
|
type RGB = {
|
||||||
|
red: number,
|
||||||
|
green: number,
|
||||||
|
blue: number
|
||||||
|
};
|
||||||
|
|
||||||
|
// -------------------- Ende Zudatzfunktionen --------------------------------------------------------------------------
|
||||||
50
ioBroker/Blockly/CardChart_History.js
Normal file
50
ioBroker/Blockly/CardChart_History.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
var sourceDP = 'alias.0.Wohnzimmer.Heizung.ACTUAL';
|
||||||
|
var targetDP = '0_userdata.0.Test.chartTest';
|
||||||
|
var rangeHours = 24;
|
||||||
|
var maxXAchsisTicks = 6;
|
||||||
|
var historyInstance = 'history.0';
|
||||||
|
|
||||||
|
on({id: sourceDP, change: "any"}, async function (obj) {
|
||||||
|
sendTo(historyInstance, 'getHistory', {
|
||||||
|
id: sourceDP,
|
||||||
|
options: {
|
||||||
|
start: Date.now() - (60 * 60 * 1000 * rangeHours),
|
||||||
|
end: Date.now(),
|
||||||
|
count: rangeHours,
|
||||||
|
limit: rangeHours,
|
||||||
|
aggregate: 'average'
|
||||||
|
}
|
||||||
|
}, function (result) {
|
||||||
|
var cardChartString = "";
|
||||||
|
var stepXAchsis = rangeHours / maxXAchsisTicks;
|
||||||
|
|
||||||
|
for (var i = 0; i < rangeHours; i++){
|
||||||
|
var deltaHour = rangeHours - i;
|
||||||
|
var targetDate = new Date(Date.now() - (deltaHour * 60 * 60 * 1000));
|
||||||
|
|
||||||
|
//Check history items for requested hours
|
||||||
|
for (var j = 0, targetValue = 0; j < result.result.length; j++) {
|
||||||
|
var valueDate = new Date(result.result[j].ts);
|
||||||
|
var value = (Math.round(result.result[j].val * 10) / 10);
|
||||||
|
|
||||||
|
if (valueDate > targetDate){
|
||||||
|
if ((targetDate.getHours() % stepXAchsis) == 0){
|
||||||
|
cardChartString += targetValue + '^' + targetDate.getHours() + ':00' + '~';
|
||||||
|
} else {
|
||||||
|
cardChartString += targetValue + '~';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
targetValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cardChartString = cardChartString.substring(0,cardChartString.length-1);
|
||||||
|
if (existsState(targetDP) == false ) {
|
||||||
|
createState(targetDP, cardChartString, true, { type: 'string' });
|
||||||
|
} else {
|
||||||
|
setState(targetDP, cardChartString, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
78
ioBroker/Blockly/CardLChart_History.js
Normal file
78
ioBroker/Blockly/CardLChart_History.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
const sourceDP = 'alias.0.Wohnzimmer.Heizung.ACTUAL';
|
||||||
|
const targetDP = '0_userdata.0.Test.chartTest';
|
||||||
|
const numberOfHoursAgo = 24; // Period of time in hours which shall be visualized
|
||||||
|
const xAxisTicksEveryM = 60; // Time after x axis gets a tick in minutes
|
||||||
|
const xAxisLabelEveryM = 240; // Time after x axis is labeled in minutes
|
||||||
|
const historyInstance = 'history.0';
|
||||||
|
|
||||||
|
const Debug = false;
|
||||||
|
const maxX = 1420;
|
||||||
|
const limitMeasurements = 35;
|
||||||
|
|
||||||
|
createState(targetDP, "", {
|
||||||
|
name: 'SensorGrid',
|
||||||
|
desc: 'Sensor Values [~<time>:<value>]*',
|
||||||
|
type: 'string',
|
||||||
|
role: 'value',
|
||||||
|
});
|
||||||
|
|
||||||
|
on({id: sourceDP, change: "any"}, async function (obj) {
|
||||||
|
sendTo(historyInstance, 'getHistory', {
|
||||||
|
id: sourceDP,
|
||||||
|
options: {
|
||||||
|
start: Date.now() - (numberOfHoursAgo * 60 * 60 * 1000 ), //Time in ms: hours * 60m * 60s * 1000ms
|
||||||
|
end: Date.now(),
|
||||||
|
count: limitMeasurements,
|
||||||
|
limit: limitMeasurements,
|
||||||
|
aggregate: 'average'
|
||||||
|
}
|
||||||
|
}, function (result) {
|
||||||
|
var ticksAndLabels = ""
|
||||||
|
var coordinates = "";
|
||||||
|
var cardLChartString = "";
|
||||||
|
|
||||||
|
let ticksAndLabelsList = []
|
||||||
|
var date = new Date();
|
||||||
|
date.setMinutes(0, 0, 0);
|
||||||
|
var ts = Math.round(date.getTime() / 1000);
|
||||||
|
var tsYesterday = ts - (numberOfHoursAgo * 3600);
|
||||||
|
|
||||||
|
for (var x = tsYesterday, i = 0; x < ts; x += (xAxisTicksEveryM * 60), i += xAxisTicksEveryM)
|
||||||
|
{
|
||||||
|
if (i % xAxisLabelEveryM)
|
||||||
|
{
|
||||||
|
ticksAndLabelsList.push(i);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
var currentDate = new Date(x * 1000);
|
||||||
|
// Hours part from the timestamp
|
||||||
|
var hours = "0" + currentDate.getHours();
|
||||||
|
// Minutes part from the timestamp
|
||||||
|
var minutes = "0" + currentDate.getMinutes();
|
||||||
|
// Seconds part from the timestamp
|
||||||
|
var seconds = "0" + currentDate.getSeconds();
|
||||||
|
var formattedTime = hours.slice(-2) + ':' + minutes.slice(-2);
|
||||||
|
ticksAndLabelsList.push(String(i) + "^" + formattedTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ticksAndLabels = ticksAndLabelsList.join("+");
|
||||||
|
|
||||||
|
let list = [];
|
||||||
|
let offSetTime = Math.round(result.result[0].ts / 1000);
|
||||||
|
let counter = Math.round((result.result[result.result.length -1 ].ts / 1000 - offSetTime) / maxX);
|
||||||
|
for (var i = 0; i < result.result.length; i++)
|
||||||
|
{
|
||||||
|
var time = Math.round(((result.result[i].ts / 1000) - offSetTime) / counter);
|
||||||
|
var value = Math.round(result.result[i].val * 10);
|
||||||
|
if ((value != null) && (value != 0)){
|
||||||
|
list.push(time + ":" + value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coordinates = list.join("~");
|
||||||
|
cardLChartString = ticksAndLabels + '~' + coordinates
|
||||||
|
setState(targetDP, cardLChartString, true);
|
||||||
|
|
||||||
|
if (Debug) console.log(cardLChartString);
|
||||||
|
});
|
||||||
|
});
|
||||||
54
ioBroker/Blockly/CardPower.js
Normal file
54
ioBroker/Blockly/CardPower.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* generate an JSON for display Power-Card on NSPanel
|
||||||
|
* Source: https://github.com/joBr99/nspanel-lovelace-ui/wiki/ioBroker-Card-Definitionen-(Seiten)#cardpower-ab-ts-script-v341
|
||||||
|
* Version: 0.1 - L4rs
|
||||||
|
*/
|
||||||
|
schedule("* * * * *", function () {
|
||||||
|
|
||||||
|
// Definition der Datenpunkte für das JSON der POWER-Card und der anzuzeigenden Leistungswerte
|
||||||
|
var powerCardJson = "0_userdata.0.NSPanel.Energie.PowerCard",
|
||||||
|
pwr1 = "", // Batterie
|
||||||
|
pwr2 = Math.round(getState("mqtt.0.SmartHome.Energie.PV.openDTU.114180710360.0.power").val), // Solar
|
||||||
|
pwr3 = "", // Wind
|
||||||
|
pwr4 = "", // Verbraucher
|
||||||
|
pwr5 = Math.round(getState("hm-rpc.0.MEQ0706303.1.POWER").val), // Stromnetz
|
||||||
|
pwr6 = 0, // Auto
|
||||||
|
pwrHome = Math.round(pwr5 - pwr2); // Berechnung des Energiefluss anstelle eines Datenpunktes
|
||||||
|
|
||||||
|
// Definition der Keys im JSON
|
||||||
|
var keys = ["id", "value", "unit", "icon", "iconColor", "speed"];
|
||||||
|
|
||||||
|
// Definition der "Kacheln", inkl. StandardIcon. Es können alle Icon aus dem Iconmapping genutzt werden.
|
||||||
|
// Kacheln die nicht genutzt werden sollen, müssen wie z.b. item1 formatiert sein
|
||||||
|
var home = [0, pwrHome, "W", "home-lightning-bolt-outline", 0]; // Icon home
|
||||||
|
var item1 = [1, pwr1, "", "", 0, ""]; // Icon battery-charging-60
|
||||||
|
var item2 = [2, pwr2, "W", "solar-power-variant-outline", 3, pwr2 > 0 ? -2 : 0]; // Icon solar-power-variant
|
||||||
|
var item3 = [3, pwr3, "", "", 0, ""]; // Icon wind-turbine
|
||||||
|
var item4 = [4, pwr4, "", "", 0, ""]; // Icon shape
|
||||||
|
var item5 = [5, pwr5, "W", "transmission-tower", 10, 10]; // Icon transmission-tower
|
||||||
|
var item6 = [6, pwr6, "kW", "car-electric-outline", 5, 0]; // Icon car
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON generieren und in den Datenpunkt schreiben,
|
||||||
|
*
|
||||||
|
* --- ab hier keine Änderungen mehr ---
|
||||||
|
*/
|
||||||
|
function func(tags, values) {
|
||||||
|
return Object.assign(
|
||||||
|
...tags.map((element, index) => ({ [element]: values[index] }))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(
|
||||||
|
powerCardJson,
|
||||||
|
JSON.stringify([
|
||||||
|
func(keys, home),
|
||||||
|
func(keys, item1),
|
||||||
|
func(keys, item2),
|
||||||
|
func(keys, item3),
|
||||||
|
func(keys, item4),
|
||||||
|
func(keys, item5),
|
||||||
|
func(keys, item6),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
});
|
||||||
109
ioBroker/Blockly/CradLChart_Influx2.js
Normal file
109
ioBroker/Blockly/CradLChart_Influx2.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
const Debug = false;
|
||||||
|
|
||||||
|
const NSPanel_Path = '0_userdata.0.NSPanel.1.';
|
||||||
|
const Path = NSPanel_Path + 'Influx2NSPanel.cardLChart.';
|
||||||
|
const InfluxInstance = 'influxdb.1';
|
||||||
|
const influxDbBucket = 'iobroker';
|
||||||
|
const numberOfHoursAgo = 24;
|
||||||
|
const xAxisTicksEveryM = 60;
|
||||||
|
const xAxisLabelEveryM = 240;
|
||||||
|
|
||||||
|
// 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';
|
||||||
|
sensors['deconz.0.Sensors.65.humidity'] = Path + 'buero_luftfeuchte';
|
||||||
|
|
||||||
|
// create data source for NsPanel on script startup
|
||||||
|
Object.keys(sensors).forEach(async x => {
|
||||||
|
await generateDateAsync(x, sensors[x]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// then listen to the sensors and update the data source states accordingly
|
||||||
|
on({ id: Object.keys(sensors), change: 'any' }, async function (obj) {
|
||||||
|
if (!obj.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await generateDateAsync(obj.id, sensors[obj.id]);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function generateDateAsync(sensorId: string, dataPointId: string) {
|
||||||
|
const query =[
|
||||||
|
'from(bucket: "' + influxDbBucket + '")',
|
||||||
|
'|> range(start: -' + numberOfHoursAgo + 'h)',
|
||||||
|
'|> filter(fn: (r) => r["_measurement"] == "' + sensorId + '")',
|
||||||
|
'|> 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('');
|
||||||
|
|
||||||
|
if (Debug) console.log('Query: ' + query);
|
||||||
|
|
||||||
|
const result : any = await sendToAsync(InfluxInstance, 'query', query);
|
||||||
|
if (result.error) {
|
||||||
|
console.error(result.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Debug) console.log(result);
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
const time = Math.round(result.result[r][i]._rtime/1000/1000/1000/60);
|
||||||
|
const value = Math.round(result.result[r][i]._value * 10);
|
||||||
|
list.push(time + ":" + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
coordinates = list.join("~");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user