workspace: rapport solaire, prevision de nuit, concierge, courrier, fiche allegee
- Fiche agent maison (AGENTS.md) : prevision de nuit relayee par script, import
EDF, regle JEEP midi, saisie vehicule/niveau/depart, concierge qui relaie
« ✅ Noté » ou « Rien n'est noté », groupe A deplace (28 649 -> 25 831 car.).
- Nouvelles references : reference-concierge.md, reference-workspace.md.
- Scripts : rapport-solaire.js, prevision-nuit.js, airtria-ha-vehicule.js,
concierge-live.js (answer ne sort plus jamais vide, mode CONCIERGE_SIMULATION).
- Courrier Orange / AIRTRIA / Gmail : audits, regles, menage, recents.
- Bancs EVCS tests-pv (G5 accepte « anomal »).
- .gitignore : liste blanche etendue aux nouveaux scripts et bancs.
This commit is contained in:
70
workspace/airtria-ha-vehicule.js
Normal file
70
workspace/airtria-ha-vehicule.js
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env node
|
||||
// airtria-ha-vehicule.js — SEULE écriture autorisée de l'agent maison vers Home Assistant :
|
||||
// quel véhicule est branché et à quel niveau (saisi par Thierry, aucun capteur ne le donne).
|
||||
// node airtria-ha-vehicule.js → lit et affiche l'état courant
|
||||
// node airtria-ha-vehicule.js --vehicule JEEP --niveau 12
|
||||
// node airtria-ha-vehicule.js --niveau 40 → niveau seul (véhicule inchangé)
|
||||
// node airtria-ha-vehicule.js --depart 16:00 → départ ponctuel AUJOURD'HUI (VW, ZERO) ; --depart aucun l'efface
|
||||
// Écrit UNIQUEMENT : input_select (véhicule, option lue dans HA), input_number (niveau, entier 0-100)
|
||||
// et input_datetime (départ ponctuel, toujours daté du JOUR, jamais une autre date).
|
||||
// Ne touche JAMAIS victron_evcs_vehicle_level_ref_kwh ni victron_evcs_vehicle_level_at : une automatisation HA
|
||||
// les mémorise seule à chaque appel (une saisie identique compte aussi).
|
||||
'use strict';
|
||||
const fs = require('fs'); const path = require('path');
|
||||
const HA_BASE = process.env.HA_BASE || 'http://10.10.10.17:8123';
|
||||
const token = () => fs.readFileSync(process.env.HA_TOKEN_FILE || path.join(__dirname, '.ha-token'), 'utf8').trim();
|
||||
const SEL = 'input_select.victron_evcs_vehicle'; // « Véhicule branché » (désigné par EVCS)
|
||||
const NIV = 'input_number.victron_evcs_vehicle_level_pct'; // niveau saisi, %
|
||||
const DEP = 'input_datetime.victron_evcs_departure_ponctuel'; // départ ponctuel du jour (00:00 = aucun)
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const opt = (n) => { const i = args.indexOf(n); return i >= 0 ? args[i + 1] : undefined; };
|
||||
const vehicule = opt('--vehicule'); const niveauArg = opt('--niveau'); const departArg = opt('--depart');
|
||||
|
||||
async function ha(method, url, body) {
|
||||
const ac = new AbortController(); const t = setTimeout(() => ac.abort(), 10000);
|
||||
try {
|
||||
const r = await fetch(`${HA_BASE}${url}`, { method, signal: ac.signal,
|
||||
headers: { Authorization: `Bearer ${token()}`, 'Content-Type': 'application/json' },
|
||||
body: body ? JSON.stringify(body) : undefined });
|
||||
if (!r.ok) throw new Error(`HA ${r.status} sur ${url}`);
|
||||
return r.json();
|
||||
} finally { clearTimeout(t); }
|
||||
}
|
||||
const etat = async (e) => ha('GET', `/api/states/${e}`);
|
||||
|
||||
(async () => {
|
||||
const sel = await etat(SEL); const niv = await etat(NIV);
|
||||
const options = sel.attributes.options || [];
|
||||
const aujourdhui = new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().slice(0, 10);
|
||||
const departTxt = (d) => (d.state.startsWith(aujourdhui) && !d.state.endsWith('00:00:00')) ? `départ ponctuel aujourd'hui ${d.state.slice(11, 16)}` : 'pas de départ ponctuel saisi';
|
||||
let branche = 'état de branchement illisible';
|
||||
try { branche = (await etat('binary_sensor.victron_evcs_vehicle_connected')).state === 'on' ? 'une voiture EST branchée' : "AUCUNE voiture n'est branchée"; } catch { /* lecture seule */ }
|
||||
const affiche = (s, n, d, prefix) =>
|
||||
console.log(`${prefix}${branche} ; véhicule sélectionné = ${s.state}, dernier niveau saisi = ${Math.round(Number(n.state))} %, ${departTxt(d)} (options : ${options.join(', ')})`);
|
||||
if (vehicule === undefined && niveauArg === undefined && departArg === undefined) { affiche(sel, niv, await etat(DEP), 'HA : '); return; }
|
||||
|
||||
if (vehicule !== undefined) {
|
||||
const o = options.find((x) => x.toLowerCase() === String(vehicule).toLowerCase());
|
||||
if (!o) { console.log(`refus : véhicule « ${vehicule} » inconnu de HA, options : ${options.join(', ')}`); process.exit(2); }
|
||||
await ha('POST', '/api/services/input_select/select_option', { entity_id: SEL, option: o });
|
||||
}
|
||||
if (niveauArg !== undefined) {
|
||||
const n = Number(niveauArg);
|
||||
if (!Number.isInteger(n) || n < 0 || n > 100) { console.log(`refus : niveau « ${niveauArg} » invalide, entier de 0 à 100 attendu`); process.exit(2); }
|
||||
await ha('POST', '/api/services/input_number/set_value', { entity_id: NIV, value: n });
|
||||
}
|
||||
if (departArg !== undefined) {
|
||||
let heure = '00:00';
|
||||
if (String(departArg).toLowerCase() !== 'aucun') {
|
||||
const m = /^([01]?\d|2[0-3]):([0-5]\d)$/.exec(String(departArg));
|
||||
if (!m) { console.log(`refus : départ « ${departArg} » invalide, heure HH:MM d'aujourd'hui attendue (ou « aucun »)`); process.exit(2); }
|
||||
heure = `${m[1].padStart(2, '0')}:${m[2]}`;
|
||||
const now = new Date(); const nowHM = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
|
||||
if (heure <= nowHM) { console.log(`refus : départ ${heure} déjà passé (il est ${nowHM}), le départ ponctuel ne vaut que pour la suite de la journée`); process.exit(2); }
|
||||
}
|
||||
await ha('POST', '/api/services/input_datetime/set_datetime', { entity_id: DEP, datetime: `${aujourdhui} ${heure}:00` });
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 800));
|
||||
affiche(await etat(SEL), await etat(NIV), await etat(DEP), 'écrit et relu — ');
|
||||
})().catch((e) => { console.log(`erreur : ${e.message}`); process.exit(1); });
|
||||
Reference in New Issue
Block a user