<php
// Zugangsdaten Netatmo
$username = "your@user.name";
$password = "yourpassword";
$app_id = "netatmo-app-id";
$app_secret = "netatmo-app-secret";
// Token anfordern
$postdata = array(
'grant_type' => "password",
'client_id' => $app_id,
'client_secret' => $app_secret,
'username' => $username,
'password' => $password,
'scope' => 'read_station'
);
$url = "https://api.netatmo.net/oauth2/token";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$response = curl_exec($ch);
curl_close($ch);
// Anfrage mit Token
$params = null;
$params = json_decode($response, true);
$api_url = "https://api.netatmo.net/api/devicelist?access_token=" . $params['access_token'];
// Daten abrufen
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_url);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$array = curl_exec($ch);
curl_close($ch);
$netatmo = json_decode($array,true);
// hier müssen die ids angepasst werden. meine Module haben komischerweise höhere Nummern weil
// in meinem Account zuerst die Module von einer befreundeten freigegebenen Station gefunden werden...
// der Vorteil der Zuordnung hier ist, dass im Folgenden nichts mehr angepasst werden muss.
$device0 = "0"; //Basisstation
$module0 = "5"; //Außenmodul
$module1 = "6"; //1. Innenmodul
$module2 = "7"; //Regenmesser
$module3 = "8"; //2. Innenmodul
// Messwerte bereitstellen
$name_station = $netatmo["body"]["devices"][$device0]["station_name"];
$date_station = $netatmo["body"]["devices"][$device0]["last_status_store"];
$name_base = $netatmo["body"]["devices"][$device0]["module_name"];
$temp_base = $netatmo["body"]["devices"][$device0]["dashboard_data"]["Temperature"];
$humi_base = $netatmo["body"]["devices"][$device0]["dashboard_data"]["Humidity"];
$CO2_base = $netatmo["body"]["devices"][$device0]["dashboard_data"]["CO2"];
$min_temp_base = $netatmo["body"]["devices"][$device0]["dashboard_data"]["min_temp"];
$max_temp_base = $netatmo["body"]["devices"][$device0]["dashboard_data"]["max_temp"];
$min_date_base = $netatmo["body"]["devices"][$device0]["dashboard_data"]["date_min_temp"];
$max_date_base = $netatmo["body"]["devices"][$device0]["dashboard_data"]["date_max_temp"];
$pressure = $netatmo["body"]["devices"][$device0]["dashboard_data"]["Pressure"];
$name_mod0 = $netatmo["body"]["modules"][$module0]["module_name"];
$temp_mod0 = $netatmo["body"]["modules"][$module0]["dashboard_data"]["Temperature"];
$humi_mod0 = $netatmo["body"]["modules"][$module0]["dashboard_data"]["Humidity"];
$min_temp_mod0 = $netatmo["body"]["modules"][$module0]["dashboard_data"]["min_temp"];
$max_temp_mod0 = $netatmo["body"]["modules"][$module0]["dashboard_data"]["max_temp"];
$min_date_mod0 = $netatmo["body"]["modules"][$module0]["dashboard_data"]["date_min_temp"];
$max_date_mod0 = $netatmo["body"]["modules"][$module0]["dashboard_data"]["date_max_temp"];
$name_rain = $netatmo["body"]["modules"][$module2]["module_name"];
$rain_1 = $netatmo["body"]["modules"][$module2]["dashboard_data"]["sum_rain_1"];
$rain_24 = $netatmo["body"]["modules"][$module2]["dashboard_data"]["sum_rain_24"];
$name_mod1 = $netatmo["body"]["modules"][$module1]["module_name"];
$temp_mod1 = $netatmo["body"]["modules"][$module1]["dashboard_data"]["Temperature"];
$humi_mod1 = $netatmo["body"]["modules"][$module1]["dashboard_data"]["Humidity"];
$CO2_mod1 = $netatmo["body"]["modules"][$module1]["dashboard_data"]["CO2"];
$min_temp_mod1 = $netatmo["body"]["modules"][$module1]["dashboard_data"]["min_temp"];
$max_temp_mod1 = $netatmo["body"]["modules"][$module1]["dashboard_data"]["max_temp"];
$min_date_mod1 = $netatmo["body"]["modules"][$module1]["dashboard_data"]["date_min_temp"];
$max_date_mod1 = $netatmo["body"]["modules"][$module1]["dashboard_data"]["date_max_temp"];
$name_mod3 = $netatmo["body"]["modules"][$module3]["module_name"];
$temp_mod3 = $netatmo["body"]["modules"][$module3]["dashboard_data"]["Temperature"];
$humi_mod3 = $netatmo["body"]["modules"][$module3]["dashboard_data"]["Humidity"];
$CO2_mod3 = $netatmo["body"]["modules"][$module3]["dashboard_data"]["CO2"];
$min_temp_mod3 = $netatmo["body"]["modules"][$module3]["dashboard_data"]["min_temp"];
$max_temp_mod3 = $netatmo["body"]["modules"][$module3]["dashboard_data"]["max_temp"];
$min_date_mod3 = $netatmo["body"]["modules"][$module3]["dashboard_data"]["date_min_temp"];
$max_date_mod3 = $netatmo["body"]["modules"][$module3]["dashboard_data"]["date_max_temp"];
// Berechnung Sonnenauf- und -untergang
$latitude = $netatmo["body"]["devices"][$device0]["place"]["location"][1];
$longitude = $netatmo["body"]["devices"][$device0]["place"]["location"][0];
$time = time();
$dst = date("I", $time);
if ($dst) {
$offset = 2;
} else {
$offset = 1;
}
$zenith = 50/60;
$zenith = $zenith + 90;
$sunrise = date_sunrise($time, SUNFUNCS_RET_TIMESTAMP, $latitude, $longitude, $zenith, $offset);
$sunrise = date("H:i", $sunrise);
$sunset = date_sunset($time, SUNFUNCS_RET_TIMESTAMP, $latitude, $longitude, $zenith, $offset);
$sunset = date("H:i", $sunset);
// Taupunkt-Berechnung
if ($temp_mod0 > 0) {
$k2 = 17.62;
$k3 = 243.12;
} else {
$k2 = 22.46;
$k3 = 272.62;
}
$dewpoint = $k3 *(($k2 * $temp_mod0) / ($k3 + $temp_mod0) + log($humi_mod0 / 100));
$dewpoint = $dewpoint / (($k2 * $k3) / ($k3 + $temp_mod0) - log($humi_mod0 / 100));
$dewpoint = round($dewpoint, 1);
// Messwerte formatieren
$min_date_mod0 = date("H:i",$min_date_mod0);
$max_date_mod0 = date("H:i",$max_date_mod0);
$min_date_base = date("H:i", $min_date_base);
$max_date_base = date("H:i", $max_date_base);
$min_date_mod1 = date("H:i", $min_date_mod1);
$max_date_mod1 = date("H:i", $max_date_mod1);
$min_date_mod3 = date("H:i", $min_date_mod3);
$max_date_mod3 = date("H:i", $max_date_mod3);
$date_station = DatumText($date_station);
$temp_mod0 = number_format($temp_mod0,1,",","");
$temp_base = number_format($temp_base,1,",","");
$temp_mod1 = number_format($temp_mod1,1,",","");
$temp_mod3 = number_format($temp_mod3,1,",","");
$pressure = number_format($pressure,1,",","");
$dewpoint = number_format($dewpoint,1,",","");
$rain_1 = number_format($rain_1,1,",","");
$rain_24 = number_format($rain_24,1,",","");
$max_temp_mod0 = number_format($max_temp_mod0,1,",","");
$min_temp_mod0 = number_format($min_temp_mod0,1,",","");
$max_temp_base = number_format($max_temp_base,1,",","");
$min_temp_base = number_format($min_temp_base,1,",","");
$max_temp_mod1 = number_format($max_temp_mod1,1,",","");
$min_temp_mod1 = number_format($min_temp_mod1,1,",","");
$max_temp_mod3 = number_format($max_temp_mod3,1,",","");
$min_temp_mod3 = number_format($min_temp_mod3,1,",","");
// Variablen
$filename = "weather-script-output.png";
// Schriftart aus den Beispielen der c't
$DEFAULT_FONT = array("regular"=>realpath("./fonts/LiberationSans-Regular.ttf"),"bold"=>realpath("./fonts/LiberationSans-Bold.ttf"),"italic"=>realpath("./fonts/LiberationSans-Italic.ttf"));
// Leere PNG-Datei mit weißem Hintergrund erstellen
$im = @ImageCreateTrueColor(640,384);
$background = ImageColorAllocate($im, 255, 255, 255);
ImageFilledRectangle($im, 0, 0, 640, 384, $background);
// über die Schriftgröße wird nachher ALLES skaliert
$fontSize = 15;
$cursorY = $fontSize*2.7;
$X0 = $fontsize;
$X1 = $fontSize*6.66;
$X2 = $fontSize*12;
$X3 = $fontSize*17.33;
$X4 = $fontSize*23;
$X5 = $fontSize*28;
$X6 = $fontSize*38;
// Farbe für Schrift und Hilfslinien festlegen
$black = ImageColorAllocate($im, 0, 0, 0);
$red = ImageColorAllocate($im, 255,0,0);
$color = $black;
$fontb = $DEFAULT_FONT['bold'];
$fontr = $DEFAULT_FONT['regular'];
// Text einfügen
ImageTTFText($im, $fontSize*2.1, 0, $X0, $cursorY, $color, $fontb, $name_station);
ImageTTFText($im, $fontSize/1.5, 0, $X5, $cursorY-$fontSize*1.1, $color, $fontr, "Sonnenaufgang ".$sunrise);
ImageTTFText($im, $fontSize/1.5, 0, $X5, $cursorY, $color, $fontr, "Sonnenuntergang ".$sunset);
$cursorY = $cursorY+$fontSize*1.5;
ImageTTFText($im, $fontSize*0.7, 0, $X0+20, $cursorY, $color, $fontr, "Letzte Aktualisierung: ".$date_station);
$cursorY = $cursorY+$fontSize*2;
ImageTTFText($im, $fontSize*1.3, 0, $X0, $cursorY, $color, $fontr, $name_mod0);
ImageTTFText($im, $fontSize*0.55, 0, $X3, $cursorY, $color, $fontr, "Luftfeuchtigkeit");
ImageTTFText($im, $fontSize*0.55, 0, $X5, $cursorY, $color, $fontr, "Luftdruck");
$cursorY = $cursorY+$fontSize*2.1;
If(intval($temp_mod0) <= 4 intval($temp_mod0) >= 30){$color = $red;} else {$color = $black;}
ImageTTFText($im, $fontSize*2, 0, $X0, $cursorY, $color, $fontr, $temp_mod0);
$color = $black;
ImageTTFText($im, $fontSize*0.55, 0, $X1, $cursorY-$fontSize*1.2, $color, $fontr, $max_temp_mod0." / ".$max_date_mod0);
ImageTTFText($im, $fontSize*0.55, 0, $X1, $cursorY-$fontSize/3, $color, $fontr, $min_temp_mod0." / ".$min_date_mod0);
ImageTTFText($im, $fontSize*1.5, 0, $X2, $cursorY-$fontSize/4, $color, $fontr, "°C");
ImageTTFText($im, $fontSize*2, 0, $X3, $cursorY, $color, $fontr, $humi_mod0);
ImageTTFText($im, $fontSize*1.5, 0, $X4, $cursorY-$fontSize/4, $color, $fontr, "%");
ImageTTFText($im, $fontSize*2, 0, $X5, $cursorY, $color, $fontr, $pressure);
ImageTTFText($im, $fontSize*1.5, 0, $X6, $cursorY-$fontSize/4, $color, $fontr, "hPa");
$cursorY = $cursorY+$fontSize*1.5;
ImageTTFText($im, $fontSize*0.55, 0, $X0, $cursorY, $color, $fontr, "Niederschlag letzte Stunde");
ImageTTFText($im, $fontSize*0.55, 0, $X3, $cursorY, $color, $fontr, "Niederschlag letzter Tag");
ImageTTFText($im, $fontSize*0.55, 0, $X5, $cursorY, $color, $fontr, "Taupunkt");
$cursorY = $cursorY+$fontSize*2.1;
ImageTTFText($im, $fontSize*2, 0, $X0, $cursorY, $color, $fontr, $rain_1);
ImageTTFText($im, $fontSize*0.8, 0, $X1, $cursorY-$fontSize/3, $color, $fontr, "mm");
ImageTTFText($im, $fontSize*2, 0, $X3, $cursorY, $color, $fontr, $rain_24);
ImageTTFText($im, $fontSize*0.8, 0, $X4, $cursorY-$fontSize/3, $color, $fontr, "mm");
ImageTTFText($im, $fontSize*2, 0, $X5, $cursorY, $color, $fontr, $dewpoint);
ImageTTFText($im, $fontSize*1.5, 0, $X6, $cursorY-$fontSize/4, $color, $fontr, "°C");
$cursorY = $cursorY+$fontSize*2.1;
ImageTTFText($im, $fontSize*1.3, 0, $X0, $cursorY, $color, $fontr, $name_base);
ImageTTFText($im, $fontSize*0.55, 0, $X3, $cursorY, $color, $fontr, "Luftfeuchtigkeit");
ImageTTFText($im, $fontSize*0.55, 0, $X5, $cursorY, $color, $fontr, "CO2-Gehalt");
$cursorY = $cursorY+$fontSize*2.1;
ImageTTFText($im, $fontSize*2, 0, $X0, $cursorY, $color, $fontr, $temp_base);
ImageTTFText($im, $fontSize*0.55, 0, $X1, $cursorY-$fontSize*1.2, $color, $fontr, $max_temp_base." / ".$max_date_base);
ImageTTFText($im, $fontSize*0.55, 0, $X1, $cursorY-$fontSize/3, $color, $fontr, $min_temp_base." / ".$min_date_base);
ImageTTFText($im, $fontSize*1.5, 0, $X2, $cursorY-$fontSize/4, $color, $fontr, "°C");
If(intval($humi_base) > 60 intval($humi_base) < 40){$color = $red;} else {$color = $black;}
ImageTTFText($im, $fontSize*2, 0, $X3, $cursorY, $color, $fontr, $humi_base);
$color =$black;
ImageTTFText($im, $fontSize*1.5, 0, $X4, $cursorY-$fontSize/4, $color, $fontr, "%");
If(intval($CO2_base) >1500){$color = $red;} else {$color = $black;}
ImageTTFText($im, $fontSize*2, 0, $X5, $cursorY, $color, $fontr, $CO2_base);
$color = $black;
ImageTTFText($im, $fontSize*1.5, 0, $X6, $cursorY-$fontSize/4, $color, $fontr, "ppm");
$cursorY = $cursorY+$fontSize*2.1;
ImageTTFText($im, $fontSize*1.3, 0, $X0, $cursorY, $color, $fontr, $name_mod1);
ImageTTFText($im, $fontSize*0.55, 0, $X3, $cursorY, $color, $fontr, "Luftfeuchtigkeit");
ImageTTFText($im, $fontSize*0.55, 0, $X5, $cursorY, $color, $fontr, "CO2-Gehalt");
$cursorY = $cursorY+$fontSize*2.1;
ImageTTFText($im, $fontSize*2, 0, $X0, $cursorY, $color, $fontr, $temp_mod1);
ImageTTFText($im, $fontSize*0.55, 0, $X1, $cursorY-$fontSize*1.2, $color, $fontr, $max_temp_mod1." / ".$max_date_mod1);
ImageTTFText($im, $fontSize*0.55, 0, $X1, $cursorY-$fontSize/3, $color, $fontr, $min_temp_mod1." / ".$min_date_mod1);
ImageTTFText($im, $fontSize*1.5, 0, $X2, $cursorY-$fontSize/4, $color, $fontr, "°C");
If(intval($humi_mod1) > 60 intval($humi_mod1) < 40){$color = $red;} else {$color = $black;}
ImageTTFText($im, $fontSize*2, 0, $X3, $cursorY, $color, $fontr, $humi_mod1);
$color = $black;
ImageTTFText($im, $fontSize*1.5, 0, $X4, $cursorY-$fontSize/4, $color, $fontr, "%");
If(intval($CO2_mod1) > 1500){$color = $red;} else {$color = $black;}
ImageTTFText($im, $fontSize*2, 0, $X5, $cursorY, $color, $fontr, $CO2_mod1);
$color = $black;
ImageTTFText($im, $fontSize*1.5, 0, $X6, $cursorY-$fontSize/4, $color, $fontr, "ppm");
$cursorY = $cursorY+$fontSize*2.1;
ImageTTFText($im, $fontSize*1.3, 0, $X0, $cursorY, $color, $fontr, $name_mod3);
ImageTTFText($im, $fontSize*0.55, 0, $X3, $cursorY, $color, $fontr, "Luftfeuchtigkeit");
ImageTTFText($im, $fontSize*0.55, 0, $X5, $cursorY, $color, $fontr, "CO2-Gehalt");
$cursorY = $cursorY+$fontSize*2.1;
ImageTTFText($im, $fontSize*2, 0, $X0, $cursorY, $color, $fontr, $temp_mod3);
ImageTTFText($im, $fontSize*0.55, 0, $X1, $cursorY-$fontSize*1.2, $color, $fontr, $max_temp_mod3." / ".$max_date_mod3);
ImageTTFText($im, $fontSize*0.55, 0, $X1, $cursorY-$fontSize/3, $color, $fontr, $min_temp_mod3." / ".$min_date_mod3);
ImageTTFText($im, $fontSize*1.5, 0, $X2, $cursorY-$fontSize/4, $color, $fontr, "°C");
If(intval($humi_mod3) > 60 intval($humi_mod3) < 40){$color = $red;} else {$color = $black;}
ImageTTFText($im, $fontSize*2, 0, $X3, $cursorY, $color, $fontr, $humi_mod3);
$color = $black;
ImageTTFText($im, $fontSize*1.5, 0, $X4, $cursorY-$fontSize/4, $color, $fontr, "%");
If(intval($CO2_mod3) >1500){$color = $red;} else {$color = $black;}
ImageTTFText($im, $fontSize*2, 0, $X5, $cursorY, $color, $fontr, $CO2_mod3);
$color = $black;
ImageTTFText($im, $fontSize*1.5, 0, $X6, $cursorY-$fontSize/4, $color, $fontr, "ppm");
// PNG-erstellen, könnte man für's c't-Projekt auch weglassen
ImagePNG($im, $filename);
// wenn Aufruf mit 'weather-script.php?debug=true', dann Ausgabe als png, sonst als Bytestream für's ePaper
if($_GET['debug'] == 'true'){
header("Content-type: image/png");
imagepng($im);
}
else{
header("X-productionMode: true"); // siehe Anleitung c't, auf false setzen um ESP32 Weboberfläche zu aktivieren
echo rawImage($im);
// echo ASCIIImage($im); // stellt die Grafik im browser aus nullen und einsen dar, nur zum testen
}
imagedestroy($im);
function rawImage($im) {
$bits = "";
$bytes = "";
$pixelcount = 0;
for ($y = 0; $y < imagesy($im); $y++) {
for ($x = 0; $x < imagesx($im); $x++) {
$rgb = imagecolorat($im, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8 ) & 0xFF;
$b = $rgb & 0xFF;
$gray = ($r + $g + $b) / 3;
if ($gray < 0xFF) {
$bits .= "1";
}else {
$bits .= "0";
}
$pixelcount++;
if ($pixelcount % 8 == 0) {
$bytes .= pack('H*', str_pad(base_convert($bits, 2, 16),2, "0", STR_PAD_LEFT));
$bits = "";
}
}
}
return $bytes;
}
// diese Funktion ist nur zum testen ob die Konvertierung grundsätzlich klappt
function ASCIIImage($im) {
$bits = "";
$bytes = "";
$pixelcount = 0;
for ($y = 0; $y < imagesy($im); $y++) {
for ($x = 0; $x < imagesx($im); $x++) {
$rgb = imagecolorat($im, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8 ) & 0xFF;
$b = $rgb & 0xFF;
$gray = ($r + $g + $b) / 3;
if ($gray < 0xFF) {
$bits .= "1";
}else {
$bits .= "0";
}
$pixelcount++;
if ($pixelcount % 8 == 0) {
$bytes .= $bits;
$bits = "";
}
}
$bytes .= "\n";
}
return $bytes;
}
function DatumText($ti) {
$time = $ti;
$weekday = date("D", $time);
$day = date("j", $time);
$month = date("n", $time);
$year = date("Y", $time);
$zeit = date("H:i", $time);
switch ($weekday) {
case "Mon":
$weekday = "Montag";
break;
case "Tue":
$weekday = "Dienstag";
break;
case "Wed":
$weekday = "Mittwoch";
break;
case "Thu":
$weekday = "Donnerstag";
break;
case "Fri":
$weekday = "Freitag";
break;
case "Sat":
$weekday = "Samstag";
break;
case "Sun":
$weekday = "Sonntag";
break;
}
switch ($month) {
case 1:
$month = "Januar";
break;
case 2:
$month = "Februar";
break;
case 3:
$month = "März";
break;
case 4:
$month = "April";
break;
case 5:
$month = "Mai";
break;
case 6:
$month = "Juni";
break;
case 7:
$month = "Juli";
break;
case 8:
$month = "August";
break;
case 9:
$month = "September";
break;
case 10:
$month = "Oktober";
break;
case 11:
$month = "November";
break;
case 12:
$month = "Dezember";
break;
}
return "$weekday, $day. $month $year $zeit";
}
?>