logique de traitement erron
This commit is contained in:
230
decoupesurplus/V2/gridhystP2.js
Normal file
230
decoupesurplus/V2/gridhystP2.js
Normal file
@@ -0,0 +1,230 @@
|
||||
// ======================================================
|
||||
// Timestamps
|
||||
// ======================================================
|
||||
// On pose un nouveau "last_on_at" si :
|
||||
// - on allume réellement EDF
|
||||
// - ou on adopte un EDF déjà ON avec latch OFF
|
||||
const setLastOnAt =
|
||||
gridAction === 'turn_on' ||
|
||||
state === 'adopt_existing_grid_on' ||
|
||||
state === 'adopt_existing_grid_on_low_soc';
|
||||
|
||||
// On pose "last_off_at" si on coupe réellement EDF
|
||||
const setLastOffAt =
|
||||
gridAction === 'turn_off';
|
||||
|
||||
// ======================================================
|
||||
// Context / alerts
|
||||
// ======================================================
|
||||
reason.push(`Grid actuel ${gridIsOn ? 'ON' : 'OFF'}`);
|
||||
reason.push(`Latch actuel ${latchIsOn ? 'ON' : 'OFF'}`);
|
||||
reason.push(`SOC ${round1(soc)}% | seuils ${gridSocOn}%/${gridSocOff}%`);
|
||||
reason.push(`PV ${Math.round(pvPowerW)}W`);
|
||||
reason.push(`Charge AC maison ${Math.round(houseAcPowerW)}W`);
|
||||
reason.push(`Puissance réseau ${Math.round(gridPowerW)}W`);
|
||||
reason.push(`EVCS ${evcsSeemsActive ? 'actif' : 'inactif'} | ${Math.round(evcsAcPowerW)}W | status_raw=${Number.isFinite(evcsStatusRaw) ? evcsStatusRaw : 'n/a'}`);
|
||||
|
||||
if (minutesSinceLastOn !== null) {
|
||||
reason.push(`Dernier ON il y a ${round1(minutesSinceLastOn)} min`);
|
||||
}
|
||||
if (minutesSinceLastOff !== null) {
|
||||
reason.push(`Dernier OFF il y a ${round1(minutesSinceLastOff)} min`);
|
||||
}
|
||||
|
||||
if (acOverSafeLimit) {
|
||||
reason.push(`⚠️ Charge AC maison ${Math.round(houseAcPowerW)}W >= limite sûre ${Math.round(acLoadsMaxSafeW)}W`);
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// Notifications
|
||||
// ======================================================
|
||||
let notifyTitle = 'Victron Grid Hysteresis';
|
||||
let notifyMessage = '';
|
||||
let notifyWanted = false;
|
||||
|
||||
if (state === 'turn_on_low_soc' || state === 'turn_on_low_soc_cooldown_bypassed') {
|
||||
notifyTitle = '⚡ EDF activé sur SOC bas';
|
||||
notifyMessage = `SOC ${round1(soc)}% <= ${gridSocOn}%. EDF activé et maintenu jusqu'à au moins ${gridSocOff}% (min ${gridMinOnMinutes} min).`;
|
||||
notifyWanted = true;
|
||||
|
||||
} else if (state === 'adopt_existing_grid_on_low_soc' || state === 'adopt_existing_grid_on') {
|
||||
notifyTitle = '🔁 EDF ON repris par hystérésis';
|
||||
notifyMessage = `EDF était déjà ON. Le workflow reprend la main et le maintient jusqu'à ${gridSocOff}% (min ${gridMinOnMinutes} min).`;
|
||||
notifyWanted = true;
|
||||
|
||||
} else if (state === 'turn_off_recovered') {
|
||||
notifyTitle = '✅ EDF coupé après recharge';
|
||||
notifyMessage = `SOC ${round1(soc)}% >= ${gridSocOff}%. Retour hors réseau autorisé.`;
|
||||
notifyWanted = true;
|
||||
|
||||
} else if (state === 'turn_off_stray_grid_on') {
|
||||
notifyTitle = '✅ EDF coupé (reprise de contrôle)';
|
||||
notifyMessage = `EDF était ON sans latch. SOC ${round1(soc)}% >= ${gridSocOff}%, coupure réseau appliquée.`;
|
||||
notifyWanted = true;
|
||||
|
||||
} else if (acOverSafeLimit && !gridIsOn) {
|
||||
notifyTitle = '🚨 Charge AC élevée hors réseau';
|
||||
notifyMessage = `Charge AC maison ${Math.round(houseAcPowerW)}W au-dessus de la limite sûre ${Math.round(acLoadsMaxSafeW)}W alors que EDF est OFF.`;
|
||||
notifyWanted = true;
|
||||
|
||||
} else if (acOverSafeLimit && gridIsOn) {
|
||||
notifyTitle = '⚠️ Charge AC élevée';
|
||||
notifyMessage = `Charge AC maison ${Math.round(houseAcPowerW)}W proche/au-dessus de la limite sûre ${Math.round(acLoadsMaxSafeW)}W.`;
|
||||
notifyWanted = true;
|
||||
}
|
||||
|
||||
const shortReason = reason.join(' | ').slice(0, 255);
|
||||
|
||||
const signature = [
|
||||
now.date,
|
||||
state,
|
||||
`dg=${desiredGrid}`,
|
||||
`dl=${desiredLatch}`,
|
||||
`sb=${socBand}`,
|
||||
`ac=${acOverSafeLimit ? '1' : '0'}`
|
||||
].join('|');
|
||||
|
||||
const signatureChanged = signature !== lastSignature;
|
||||
|
||||
const result = {
|
||||
timestamp_paris: now.stamp,
|
||||
|
||||
inputs: {
|
||||
soc_percent: soc,
|
||||
grid_state: gridIsOn ? 'on' : 'off',
|
||||
pv_power_w: pvPowerW,
|
||||
house_ac_power_w: houseAcPowerW,
|
||||
grid_power_w: gridPowerW,
|
||||
evcs_ac_power_w: evcsAcPowerW,
|
||||
evcs_charging: evcsCharging,
|
||||
evcs_status_raw: Number.isFinite(evcsStatusRaw) ? evcsStatusRaw : null,
|
||||
hysteresis_enabled: hysteresisEnabled,
|
||||
notifications_enabled: notificationsEnabled,
|
||||
grid_latch: latchIsOn,
|
||||
grid_soc_on: gridSocOn,
|
||||
grid_soc_off: gridSocOff,
|
||||
grid_min_on_minutes: gridMinOnMinutes,
|
||||
grid_cooldown_minutes: gridCooldownMinutes,
|
||||
ac_loads_max_safe_w: acLoadsMaxSafeW,
|
||||
last_on_at: lastOnAt || null,
|
||||
last_off_at: lastOffAt || null,
|
||||
last_signature: lastSignature || null
|
||||
},
|
||||
|
||||
computed: {
|
||||
desired_grid: desiredGrid,
|
||||
desired_latch: desiredLatch,
|
||||
minutes_since_last_on: minutesSinceLastOn !== null ? round1(minutesSinceLastOn) : null,
|
||||
minutes_since_last_off: minutesSinceLastOff !== null ? round1(minutesSinceLastOff) : null,
|
||||
min_on_elapsed: minOnElapsed,
|
||||
cooldown_elapsed: cooldownElapsed,
|
||||
ac_over_safe_limit: acOverSafeLimit,
|
||||
evcs_seems_active: evcsSeemsActive
|
||||
},
|
||||
|
||||
decision: {
|
||||
state,
|
||||
grid_action: gridAction,
|
||||
latch_action: latchAction,
|
||||
set_last_on_at: setLastOnAt,
|
||||
set_last_off_at: setLastOffAt,
|
||||
should_notify: notificationsEnabled && signatureChanged && notifyWanted,
|
||||
signature,
|
||||
signature_changed: signatureChanged,
|
||||
reason,
|
||||
short_reason: shortReason
|
||||
},
|
||||
|
||||
ha_service_calls: {
|
||||
helper_state: {
|
||||
domain: 'input_text',
|
||||
service: 'set_value',
|
||||
data: {
|
||||
entity_id: 'input_text.victron_grid_control_state',
|
||||
value: state
|
||||
}
|
||||
},
|
||||
|
||||
helper_reason: {
|
||||
domain: 'input_text',
|
||||
service: 'set_value',
|
||||
data: {
|
||||
entity_id: 'input_text.victron_grid_control_reason',
|
||||
value: shortReason || 'Aucune raison'
|
||||
}
|
||||
},
|
||||
|
||||
helper_signature: {
|
||||
domain: 'input_text',
|
||||
service: 'set_value',
|
||||
data: {
|
||||
entity_id: 'input_text.victron_grid_control_last_signature',
|
||||
value: signature
|
||||
}
|
||||
},
|
||||
|
||||
helper_latch: {
|
||||
domain: 'input_boolean',
|
||||
service: desiredLatch === 'on' ? 'turn_on' : 'turn_off',
|
||||
data: {
|
||||
entity_id: 'input_boolean.victron_grid_hysteresis_latch'
|
||||
}
|
||||
},
|
||||
|
||||
helper_last_on_at: {
|
||||
domain: 'input_datetime',
|
||||
service: 'set_datetime',
|
||||
data: {
|
||||
entity_id: 'input_datetime.victron_grid_last_on_at',
|
||||
datetime: now.stamp
|
||||
}
|
||||
},
|
||||
|
||||
helper_last_off_at: {
|
||||
domain: 'input_datetime',
|
||||
service: 'set_datetime',
|
||||
data: {
|
||||
entity_id: 'input_datetime.victron_grid_last_off_at',
|
||||
datetime: now.stamp
|
||||
}
|
||||
},
|
||||
|
||||
grid: {
|
||||
domain: 'switch',
|
||||
service: desiredGrid === 'on' ? 'turn_on' : 'turn_off',
|
||||
data: {
|
||||
entity_id: 'switch.edf_general_ok_switch'
|
||||
}
|
||||
},
|
||||
|
||||
logbook: {
|
||||
domain: 'logbook',
|
||||
service: 'log',
|
||||
data: {
|
||||
name: 'Victron Grid Hysteresis',
|
||||
message: `${state} | Grid=${desiredGrid} | Latch=${desiredLatch} | SOC=${round1(soc)}% | AC=${Math.round(houseAcPowerW)}W | ${shortReason}`,
|
||||
entity_id: 'switch.edf_general_ok_switch'
|
||||
}
|
||||
},
|
||||
|
||||
mobile_notification: {
|
||||
domain: cfg.mobileNotifyDomain,
|
||||
service: cfg.mobileNotifyService,
|
||||
data: {
|
||||
title: notifyTitle,
|
||||
message: notifyMessage,
|
||||
data: {
|
||||
push: {
|
||||
"interruption-level": "time-sensitive",
|
||||
sound: { name: "default" }
|
||||
},
|
||||
ttl: 0,
|
||||
priority: "high"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return [{ json: result }];
|
||||
|
||||
Reference in New Issue
Block a user