// ──────────────────────────────────────────────── // DEBUT CONFIGURATION AUTO // ──────────────────────────────────────────────── $parameters = parameters(); // URL complète fournie → extraction automatique du nom de la ville et du code $url = $parameters['url']; // CHANGE ICI le nombre de jours souhaité (1 à 14) $nb_jours_max = $parameters['nbJoursMax']; // à mettre sur false si on ne veut pas des infos détaillées de la journée $detailJour = $parameters['detailJour']; // ──────────────────────────────────────────────── // FIN CONFIGURATION MANUELLE // ──────────────────────────────────────────────── // récupère le nom de la ville et le code meteoblue if (preg_match('#/semaine/([^/_]+)_([^/_]+)_(\d+)#i', $url, $matches)) { $nomVille = urldecode($matches[1]); // saint-avertin $codeVille = $matches[2]; // 2981512 $scenario->setLog("URL analysée → ville : $nomVille | code : $codeVille"); } else { $scenario->setLog("ERREUR : impossible d'extraire ville et code depuis l'URL fournie"); $scenario->setLog("└── Script arrêté (format URL invalide)"); return; } $nomVilleSansAccent = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $nomVille); $nomVilleSansAccent = preg_replace('/[^a-zA-Z0-9 -]+/', '', $nomVilleSansAccent); // paramètres pour le plugin virtual $nomVilleAffiche = ucwords(str_replace('-', ' ', $nomVilleSansAccent)); // si tiret remplace par un espace + majuscule, par exemple: saint-avertin → Saint Avertin $nomAffiche = 'météo ' . $nomVilleAffiche; // → détermine le nom du virtuel, par exemple: météo Saint Avertin // Construction du logicalId (format compatible Jeedom : minuscules, underscores) à adapter si vous avez des caractères spéciaux dans le nom de votre ville (au cas où...) $logicalId = 'meteo_' . str_replace('-', '_', strtolower($nomVilleSansAccent)); // → meteo_saint_avertin // et c'est parti if (!is_int($nb_jours_max) || $nb_jours_max < 1 || $nb_jours_max > 14) { $nb_jours_max = 7; // valeur par défaut sécurisée $scenario->setLog("ATTENTION : nb_jours_max invalide → forcé à 7"); } $scenario->setLog("┌── Début script Météo $nomVille ── ($nb_jours_max jours demandés)"); $scenario->setLog("→ URL : " . $url); $scenario->setLog("Récupération page meteoblue..."); // Récupération via cURL $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 15); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'); $html = curl_exec($ch); if ($html === false) { $error = curl_error($ch); $scenario->setLog("ERREUR cURL : " . $error); curl_close($ch); $scenario->setLog("└── Script arrêté (erreur réseau)"); return; } $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $scenario->setLog("Code HTTP : " . $http_code . " | Taille HTML : " . strlen($html) . " octets"); if ($http_code != 200 || strlen($html) < 5000) { $scenario->setLog("ERREUR : page invalide ou trop courte → abandon"); $scenario->setLog("└── Fin script (échec chargement page)"); return; } // Parsing DOM $scenario->setLog("Parsing HTML..."); $dom = new DOMDocument(); libxml_use_internal_errors(true); $dom->loadHTML('' . $html); libxml_clear_errors(); $xpath = new DOMXPath($dom); // Extraction jours et données $jours_nodes = $xpath->query("//time[contains(concat(' ', normalize-space(@class), ' '), ' date ')]"); $jours_data_nodes = $xpath->query("//div[contains(concat(' ', normalize-space(@class), ' '), ' tab-content ')]"); $nb_jours_dispo = $jours_nodes->length; $nb_data_dispo = $jours_data_nodes->length; $nb_jours = min($nb_jours_max, $nb_jours_dispo); $nb_data = min($nb_jours_max, $nb_data_dispo); $scenario->setLog("Jours disponibles sur meteoblue : $nb_jours_dispo"); $scenario->setLog("Jours retenus (limite demandée) : $nb_jours"); $vent = $direction = $tmax = $tmin = $pluie = $soleil = []; for ($i = 0; $i < $nb_data; $i++) { $day = $jours_data_nodes->item($i); // Vent $vent_div_n = $xpath->query(".//div[contains(concat(' ', normalize-space(@class), ' '), ' wind ')]", $day); if ($vent_div_n->length > 0) { $vent_div = $vent_div_n->item(0); $vitesse = trim(str_replace('km/h', '', $vent_div->textContent)); $span_n = $xpath->query(".//span", $vent_div); $dir_val = ($span_n->length > 0) ? end(explode(' ', trim($span_n->item(0)->getAttribute('class')))) : 'N'; } else { $vitesse = '0'; $dir_val = 'N'; } $vent[] = $vitesse; $direction[] = $dir_val; // Autres valeurs $node = $xpath->query(".//div[contains(@class,'tab-temp-max')]", $day)->item(0); $tmax[] = $node ? trim($node->textContent) : ''; $node = $xpath->query(".//div[contains(@class,'tab-temp-min')]", $day)->item(0); $tmin[] = $node ? trim($node->textContent) : ''; $node = $xpath->query(".//div[contains(@class,'tab-precip')]", $day)->item(0); $pluie[] = $node ? trim($node->textContent) : ''; $node = $xpath->query(".//div[contains(@class,'tab-sun')]", $day)->item(0); $soleil[] = $node ? trim($node->textContent) : ''; } $scenario->setLog("Données extraites pour " . count($vent) . " jours"); if (count($vent) > 0) { $scenario->setLog("Exemple jour 0 → Tmax: " . ($tmax[0] ?: '?') . " | Pluie: " . ($pluie[0] ?: '?')); } // ──────────────────────────────────────────────── // Fonctions utilitaires // ──────────────────────────────────────────────── function wind_arrow($dir_val) { $mapping = [ "n" => 0, "nne" => 22.5, "ne" => 45, "ene" => 67.5, "e" => 90, "ese" => 112.5, "se" => 135, "sse" => 157.5, "s" => 180, "sso" => 202.5, "ssw" => 202.5, "so" => 225, "sw" => 225, "oso" => 247.5, "wsw" => 247.5, "o" => 270, "w" => 270, "ono" => 292.5, "wnw" => 292.5, "no" => 315, "nw" => 315, "nno" => 337.5, "nnw" => 337.5 ]; $lower = strtolower(trim($dir_val)); $angle = ($mapping[$lower] ?? 0) + 180; $angle = $angle % 360; return " ↑ " . " " . strtoupper($dir_val) . ""; } function temp_color($temp) { preg_match_all('/\d+\.?\d*/', $temp, $m); $nums = array_map('floatval', $m[0]); $s = $nums ? max($nums) : 0; if ($s <= 0) return "#3828c7"; if ($s <= 5) return "#6FA8FF"; if ($s <= 15) return "#b3e146"; if ($s <= 20) return "#3CFF3C"; if ($s <= 28) return "#FFA500"; return "#FF4040"; } function vent_color($speed) { preg_match_all('/\d+\.?\d*/', $speed, $m); $nums = array_map('floatval', $m[0]); $s = $nums ? max($nums) : 0; if ($s < 30) return "#3CFF3C"; if ($s <= 50) return "#FFA500"; return "#FF4040"; } function format_vent($value) { preg_match_all('/\d+\.?\d*/', $value, $m); $nums = $m[0]; if (count($nums) >= 2) { return $nums[0] . " ↯" . end($nums) . ""; } return $value ?: '0'; } function pluie_bg($value) { preg_match_all('/\d+\.?\d*/', $value, $m); $nums = array_map('floatval', $m[0]); $p = $nums ? max($nums) : 0; if ($p == 0) return "#a562a5"; if ($p <= 2) return "#FFD700"; if ($p <= 5) return "#FF8C00"; return "#FF0000"; } function pluie_percent($value) { preg_match_all('/\d+\.?\d*/', $value, $m); $nums = array_map('floatval', $m[0]); $p = $nums ? max($nums) : 0; if ($p == 0) return "#a562a5"; if ($p <= 20) return "#FFD700"; if ($p <= 50) return "#FF8C00"; return "#FF0000"; } function soleil_bar($value, $max_h = 10) { preg_match_all('/\d+/', $value, $m); $h = intval($m[0][0] ?? 0); $ratio = min($h / $max_h, 1); $width = intval($ratio * 100); return "
| Jours : | "; $count_jours = 0; foreach ($jours_nodes as $index => $j) { if ($count_jours >= $nb_jours) { break; } $short_node = $xpath->query(".//div[contains(concat(' ', normalize-space(@class), ' '), ' tab-day-short ')]", $j)->item(0); $short_day = $short_node ? trim($short_node->textContent) : ''; $long_node = $xpath->query(".//div[contains(concat(' ', normalize-space(@class), ' '), ' tab-day-long ')]", $j)->item(0); $long_day = $long_node ? trim($long_node->textContent) : ''; $data_meteo[$index] = [ 'jour_court' => $short_day, 'jour_long' => $long_day, 'donnees' => [] ]; $table .= ""
. htmlspecialchars($short_day) . " " . "" . htmlspecialchars($long_day) . " | ";
$count_jours++;
}
$table .= "
| {$icon} {$label} : | "; for ($col = 0; $col < $nb_jours; $col++) { $v = $values[$col] ?? ''; // sécurité si tableau plus court $data_meteo[$col]['donnees'][$label] = $v; $cell_bg = $bg; $text_color = "white"; $display = htmlspecialchars($v); $size = in_array($label, ["Tmax", "Tmin"]) ? "1.1em" : "1em"; if ($label == "Vent") { $display = format_vent($v); $text_color = vent_color($v); } elseif ($label == "Direction") { $display = wind_arrow($direction[$col] ?? 'N'); $text_color = vent_color($vent[$col] ?? '0'); } elseif ($label == "Pluie") { $display = preg_replace('/(mm)/', "$1", htmlspecialchars($v)); $cell_bg = pluie_bg($v); $text_color = "black"; } elseif ($label == "Soleil") { $display = soleil_bar($v); } else { $text_color = $COLORS[$label] ?? 'rgb(var(--txt-color))'; } $table .= "" . $display . " | "; } $table .= "
Tableau horaire introuvable
"; $three_hourly = $xpath->query("//table[contains(@class,'picto') and contains(@class,'three-hourly-view')]"); if ($three_hourly->length > 0) { $tbl = $three_hourly->item(0); $time_cells = $xpath->query(".//tr[contains(@class,'times')]//td", $tbl); $icon_cells = $xpath->query(".//tr[contains(@class,'icons')]//img", $tbl); $temp_cells = $xpath->query(".//tr[contains(@class,'temperatures')]//td//div[contains(@class,'cell')]", $tbl); $feel_cells = $xpath->query(".//tr[contains(@class,'windchills')]//td//div[contains(@class,'cell')]", $tbl); $wind_cells = $xpath->query(".//tr[contains(@class,'windspeeds')]//td//div[contains(@class,'cell')]", $tbl); $dir_cells = $xpath->query(".//tr[contains(@class,'winddirs')]//td//div[contains(@class,'winddir')]", $tbl); $pluie_cells = $xpath->query(".//tr[contains(@class,'precipprobs')]//td//span[contains(@class,'precip-prob')]", $tbl); $pluie_mm = $xpath->query(".//tr[contains(@class,'precips')]//td//div[contains(@class,'cell')]//span", $tbl); $hourly_blocks = $xpath->query(".//div[contains(@class, 'precip-bar-flex')]", $tbl); $rainspot_cells = $xpath->query(".//tr[contains(@class,'rainspots')]//td//div[contains(@class,'rainspot')]", $tbl); $today_content = '| Heure | ' . (($parameters['detailC2'] ?? false) ? 'Icône | ' : '') . (($parameters['detailC3'] ?? false) ? 'Temp | ' : '') . (($parameters['detailC4'] ?? false) ? 'Ressenti | ' : '') . (($parameters['detailC5'] ?? false) ? 'Vent | ' : '') . (($parameters['detailC6'] ?? false) ? 'Pluie | ' : '') . (($parameters['detailC7'] ?? false) ? '(mm/3h) | ' : '') . (($parameters['detailC8'] ?? false) ? 'RainSPOT | ' : '') . (($parameters['detailC9'] ?? false) ? '(% mm/h1) | ' : '') . (($parameters['detailC10'] ?? false) ? '(% mm/h2) | ' : '') . (($parameters['detailC11'] ?? false) ? '(% mm/h3) | ' : '') . '
|---|---|---|---|---|---|---|---|---|---|---|
| ' . htmlspecialchars($h) . ' | ' . (($parameters['detailC2'] ?? false) ? '' . $icon_html . ' | ' : '') . (($parameters['detailC3'] ?? false) ? '' . htmlspecialchars($temp) . ' | ' : '') . (($parameters['detailC4'] ?? false) ? '' . htmlspecialchars($ressenti) . ' | ' : '') . (($parameters['detailC5'] ?? false) ? '' . $vent_display . ' | ' : '') . (($parameters['detailC6'] ?? false) ? '' . htmlspecialchars($pluie) . ' | ' : '') . (($parameters['detailC7'] ?? false) ? '' . htmlspecialchars($pluie_extract_mm) . ' | ' : '') . (($parameters['detailC8'] ?? false) ? '' . $rainspot_html . ' | ' : '') . (($parameters['detailC9'] ?? false) ? '' . htmlspecialchars($probability[0] ?? '0%') . ' ' . htmlspecialchars($precipitation[0] ?? '0 mm') . ' | ' : '') . (($parameters['detailC10'] ?? false) ? '' . htmlspecialchars($probability[1] ?? '0%') . ' ' . htmlspecialchars($precipitation[1] ?? '0 mm') . ' | ' : '') . (($parameters['detailC11'] ?? false) ? '' . htmlspecialchars($probability[2] ?? '0%') . ' ' . htmlspecialchars($precipitation[2] ?? '0 mm') . ' | ' : '') . '