🌩 WIKI : Station mĂ©tĂ©o Netatmo sur IPX800 V4

Bonjour,
DĂ©solĂ© j’ai complĂštement mis en pause le sujet pour d’autres prioritĂ©s
 :roll_eyes:
Si quelqu’un reprend, je peux fournir au besoin ce que j’ai dĂ©jĂ  dĂ©boguĂ©.
Pour les plus patients, je me remettrai surement un jour dessus.
Bonne continuation.
Jon

Bonjour,

Je vais me mettre en premier sur la liste des plus patients alors ! :wink:

Bonne journée
Patrice

2 « J'aime »

Salut les gars
Bon j’ai enfin rĂ©ussi a obtenir quelque chose de stable avec un token Netatmo qui se raffraichit. Merci ChatgGPT.
Sur le serveur Web de mon NAS j’ai mis 3 fichiers : Le scrpit PHP, un fichier tokens.json et un fichier NetatmoFic.json
Pour le premier lancement de la routine je récupÚre le token sur le site de Netatmo et je le colle dans le fichier tokens.json, avec le refresh token. Ensuite cela se met à jour tout seul.

tokens.json :

{"scope":["read_station"],"access_token":"63bdc885d0e552e0e60995a0|d8bf432010ae1f06630e174cc00cc979","refresh_token":"63bdc885d0e552e0e60995a0|339c3a04a7d61f82259d695c34e349bb","expires_in":10800,"expire_in":10800}

NetatmoFic.json

{"body":{"modules":[{"_id":"02:00:00:a2:39:38","main_device":"70:ee:50:9c:db:40","type":"NAModule1","data_type":["Temperature","Humidity"],"module_name":"Ext\u00e9rieur","firmware":53,"last_message":1719767900,"last_seen":1719767887,"rf_status":71,"battery_vp":5310,"dashboard_data":{"time_utc":1719767887,"Temperature":24.7,"Humidity":66,"min_temp":20.5,"max_temp":24.9,"date_max_temp":1719763324,"date_min_temp":1719725897,"temp_trend":"stable"},"date_setup":{"sec":1674312392,"usec":0}},{"_id":"03:00:00:0d:06:92","main_device":"70:ee:50:9c:db:40","type":"NAModule4","data_type":["Temperature","CO2","Humidity"],"module_name":"Bas","firmware":53,"last_message":1719767900,"last_seen":1719767900,"rf_status":49,"battery_vp":5976,"dashboard_data":{"time_utc":1719767848,"Temperature":26.1,"CO2":658,"Humidity":66,"min_temp":20.9,"max_temp":26.4,"date_max_temp":1719759390,"date_min_temp":1719715042,"temp_trend":"stable"},"date_setup":{"sec":1674315041,"usec":0}},{"_id":"06:00:00:06:8e:70","main_device":"70:ee:50:9c:db:40","type":"NAModule2","data_type":["Wind"],"module_name":"An\u00e9mom\u00e8tre","firmware":27,"last_message":1719767900,"last_seen":1719767900,"rf_status":72,"battery_vp":5636,"dashboard_data":{"time_utc":1719767893,"WindStrength":4,"WindAngle":261,"GustStrength":15,"GustAngle":238,"max_wind_str":19,"max_wind_angle":308,"date_max_wind_str":1719746661},"date_setup":{"sec":1674390316,"usec":0}},{"_id":"05:00:00:0a:5f:86","main_device":"70:ee:50:9c:db:40","type":"NAModule3","data_type":["Rain"],"module_name":"Pluviom\u00e8tre","firmware":14,"last_message":1719767900,"last_seen":1719767893,"rf_status":75,"battery_vp":5312,"dashboard_data":{"time_utc":1719767893,"Rain":0,"sum_rain_1":0,"sum_rain_24":0},"date_setup":{"sec":1674394512,"usec":0}},{"_id":"03:00:00:0e:7c:c8","main_device":"70:ee:50:9c:db:40","type":"NAModule4","data_type":["Temperature","CO2","Humidity"],"module_name":"Cellier","firmware":53,"last_message":1719765689,"last_seen":1719765682,"rf_status":95,"battery_vp":5512,"date_setup":{"sec":1705504846,"usec":0}},{"_id":"03:00:00:0e:74:a2","main_device":"70:ee:50:9c:db:40","type":"NAModule4","data_type":["Temperature","CO2","Humidity"],"module_name":"Serre","firmware":53,"last_message":1719767900,"last_seen":1719767900,"rf_status":72,"battery_vp":5603,"dashboard_data":{"time_utc":1719767848,"Temperature":34.1,"CO2":623,"Humidity":40,"min_temp":19.4,"max_temp":37.4,"date_max_temp":1719755391,"date_min_temp":1719724987,"temp_trend":"down"},"date_setup":{"sec":1705590549,"usec":0}}],"devices":[{"_id":"70:ee:50:9c:db:40","type":"NAMain","co2_calibrating":false,"date_setup":{"sec":1674312390,"usec":0},"firmware":204,"last_status_store":1719767901,"module_name":"Etage","station_name":"Les Colibris (Etage)","place":{"altitude":104,"city":"Salon de Provence","country":"FR","timezone":"Europe\/Paris","location":[5.129619,43.63725]},"wifi_status":43,"data_type":["Temperature","CO2","Humidity","Noise","Pressure"],"dashboard_data":{"time_utc":1719767899,"Temperature":29.5,"CO2":553,"Humidity":54,"Noise":37,"Pressure":1012.9,"AbsolutePressure":1000.5,"min_temp":23.3,"max_temp":29.6,"date_max_temp":1719763358,"date_min_temp":1719715076,"temp_trend":"stable","pressure_trend":"stable"},"modules":["02:00:00:a2:39:38","03:00:00:0d:06:92","06:00:00:06:8e:70","05:00:00:0a:5f:86","03:00:00:0e:7c:c8","03:00:00:0e:74:a2"]}]},"status":"ok","time_exec":0.09278488159179688,"time_server":1719768002}

Et le sript PHP avec gestion des erreurs :

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

$CLIENT_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$CLIENT_SECRET = "yyyyyyyyyyyyyyyyyyyyyyyyyy";
$IP_IPX800 = "192.168.1.250";
$API_key = "apikey";
$TOKENS_FILE = '/volume1/web/Netatmo/tokens.json';
$OUTPUT_FILE = '/volume1/web/Netatmo/NetatmoFic.json';

// Fonction pour lire les tokens depuis le fichier
function read_tokens($file) {
    if (!file_exists($file)) {
        die("Fichier des tokens non trouvé.");
    }
    $tokensJson = file_get_contents($file);
    $tokens = json_decode($tokensJson, true);
    if ($tokens === null) {
        die("Erreur lors de la lecture du fichier des tokens.");
    }
    return $tokens;
}

// Fonction pour écrire les tokens dans le fichier
function write_tokens($file, $tokens) {
    file_put_contents($file, json_encode($tokens));
}

// Fonction pour rafraĂźchir les tokens
function refresh_tokens($refresh_token, $client_id, $client_secret) {
    $refreshApiUrl = "https://api.netatmo.com/oauth2/token";
    $refreshData = [
        'grant_type' => 'refresh_token',
        'refresh_token' => $refresh_token,
        'client_id' => $client_id,
        'client_secret' => $client_secret,
    ];

    $options = [
        'http' => [
            'method' => 'POST',
            'header' => 'Content-Type: application/x-www-form-urlencoded',
            'content' => http_build_query($refreshData),
        ],
    ];

    $context = stream_context_create($options);
    $refreshResponse = file_get_contents($refreshApiUrl, false, $context);

    if ($refreshResponse === false) {
        $error = error_get_last();
        die("Erreur lors du rafraĂźchissement du token d'accĂšs: " . $error['message']);
    }

    $newTokens = json_decode($refreshResponse, true);

    if (!isset($newTokens['access_token']) || !isset($newTokens['refresh_token'])) {
        die("Réponse invalide lors du rafraßchissement du token d'accÚs.");
    }

    return $newTokens;
}

// Fonction pour récupérer la liste des périphériques
function fetch_device_list($access_token) {
    $url = "https://api.netatmo.net/api/devicelist?access_token=$access_token";
    $response = file_get_contents($url);
    if ($response === false) {
        $error = error_get_last();
        die("Erreur lors de la récupération de la liste des périphériques: " . $error['message']);
    }
    return json_decode($response, true);
}

// Lire les tokens
$tokens = read_tokens($TOKENS_FILE);
$ACCESS_TOKEN = $tokens['access_token'];
$REFRESH_TOKEN = $tokens['refresh_token'];

// Récupérer la liste des périphériques
$json_devices = fetch_device_list($ACCESS_TOKEN);

if ($json_devices === false || !isset($json_devices['body'])) {
    // Si la récupération échoue, rafraßchir les tokens et réessayer
    $tokens = refresh_tokens($REFRESH_TOKEN, $CLIENT_ID, $CLIENT_SECRET);
    write_tokens($TOKENS_FILE, $tokens);
    $ACCESS_TOKEN = $tokens['access_token'];

    // Réessayer de récupérer la liste des périphériques
    $json_devices = fetch_device_list($ACCESS_TOKEN);
    if ($json_devices === false || !isset($json_devices['body'])) {
        die("Échec de la rĂ©cupĂ©ration de la liste des pĂ©riphĂ©riques aprĂšs le rafraĂźchissement du token.");
    }
}

// Enregistrer la liste des périphériques dans un fichier
if (file_put_contents($OUTPUT_FILE, json_encode($json_devices)) === false) {
    die("Erreur lors de l'écriture du fichier $OUTPUT_FILE.");
}
echo "C'est gagné\n";

// Fonction pour calculer le niveau de batterie
function Batterie1($valeur) {
    $T1 = [0, 10, 25, 50, 75, 100];
    $valeur = intval(0.002 * $valeur - 6);
    return ($valeur >= 5) ? 5 : (($valeur < 1) ? 1 : $valeur);
}

function Batterie2($valeur) {
    $T2 = [0, 10, 25, 50, 75, 100];
    $valeur = intval(0.0024 * $valeur - 8.6341);
    return ($valeur >= 5) ? 5 : (($valeur < 1) ? 1 : $valeur);
}

// Extraire et préparer les données pour le push
$ext_temp = floatval($json_devices["body"]["modules"][0]["dashboard_data"]["Temperature"]) * 10 + 1000;
$ext_temp_min = floatval($json_devices["body"]["modules"][0]["dashboard_data"]["min_temp"]) * 10 + 1000;
$ext_temp_max = floatval($json_devices["body"]["modules"][0]["dashboard_data"]["max_temp"]) * 10 + 1000;
$ext_humidite = floatval($json_devices["body"]["modules"][0]["dashboard_data"]["Humidity"]) * 10 + 1000;
$ext_battery = Batterie1(floatval($json_devices["body"]["modules"][0]["battery_vp"]));

$M1_temp = floatval($json_devices["body"]["modules"][1]["dashboard_data"]["Temperature"]) * 10;
$M1_temp_min = floatval($json_devices["body"]["modules"][1]["dashboard_data"]["min_temp"]) * 10;
$M1_temp_max = floatval($json_devices["body"]["modules"][1]["dashboard_data"]["max_temp"]) * 10;
$M1_humidite = floatval($json_devices["body"]["modules"][1]["dashboard_data"]["Humidity"]) * 10;
$M1_battery = Batterie2(floatval($json_devices["body"]["modules"][1]["battery_vp"]));
$M1_CO2 = floatval($json_devices["body"]["modules"][1]["dashboard_data"]["CO2"]);

$M3_Rain = floatval($json_devices["body"]["modules"][3]["dashboard_data"]["Rain"]) * 1000;
$M3_Rain_24 = floatval($json_devices["body"]["modules"][3]["dashboard_data"]["sum_rain_24"]) * 1000;
$M3_Rain_1 = floatval($json_devices["body"]["modules"][3]["dashboard_data"]["sum_rain_1"]) * 1000;
$M3_battery = Batterie1(floatval($json_devices["body"]["modules"][3]["battery_vp"]));

$M5_Wind = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["WindStrength"]);
$M5_Wind_A = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["WindAngle"]);
$M5_Gust = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["GustStrength"]);
$M5_Gust_A = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["GustAngle"]);
$M5_Wind_Max = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["max_wind_str"]);
$M5_Wind_Max_A = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["max_wind_angle"]);
$M5_battery = Batterie2(floatval($json_devices["body"]["modules"][2]["battery_vp"]));

if ($M5_Wind < 0) $M5_Wind = 0;
if ($M5_Wind_A < 0) $M5_Wind_A = 0;

$MS_temp = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["Temperature"]) * 10;
$MS_temp_min = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["min_temp"]) * 10;
$MS_temp_max = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["max_temp"]) * 10;
$MS_humidite = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["Humidity"]) * 10;
$MS_CO2 = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["CO2"]);
$MS_Pressure = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["Pressure"]) * 10;
$MS_Noise = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["Noise"]) * 10;

$URL_Push = "http://$IP_IPX800/api/xdevices.json?key=$API_key"
    . "&SetVA01=$ext_temp&SetVA02=$ext_temp_min&SetVA03=$ext_temp_max"
    . "&SetVA04=$ext_humidite&SetVA05=$ext_battery&SetVA06=$M1_temp"
    . "&SetVA07=$M1_temp_min&SetVA08=$M1_temp_max&SetVA09=$M1_humidite"
    . "&SetVA10=$M1_battery&SetVA11=$M1_CO2&SetVA18=$M3_Rain"
    . "&SetVA19=$M3_Rain_24&SetVA20=$M3_Rain_1&SetVA21=$M3_battery"
    . "&SetVA22=$MS_temp&SetVA23=$MS_temp_min&SetVA24=$MS_temp_max"
    . "&SetVA25=$MS_humidite&SetVA26=$MS_CO2&SetVA27=$MS_Pressure"
    . "&SetVA28=$MS_Noise&SetVA29=$M5_Wind&SetVA30=$M5_Wind_A"
    . "&SetVA31=$M5_Gust&SetVA32=$M5_battery";

// Affichage de l'URL générée
echo $URL_Push . "<br>";

// Envoi des données à l'API
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $URL_Push);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$data = curl_exec($ch);
curl_close($ch);


// Fonction pour enregistrer des logs
function write_log($message) {
    $log_file = '/volume1/web/Netatmo/Netatmo.log';
    $date = date('Y-m-d H:i:s');
    file_put_contents($log_file, "[$date] $message\n", FILE_APPEND);
}

// Exemple d'utilisation des logs
write_log("Début du script");

// Lire les tokens
$tokens = read_tokens($TOKENS_FILE);
$ACCESS_TOKEN = $tokens['access_token'];
$REFRESH_TOKEN = $tokens['refresh_token'];
write_log("Tokens lus avec succĂšs");

// Récupérer la liste des périphériques
$json_devices = fetch_device_list($ACCESS_TOKEN);
write_log("Liste des périphériques récupérée");

// Enregistrer la liste des périphériques dans un fichier
if (file_put_contents($OUTPUT_FILE, json_encode($json_devices)) === false) {
    write_log("Erreur lors de l'écriture du fichier $OUTPUT_FILE.");
    die("Erreur lors de l'écriture du fichier $OUTPUT_FILE.");
}
write_log("Liste des périphériques enregistrée dans $OUTPUT_FILE");

// ... (le reste du script)

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $URL_Push);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$data = curl_exec($ch);
curl_close($ch);
write_log("Données envoyées à l'API IPX800: " . $URL_Push);

write_log("Fin du script");

?>
2 « J'aime »

La derniĂšre partie du scrip enregistre les logs dans un fichier « Netatmo.log Â»

[2024-07-01 11:50:02] Début du script
[2024-07-01 11:50:02] Tokens lus avec succĂšs
[2024-07-01 11:50:02] Liste des périphériques récupérée
[2024-07-01 11:50:02] Liste des périphériques enregistrée dans /volume1/web/Netatmo/NetatmoFic.json
[2024-07-01 11:50:02] Données envoyées à l'API IPX800: http://192.168.1.250/api/xdevices.json?key=apikey&SetVA01=1232&SetVA02=1207&SetVA03=1232&SetVA04=1650&SetVA05=4&SetVA06=238&SetVA07=223&SetVA08=238&SetVA09=650&SetVA10=5&SetVA11=671&SetVA18=0&SetVA19=0&SetVA20=0&SetVA21=4&SetVA22=256&SetVA23=244&SetVA24=256&SetVA25=560&SetVA26=491&SetVA27=10124&SetVA28=440&SetVA29=4&SetVA30=315&SetVA31=12&SetVA32=4
[2024-07-01 11:50:02] Fin du script
2 « J'aime »

Avec le Refresh Token automatique et l’écriture des logs :

$CLIENT_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxx";
$CLIENT_SECRET = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
$IP_IPX800 = "192.168.1.250";
$API_key = "apikey";
$TOKENS_FILE = '/volume1/web/Netatmo/tokens.json';
$LOG_FILE = '/volume1/web/Netatmo/script.log';

// Fonction pour écrire des logs
function log_message($message) {
    global $LOG_FILE;
    error_log(date('[Y-m-d H:i:s] ') . $message . PHP_EOL, 3, $LOG_FILE);
}

// Lire les tokens depuis le fichier
function read_tokens($file) {
    log_message("Lecture des tokens depuis le fichier $file.");
    $tokensJson = file_get_contents($file);
    return json_decode($tokensJson, true);
}

// Écrire les tokens dans le fichier
function write_tokens($file, $tokens) {
    log_message("Écriture des tokens dans le fichier $file.");
    file_put_contents($file, json_encode($tokens));
}

// RafraĂźchir le token d'accĂšs
function refresh_access_token($refresh_token, $client_id, $client_secret) {
    log_message("RafraĂźchissement du token d'accĂšs.");
    $refreshApiUrl = "https://api.netatmo.com/oauth2/token";
    $refreshData = [
        'grant_type' => 'refresh_token',
        'refresh_token' => $refresh_token,
        'client_id' => $client_id,
        'client_secret' => $client_secret,
    ];

    $options = [
        'http' => [
            'method' => 'POST',
            'header' => 'Content-Type: application/x-www-form-urlencoded',
            'content' => http_build_query($refreshData),
        ],
    ];

    $context = stream_context_create($options);
    $refreshResponse = file_get_contents($refreshApiUrl, false, $context);
    if ($refreshResponse === false) {
        log_message("Erreur lors du rafraĂźchissement du token d'accĂšs.");
        die("Erreur lors du rafraĂźchissement du token d'accĂšs.");
    }

    $newTokens = json_decode($refreshResponse, true);
    log_message("Token d'accĂšs rafraĂźchi avec succĂšs.");
    return $newTokens;
}

// Vérifier l'expiration du token et rafraßchir si nécessaire
function get_valid_access_token($tokens, $client_id, $client_secret) {
    $expires_in = $tokens['expires_in'];
    $last_refreshed = $tokens['last_refreshed'];
    $current_time = time();

    // Vérifier si le token d'accÚs a expiré
    if ($current_time > $last_refreshed + $expires_in) {
        log_message("Token d'accÚs expiré, rafraßchissement nécessaire.");
        $tokens = refresh_access_token($tokens['refresh_token'], $client_id, $client_secret);
        $tokens['last_refreshed'] = time();
        write_tokens($GLOBALS['TOKENS_FILE'], $tokens);
    }

    return $tokens['access_token'];
}

// Initialisation
log_message("Début de l'exécution du script.");
$tokens = read_tokens($TOKENS_FILE);
$tokens['last_refreshed'] = $tokens['last_refreshed'] ?? time();
$ACCESS_TOKEN = get_valid_access_token($tokens, $CLIENT_ID, $CLIENT_SECRET);

// Appel de l'API Netatmo pour récupérer la liste des périphériques
$response = file_get_contents("https://api.netatmo.net/api/devicelist?access_token=$ACCESS_TOKEN");

// Vérifier si la réponse contient "body"
if (strpos($response, 'body') !== false) {
    file_put_contents('/volume1/web/Netatmo/NetatmoFic.json', $response);
    log_message("Récupération de la liste des périphériques réussie.");
    echo "C'est gagné\n";
} else {
    log_message("Erreur lors de la récupération de la liste des périphériques.");
    die("Erreur lors de la récupération de la liste des périphériques.");
}

// Tentative de PUSH sur IPX800V4
$json_devices = json_decode($response, true);

// Ajouter des logs pour chaque module et périphérique traité
function Batterie1($valeur) {
    log_message("Calcul de la batterie pour Batterie1 avec valeur $valeur.");
    $T1 = array(0, 10, 25, 50, 75, 100);
    $valeur = intval(0.002 * $valeur - 6);
    if ($valeur >= 5) {
        $valeur = 5;
    } else if ($valeur < 1) {
        $valeur = 1;
    }
    return $T1[$valeur];
}

function Batterie2($valeur) {
    log_message("Calcul de la batterie pour Batterie2 avec valeur $valeur.");
    $T2 = array(0, 10, 25, 50, 75, 100);
    $valeur = intval(0.0024 * $valeur - 8.6341);
    if ($valeur >= 5) {
        $valeur = 5;
    } else if ($valeur < 1) {
        $valeur = 1;
    }
    return $T2[$valeur];
}

// Module 0 (Terrasse)
log_message("Traitement du module 0 (Terrasse).");
$ext_temp = floatval($json_devices["body"]["modules"][0]["dashboard_data"]["Temperature"]) * 10 + 1000;
$ext_temp_min = floatval($json_devices["body"]["modules"][0]["dashboard_data"]["min_temp"]) * 10 + 1000;
$ext_temp_max = floatval($json_devices["body"]["modules"][0]["dashboard_data"]["max_temp"]) * 10 + 1000;
$ext_humidite = floatval($json_devices["body"]["modules"][0]["dashboard_data"]["Humidity"]) * 10 + 1000;
$ext_battery = Batterie1(floatval($json_devices["body"]["modules"][0]["battery_vp"]));

// Module 1 (Intérieur Bas)
log_message("Traitement du module 1 (Intérieur Bas).");
$M1_temp = floatval($json_devices["body"]["modules"][1]["dashboard_data"]["Temperature"]) * 10;
$M1_temp_min = floatval($json_devices["body"]["modules"][1]["dashboard_data"]["min_temp"]) * 10;
$M1_temp_max = floatval($json_devices["body"]["modules"][1]["dashboard_data"]["max_temp"]) * 10;
$M1_humidite = floatval($json_devices["body"]["modules"][1]["dashboard_data"]["Humidity"]) * 10;
$M1_battery = Batterie2(floatval($json_devices["body"]["modules"][1]["battery_vp"]));
$M1_CO2 = floatval($json_devices["body"]["modules"][1]["dashboard_data"]["CO2"]);

// Module 3 : Pluie
log_message("Traitement du module 3 (Pluie).");
$M3_Rain = floatval($json_devices["body"]["modules"][3]["dashboard_data"]["Rain"]) * 1000;
$M3_Rain_24 = floatval($json_devices["body"]["modules"][3]["dashboard_data"]["sum_rain_24"]) * 1000;
$M3_Rain_1 = floatval($json_devices["body"]["modules"][3]["dashboard_data"]["sum_rain_1"]) * 1000;
$M3_battery = Batterie1(floatval($json_devices["body"]["modules"][3]["battery_vp"]));

// Module 2 : Vent
log_message("Traitement du module 2 (Vent).");
$M5_Wind = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["WindStrength"]); // vitesse du vent
$M5_Wind_A = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["WindAngle"]); // angle du vent
$M5_Gust = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["GustStrength"]); // vitesse rafale
$M5_Gust_A = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["GustAngle"]); // angle rafale
$M5_Wind_Max = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["max_wind_str"]); // vitesse max
$M5_Wind_Max_A = floatval($json_devices["body"]["modules"][2]["dashboard_data"]["max_wind_angle"]); // angle max
$M5_battery = Batterie2(floatval($json_devices["body"]["modules"][2]["battery_vp"]));

if ($M5_Wind < 0) { $M5_Wind = 0; }
if ($M5_Wind_A < 0) { $M5_Wind_A = 0; }

// Device 0 Station Etage
log_message("Traitement du device 0 (Station Etage).");
$MS_temp = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["Temperature"]) * 10;
$MS_temp_min = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["min_temp"]) * 10;
$MS_temp_max = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["max_temp"]) * 10;
$MS_humidite = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["Humidity"]) * 10;
$MS_CO2 = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["CO2"]);
$MS_Pressure = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["Pressure"]) * 10;
$MS_Noise = floatval($json_devices["body"]["devices"][0]["dashboard_data"]["Noise"]) * 10;

// PUSH IPX800V4
log_message("Envoi des données à l'IPX800V4.");
$URL_Push = "http://" . $IP_IPX800 . "/api/xdevices.json?key=" . $API_key . "&SetVA01=" . $ext_temp . "&SetVA02=" . $ext_temp_min .
    "&SetVA03=" . $ext_temp_max . "&SetVA04=" . $ext_humidite . "&SetVA05=" . $ext_battery . "&SetVA06=" . $M1_temp .
    "&SetVA07=" . $M1_temp_min . "&SetVA08=" . $M1_temp_max . "&SetVA09=" . $M1_humidite . "&SetVA10=" . $M1_battery .
    "&SetVA11=" . $M1_CO2 . "&SetVA18=" . $M3_Rain . "&SetVA19=" . $M3_Rain_24 . "&SetVA20=" . $M3_Rain_1 . "&SetVA21=" . $M3_battery .
    "&SetVA22=" . $M5_Wind . "&SetVA23=" . $M5_Wind_A . "&SetVA24=" . $M5_Gust . "&SetVA25=" . $M5_Gust_A . "&SetVA26=" . $M5_Wind_Max .
    "&SetVA27=" . $M5_Wind_Max_A . "&SetVA28=" . $M5_battery . "&SetVA29=" . $MS_temp . "&SetVA30=" . $MS_temp_min .
    "&SetVA31=" . $MS_temp_max . "&SetVA32=" . $MS_humidite . "&SetVA33=" . $MS_CO2 . "&SetVA34=" . $MS_Pressure . "&SetVA35=" . $MS_Noise;

$output = shell_exec('curl "' . $URL_Push . '"');
log_message("Données envoyées à l'IPX800V4 avec succÚs.");
echo "C'est gagné\n";

// Fin du script
log_message("Fin de l'exécution du script.");
?>

Merci @AlexFisho pour le partage !

Le dernier script php apporte quelles modifs ?

j’ai un petit soucis sur l’humiditĂ© du module extĂ©rieur qui affiche 155% au lieu de 55%, alors que c’est OK pour le module principal.
Au niveau de l’IPX, la jauge de la batterie ne s’affiche pas, as tu modifiĂ© le code par rapport au tuto de @fgtoul ?

Hello
Le dernier script vĂ©rifie la validitĂ© du token afin de prendre le refresh Ă  temps. D’aprĂšs ce que j’ai compris des explications de ChatGPT. J’ai laissĂ© ChatGPT modifier le code avec des demandes de prĂ©cisions jusqu’à obtenir un script fonctionnel. Pour l’humiditĂ© j’ai cette conversion en JS sur mon Dashboard : let H0=(datasources[« STATUS Â»][« response Â»][« analogV3 Â»]-1000)/10;

//MODULE EXTERIEUR
let T0=(datasources["STATUS"]["response"]["analogV0"]-1000)/10;
let Tmin0=(datasources["STATUS"]["response"]["analogV1"]-1000)/10;
let Tmax0=(datasources["STATUS"]["response"]["analogV2"]-1000)/10;
let H0=(datasources["STATUS"]["response"]["analogV3"]-1000)/10;
let B0=datasources["STATUS"]["response"]["analogV4"];
let NB0=B0;
if (B0==100){NB0='full'};
return `
<br>
<span style="float:left;margin-left:20px;font-weight: bold; font-size: 2.5em;">${T0}°</span>
<span style="float:right;margin-right:10px;font-weight: bold; font-size: 2em;">${H0}%</span>
<br><br><br>
<span style="float:left; margin-left:10px;">&#8681;</span><span style="font-weight: bold;">${Tmin0}°</span>&nbsp;<span>&#8679;</span><span style="font-weight: bold;">${Tmax0}°</span>
<br><br>
<span style="float:left; margin-left:15px;" class="glyphicons glyphicons-battery-${NB0}"></span>
`;

image

Ok pour la formule sur l’humiditĂ©, j’avais une erreur, par contre toujours pas de symbole batterie.

j’ai l’impression que le nouveau script ne fonctionne pas, j’ai plus rien dans les log ?

Bonjour @AlexFisho , ton script est toujours fonctionnel ?

Bonjour
Avec les tokens que je rĂ©cupĂšre manuellement tous les matins et que je colle dans le fichier tokens.json ça fonctionne, lais je n’arrive toujours pas Ă  avoir un Refresh token pour continuer, donc tous les matins je me connecte sur Netatmo et je regĂ©nĂšre un token.

OK, je comprends. Bonne journée.

Bonjour la communauté,
Je rĂ©pond Ă  ce poste pour vous donner une solution Ă  votre problĂšme
 si toutefois vous ne l’avez toujours pas trouvĂ©e !
Il faut crĂ©er dans le volume « web Â» d’un NAS un dossier (que j’ai nommĂ© « Netatmo Â»).
Dans ce dossier il faut crĂ©er un fichier JSON qui ce nommera « tokens.json Â» et dans le quel vous inscrirez vos tokens (acces token et refresh token) de la façon suivante :

{"access_token":"xxxxxxxxxx","refresh_token":"yyyyyyyyyyyy","expires_in":10800,"expire_in":10800,"scope":["read_station"]}

Ensuite lancer le script qui suit toutes les 10 minutes depuis le NAS :

<?php

$CLIENT_ID = "aaaaaaaaaaaaaaaa";
$CLIENT_SECRET = "bbbbbbbbbbbbbbbbb";

// Récupérer les jetons depuis le fichier tokens.json
$tokensJson = file_get_contents('file:///Volumes/web/Netatmo/tokens.json');
$tokens = json_decode($tokensJson, true);
$ACCESS_TOKEN = $tokens['access_token'];
$REFRESH_TOKEN = $tokens['refresh_token'];

// Appeler l'API Netatmo pour récupérer la liste des périphériques (Scopes "read_station")
$response = file_get_contents("https://api.netatmo.net/api/devicelist?access_token=$ACCESS_TOKEN");

// Vérifier si la réponse contient "body" (si le fichier est valide)
if (strpos($response, 'body') !== false) {
    file_put_contents('/Volumes/web/Netatmo/NetatmoFic.json', json_encode($response));
    echo "C'est gagné";
} else {
    // sinon il faut générer un nouveau token d'accÚs en utilisant le token de rafraichissement
    $refreshApiUrl = "https://api.netatmo.com/oauth2/token";
    $refreshData = [
        'grant_type' => 'refresh_token',
        'refresh_token' => $REFRESH_TOKEN,
        'client_id' => $CLIENT_ID,
        'client_secret' => $CLIENT_SECRET,
    ];

    $options = [
        'http' => [
            'method' => 'POST',
            'header' => 'Content-Type: application/x-www-form-urlencoded',
            'content' => http_build_query($refreshData),
        ],
    ];

    $context = stream_context_create($options);
    $refreshResponse = file_get_contents($refreshApiUrl, false, $context);

    // Mettre Ă  jour les tokens dans le fichier tokens.json
    $newTokens = json_decode($refreshResponse, true);
    file_put_contents('/Volumes/web/Netatmo/tokens.json', json_encode($newTokens));

    // Mettre Ă  jour le token d'accĂšs
    $ACCESS_TOKEN = $newTokens['access_token'];

    // Appeler Ă  nouveau l'API Netatmo
    $response = file_get_contents("https://api.netatmo.net/api/devicelist?access_token=$ACCESS_TOKEN");
    file_put_contents('/Volumes/web/Netatmo/NetatmoFic.json', json_encode($response));
    echo "C'est re-gagné";
}
?>

Ce script créera un fichier JSON (NetatmoFic.json) dans lequel vous trouverez toutes les infos de vos capteurs Netatmo !
En principe vous n’aurez plus Ă  intervenir manuellement pour rĂ©cupĂ©rer les jetons !
Essayez cette solution et dites moi si cela fonctionne !
Bien amicalement Ă  tous.

4 « J'aime »

Bonjour @jppouma , merci pour ton aide. je viens d’essayer et ça ne fonctionne pas chez moi.
je te mets le dĂ©but du log de l’exĂ©cution du script.

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  2244    0  2244    0     0   4006      0 --:--:-- --:--:-- --:--:--  4007
100  2244    0  2244    0     0   3999      0 --:--:-- --:--:-- --:--:--  4000

Warning: file_get_contents(file:///Volumes/web/Netatmo/tokens.json): failed to open stream: No such file or directory in /volume1/web/Netatmo/Script_Netatmo.php on line 7

Call Stack:
    0.0016     129144   1. {main}() /volume1/web/Netatmo/Script_Netatmo.php:0
    0.0016     129368   2. file_get_contents() /volume1/web/Netatmo/Script_Netatmo.php:7


Warning: file_get_contents(https://api.netatmo.net/api/devicelist?access_token=): failed to open stream: HTTP request failed! HTTP/1.1 400 Missing token argument
 in /volume1/web/Netatmo/Script_Netatmo.php on line 13

Bonjour @pat49700

problĂšme de chemin peut-ĂȘtre
/Volumes /web/Netatmo/Script_Netatmo.php

/volume1 /web/Netatmo/Script_Netatmo.php

$tokensJson = file_get_contents('file:///Volumes/web/Netatmo/tokens.json');

devrait peut-ĂȘtre ĂȘtre :

$tokensJson = file_get_contents('file:///volume1/web/Netatmo/tokens.json');

puisque le script est exécuté depuis ce chemin

modifie aussi

 file_put_contents('/Volumes/web/Netatmo/tokens.json', json_encode($newTokens));

→

 file_put_contents('/volume1/web/Netatmo/tokens.json', json_encode($newTokens));

et modifie

file_put_contents('/Volumes/web/Netatmo/NetatmoFic.json', json_encode($response));

→

file_put_contents('/volume1/web/Netatmo/NetatmoFic.json', json_encode($response));
1 « J'aime »

Merci @cce66

Je viens de remplacer les 4 lignes et j’ai toujours le mĂȘme log !

peut ĂȘtre vĂ©rifier les droits fichier et rĂ©pertoire ??

chmod 644 /volume1/web/Netatmo/tokens.json
chmod 755 /volume1/web/Netatmo/

sinon tente avec des chemin relatifs

<?php

$CLIENT_ID = "aaaaaaaaaaaaaaaa";
$CLIENT_SECRET = "bbbbbbbbbbbbbbbbb";

// Chemin relatif pour tokens.json
$TOKENS_FILE = __DIR__ . '/tokens.json';

// Récupérer les jetons depuis le fichier tokens.json
$tokensJson = file_get_contents($TOKENS_FILE);
$tokens = json_decode($tokensJson, true);
$ACCESS_TOKEN = $tokens['access_token'];
$REFRESH_TOKEN = $tokens['refresh_token'];

// Chemin relatif pour le fichier de sortie
$OUTPUT_FILE = __DIR__ . '/NetatmoFic.json';

// Appeler l'API Netatmo pour récupérer la liste des périphériques
$response = file_get_contents("https://api.netatmo.net/api/devicelist?access_token=$ACCESS_TOKEN");

// Vérifier si la réponse contient "body"
if (strpos($response, 'body') !== false) {
    file_put_contents($OUTPUT_FILE, json_encode($response));
    echo "C'est gagné";
} else {
    // sinon il faut générer un nouveau token d'accÚs
    $refreshApiUrl = "https://api.netatmo.com/oauth2/token";
    $refreshData = [
        'grant_type' => 'refresh_token',
        'refresh_token' => $REFRESH_TOKEN,
        'client_id' => $CLIENT_ID,
        'client_secret' => $CLIENT_SECRET,
    ];

    $options = [
        'http' => [
            'method' => 'POST',
            'header' => 'Content-Type: application/x-www-form-urlencoded',
            'content' => http_build_query($refreshData),
        ],
    ];

    $context = stream_context_create($options);
    $refreshResponse = file_get_contents($refreshApiUrl, false, $context);

    // Mettre Ă  jour les tokens dans le fichier tokens.json
    $newTokens = json_decode($refreshResponse, true);
    file_put_contents($TOKENS_FILE, json_encode($newTokens));

    // Mettre Ă  jour le token d'accĂšs
    $ACCESS_TOKEN = $newTokens['access_token'];

    // Appeler Ă  nouveau l'API Netatmo
    $response = file_get_contents("https://api.netatmo.net/api/devicelist?access_token=$ACCESS_TOKEN");
    file_put_contents($OUTPUT_FILE, json_encode($response));
    echo "C'est re-gagné";
}
?>

amélioré par Grok ca donnes (à tester) :

<?php

$CLIENT_ID = "aaaaaaaaaaaaaaaa";
$CLIENT_SECRET = "bbbbbbbbbbbbbbbbb";
$TOKENS_FILE = __DIR__ . '/tokens.json'; // Chemin relatif au script
$OUTPUT_FILE = __DIR__ . '/NetatmoFic.json'; // Chemin relatif au script

// Vérifier si le fichier tokens.json existe
if (!file_exists($TOKENS_FILE)) {
    die("Erreur: Le fichier tokens.json n'existe pas Ă  l'emplacement: $TOKENS_FILE");
}

// Récupérer les jetons depuis le fichier tokens.json
$tokensJson = file_get_contents($TOKENS_FILE);
if ($tokensJson === false) {
    die("Erreur: Impossible de lire le fichier tokens.json");
}

$tokens = json_decode($tokensJson, true);
if (json_last_error() !== JSON_ERROR_NONE) {
    die("Erreur: Fichier tokens.json invalide - " . json_last_error_msg());
}

if (!isset($tokens['access_token']) || !isset($tokens['refresh_token'])) {
    die("Erreur: Tokens manquants dans le fichier JSON");
}

$ACCESS_TOKEN = $tokens['access_token'];
$REFRESH_TOKEN = $tokens['refresh_token'];

// Fonction pour appeler l'API Netatmo avec gestion des erreurs
function callNetatmoAPI($accessToken) {
    $url = "https://api.netatmo.net/api/devicelist?access_token=" . urlencode($accessToken);
    $context = stream_context_create([
        'http' => [
            'ignore_errors' => true // Pour lire les rĂ©ponses mĂȘme en cas d'erreur HTTP
        ]
    ]);
    $response = @file_get_contents($url, false, $context);
    
    if ($response === false) {
        $error = error_get_last();
        die("Erreur API: " . ($error['message'] ?? 'Erreur inconnue'));
    }
    
    return $response;
}

// Premier essai avec le token actuel
$response = callNetatmoAPI($ACCESS_TOKEN);
$decoded = json_decode($response, true);

if (isset($decoded['body'])) {
    file_put_contents($OUTPUT_FILE, $response);
    echo "C'est gagné";
    exit;
}

// Si on arrive ici, c'est que le token a probablement expiré
// RafraĂźchir le token
$refreshApiUrl = "https://api.netatmo.com/oauth2/token";
$refreshData = [
    'grant_type' => 'refresh_token',
    'refresh_token' => $REFRESH_TOKEN,
    'client_id' => $CLIENT_ID,
    'client_secret' => $CLIENT_SECRET,
];

$options = [
    'http' => [
        'method' => 'POST',
        'header' => 'Content-Type: application/x-www-form-urlencoded',
        'content' => http_build_query($refreshData),
    ],
];

$context = stream_context_create($options);
$refreshResponse = @file_get_contents($refreshApiUrl, false, $context);

if ($refreshResponse === false) {
    die("Erreur lors du rafraĂźchissement du token");
}

$newTokens = json_decode($refreshResponse, true);
if (json_last_error() !== JSON_ERROR_NONE) {
    die("Erreur: Réponse de rafraßchissement invalide - " . json_last_error_msg());
}

// Sauvegarder les nouveaux tokens
if (file_put_contents($TOKENS_FILE, json_encode($newTokens)) === false) {
    die("Erreur: Impossible d'écrire dans le fichier tokens.json");
}

// Réessayer avec le nouveau token
$response = callNetatmoAPI($newTokens['access_token']);
if (file_put_contents($OUTPUT_FILE, $response) === false) {
    die("Erreur: Impossible d'écrire dans le fichier de sortie");
}

echo "C'est re-gagné";
?>