Autor Tema: Proxy transparente para Ecowitt  (Leído 48 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado ipe

  • Jr. Member
  • **
  • Mensajes: 51
    • Meteo San Antón
  • Estación: ESAND2900000029018A
Proxy transparente para Ecowitt
« en: 31 de Marzo de 2025, 07:00:54 »
Este script php está diseñado para interceptar las solicitudes de la conexiones desde las estaciones Ecowitt hacia las redes meteorológicas "a medida" (customizables). Además, puede reenviar la información a otras redes, o a Home Assistant. Todo lo que tu imaginación y tus conocimientos te permitan.

Necesita ser instalada en un servidor con php (preferentemente, cuyo ISP no sea Movistar, que aparentemente bloquea al servidor de la API de Meteoclimatic los días de partido de futbol)

Es decir:

Antes: Ecowitt-Customizable ->  api.m11c.net
Ahora: Ecowitt-Customizable ->  proxy.php -> api.m11c.net

Código: [Seleccionar]
<?php
require_once("/share/Web/comun/comun.php"); //Este archivo esconde las claves, y la librería mongodb

//*******************************************************************
//*******************************************************************
//               Funciones a medida, WUnderground, Windguru
//*******************************************************************
//*******************************************************************


/**
 * Convierte los datos posteados en protocolo ECOWITT a protocolo WUNDERGROUND (AWEKAS)
 * @param mixed $data Datos posteados
 * @param mixed $WU_ID
 * @param mixed $WU_KEY
 * @return array|array{ID: mixed, PASSWORD: mixed, UV: mixed, action: string, baromin: mixed, dailyrainin: mixed, dateutc: string, dewptf: mixed, humidity: mixed, rainin: mixed, softwaretype: string, solarRadiation: mixed, tempf: mixed, totalrainin: mixed, winddir: mixed, windspeedmph: mixed}
 */

function ew2wu($data$WU_ID$WU_KEY)
{
    try {
        return [
            
'ID' => $WU_ID,
            
'PASSWORD' => $WU_KEY,
            
'dateutc' => 'now',
            
'tempf' => $data['tempf'] ?? null,
            
'humidity' => $data['humidity'] ?? null,
            
'dewptf' => $data['dewptf'] ?? null,
            
'baromin' => $data['baromrelin'] ?? null,
            
'windgustmph' => $data['windgustmph'] ?? null,
            
'windspeedmph' => $data['windspeedmph'] ?? null,
            
'winddir' => $data['winddir'] ?? null,
            
'rainin' => $data['rainratein'] ?? null,
            
'dailyrainin' => $data['dailyrainin'] ?? null,
            
'totalrainin' => $data['totalrainin'] ?? null,
            
'solarRadiation' => isset($data['solarradiation']) ? round($data['solarradiation']) : null,
            
'UV' => $data['uv'] ?? null,
            
'softwaretype' => 'Ecowitt-Converter-PHP',
            
'action' => 'updateraw'
        
];
    } catch (
Exception $e) {
        
//error_log("Error en ew2wu: " . $e->getMessage());
        
return [];
    }
}

//https://blog.meteodrenthe.nl/2021/12/27/uploading-to-the-weather-underground-api/

/**
 * Convierte los datos de Ecowitt a formato WindGuru y devuelve el array de parámetros.
 * @param mixed $data Datos posteados
 * @param mixed $user Usuario de WindGuru
 * @param mixed $password Contraseña de WindGuru
 * @param mixed $stationUID UID de la estación de WindGuru
 * @return array El array con los parámetros que se enviarían a WindGuru
 */
function ew2wg($data$user$passwo)
{
    try {
        
$salt = new DateTime();
        
$dt = new DateTime("now", new DateTimeZone("UTC"));
        
$salt $dt->getTimestamp(); // Unix timestamp en segundos
        
$unixtime=$salt;
        
$salt=$salt*1000;
        
// Generar el hash
        
$hash md5($salt $user $passwo);
    
$conditions = [
        
'uid' => $user,
        
'salt' => $salt,
        
'hash'=>$hash,
        
'interval'=>60,
        
'unixtime' =>$unixtime ,  // Hora actual 
        
'temperature' => isset($data['tempf']) ? round(($data['tempf'] - 32) * 91) : null,   // Temperatura
        
'wind_avg' => isset($data['windspeedmph']) ? $data['windspeedmph'] * 0.868976 null// Velocidad del viento en nudos (1 mph = 0.868976 nudos)
        
'wind_max' => isset($data['windgustmph']) ? $data['windgustmph'] * 0.868976 null,   // Ráfagas de viento en nudos
        
'wind_direction' => isset($data['winddir']) ? $data['winddir'] : null,   // Dirección del viento
        
'mslp' => isset($data['baromrelin']) ? $data['baromrelin'] * 33.8639 null,  // Presión en hPa convertida a mbar
        
'rh' => isset($data['humidity']) ? $data['humidity'] : null,   // Humedad
        
'precip' => isset($data['rainratein']) ? $data['rainratein'] * 25.4 null,  // Precipitación en mm
    
];
    return 
$conditions;
} catch (
Exception $e) {
    return [];
}
}
//*******************************************************************
//*******************************************************************
//          Son datos procesables?
//*******************************************************************
//*******************************************************************

if(empty($_POST)){
    echo 
"Necesita datos \$_POST. No es para usar de otra forma";
    return;
}
$data $_POST;

session_start();
$limiteTiempo 3600;
$ultimaAlerta $_SESSION['ultima_alerta'] ?? 0;

$datosok = isset($data['tempf']);
if (!
$datosok) {
    
$tiempoActual time();

    if ((
$tiempoActual $ultimaAlerta) > $limiteTiempo) {
        
$_SESSION['ultima_alerta'] = $tiempoActual;
        
$cuerpo "Se ha recibido un conjunto de datos incorrectos:
        " 
print_r($datatrue);
        
$cuerpo addslashes($cuerpo); // Escapar comillas dentro del cuerpo
        
        
$json_message json_encode([
            
'asunto' => 'Datos de la estación incorrectos',
            
'destinatario' => 'ipe***@gmail.com',
            
'cuerpo' => $cuerpo
        
]);

        
publicaMqtt("email"$json_message);
        }
    echo 
"NOOK";
    exit();
}

//*******************************************************************
//*******************************************************************
//               MQTT
//*******************************************************************
//*******************************************************************

// Eliminamos datos irrelevantes
if (isset($_POST['PASSKEY'])) unset($data['PASSKEY']);
if (isset(
$_POST['stationtype'])) unset($data['stationtype']);
if (isset(
$_POST['wh65batt'])) unset($data['wh65batt']);
if (isset(
$_POST['console_batt'])) unset($data['console_batt']);
if (isset(
$_POST['model'])) unset($data['model']);
if (isset(
$_POST['freq'])) unset($data['freq']);
if (isset(
$_POST['interval'])) unset($data['interval']);

publicaMqtt("meteo/posted",json_encode($data));
// El demonio de casium2 lo guarda en mongodb


//*******************************************************************
//*******************************************************************
//          Métodos GET     Awekas (wu), Windy (wu), Windguru (wg)
//*******************************************************************
//*******************************************************************
// Recibir datos de Ecowitt
$datos $_POST;


$awquery=http_build_query(ew2wu($datos,$AW_ID,$AW_PW));
$awekasUrl "http://ws.awekas.at/weatherstation/updateweatherstation.php?".$awquery;
if (
date('i') % == 0)
    @
file_get_contents($awekasUrl);
//file_put_contents("/share/Web/casa/proxy/datos.txt", "$awekasUrl\n", FILE_APPEND);


$windy_url "https://stations.windy.com/pws/update/$WD_KEY?stationId=0&".$awquery;
if (
date('i') % == 0)
    @
file_get_contents($windy_url);
// file_put_contents("/share/Web/casa/proxy/datos.txt", "$windy_url\n", FILE_APPEND);

$wg='http://www.windguru.cz/upload/api.php?';
$wgquery=$wg.http_build_query(ew2wg($datos,$WG_ID,$WG_PW));
if (
date('i') % == 0){
    @
file_get_contents($wgquery);
    
//file_put_contents("/share/Web/casa/proxy/datos.txt", "$wgquery\n", FILE_APPEND);
    
}


//*******************************************************************
//*******************************************************************
//          Método POST: Meteobridge, HomeAssistant, Meteoclimatic
//*******************************************************************
//*******************************************************************

// https://github.com/leoherzog/WundergroundStationForwarder/blob/main/code.gs
// URLs de los destinos a los que se reenviarán los datos
$destinos = [
    
"http://192.168.1.151/public/ecowitt0.cgi",
    
"http://192.168.1.151/public/ecowitt2.cgi",
    
"http://192.168.1.91:8123/api/webhook/d06588e76a6dbdf49abe527872ff58a2",
    
"http://api.m11c.net/v2/ew/ESAND2900000029018A/$MC_KEY",
    
"http://***.uma.es/proxy/index.php"
    
];

function 
enviarDatos($url$datos) {
    try{

    
$ch curl_init();
    
    
curl_setopt($chCURLOPT_URL$url);
    
curl_setopt($chCURLOPT_POSTtrue);
    
curl_setopt($chCURLOPT_POSTFIELDShttp_build_query($datos));
    
curl_setopt($chCURLOPT_HTTPHEADER, ["Content-Type: application/x-www-form-urlencoded"]);
    
curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
    
curl_setopt($chCURLOPT_TIMEOUT5);
    
curl_setopt($chCURLOPT_FOLLOWLOCATIONtrue);

    
$response curl_exec($ch);
    
$httpCode curl_getinfo($chCURLINFO_HTTP_CODE);
    
curl_close($ch);

    return (
$httpCode >= 200 && $httpCode 300); // Retorna true si la respuesta es exitosa
}catch(Exception $e)
{
    return 
false;
}

}

//$alternativo="https://casium2.uma.es/proxy/index.php";
enviarDatos($destinos[0], $datos);
enviarDatos($destinos[1], $datos);
enviarDatos($destinos[2], $datos);
if(!
enviarDatos($destinos[3], $datos))  //Problema Movistar
    
enviarDatos($destinos[4], $datos);


Desconectado ipe

  • Jr. Member
  • **
  • Mensajes: 51
    • Meteo San Antón
  • Estación: ESAND2900000029018A
Re:Proxy transparente para Ecowitt
« Respuesta #1 en: 31 de Marzo de 2025, 07:04:24 »
Esta es la versión simplificada en caso de que falle Movistar:

Antes: Ecowitt-Customizable ->  proxy.php -> api.m11c.net
Ahora: Ecowitt-Customizable ->  proxy.php con fallo Movistar ->proxy2.php -> api.m11c.net


Código: [Seleccionar]
<?php
$MC_KEY
="****";
$datos $_POST;


$destino "http://api.m11c.net/v2/ew/ESAND2900000029018A/$MC_KEY";


function 
enviarDatos($url$datos) {
    try{

        
$ch curl_init();
        
        
curl_setopt($chCURLOPT_URL$url);
        
curl_setopt($chCURLOPT_POSTtrue);
        
curl_setopt($chCURLOPT_POSTFIELDShttp_build_query($datos));
        
curl_setopt($chCURLOPT_HTTPHEADER, ["Content-Type: application/x-www-form-urlencoded"]);
        
curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
        
curl_setopt($chCURLOPT_TIMEOUT5);
        
curl_setopt($chCURLOPT_FOLLOWLOCATIONtrue);
    
        
$response curl_exec($ch);
        
$httpCode curl_getinfo($chCURLINFO_HTTP_CODE);
        
curl_close($ch);
    
        return (
$httpCode >= 200 && $httpCode 300); // Retorna true si la respuesta es exitosa
    
}catch(Exception $e)
    {
        return 
false;
    }

}

enviarDatos($destino$datos);
« Última modificación: 31 de Marzo de 2025, 08:54:59 por ipe »