Hier mein Script das Ladeplan 2/3 ausliest aber so wieder mit zurück gibt.
Wenn das Auto abgeschlossen ist hats geklappt :D. Bei meinen Versuchen lag der Schlüssel immer im Auto. Dann zogen die Pläne irgendwie nicht.
/**
* ============================================================
* Fiat 500e – Zielladungs-Script für ioBroker JavaScript
* ============================================================
* Autor: basierend auf Blockly-Script von alw (ioBroker Forum)
* Version: 2.2.0
*
* Funktion:
* - Erkennt automatisch wenn das Ladekabel eingesteckt wird
* - Lädt das Auto bis zum konfigurierbaren Ladeziel (default 80%)
* - Stoppt das Laden automatisch wenn das Ziel erreicht ist
* - Ladeplan 3 (schedules03) dient als Fernschalter (via Fiat App)
* - Debug-Ausgabe via ioBroker-Log
* - Robuste Fehlerbehandlung und Timeout-Schutz
*
* Voraussetzungen:
* - ioBroker JavaScript-Adapter
* - Fiat-Adapter installiert und konfiguriert
*
* Konfiguration:
* - VIN anpassen (ZFAXXX...)
* - adapterInstance anpassen falls der Fiat-Adapter nicht als "fiat.0" läuft
* - Datenpunkt für Ladeziel wird automatisch erstellt
* ============================================================
*/
// ============================================================
// IIFE – kapselt alle Variablen damit es keine Konflikte
// mit anderen ioBroker-Scripts im globalen Scope gibt
// ============================================================
(function () {
// ============================================================
// KONFIGURATION – hier anpassen
// ============================================================
const CONFIG = {
// Fahrzeug-Identifikationsnummer (VIN / FIN)
vin: 'ZFAXXX...',
// Instanz des Fiat-Adapters (z.B. 'fiat.0', 'fiat.1', 'meinAdapter.0')
adapterInstance: 'fiat.0',
// ioBroker Datenpunkt-Pfad für das Ladeziel (wird automatisch erstellt)
// Wert kann in ioBroker-Admin oder VIS geändert werden
chargeTargetDP: 'javascript.0.fiat.chargeTarget',
// Standard-Ladeziel in Prozent (wird beim ersten Start gesetzt)
chargeTargetDefault: 80,
// Ladeplan-Zeiten: Laden aktiv (fast ganzer Tag)
chargeStart: '00:00',
chargeEnd: '23:55',
// Ladeplan-Zeiten: Laden blockiert (5-Minuten-Fenster das nie kommt)
noChargeStart: '00:00',
noChargeEnd: '00:05',
// Wie oft (in Minuten) der Ladestand während des Ladens abgefragt wird
pollIntervalMinutes: 10,
// Maximale Ladezeit in Stunden bevor das Script abbricht (Schutz vor Endlos-Schleife)
maxChargeHours: 48,
// ioBroker Datenpunkt für Debug-Nachrichten (wird automatisch angelegt)
debugDP: 'javascript.0.fiat.debug',
// Erstinitialisierung: einmalig auf TRUE setzen (Kabel muss draußen sein!)
// Script sendet dann den No-Charge-Plan ans Auto und setzt den Wert selbst zurück.
initializedDP: 'javascript.0.fiat.initialized',
};
// ============================================================
// INTERNE VARIABLEN – nicht ändern
// ============================================================
let pollInterval = null; // Referenz auf den aktiven Poll-Timer
let chargeStartTime = null; // Zeitstempel wann das Laden begann (für Timeout)
let isCharging = false; // Interner Status ob wir gerade steuern
// Abgeleitete ioBroker Datenpfade aus Adapter-Instanz und VIN
const _base = `${CONFIG.adapterInstance}.${CONFIG.vin}`;
const DP = {
plugInStatus: `${_base}.status.evInfo.battery.plugInStatus`,
stateOfCharge: `${_base}.status.evInfo.battery.stateOfCharge`,
schedules03enable: `${_base}.status.evInfo.schedules03.enableScheduleType`,
s01_startTime: `${_base}.status.evInfo.schedules01.startTime`,
s01_endTime: `${_base}.status.evInfo.schedules01.endTime`,
deepRefresh: `${_base}.remote.DEEPREFRESH`,
cpplus: `${_base}.remote.CPPLUS`,
};
// ============================================================
// MODUL: Logging
// Schreibt ins ioBroker-Log UND in den Datenpunkt javascript.0.fiat.debug
// ============================================================
function log(level, message) {
const timestamp = new Date().toISOString();
const fullMessage = `[Fiat500e] ${message}`;
if (level === 'error') {
console.error(fullMessage);
} else if (level === 'warn') {
console.warn(fullMessage);
} else {
console.log(fullMessage);
}
setState(CONFIG.debugDP, JSON.stringify({
timestamp: timestamp,
level: level,
message: message,
vin: CONFIG.vin,
}), true);
}
// ============================================================
// MODUL: Ladeziel lesen
// Liest den konfigurierbaren Ladeziel-Datenpunkt
// ============================================================
function getChargeTarget() {
const val = getState(CONFIG.chargeTargetDP).val;
// Plausibilitäts-Check: muss zwischen 20 und 100 liegen
if (typeof val === 'number' && val >= 20 && val <= 100) {
return val;
}
log('warn', `Ungültiges Ladeziel (${val}), nutze Default: ${CONFIG.chargeTargetDefault}%`);
return CONFIG.chargeTargetDefault;
}
// ============================================================
// MODUL: Einzelnen Schedule aus Datenpunkten lesen
// Liest alle Felder eines Plans einzeln aus ioBroker und
// gibt ein fertiges Objekt zurück – unverändert wie im Auto
// ============================================================
function readSchedule(nr) {
const base = `${CONFIG.adapterInstance}.${CONFIG.vin}.status.evInfo.schedules0${nr}`;
return {
cabinPriority: getState(`${base}.cabinPriority`).val || false,
chargeToFull: getState(`${base}.chargeToFull`).val || false,
enableScheduleType: getState(`${base}.enableScheduleType`).val || false,
endTime: getState(`${base}.endTime`).val || '00:00',
repeatSchedule: getState(`${base}.repeatSchedule`).val || false,
scheduleType: getState(`${base}.scheduleType`).val || 'CHARGE',
scheduledDays: {
friday: getState(`${base}.scheduledDays.friday`).val || false,
monday: getState(`${base}.scheduledDays.monday`).val || false,
saturday: getState(`${base}.scheduledDays.saturday`).val || false,
sunday: getState(`${base}.scheduledDays.sunday`).val || false,
thursday: getState(`${base}.scheduledDays.thursday`).val || false,
tuesday: getState(`${base}.scheduledDays.tuesday`).val || false,
wednesday: getState(`${base}.scheduledDays.wednesday`).val || false,
},
startTime: getState(`${base}.startTime`).val || '00:00',
};
}
// ============================================================
// MODUL: Ladeplan setzen
// - Plan 1: wird hardcoded gesetzt (nur Zeiten dynamisch)
// - Plan 2 & 3: live vom Auto gelesen und unverändert zurückgeschickt
// - Alles als JSON-String an CPPLUS senden
// ============================================================
function setChargeSchedule(startTime, endTime, callback) {
log('info', `Setze Ladeplan: ${startTime} – ${endTime}`);
// Plan 1: Ladeplan – nur Zeiten und Tage werden gesetzt
const plan1 = {
cabinPriority: false,
chargeToFull: false,
enableScheduleType: true,
endTime: endTime,
repeatSchedule: true,
scheduleType: 'CHARGE',
scheduledDays: {
friday: true, monday: true, saturday: true,
sunday: true, thursday: true, tuesday: true, wednesday: true,
},
startTime: startTime,
};
// Plan 2 & 3: unverändert vom Auto lesen
const plan2 = readSchedule(2);
const plan3 = readSchedule(3);
log('info', `Plan 2 gelesen: ${plan2.scheduleType} ${plan2.startTime}–${plan2.endTime}`);
log('info', `Plan 3 gelesen: ${plan3.scheduleType} ${plan3.startTime}–${plan3.endTime}`);
// Alle 3 Pläne als JSON-String an CPPLUS senden
setState(DP.cpplus, JSON.stringify([plan1, plan2, plan3]), false, (err) => {
if (err) {
log('error', `Fehler beim Setzen des Ladeplans: ${err}`);
if (typeof callback === 'function') callback(err);
return;
}
log('info', `Ladeplan gesendet: ${startTime} – ${endTime}`);
if (typeof callback === 'function') callback(null);
});
}
// ============================================================
// MODUL: Deep Refresh
// Fordert aktuelle Fahrzeugdaten vom Fiat-Server an
// ============================================================
function triggerDeepRefresh(callback) {
log('info', 'Starte Deep Refresh...');
setState(DP.deepRefresh, true, false, (err) => {
if (err) {
log('error', `Deep Refresh fehlgeschlagen: ${err}`);
if (typeof callback === 'function') callback(err);
return;
}
// 30 Sekunden warten bis die Daten vom Auto angekommen sind
setTimeout(() => {
log('info', 'Deep Refresh abgeschlossen');
if (typeof callback === 'function') callback(null);
}, 30000);
});
}
// ============================================================
// MODUL: Lade-Polling stoppen
// Räumt den aktiven Poll-Timer auf
// ============================================================
function stopPolling() {
if (pollInterval !== null) {
clearInterval(pollInterval);
pollInterval = null;
log('info', 'Lade-Polling gestoppt');
}
isCharging = false;
}
// ============================================================
// MODUL: Laden beenden
// Setzt den "No Charge" Plan und stoppt das Polling
// ============================================================
function stopCharging(reason) {
log('info', `Lade-Stopp: ${reason}`);
stopPolling();
setChargeSchedule(CONFIG.noChargeStart, CONFIG.noChargeEnd, (err) => {
if (!err) {
log('info', 'Auto wurde auf "Nicht laden" gesetzt – bereit für nächsten Zyklus');
}
});
}
// ============================================================
// MODUL: Lade-Polling starten
// Prüft alle X Minuten den Ladestand und stoppt bei Zielerreichung
// ============================================================
function startPolling() {
if (isCharging) {
log('warn', 'Polling läuft bereits – kein Doppelstart');
return;
}
isCharging = true;
chargeStartTime = Date.now();
const maxChargeMs = CONFIG.maxChargeHours * 60 * 60 * 1000;
log('info', `Lade-Polling gestartet (alle ${CONFIG.pollIntervalMinutes} min, max ${CONFIG.maxChargeHours}h)`);
pollInterval = setInterval(() => {
// Timeout-Schutz: Nach maxChargeHours abbrechen
if (Date.now() - chargeStartTime > maxChargeMs) {
log('warn', `Timeout nach ${CONFIG.maxChargeHours}h – Laden wird gestoppt`);
stopCharging('Timeout erreicht');
return;
}
// Kabel noch drin?
const plugged = getState(DP.plugInStatus).val;
if (!plugged) {
log('info', 'Kabel wurde gezogen – Polling wird gestoppt');
stopPolling();
return;
}
// Notaus aktiv (schedules03)?
const emergencyStop = getState(DP.schedules03enable).val;
if (emergencyStop) {
log('info', 'Notaus aktiv (schedules03=true) – Polling wird gestoppt');
stopPolling();
return;
}
// Deep Refresh um aktuellen Ladestand zu bekommen
triggerDeepRefresh(() => {
const soc = getState(DP.stateOfCharge).val;
const target = getChargeTarget();
log('info', `Ladestand: ${soc}% / Ziel: ${target}%`);
if (soc >= target) {
stopCharging(`Ladeziel ${target}% erreicht (aktuell ${soc}%)`);
}
});
}, CONFIG.pollIntervalMinutes * 60 * 1000);
}
// ============================================================
// MODUL: Kabelstatus geändert – Hauptlogik
// ============================================================
function onPlugStatusChange(obj) {
const plugged = obj.state.val;
const soc = getState(DP.stateOfCharge).val;
const target = getChargeTarget();
const emergency = getState(DP.schedules03enable).val;
log('info', `Kabelstatus geändert: ${plugged ? 'Eingesteckt' : 'Gezogen'} | SOC: ${soc}% | Ziel: ${target}%`);
// Kabel gezogen → aufräumen
if (!plugged) {
log('info', 'Kabel gezogen – Script wartet auf nächstes Einstecken');
stopPolling();
// Ladeplan für nächstes Mal vorbereiten (Charge-Zeiten wiederherstellen)
if (!emergency) {
setChargeSchedule(CONFIG.chargeStart, CONFIG.chargeEnd, () => {
log('info', 'Ladeplan für nächsten Zyklus vorbereitet');
});
}
return;
}
// Kabel eingesteckt → prüfen ob wir steuern sollen
if (emergency) {
log('info', 'Notaus aktiv (schedules03=true) – Script ist deaktiviert');
return;
}
if (soc >= target) {
log('info', `Ladestand ${soc}% bereits >= Ziel ${target}% – kein Laden nötig`);
stopCharging('Bereits am Ziel beim Einstecken');
return;
}
// Laden starten
log('info', `Starte Zielladung auf ${target}% (aktuell ${soc}%)`);
// Erst Deep Refresh für genauen Startladestand
triggerDeepRefresh(() => {
const socFresh = getState(DP.stateOfCharge).val;
log('info', `Ladestand nach Refresh: ${socFresh}%`);
if (socFresh >= target) {
log('info', 'Bereits am Ziel nach Refresh – kein Laden nötig');
stopCharging('Bereits am Ziel nach Deep Refresh');
return;
}
// Ladeplan aktivieren
setChargeSchedule(CONFIG.chargeStart, CONFIG.chargeEnd, (err) => {
if (err) {
log('error', 'Ladeplan konnte nicht gesetzt werden – abbruch');
return;
}
// Polling starten
startPolling();
});
});
}
// ============================================================
// INITIALISIERUNG
// Wird einmal beim Script-Start ausgeführt
// ============================================================
function init() {
log('info', '=== Fiat 500e Zielladungs-Script gestartet ===');
log('info', `VIN: ${CONFIG.vin}`);
// Ladeziel-Datenpunkt erstellen falls er noch nicht existiert
createState(CONFIG.chargeTargetDP, CONFIG.chargeTargetDefault, {
name: 'Fiat 500e Ladeziel (%)',
type: 'number',
unit: '%',
min: 20,
max: 100,
role: 'value',
desc: 'Ladeziel für die automatische Zielladung (20-100%)',
}, () => {
log('info', `Ladeziel-Datenpunkt bereit: ${CONFIG.chargeTargetDP}`);
});
// Debug-Datenpunkt erstellen falls er noch nicht existiert
createState(CONFIG.debugDP, '', {
name: 'Fiat 500e Debug',
type: 'string',
role: 'value',
desc: 'Letzter Debug-Log-Eintrag als JSON (timestamp, level, message)',
}, () => {
log('info', `Debug-Datenpunkt bereit: ${CONFIG.debugDP}`);
});
// Initialisierungs-Datenpunkt erstellen
// Auf TRUE setzen (Kabel draußen!) um einmalig den No-Charge-Plan ans Auto zu senden
createState(CONFIG.initializedDP, false, {
name: 'Fiat 500e Initialisierung auslösen',
type: 'boolean',
role: 'button',
desc: 'Einmalig TRUE setzen (Kabel muss draußen sein!) – Script sendet No-Charge-Plan und setzt Wert zurück',
}, () => {
log('info', `Initialisierungs-Datenpunkt bereit: ${CONFIG.initializedDP}`);
// Auf Änderung horchen – wenn auf TRUE gesetzt wird No-Charge-Plan gesendet
on({ id: CONFIG.initializedDP, val: true }, () => {
const plugged = getState(DP.plugInStatus).val;
if (plugged) {
log('warn', 'Initialisierung abgebrochen – Kabel ist noch eingesteckt! Bitte zuerst Kabel ziehen.');
setState(CONFIG.initializedDP, false, true);
return;
}
log('info', 'Initialisierung: sende No-Charge-Plan ans Auto...');
setChargeSchedule(CONFIG.noChargeStart, CONFIG.noChargeEnd, (err) => {
if (!err) {
log('info', 'Initialisierung abgeschlossen – No-Charge-Plan gesetzt. Kabel kann jetzt eingesteckt werden.');
}
setState(CONFIG.initializedDP, false, true);
});
});
});
// Aktuellen Status beim Start prüfen
// (falls Script neu gestartet wurde während Kabel schon steckt)
const plugged = getState(DP.plugInStatus).val;
const soc = getState(DP.stateOfCharge).val;
const emergency = getState(DP.schedules03enable).val;
log('info', `Startstatus: Kabel=${plugged}, SOC=${soc}%, Notaus=${emergency}`);
if (plugged && !emergency) {
const target = getChargeTarget();
if (soc < target) {
log('info', 'Kabel steckt bereits beim Start – starte Zielladung');
onPlugStatusChange({ state: { val: true } });
} else {
log('info', 'Kabel steckt, aber Ziel bereits erreicht – kein Laden nötig');
}
}
// Auf Kabelstatus-Änderungen horchen
on({ id: DP.plugInStatus, change: 'ne' }, onPlugStatusChange);
log('info', `Warte auf Kabeländerungen an: ${DP.plugInStatus}`);
log('info', '=== Initialisierung abgeschlossen ===');
}
// ============================================================
// START
// ============================================================
init();
})(); // Ende IIFE