EDIT 20/07/2023: Le code à été mit à jour et devrait résoudre le problème de l’erreur “Cannot read properties of undefined (reading ‘lastUpdated’)”
Bonsoir,
Je partage un script permettant de surveiller facilement les appareils sur batteries. (Modifiable pour tout autre type d’appareil)
D’abord pourquoi j’ai fais ceci? J’ai un certain nombre d’appareil sur batterie (capteurs d’ouverture, sondes, détecteur de mouvement, …) et je me rend compte, souvent longtemps après, qu’un appareil n’a plus de batterie.
J’ai beau utiliser différents flow pour surveiller mes appareils, aucun n’est vraiment fiable.
Celui ci dessous par exemple ne trouve pas les capteurs qui n’ont qu’une alarme batterie (certain ne donne pas leur pourcentage de batterie). Ou que le niveau de batterie était supérieur au seuil de 5% avant de ne plus avoir de batterie (c’est beaucoup le cas sur les sondes Aqara ou le niveau de batterie est très mal relevé).
J’utilise celui ci également. Alors lui renvoie tout et n’importe quoi, je ne sais pas ce que prend en compte la carte “Un appareil n’a pas émis de signal pendant 1 jour” mais je reçois beaucoup de fausse alerte (des sondes de température par exemple, quand on sait que la température change quand même plusieurs fois dans une journée, des lumières dont c’est sur qu’elles ont été allumées dans la journée, des volets qui sont ouvert et fermé tous les jours…)
De plus il plafonne à 1 jour, donc c’est super court, et on ne peut rien filtrer. Et on reçois 1 notif à chaque fois! Donc c’est certainement vrai pour quelques appareils mais ils sont perdu dans une foule de fausses infos.
Mon idée était donc de faire un script pour récupérer une liste d’appareils avec certains critères:
- filtrer uniquement les appareils sur batteries
- rechercher en premier si l’appareil n’a pas de soucis de communication
- récupérer toutes les dates qui sont disponibles dans leur paramètres et n’en sortir que la plus récente
- pouvoir personnaliser le temps entre maintenant et le dernier signal de l’appareil (si on veut une alerte à 1h ou 15jours sans signal, c’est possible!)
- pouvoir ajouter des appareils à une liste blanche pour qu’il ne soit pas pris en compte
- et surtout, tout récupérer sous une forme de liste à peu près lisible (et pas 1 notif par appareil)
Le code est donc le suivant:
let myArgs = JSON.parse(args[0]);
const hoursSinceLastUpdate = myArgs.hoursSinceLastUpdate;
let whiteList;
if (myArgs.whiteList) {
whiteList = (myArgs.whiteList.replace(/\s/g, '')); //Get the devices on white list and remove spaces in their name (get problem with some device who have space in name)
}
else whiteList = 'null';
const devices = await Homey.devices.getDevices();// Get all devices
let nextList = "";
let previousList = "";
let deviceName;
let idActuel = Object.values(await Homey.devices.getDevices())[0].id; //Récupère l'id de Homey pour avoir un id de base
let actualDevice;
let actualDeviceString;
let actualDeviceCapabilitiesString;
let capabilityObjString;
let deviceNameString;
let dateNow;
let batteryDate;
let temperatureDate;
let pressureDate;
let humidityDate;
let luminanceDate;
let alarmBatteryDate;
let alarmContactDate;
let alarmTamperDate;
let alarmMotionDate;
let alarmHeatDate;
let alarmSmokeDate;
let alarmWaterDate;
let dateCompare;
for (const device of Object.values(devices)) {
dateNow = new Date();
idActuel = device.id;
actualDevice = await Homey.devices.getDevice({ id: idActuel });
actualDeviceString = JSON.stringify(actualDevice);
actualDeviceCapabilitiesString = JSON.stringify(actualDevice.capabilities);
capabilityObjString = JSON.stringify(actualDevice.capabilitiesObj);
deviceNameString = JSON.stringify(actualDevice.name);
if (!whiteList.toLowerCase().includes(((actualDevice.name).toLowerCase()).replace(/\s/g, ''))) { //Check if device is on white list
if (!actualDeviceCapabilitiesString.includes("measure_battery")) {
continue; //if device hasn't battery, ignore it
}
// Search if there an error message
if ((JSON.stringify(actualDevice.unavailableMessage)) !== "null") {
previousList = nextList;
nextList = previousList + actualDevice.name + ": " + actualDevice.unavailableMessage + " ⚠️" + "\n\n";
continue;
}
//search if the device has a battery. Then get all last update of the capabilities (if exist)
if (actualDeviceCapabilitiesString.includes("measure_battery")) {
//log(actualDevice.name);
batteryDate = new Date(actualDevice.capabilitiesObj.measure_battery.lastUpdated);
dateCompare = batteryDate;
temperatureDate = 0;
pressureDate = 0;
humidityDate = 0;
luminanceDate = 0;
alarmBatteryDate = 0;
alarmContactDate = 0;
alarmTamperDate = 0;
alarmMotionDate = 0;
alarmHeatDate = 0;
alarmSmokeDate = 0;
alarmWaterDate = 0;
if (capabilityObjString.includes("measure_temperature")) {
temperatureDate = new Date(actualDevice.capabilitiesObj.measure_temperature.lastUpdated);
}
if (capabilityObjString.includes("measure_pressure")) {
pressureDate = new Date(actualDevice.capabilitiesObj.measure_pressure.lastUpdated);
}
if (capabilityObjString.includes("measure_humidity")) {
humidityDate = new Date(actualDevice.capabilitiesObj.measure_humidity.lastUpdated);
}
if (capabilityObjString.includes("alarm_battery")) {
alarmBatteryDate = new Date(actualDevice.capabilitiesObj.alarm_battery.lastUpdated);
}
if (capabilityObjString.includes("alarm_contact")) {
alarmContactDate = new Date(actualDevice.capabilitiesObj.alarm_contact.lastUpdated);
}
if (capabilityObjString.includes("alarm_tamper")) {
alarmTamperDate = new Date(actualDevice.capabilitiesObj.alarm_tamper.lastUpdated);
}
if (capabilityObjString.includes("alarm_motion")) {
alarmMotionDate = new Date(actualDevice.capabilitiesObj.alarm_motion.lastUpdated);
}
if (capabilityObjString.includes("measure_luminance")) {
luminanceDate = new Date(actualDevice.capabilitiesObj.measure_luminance.lastUpdated);
}
if (capabilityObjString.includes("alarm_heat")) {
alarmHeatDate = new Date(actualDevice.capabilitiesObj.alarm_heat.lastUpdated);
}
if (capabilityObjString.includes("alarm_smoke")) {
alarmSmokeDate = new Date(actualDevice.capabilitiesObj.alarm_smoke.lastUpdated);
}
if (capabilityObjString.includes("alarm_water")) {
alarmWaterDate = new Date(actualDevice.capabilitiesObj.alarm_water.lastUpdated);
}
}
//compare all dates and get the most recent
if ((dateCompare < temperatureDate) && (temperatureDate !== 0)) {
dateCompare = temperatureDate
}
if ((dateCompare < pressureDate) && (pressureDate !== 0)) {
dateCompare = pressureDate;
}
if ((dateCompare < humidityDate) && (humidityDate !== 0)) {
dateCompare = humidityDate;
}
if ((dateCompare < luminanceDate) && (luminanceDate !== 0)) {
dateCompare = luminanceDate;
}
if ((dateCompare < alarmBatteryDate) && (alarmBatteryDate !== 0)) {
dateCompare = alarmBatteryDate;
}
if ((dateCompare < alarmContactDate) && (alarmContactDate !== 0)) {
dateCompare = alarmContactDate;
}
if ((dateCompare < alarmHeatDate) && (alarmHeatDate !== 0)) {
dateCompare = alarmHeatDate;
}
if ((dateCompare < alarmMotionDate) && (alarmMotionDate !== 0)) {
dateCompare = alarmMotionDate;
}
if ((dateCompare < alarmSmokeDate) && (alarmMotionDate !== 0)) {
dateCompare = alarmSmokeDate;
}
if ((dateCompare < alarmTamperDate) && (alarmTamperDate !== 0)) {
dateCompare = alarmTamperDate;
}
if ((dateCompare < alarmWaterDate) && (alarmWaterDate !== 0)) {
dateCompare = alarmWaterDate;
}
//chech if last update is older than we want, if it is, log the device in the list
let difference = dateNow - dateCompare;
if (difference > (((hoursSinceLastUpdate * 60) * 60) * 1000)) { //convert millisecond in hour
previousList = nextList;
nextList = previousList + actualDevice.name + ": " + dateCompare.toLocaleString("fr-FR") + "\n\n";
}
}
}
//create a String tag from the list
if (nextList === "") { //tag cant be empty, so if no device returned by the script write it (or what you want)
nextList = "No device was returned by the script";
await tag('No Signal List ', nextList);
return false;
}
else {
await tag('No Signal List ', nextList);
//log(nextList);
return true;
}
Le flow que j’ai mis en place est celui ci:
La carte HomeyScript est une carte ET “Run script with argument”. Elle est à paramétrer avec le nom de votre script et l’argument est le suivant:
{"hoursSinceLastUpdate": 120} // Paramètre obligatoire, 120 (5jrs) correspond au nombre d'heure entre le dernier signal de l'appareil et maintenant
Si vous souhaitez ajouter des appareils à ignorer, ajoutez “whiteList”:
{"hoursSinceLastUpdate": 120, "whiteList": "Les noms de vos appareils séparés par une virgule cette phrase ne fait qu'un appareil, celle ci un autre, encore un..."}
La “whiteList” n’est pas sensible à la casse ni au espace. “Sonde Séjour” ou “sonDE SéJoUr” ou “sondeséjour” fonctionnera. En revanche n’oubliez pas les accents!
Cela ressort ainsi:
Je ne suis pas codeur, ni dans l’informatique, je suis menuisier donc un peu à l’opposé! Alors mon code n’est peut être pas très propre, donc n’hésitez pas à partager toute amélioration.
Si vous souhaitez y ajouter des choses ou fonctionnalités dites le moi je pourrai essayer.
Bonne soirée