Files
EVCS/tests/sdb/run.mjs

101 lines
8.3 KiB
JavaScript

#!/usr/bin/env node
// Tests du workflow SDB Climate & Surplus (10/10/2026) : vrai code de COMPUTE et
// BUILD_ACTIONS en vm, horloge figee au 10/10/2026 00:15 (Paris).
import fs from 'fs';
import vm from 'vm';
import path from 'path';
import { fileURLToPath } from 'url';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const COMPUTE = fs.readFileSync(path.join(ROOT, 'src/sdb/COMPUTE.js'), 'utf8');
const BUILD = fs.readFileSync(path.join(ROOT, 'src/sdb/BUILD_ACTIONS.js'), 'utf8');
const FIXE = Date.parse('2026-10-10T00:15:00+02:00');
class D extends Date { constructor(...a) { super(...(a.length ? a : [FIXE])); } static now() { return FIXE; } }
function run(code, json) {
const ctx = { $input: { first: () => ({ json }) }, console, Date: D, Intl, Math, Number, Object, String, JSON, Array, Boolean };
vm.createContext(ctx);
return vm.runInContext(`(function(){\n${code}\n})()`, ctx);
}
// Releve reel du 10/10 00:15 : capteur muet depuis le 04/10, deshumidificateur et
// prise SDB allumes et memorises, SOC 26 %, EDF ouvert, nuit.
const base = {
soc_percent: '26', battery_current_a: '-28', pv_power_w: '0', pv_power_10m_avg_w: '0',
house_ac_power_w: '1400', grid_power_w: '0', grid_state: 'off', victron_acin1_available: '0',
sdb_master_power: 'on', sdb_master_energy_total: '4.1', deshumide_switch: 'on', deshumide_energy_total: '3.0',
radiator_switch: 'off', radiator_energy_total: '0',
sdb_temperature_c: 'unavailable', sdb_humidity_pct: 'unavailable', sdb_humidity_age_min: '7900',
auto_enabled: 'on', notifications_enabled: 'on', manual_adopt_enabled: 'on',
master_power_latch: 'on', deshumide_latch: 'on', radiator_latch: 'off',
temp_on_min_c: '20', temp_off_max_c: '24', humidity_on_pct: '66', humidity_off_pct: '41',
deshumide_min_on_minutes: '15', deshumide_min_off_minutes: '10',
radiator_auto_enabled: 'on', radiator_soc_on: '85', radiator_soc_off: '80',
master_last_on_at: '2026-10-09 22:39:35', deshumide_last_on_at: '2026-10-09 20:00:35',
deshumide_last_off_at: '2026-05-17 02:50:35', master_last_off_at: '2026-05-17 05:45:35',
balance_required_soc_prudent: '60', balance_status: 'ok',
};
const dec = (o) => run(COMPUTE, { data: JSON.stringify({ ...base, ...o }) })[0].json.decision;
let f = 0;
const ok = (l, c, d = '') => { if (c) console.log(`✅ ${l}`); else { f++; console.log(`❌ ${l} ${d}`); } };
let d = dec({});
ok('muet-releve-1010-coupe', d.deshumide_action === 'turn_off' && d.master_action === 'turn_off' && d.deshumide_latch_action === 'turn_off', JSON.stringify(d));
d = dec({ deshumide_last_on_at: '2026-10-10 00:10:00' });
ok('muet-temps-mini-respecte', d.deshumide_action === 'none', JSON.stringify(d));
d = dec({ deshumide_switch: 'off', deshumide_latch: 'off', master_power_latch: 'off', sdb_master_power: 'off' });
ok('muet-ne-demarre-pas', d.deshumide_action === 'none' && d.master_action === 'none', JSON.stringify(d));
const vivant = { sdb_temperature_c: '26.3', sdb_humidity_pct: '70', sdb_humidity_age_min: '2', deshumide_switch: 'off', deshumide_latch: 'off', master_power_latch: 'off', sdb_master_power: 'off' };
d = dec(vivant);
ok('batterie-reservee-pas-de-demarrage', d.deshumide_action === 'none', JSON.stringify(d));
d = dec({ ...vivant, grid_state: 'on' });
ok('edf-ferme-demarre', d.deshumide_action === 'turn_on' && d.master_action === 'turn_on', JSON.stringify(d));
d = dec({ ...vivant, soc_percent: '80' });
ok('batterie-au-dessus-du-bilan-demarre', d.deshumide_action === 'turn_on', JSON.stringify(d));
d = dec({ ...vivant, pv_power_10m_avg_w: '2500', house_ac_power_w: '1200' });
ok('surplus-solaire-demarre', d.deshumide_action === 'turn_on', JSON.stringify(d));
d = dec({ ...vivant, balance_status: 'degrade' });
ok('bilan-illisible-comportement-anterieur', d.deshumide_action === 'turn_on', JSON.stringify(d));
d = dec({ sdb_temperature_c: '26.3', sdb_humidity_pct: '56', sdb_humidity_age_min: '2' });
ok('batterie-reservee-coupe-apres-temps-mini', d.deshumide_action === 'turn_off', JSON.stringify(d));
d = dec({ sdb_temperature_c: '26.3', sdb_humidity_pct: '56', sdb_humidity_age_min: '2', soc_percent: '80' });
ok('hysteresis-humidite-inchangee', d.deshumide_action === 'none', JSON.stringify(d));
d = dec({ sdb_temperature_c: '26.3', sdb_humidity_pct: '56', sdb_humidity_age_min: '400', soc_percent: '80' });
ok('valeur-figee-3h-muet', d.deshumide_action === 'turn_off', JSON.stringify(d));
// MODE SURPLUS (10/10) : releve du 08/10 16 h — SOC 96, batterie 2 A, solaire bride.
const D16 = class extends Date { constructor(...a) { super(...(a.length ? a : [Date.parse('2026-10-08T16:00:00+02:00')])); } static now() { return Date.parse('2026-10-08T16:00:00+02:00'); } };
const decAt = (o, DC = D16) => { const ctx = { $input: { first: () => ({ json: { data: JSON.stringify({ ...base, ...o }) } }) }, console, Date: DC, Intl, Math, Number, Object, String, JSON, Array, Boolean };
vm.createContext(ctx); return vm.runInContext(`(function(){\n${COMPUTE}\n})()`, ctx)[0].json.decision; };
const soleil = { soc_percent: '96', battery_current_a: '2', pv_power_w: '4646', pv_power_10m_avg_w: '4600', house_ac_power_w: '900',
sdb_temperature_c: '24', sdb_humidity_pct: '50', sdb_humidity_age_min: '3',
mppt_operation_modes: 'voltage_current_limited,voltage_current_limited,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active',
sdb_master_power: 'off', deshumide_switch: 'off', radiator_switch: 'off', master_power_latch: 'off', deshumide_latch: 'off', radiator_latch: 'off',
deshumide_last_on_at: '2026-10-08 10:00:00', deshumide_last_off_at: '2026-10-08 10:30:00',
master_last_on_at: '2026-10-08 10:00:00', master_last_off_at: '2026-10-08 10:30:00',
evcs_vehicle_connected: 'off', evcs_ac_power_w: '0', evcs_surplus_state: 'vehicle_not_connected' };
d = decAt(soleil);
ok('surplus-0810-les-deux-demarrent', d.deshumide_action === 'turn_on' && d.radiator_action === 'turn_on' && d.master_action === 'turn_on', JSON.stringify(d));
d = decAt({ ...soleil, sdb_temperature_c: '28.2' });
ok('surplus-28c-seche-serviette-non', d.deshumide_action === 'turn_on' && d.radiator_action === 'none', JSON.stringify(d));
d = decAt({ ...soleil, sdb_temperature_c: 'unavailable', sdb_humidity_pct: 'unavailable' });
ok('surplus-temperature-inconnue-seche-serviette-non', d.radiator_action === 'none' && d.deshumide_action === 'turn_on', JSON.stringify(d));
d = decAt({ ...soleil, evcs_vehicle_connected: 'on', evcs_surplus_state: 'idle_wait_conditions' });
ok('surplus-voiture-branchee-passe-dabord', d.deshumide_action === 'none' && d.radiator_action === 'none', JSON.stringify(d));
d = decAt({ ...soleil, grid_state: 'on' });
ok('surplus-edf-ferme-non', d.radiator_action === 'none', JSON.stringify(d));
d = decAt({ ...soleil, mppt_operation_modes: 'mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active' });
ok('surplus-aucun-chargeur-bride-non', d.radiator_action === 'none' && d.deshumide_action === 'none', JSON.stringify(d));
const enMarche = { ...soleil, radiator_switch: 'on', radiator_latch: 'on', deshumide_switch: 'on', deshumide_latch: 'on', sdb_master_power: 'on', master_power_latch: 'on',
radiator_last_on_at: '2026-10-08 15:00:00', deshumide_last_on_at: '2026-10-08 15:00:00', master_last_on_at: '2026-10-08 15:00:00',
last_signature: '2026-10-08|deshumide_and_radiator_on|m=1|d=1|r=1|sp=1',
mppt_operation_modes: 'mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active,mppt_active' };
d = decAt({ ...enMarche, battery_current_a: '1' });
ok('surplus-maintien-batterie-ne-se-decharge-pas', d.radiator_action === 'none' && d.deshumide_action === 'none', JSON.stringify(d));
d = decAt({ ...enMarche, battery_current_a: '-12' });
ok('surplus-fin-batterie-se-decharge-tout-coupe', d.radiator_action === 'turn_off' && d.deshumide_action === 'turn_off' && d.master_action === 'turn_off', JSON.stringify(d));
d = decAt({ ...enMarche, battery_current_a: '1', sdb_temperature_c: '28.5' });
ok('surplus-28c-arrete-le-seche-serviette', d.radiator_action === 'turn_off' && d.deshumide_action === 'none', JSON.stringify(d));
const out = run(COMPUTE, { data: JSON.stringify(base) })[0].json;
const acts = run(BUILD, out).map((x) => x.json.action);
ok('build-coupe-enfant-puis-master', acts.indexOf('deshumide') >= 0 && acts.indexOf('deshumide') < acts.indexOf('master_power'), acts.join(','));
console.log(f ? `\n${f} ECHEC(S)` : '\nSDB OK');
process.exit(f ? 1 : 0);