Piles : acquittements a cle courte groupes par date (255 caracteres)
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
import { lireAcquittements, pilesAAlerter } from '../../tools/piles.mjs';
|
import { cleCourte, lireAcquittements, pilesAAlerter } from '../../tools/piles.mjs';
|
||||||
const T = Date.parse('2026-10-10T00:30:00+02:00');
|
const T = Date.parse('2026-10-10T00:30:00+02:00');
|
||||||
const e = (entity_id, state, attrs = {}, last = '2026-10-09T22:00:00+02:00') => ({ entity_id, state, attributes: attrs, last_changed: last });
|
const e = (entity_id, state, attrs = {}, last = '2026-10-09T22:00:00+02:00') => ({ entity_id, state, attributes: attrs, last_changed: last });
|
||||||
// Extrait reel des etats HA du 10/10 00:30.
|
// Extrait reel des etats HA du 10/10 00:30.
|
||||||
@@ -32,12 +32,15 @@ ok('hysteresis-tient-a-25', pilesAAlerter(grenier(25), T, [], { precedentes: ['s
|
|||||||
ok('hysteresis-pas-de-nouvelle-a-25', pilesAAlerter(grenier(25), T, [], { precedentes: [] }).alertes.length === 0);
|
ok('hysteresis-pas-de-nouvelle-a-25', pilesAAlerter(grenier(25), T, [], { precedentes: [] }).alertes.length === 0);
|
||||||
ok('hysteresis-retombe-a-40', pilesAAlerter(grenier(40), T, [], { precedentes: ['sensor.salle_de_bain_bruz_batterie'] }).alertes.length === 0);
|
ok('hysteresis-retombe-a-40', pilesAAlerter(grenier(40), T, [], { precedentes: ['sensor.salle_de_bain_bruz_batterie'] }).alertes.length === 0);
|
||||||
// Acquittement : « vu, je change dans 3 jours » -> plus de rappel jusqu'a la date.
|
// Acquittement : « vu, je change dans 3 jours » -> plus de rappel jusqu'a la date.
|
||||||
const acq = lireAcquittements('salle_de_bain_bruz_batterie:2026-10-16, mauvais format, x:2026-13-99');
|
const kG = cleCourte('sensor.salle_de_bain_bruz_batterie');
|
||||||
|
const acq = lireAcquittements(`2026-10-16:${kG}|abcdef, mauvais format, 2026-10-12:zz`);
|
||||||
const ra = pilesAAlerter(grenier(6), Date.parse('2026-10-14T09:00:00+02:00'), [], { acquittements: acq });
|
const ra = pilesAAlerter(grenier(6), Date.parse('2026-10-14T09:00:00+02:00'), [], { acquittements: acq });
|
||||||
ok('acquittee-pas-de-rappel', ra.alertes.length === 1 && ra.a_rappeler.length === 0 && ra.acquittees[0].jusqu_au === '2026-10-16' && /jusqu'au 16\/10/.test(ra.texte), JSON.stringify(ra));
|
ok('acquittee-pas-de-rappel', ra.alertes.length === 1 && ra.a_rappeler.length === 0 && ra.acquittees[0].jusqu_au === '2026-10-16' && /jusqu'au 16\/10/.test(ra.texte), JSON.stringify(ra));
|
||||||
const rb = pilesAAlerter(grenier(6), Date.parse('2026-10-17T08:00:00+02:00'), [], { acquittements: acq });
|
const rb = pilesAAlerter(grenier(6), Date.parse('2026-10-17T08:00:00+02:00'), [], { acquittements: acq });
|
||||||
ok('acquittement-expire-rappel', rb.a_rappeler.length === 1 && rb.a_rappeler[0].cle === 'salle_de_bain_bruz_batterie');
|
ok('acquittement-expire-rappel', rb.a_rappeler.length === 1 && rb.a_rappeler[0].cle === kG && /^[0-9a-f]{6}$/.test(kG));
|
||||||
ok('acquittement-format-tolerant', Object.keys(acq).length === 1);
|
ok('acquittement-format-tolerant', Object.keys(acq).length === 2 && acq[kG].jusquAu === '2026-10-16');
|
||||||
|
// 9 alertes groupees sur une date : bien sous les 255 caracteres du helper.
|
||||||
|
ok('format-groupe-tient-dans-255', `2026-10-20:${Array.from({ length: 9 }, (_, i) => cleCourte('sensor.x' + i)).join('|')}`.length < 80);
|
||||||
const gerard = (v) => [{ entity_id: 'sensor.chambre_gerard_etat_batterie', state: v, last_changed: new Date(T).toISOString(), attributes: { device_class: 'enum', friendly_name: 'Chambre Gerard Etat batterie' } }];
|
const gerard = (v) => [{ entity_id: 'sensor.chambre_gerard_etat_batterie', state: v, last_changed: new Date(T).toISOString(), attributes: { device_class: 'enum', friendly_name: 'Chambre Gerard Etat batterie' } }];
|
||||||
ok('enum-low-middle-tient', pilesAAlerter(gerard('middle'), T, [], { precedentes: ['sensor.chambre_gerard_etat_batterie'] }).alertes.length === 1
|
ok('enum-low-middle-tient', pilesAAlerter(gerard('middle'), T, [], { precedentes: ['sensor.chambre_gerard_etat_batterie'] }).alertes.length === 1
|
||||||
&& pilesAAlerter(gerard('middle'), T, []).alertes.length === 0 && pilesAAlerter(gerard('high'), T, [], { precedentes: ['sensor.chambre_gerard_etat_batterie'] }).alertes.length === 0);
|
&& pilesAAlerter(gerard('middle'), T, []).alertes.length === 0 && pilesAAlerter(gerard('high'), T, [], { precedentes: ['sensor.chambre_gerard_etat_batterie'] }).alertes.length === 0);
|
||||||
|
|||||||
@@ -2169,7 +2169,7 @@ async function publierPiles() {
|
|||||||
await publierCapteur('sensor.evcs_piles', p.a_rappeler.length, {
|
await publierCapteur('sensor.evcs_piles', p.a_rappeler.length, {
|
||||||
friendly_name: 'Piles des capteurs a changer', icon: 'mdi:battery-alert-variant-outline',
|
friendly_name: 'Piles des capteurs a changer', icon: 'mdi:battery-alert-variant-outline',
|
||||||
alertes: p.alertes, a_rappeler: p.a_rappeler, acquittees: p.acquittees, texte: p.texte, ignorees: ign,
|
alertes: p.alertes, a_rappeler: p.a_rappeler, acquittees: p.acquittees, texte: p.texte, ignorees: ign,
|
||||||
format_acquittement: 'input_text.evcs_piles_acquittees = cle:AAAA-MM-JJ[,cle:AAAA-MM-JJ] (cle = attribut cle de l alerte)',
|
format_acquittement: 'input_text.evcs_piles_acquittees = AAAA-MM-JJ:cle|cle[,AAAA-MM-JJ:cle] (cle = attribut cle de l alerte, 6 caracteres)',
|
||||||
});
|
});
|
||||||
pilesPubliees = true;
|
pilesPubliees = true;
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -23,17 +23,24 @@ const EXCLUS = /^(sensor|number|binary_sensor)\.(victron_|gx_device_|airtria_8_|
|
|||||||
*/
|
*/
|
||||||
// ACQUITTEMENT (13/10, demande du proprietaire) : « j'ai compris, je change les
|
// ACQUITTEMENT (13/10, demande du proprietaire) : « j'ai compris, je change les
|
||||||
// piles dans N jours » -> plus de rappel pour cet appareil jusqu'a la date dite.
|
// piles dans N jours » -> plus de rappel pour cet appareil jusqu'a la date dite.
|
||||||
// input_text.evcs_piles_acquittees = « cle:AAAA-MM-JJ,cle:AAAA-MM-JJ », cle =
|
// input_text.evcs_piles_acquittees, ecrit par le concierge (SPARK), 255 car. max.
|
||||||
// entite sans « sensor. » ; valable jusqu'a la fin de ce jour (heure de Paris).
|
// 14/10 : les identifiants complets ne tenaient pas (9 alertes ~ 370 car.) ->
|
||||||
// Ecrit par le concierge (SPARK) ; une cle d'un appareil qui n'est plus en
|
// CLE COURTE (6 caracteres, sha1 de l'entite, attribut `cle`) et format GROUPE
|
||||||
// alerte (piles changees) est simplement sans effet.
|
// PAR DATE : « AAAA-MM-JJ:cle|cle,AAAA-MM-JJ:cle » (~75 car. pour 9 alertes).
|
||||||
|
// Valable jusqu'a la fin de ce jour (heure de Paris). Une cle d'un appareil qui
|
||||||
|
// n'est plus en alerte (piles changees) est sans effet.
|
||||||
|
import { createHash } from 'node:crypto';
|
||||||
|
export const cleCourte = (entite) => createHash('sha1').update(String(entite)).digest('hex').slice(0, 6);
|
||||||
export function lireAcquittements(texte) {
|
export function lireAcquittements(texte) {
|
||||||
const out = {};
|
const out = {}; // cle courte -> { jusquAu, finMs }
|
||||||
for (const part of String(texte || '').split(',')) {
|
for (const groupe of String(texte || '').split(',')) {
|
||||||
const m = /^\s*([a-z0-9_]+)\s*:\s*(\d{4}-\d{2}-\d{2})\s*$/.exec(part);
|
const m = /^\s*(\d{4}-\d{2}-\d{2})\s*:\s*([0-9a-f|\s]+)$/.exec(groupe);
|
||||||
if (!m) continue;
|
if (!m) continue;
|
||||||
const fin = Date.parse(`${m[2]}T23:59:59+02:00`);
|
const fin = Date.parse(`${m[1]}T23:59:59+02:00`);
|
||||||
if (Number.isFinite(fin)) out[`sensor.${m[1]}`] = { jusquAu: m[2], finMs: fin };
|
if (!Number.isFinite(fin)) continue;
|
||||||
|
for (const k of m[2].split('|').map((x) => x.trim()).filter((x) => /^[0-9a-f]{6}$/.test(x))) {
|
||||||
|
if (!out[k] || out[k].finMs < fin) out[k] = { jusquAu: m[1], finMs: fin };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -75,10 +82,10 @@ export function pilesAAlerter(etats, maintenantMs, ignorees = [], opts = {}) {
|
|||||||
}
|
}
|
||||||
alertes.sort((x, y) => (x.statut === y.statut ? x.nom.localeCompare(y.nom) : x.statut === 'faible' ? -1 : 1));
|
alertes.sort((x, y) => (x.statut === y.statut ? x.nom.localeCompare(y.nom) : x.statut === 'faible' ? -1 : 1));
|
||||||
for (const a of alertes) {
|
for (const a of alertes) {
|
||||||
const k = acq[a.entite];
|
a.cle = cleCourte(a.entite);
|
||||||
|
const k = acq[a.cle];
|
||||||
a.acquittee = !!(k && k.finMs >= maintenantMs);
|
a.acquittee = !!(k && k.finMs >= maintenantMs);
|
||||||
if (a.acquittee) a.jusqu_au = k.jusquAu;
|
if (a.acquittee) a.jusqu_au = k.jusquAu;
|
||||||
a.cle = a.entite.replace(/^sensor\./, '');
|
|
||||||
}
|
}
|
||||||
const aRappeler = alertes.filter((a) => !a.acquittee);
|
const aRappeler = alertes.filter((a) => !a.acquittee);
|
||||||
const acquittees = alertes.filter((a) => a.acquittee);
|
const acquittees = alertes.filter((a) => a.acquittee);
|
||||||
|
|||||||
Reference in New Issue
Block a user