Bon j'ai réussit mais non sans mal à intégrer l'affichage de hydrao dans eedomus.
J'ai repris de ton script en modèle et j'ai tout remodifier avec une autre stratégie pour y arriver, dont voici l'explication.
1. Préparation du NAS Synology • Activation de Web Station
o Installation du paquet Web Station.
o Création d’un Virtual Host pointant vers /web/hydrao.
• Terminal & SNMP
o Activation du service SSH pour accéder au NAS en ligne de commande.
o Création de l’utilisateur nino77144 pour les scripts.
2. DNS dynamique & certificat SSL • DynDNS
o Configuration d’un nom de domaine (mon-nas.zapto.org) pour pointer sur l’IP publique.
• Port forwarding
o Redirection du port 80 (HTTP) et 443 (HTTPS) du routeur vers le NAS.
• Certificat Let’s Encrypt
o Obtention d’un certificat SSL via l’interface DSM pour mon-nas.zapto.org.
o Activation du HTTPS dans Web Station.
3. Arborescence & permissions • Création du dossier /web/hydrao et placement de tous les fichiers :
hydrao_proxy.php,
hydrao_tokens.conf,
run_refresh.sh,
refresh_hydrao_token.sh,
hydrao.html,
hydrao_cache.json,
hydrao_tokens.json,
refresh_hydrao.log.
• Droits POSIX :
o Fichiers PHP et JSON servables : http:users 644.
o Scripts bash exécutables : nino77144:users 755.
o JSON de cache/token modifiables par le script : nino77144:users 664.
4. Configuration des tokens (hydrao_tokens.conf) *** SCRIPT - hydrao_tokens.conf ***- Code : Tout sélectionner
bash
Copier le code
API_KEY="…"
REFRESH_TOKEN="…"
TOKEN_URL="https://api.hydrao.com/sessions"
STORAGE_FILE="/volume3/web/hydrao/hydrao_tokens.json"
USER_EMAIL="ton-email@example.com"
5. Script de rafraîchissement (refresh_hydrao_token.sh)
• Lit le token actuel et son fetched_at/expires_in.
• Ne relance l’API que si le token expire dans moins de 60 s.
• Appelle POST /sessions pour obtenir un nouveau token.
• Injecte fetched_at dans le JSON et remet les droits chown/chmod.
• Lance immédiatement une requête vers /user-stats pour préremplir le cache.
*** SCRIPT - refresh_hydrao_token.sh ***
- Code : Tout sélectionner
#!/usr/bin/env bash
set -euo pipefail
CONFIG="$(dirname "$0")/hydrao_tokens.conf"
STORAGE="$(dirname "$0")/hydrao_tokens.json"
CACHE="$(dirname "$0")/hydrao_cache.json"
LOG="$(dirname "$0")/refresh_hydrao.log"
ME="nino77144:users" # utilisateur:group pour chown
echo "=== Refresh start $(date '+%F %T') ===" >> "$LOG"
# 1) Charger la config source "$CONFIG"
# 2) Lire fetched_at & expires_in (0 si manquant)
if jq -e '.fetched_at and .expires_in' "$STORAGE" &>/dev/null; then
fetched_at=$(jq -r '.fetched_at' "$STORAGE")
expires_in=$(jq -r '.expires_in' "$STORAGE")
else
fetched_at=0
expires_in=0
fi
now=$(date +%s)
# 3) Si le token reste valable encore > 60 s, on skip
if (( now + 60 < fetched_at + expires_in )); then
echo "[ $(date '+%F %T') ] Skip refresh: token still valid." >> "$LOG"
echo "=== Refresh end $(date '+%F %T') ===" >> "$LOG"
exit 0
fi
# 4) Rafraîchir le token
TMP=$(mktemp)
http_code=$(
curl -s -X POST "$TOKEN_URL" \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"grant_type\":\"refresh_token\",\"refresh_token\":\"$REFRESH_TOKEN\",\"email\":\"$USER_EMAIL\"}" \
-o "$TMP" \
-w "%{http_code}"
)
if [[ "$http_code" == "200" ]] && jq -e '.access_token' "$TMP" &>/dev/null; then
# injecter fetched_at
jq --arg now "$now" '. + {fetched_at:($now|tonumber)}' "$TMP" > "${TMP}.2"
mv -f "${TMP}.2" "$STORAGE"
chown $ME "$STORAGE" && chmod 664 "$STORAGE"
echo "[ $(date '+%F %T') ] Token rafraîchi avec succès." >> "$LOG"
# 5) Mettre à jour le cache seulement si HTTP 200
ACCESS_TOKEN=$(jq -r .access_token "$STORAGE")
TMP2=$(mktemp)
stats_code=$(
curl -s 'https://api.hydrao.com/user-stats' \
-H "x-api-key: $API_KEY" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-o "$TMP2" \
-w "%{http_code}"
)
if [[ "$stats_code" == "200" ]]; then
mv -f "$TMP2" "$CACHE"
chown $ME "$CACHE" && chmod 664 "$CACHE"
echo "[ $(date '+%F %T') ] Stats mises en cache." >> "$LOG"
else
echo "[ $(date '+%F %T') ] Stat cache skipped (HTTP $stats_code)." >> "$LOG"
rm -f "$TMP2"
fi
else
echo "[ $(date '+%F %T') ] Échec refresh (HTTP $http_code)." >> "$LOG"
cat "$TMP" >> "$LOG"
rm -f "$TMP"
fi
echo "=== Refresh end $(date '+%F %T') ===" >> "$LOG"
#!/usr/bin/env bash
set -euo pipefail
CONFIG="$(dirname "$0")/hydrao_tokens.conf"
STORAGE="$(dirname "$0")/hydrao_tokens.json"
CACHE="$(dirname "$0")/hydrao_cache.json"
LOG="$(dirname "$0")/refresh_hydrao.log"
ME="nino77144:users" # utilisateur:group pour chown
echo "=== Refresh start $(date '+%F %T') ===" >> "$LOG"
# 1) Charger la config
source "$CONFIG"
# 2) Lire fetched_at & expires_in (0 si manquant)
if jq -e '.fetched_at and .expires_in' "$STORAGE" &>/dev/null; then
fetched_at=$(jq -r '.fetched_at' "$STORAGE")
expires_in=$(jq -r '.expires_in' "$STORAGE")
else
fetched_at=0
expires_in=0
fi
now=$(date +%s)
# 3) Si le token reste valable encore > 60 s, on skip
if (( now + 60 < fetched_at + expires_in )); then
echo "[ $(date '+%F %T') ] Skip refresh: token still valid." >> "$LOG"
echo "=== Refresh end $(date '+%F %T') ===" >> "$LOG"
exit 0
fi
# 4) Rafraîchir le token
TMP=$(mktemp)
http_code=$(
curl -s -X POST "$TOKEN_URL" \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"grant_type\":\"refresh_token\",\"refresh_token\":\"$REFRESH_TOKEN\",\"email\":\"$USER_EMAIL\"}" \
-o "$TMP" \
-w "%{http_code}"
)
if [[ "$http_code" == "200" ]] && jq -e '.access_token' "$TMP" &>/dev/null; then
# injecter fetched_at
jq --arg now "$now" '. + {fetched_at:($now|tonumber)}' "$TMP" > "${TMP}.2"
mv -f "${TMP}.2" "$STORAGE"
chown $ME "$STORAGE" && chmod 664 "$STORAGE"
echo "[ $(date '+%F %T') ] Token rafraîchi avec succès." >> "$LOG"
# 5) Mettre à jour le cache seulement si HTTP 200
ACCESS_TOKEN=$(jq -r .access_token "$STORAGE")
TMP2=$(mktemp)
stats_code=$(
curl -s 'https://api.hydrao.com/user-stats' \
-H "x-api-key: $API_KEY" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-o "$TMP2" \
-w "%{http_code}"
)
if [[ "$stats_code" == "200" ]]; then
mv -f "$TMP2" "$CACHE"
chown $ME "$CACHE" && chmod 664 "$CACHE"
echo "[ $(date '+%F %T') ] Stats mises en cache." >> "$LOG"
else
echo "[ $(date '+%F %T') ] Stat cache skipped (HTTP $stats_code)." >> "$LOG"
rm -f "$TMP2"
fi
else
echo "[ $(date '+%F %T') ] Échec refresh (HTTP $http_code)." >> "$LOG"
cat "$TMP" >> "$LOG"
rm -f "$TMP"
fi
echo "=== Refresh end $(date '+%F %T') ===" >> "$LOG"
6. Wrapper de log et dry-run (run_refresh.sh) *** SCRIPT - run_refresh.sh ***
- Code : Tout sélectionner
bash
Copier le code
#!/usr/bin/env bash
LOG="$(dirname "$0")/refresh_hydrao.log"
echo "=== START $(date) ===" >> "$LOG"
[ "$1" = "--dry-run" ] && echo "[…] DRY RUN" >> "$LOG" && exit
bash "$(dirname "$0")/refresh_hydrao_token.sh" >> "$LOG" 2>&1
echo "=== END $(date) ===" >> "$LOG"
7. Tâche planifiée DSM7• Création d’une tâche “Script” sous l’utilisateur nino77144.
• Commande :
/web/hydrao/run_refresh.sh >>
/web/hydrao/refresh_hydrao.log 2>&1
• Programmation : exécution toutes les 4 heures.
8. Proxy PHP + mise en cache (hydrao_proxy.php) *** SCRIPT - hydrao_proxy.php ***- Code : Tout sélectionner
php
Copier le code
// 1) CORS + JSON
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
// 2) Lecture du token
$cfg = json_decode(file_get_contents(__DIR__.'/hydrao_tokens.json'), true);
if (empty($cfg['access_token'])) { http_response_code(500); echo json_encode(['error'=>'no_token']); exit; }
$token = $cfg['access_token'];
// 3) Clé API
$apikey = $_GET['apikey'] ?? $API_KEY_PAR_DEFAUT;
// 4) Appel user-stats
$cache = __DIR__.'/hydrao_cache.json';
$ch = curl_init('https://api.hydrao.com/user-stats');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER=>true,
CURLOPT_HTTPHEADER=>[
"x-api-key: $apikey",
"Authorization: Bearer $token"
],
CURLOPT_TIMEOUT=>5,
]);
$resp = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// 5) Succès → mise à jour du cache
if ($http === 200) {
file_put_contents($cache, $resp, LOCK_EX);
http_response_code(200);
echo $resp;
exit;
}
// 6) Erreur → fallback cache
if (is_readable($cache)) {
http_response_code(200);
echo file_get_contents($cache);
exit;
}
// 7) Sinon renvoi de l’erreur
http_response_code($http);
echo $resp;
9. Page HTML (hydrao.html) • Deux <img> côte à côte pour hydrao.png + hydrao_logo.png avec max-height ajusté (ex. 50px).


• Zone de chargement, puis fetch() sur le proxy, fallback sur hydrao_cache.json.
• Affichage de la conso moyenne, durée, économies, etc.
• Sélecteur pour 10/100/500 dernières douches et affichage dynamique.
*** SCRIPT - hydrao.html ***- Code : Tout sélectionner
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Hydrao Dashboard</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
.logo-container {
display: flex;
justify-content: center;
align-items: center;
margin: 20px 0;
gap: 20px;
}
/* Pommeau Hydrao réduit */
.logo-container img:first-child {
max-height: 50px;
display: block;
}
/* Logo Hydrao encore plus petit */
.logo-container img:last-child {
max-height: 20px;
display: block;
}
.wrapper {
display: flex;
flex-direction: column;
height: 270px;
text-align: center;
}
#body { display: none; }
</style>
</head>
<body>
<div class="logo-container">
<img src="hydrao.png" alt="Pommeau Hydrao">
<img src="hydrao_logo.png" alt="Logo Hydrao">
</div>
<div class="wrapper">
<div id="msg" style="font-size:larger; margin-top:20px;">
Chargement en cours…
</div>
<div id="body">
<p>
Mes
<select id="mySelect" onchange="mySelectF()">
<option value="10" selected>10</option>
<option value="100">100</option>
<option value="500">500</option>
</select>
dernières douches
</p>
</div>
<div id="result" style="flex:1; overflow:auto;"></div>
<p id="result2" style="margin:0;"></p>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
document.getElementById('msg').style.display = 'none';
document.getElementById('body').style.display = 'block';
}, 1200);
const apikey = new URLSearchParams(location.search).get('apikey');
const proxyUrl = `hydrao_proxy.php?apikey=${apikey}`;
const cacheUrl = `hydrao_cache.json`;
function render(userStat) {
const avgVol = userStat.average_volume;
const avgDur = userStat.average_duration;
const m = Math.floor(avgDur.value/60), s = avgDur.value%60;
const finalTime = String(m).padStart(2,'') + ':' + String(s).padStart(2,'0');
const colorV = avgVol.trend>0 ? 'red' : 'green';
const arrowV = avgVol.trend>0 ? '↑' : '↓';
const colorD = avgDur.trend>0 ? 'red' : 'green';
const arrowD = avgDur.trend>0 ? '↑' : '↓';
document.getElementById('result').innerHTML = `
<table align="center"><tr>
<td>
<div><font size="+3">${avgVol.value}</font> L</div>
<div><font color="${colorV}">${arrowV}${(avgVol.trend*100).toFixed(1)}%</font></div>
<div>Conso moyenne</div>
</td>
<td style="border-left:1px dotted #000; padding-left:10px;">
<div><font size="+3">${finalTime}</font></div>
<div><font color="${colorD}">${arrowD}${(avgDur.trend*100).toFixed(1)}%</font></div>
<div>Durée moyenne</div>
</td>
</tr></table>`;
window.vartxtab = {};
userStat.savings.forEach(item => {
let volSave = item.saved_volume, unit='LITRES';
if (volSave > 99) {
volSave = (volSave/1000).toFixed(1);
unit = 'M³';
}
window.vartxtab[item.nb_showers] = `
<table align="center"><tr>
<td><font size="+2">${item.saved_money}</font> €<br><small>Économies</small></td>
<td><font size="+2">${volSave}</font> ${unit}<br><small>Eco. Eau</small></td>
<td><font size="+2">${item.saved_energy.toFixed(0)}</font> KWH<br><small>Eco. Énergie</small></td>
</tr></table>`;
});
document.getElementById('result2').innerHTML = window.vartxtab[10]||'';
}
fetch(proxyUrl)
.then(r => {
if (!r.ok) throw new Error('proxy');
return r.json();
})
.catch(_ =>
fetch(cacheUrl).then(r => {
if (!r.ok) throw new Error('cache');
return r.json();
})
)
.then(render)
.catch(err => {
console.error('Hydrao error:', err);
document.getElementById('msg').innerText = 'Impossible de récupérer les données.';
});
});
function mySelectF(){
const n = document.getElementById('mySelect').value;
document.getElementById('result2').innerHTML = window.vartxtab[n] || '';
}
</script>
</body>
</html>
10. Intégration eedomus • Création d’un périphérique “http Capteur” dans l’interface eedomus.
• URL pointant vers
https://mon-nas.zapto.org/hydrao/hydrao.html?apikey=….
• La page se recharge automatiquement, et en cas de jeton expiré, elle utilise le cache.
• Hauteur d'affichage 6 unitées
Voilà le résultat