Habe mir eine WebApp für den Batterie-Status gebaut
Anzeige
Re: Habe mir eine WebApp für den Batterie-Status gebaut
- Profil
- Beiträge: 467
- Registriert: So 8. Jan 2017, 15:28
- Wohnort: Hamburg
- Hat sich bedankt: 36 Mal
- Danke erhalten: 16 Mal
Ich mache jetzt keinen neuen Thread draus
Basierend auf der API, hab ich mir einen "Amazon Alexa Skill" (App/Erweiterung für Alexa) gebastelt, der mir den aktuellen Batterie-Status ansagt. Ich sage lediglich: "Alexa, frage i3 nach dem Batterie-Status". Und schon liest Alexa den aktuellen Status vor. Die Ansage unterscheidet sich je nach dem, ob der Wagen lädt oder eben nicht.
So ungefähr nach diesem Muster wird geantwortet, wenn der Ladevorgang aktiv ist: "Batterie Status: Der Ladevorgang ist aktiv und endet voraussichtlich in 2 Stunden und 51 Minuten. Die verbleibende Batterieladung beträgt 82 %. Daraus ergibt sich eine Reichweite von 106 km."
Ich hab mal ein Anfängervideo gedreht (miese Quali, ich weiß), damit ihr ungefähr wisst, wie sich das anhört.
https://www.dropbox.com/s/ewe73qfsjl1t7 ... 5.m4v?dl=0
P.S. Es gibt bereits eine BMW Connected "App" für Alexa, die ich aber nicht nehmen wollte, da sie den vollständigen Remote-Zugriff auf das Fahrzeug zulässt. Und das wollte ich nicht, da ich Alexa nicht alleine nutze und vermeiden will, dass der Wagen "zufällig" z.B. entsperrt wird. Ein Read-only Zugriff ist mir da viel lieber
Basierend auf der API, hab ich mir einen "Amazon Alexa Skill" (App/Erweiterung für Alexa) gebastelt, der mir den aktuellen Batterie-Status ansagt. Ich sage lediglich: "Alexa, frage i3 nach dem Batterie-Status". Und schon liest Alexa den aktuellen Status vor. Die Ansage unterscheidet sich je nach dem, ob der Wagen lädt oder eben nicht.
So ungefähr nach diesem Muster wird geantwortet, wenn der Ladevorgang aktiv ist: "Batterie Status: Der Ladevorgang ist aktiv und endet voraussichtlich in 2 Stunden und 51 Minuten. Die verbleibende Batterieladung beträgt 82 %. Daraus ergibt sich eine Reichweite von 106 km."
Ich hab mal ein Anfängervideo gedreht (miese Quali, ich weiß), damit ihr ungefähr wisst, wie sich das anhört.
https://www.dropbox.com/s/ewe73qfsjl1t7 ... 5.m4v?dl=0
P.S. Es gibt bereits eine BMW Connected "App" für Alexa, die ich aber nicht nehmen wollte, da sie den vollständigen Remote-Zugriff auf das Fahrzeug zulässt. Und das wollte ich nicht, da ich Alexa nicht alleine nutze und vermeiden will, dass der Wagen "zufällig" z.B. entsperrt wird. Ein Read-only Zugriff ist mir da viel lieber
Re: Habe mir eine WebApp für den Batterie-Status gebaut
- Profil
- Beiträge: 3577
- Registriert: Sa 28. Dez 2013, 20:01
- Wohnort: Bei Stuttgart
- Hat sich bedankt: 16 Mal
- Danke erhalten: 10 Mal
-
DANKE - vor allem dafür den Stein hier ins Rollen gebracht zu haben
Jetzt habe ich mir noch den Verbrauch zwischen zwei Abrufen reingequetscht. Wird so langsam eng (Einheiten habe ich schon kleiner dargestellt).Technikblog & Shop: http://www.elektrifiziert.net
PV 18,2kWp - BHKW EcoPower 1.0, 30kWh LiON Sunny Island System
BMW i3 60Ah (Verbrauch ca. 13,8kWh/100km; SW: I001-16-07-506)
PV 18,2kWp - BHKW EcoPower 1.0, 30kWh LiON Sunny Island System
BMW i3 60Ah (Verbrauch ca. 13,8kWh/100km; SW: I001-16-07-506)
Re: Habe mir eine WebApp für den Batterie-Status gebaut
- Profil
- Beiträge: 3577
- Registriert: Sa 28. Dez 2013, 20:01
- Wohnort: Bei Stuttgart
- Hat sich bedankt: 16 Mal
- Danke erhalten: 10 Mal
-
Das wäre ja zwecklos.Knobi hat geschrieben:Gibts da irgendwie ne Anleitung für Doofe?
Als erstes braucht Du einen webspace mit PHP Umgebung. Gibt es für <10€/monat. Wenn Du daheim einen Rechner immer laufen hast kannst Du auch dort "hosten" - musst Dich dann aber mit nginx, Apache oder ISS .... beschäftigen. Wenn der Serverteil bei Dir läuft helfe ich auch gerne weiter.
Technikblog & Shop: http://www.elektrifiziert.net
PV 18,2kWp - BHKW EcoPower 1.0, 30kWh LiON Sunny Island System
BMW i3 60Ah (Verbrauch ca. 13,8kWh/100km; SW: I001-16-07-506)
PV 18,2kWp - BHKW EcoPower 1.0, 30kWh LiON Sunny Island System
BMW i3 60Ah (Verbrauch ca. 13,8kWh/100km; SW: I001-16-07-506)
Re: Habe mir eine WebApp für den Batterie-Status gebaut
- Profil
- Beiträge: 467
- Registriert: So 8. Jan 2017, 15:28
- Wohnort: Hamburg
- Hat sich bedankt: 36 Mal
- Danke erhalten: 16 Mal
Sehr gerne.endurance hat geschrieben:DANKE - vor allem dafür den Stein hier ins Rollen gebracht zu haben
Man könnte auch den Batterie-Prozentwert unten bei den Metriken abbilden, dann hätte man noch die Hälfte der Höhe für weitere Werte.endurance hat geschrieben: Wird so langsam eng (Einheiten habe ich schon kleiner dargestellt).
Re: Habe mir eine WebApp für den Batterie-Status gebaut
- Profil
- Beiträge: 3577
- Registriert: Sa 28. Dez 2013, 20:01
- Wohnort: Bei Stuttgart
- Hat sich bedankt: 16 Mal
- Danke erhalten: 10 Mal
-
bzgl. Platz: ich habe jetzt ne Weile rumgespielt aber stehe scheints mit CSS auf Kriegsfuß wie bekomme ich den Fuß mit den Werten dichter an der SOC % Wasserstandsmelder? Die Positions der % Anzeige soll so bleiben
]
Code: Alles auswählen
main {
top: 40%;
left: 50%;
width: 200px;
height: 200px;
position: absolute;
border: 12px solid var(--color-main-border);
border-radius: 100%;
overflow: hidden;
transform: translate(-50%, -50%);
box-shadow: 0 0 0 transparent;
transition: all 1.6s cubic-bezier(0.165, 0.84, 0.44, 1);
}...
Technikblog & Shop: http://www.elektrifiziert.net
PV 18,2kWp - BHKW EcoPower 1.0, 30kWh LiON Sunny Island System
BMW i3 60Ah (Verbrauch ca. 13,8kWh/100km; SW: I001-16-07-506)
PV 18,2kWp - BHKW EcoPower 1.0, 30kWh LiON Sunny Island System
BMW i3 60Ah (Verbrauch ca. 13,8kWh/100km; SW: I001-16-07-506)
Re: Habe mir eine WebApp für den Batterie-Status gebaut
- Profil
- Beiträge: 467
- Registriert: So 8. Jan 2017, 15:28
- Wohnort: Hamburg
- Hat sich bedankt: 36 Mal
- Danke erhalten: 16 Mal
Ich glaube, da muss du an dem height-Wert bei <footer> drehen. Je höhe, desto mehr Höhe hat der Container und somit näher zur Mitte.
Re: Habe mir eine WebApp für den Batterie-Status gebaut
- Profil
- Beiträge: 3577
- Registriert: Sa 28. Dez 2013, 20:01
- Wohnort: Bei Stuttgart
- Hat sich bedankt: 16 Mal
- Danke erhalten: 10 Mal
-
war mir sicher das ich das schon ausprobiert hatte - aber warscheinlich den refresh vergessen. Funkioniert- danke.
Jetz sehe ich endlich auch die mileage auf dem iphone ohne vollbild oder scrollen.
was jetzt auch besser trackbar und schriftlich einfacher festzuhalten sind die SOC Sprünge
14:04 Auto abgestellt und 19:00 eingesteckt. Die erste Zeit ist nat. falsch - war der Server wohl gerade mitten am aktuaslisieren oder bug bei mir im code. Aber der SOC Sprung von 16.7 auf 17.7 (ohne dazwischen zu laden) zeigt, dass nicht nur NUll und MAxdurchgang von der Spannung abhängen. Da ich kurz vor dem Abstellen bei Kälte rechtzügig gefahren bin (31kWh/100km) ist ein SPannungseinbruch zu erklären, genauso der Anstieg beim Einstecken. Wäre richtig cool wenn man Strom, Spannung etc. auch via App abfragen könnte.
Jetz sehe ich endlich auch die mileage auf dem iphone ohne vollbild oder scrollen.
was jetzt auch besser trackbar und schriftlich einfacher festzuhalten sind die SOC Sprünge
Code: Alles auswählen
29.01.2017 14:04; 1485698662000; 103; 97; 16,69; 17,52; --:--; 42622; -.-; 31.3
29.01.2017 14:04; 1485698662000; 103; 97; 17,70; 18,46; --:--; 42622; --.-; 31.3
29.01.2017 19:01; 1485716519000; 109; 96; 17,71; 18,46; 01:10; 42622; --.-; --.-
29.01.2017 19:04; 1485716687000; 109; 96; 17,73; 18,46; 01:07; 42622; 0.4; --.-
29.01.2017 19:05; 1485716713000; 109; 96; 17,74; 18,46; 01:06; 42622; 1.4; --.-
29.01.2017 19:06; 1485716791000; 109; 96; 17,75; 18,46; 01:05; 42622; 0.5; --.-
Technikblog & Shop: http://www.elektrifiziert.net
PV 18,2kWp - BHKW EcoPower 1.0, 30kWh LiON Sunny Island System
BMW i3 60Ah (Verbrauch ca. 13,8kWh/100km; SW: I001-16-07-506)
PV 18,2kWp - BHKW EcoPower 1.0, 30kWh LiON Sunny Island System
BMW i3 60Ah (Verbrauch ca. 13,8kWh/100km; SW: I001-16-07-506)
Re: Habe mir eine WebApp für den Batterie-Status gebaut
- Profil
- Beiträge: 3577
- Registriert: Sa 28. Dez 2013, 20:01
- Wohnort: Bei Stuttgart
- Hat sich bedankt: 16 Mal
- Danke erhalten: 10 Mal
-
coole Sache gibt es das auch zum reuse? Was sind die Requirements - spezielles Aufnahmedevice, echo dot...? Scheint mir auch für Smarthome ganz interessant... gerade selber was gefundensystematic hat geschrieben: Ich hab mal ein Anfängervideo gedreht (miese Quali, ich weiß), damit ihr ungefähr wisst, wie sich das anhört.
https://www.dropbox.com/s/ewe73qfsjl1t7 ... 5.m4v?dl=0
http://www.computerbild.de/artikel/cb-N ... 18903.html
https://developer.amazon.com/public/sol ... -skill-api
Wobei hmm muss mir mal anschauen was da so für daten hin und her gehen.
Und hier nochmals meine php und html Datei, hab noch einige Verbesserungen eingebaut um PHP Fehler/Warnungen zu vermeiden. Auch wird der letzte bekannte Verbrauch/Ladeleistung nun angezeigt (vorher -- wenn kein SOC unterschied).
Code: Alles auswählen
<?php
class Battery_API {
private $token_file = './access/token.json';
private $auth_file = './access/auth.json';
private $stats_file = './appstats.txt';
private $auth_api = 'https://customer.bmwgroup.com/gcdm/oauth/authenticate';
private $vehicle_api = 'https://www.bmw-connecteddrive.de/api/vehicle';
private $auth;
private $token;
private $json;
function __construct () {
$this->check_security();
$this->auth = $this->get_auth_data();
$this->token = $this->get_token();
$this->json = $this->get_vehicle_data();
$this->send_response_json();
}
function check_security() {
if ( empty( $_SERVER['HTTP_REFERER'] ) OR strcmp( parse_url( $_SERVER['HTTP_REFERER'], PHP_URL_HOST ), $_SERVER['SERVER_NAME'] ) !== 0 ) {
http_response_code( 403 ) && exit;
}
}
function get_auth_data() {
return json_decode(
@file_get_contents(
$this->auth_file
)
);
}
function cache_remote_token( $token_data ) {
file_put_contents(
$this->token_file,
json_encode( $token_data )
);
}
function get_cached_token() {
return json_decode(
@file_get_contents(
$this->token_file
)
);
}
function get_token() {
// Get cached token
if ( $cached_token_data = $this->get_cached_token() ) {
if ( $cached_token_data->expires > time() ) {
$token = $cached_token_data->token;
}
}
// Get remote token
if ( empty( $token ) ) {
$token_data = $this->get_remote_token();
$token = $token_data->token;
$this->cache_remote_token( $token_data );
}
return $token;
}
function get_remote_token() {
// Init cURL
$ch = curl_init();
// Set cURL options
curl_setopt( $ch, CURLOPT_URL, $this->auth_api );
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, false );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch, CURLOPT_HEADER, true );
curl_setopt( $ch, CURLOPT_NOBODY, true );
curl_setopt( $ch, CURLOPT_COOKIESESSION, true );
curl_setopt( $ch, CURLOPT_POST, true );
curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/x-www-form-urlencoded' ) );
curl_setopt( $ch, CURLOPT_POSTFIELDS, 'username=' . urlencode( $this->auth->username) . '&password=' . urlencode( $this->auth->password) . '&client_id=dbf0a542-ebd1-4ff0-a9a7-55172fbfce35&redirect_uri=https%3A%2F%2Fwww.bmw-connecteddrive.com%2Fapp%2Fdefault%2Fstatic%2Fexternal-dispatch.html&response_type=token&scope=authenticate_user%20fupo&state=eyJtYXJrZXQiOiJkZSIsImxhbmd1YWdlIjoiZGUiLCJkZXN0aW5hdGlvbiI6ImxhbmRpbmdQYWdlIn0&locale=DE-de' );
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false);
// Exec curl request
$response = curl_exec( $ch );
// Close connection
curl_close( $ch );
// Extract token
preg_match( '/access_token=([\w\d]+).*token_type=(\w+).*expires_in=(\d+)/', $response, $matches );
// Check token type
if ( empty( $matches[2] ) OR $matches[2] !== 'Bearer' ) {
http_response_code( 424 ) && exit;
}
return (object) array(
'token' => $matches[1],
'expires' => time() + $matches[3]
);
}
function get_vehicle_data() {
// Init cURL
$ch_1 = curl_init();
$ch_2 = curl_init();
// Set cURL options
curl_setopt( $ch_1, CURLOPT_URL, $this->vehicle_api . '/dynamic/v1/' . $this->auth->vehicle . '?offset=-60' );
curl_setopt( $ch_1, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json' , 'Authorization: Bearer ' . $this->token ) );
curl_setopt( $ch_1, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch_1, CURLOPT_FOLLOWLOCATION, true );
curl_setopt( $ch_1, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt( $ch_2, CURLOPT_URL, $this->vehicle_api . '/navigation/v1/' . $this->auth->vehicle );
curl_setopt( $ch_2, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json' , 'Authorization: Bearer ' . $this->token ) );
curl_setopt( $ch_2, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch_2, CURLOPT_FOLLOWLOCATION, true );
curl_setopt( $ch_2, CURLOPT_SSL_VERIFYPEER, false);
// Build the multi-curl handle
$mh = curl_multi_init();
curl_multi_add_handle( $mh, $ch_1 );
curl_multi_add_handle( $mh, $ch_2 );
// Execute all queries simultaneously
$running = null;
do {
curl_multi_exec( $mh, $running );
} while ( $running );
// Close the handles
curl_multi_remove_handle( $mh, $ch_1 );
curl_multi_remove_handle( $mh, $ch_2 );
curl_multi_close( $mh );
// all of our requests are done, we can now access the results
$response_1 = curl_multi_getcontent( $ch_1 );
$response_2 = curl_multi_getcontent( $ch_2 );
// Decode response
$json = (object)array_merge(
json_decode( $response_1, true )['attributesMap'],
json_decode( $response_2, true )
);
// Exit if error
if ( json_last_error() ) {
http_response_code( 500 ) && exit;
}
return $json;
}
function read_last_line() {
// check for empty file
if(filesize($this->stats_file)===0)
return " ";
$fp = fopen($this->stats_file, "r");
$pos = -2;
$t = " ";
if($fp)
{
while ($t != "\n")
{
if(fseek($fp, $pos, SEEK_END)!=0)
{
break;
}
$t = fgetc($fp);
$pos = $pos - 1;
}
$t = fgets($fp);
fclose($fp);
}
return $t;
}
function send_response_json() {
// Set JSON vars
$attributes = $this->json;
$chargingPower='-.-';
$consumption='-.-';
$consumptionPower=0.0;
$act_stateOfCharge=0.0;
$last_stateOfCharge=0.0;
$elapsedtime=0;
$socchange=0.0;
$mileagechange=0.0;
$updateTime = $attributes->updateTime_converted;
$updateTime_timestamp = $attributes->updateTime_converted_timestamp;
$electricRange = intval( $attributes->beRemainingRangeElectricKm );
$chargingLevel = intval( $attributes->chargingLevelHv );
$chargingActive = intval( $attributes->chargingSystemStatus === 'CHARGINGACTIVE' );
// remaining charging time is only included while charging so check first to avoid PHP notice
if(isset($attributes->chargingTimeRemaining))
$chargingTimeRemaining = intval( $attributes->chargingTimeRemaining );
else
$chargingTimeRemaining=0;
//$chargingClock = strftime("%a %H:%M",time()+mktime(0,$chargingTimeRemaining,0,1,1,1970));
$chargingClock = ($chargingTimeRemaining ? strftime("%a %H:%M",time()+mktime(1,$chargingTimeRemaining,0,1,1,1970)):'--:--' );
$chargingTimeRemaining = ( $chargingTimeRemaining ? ( date( 'H:i', mktime( 0, $chargingTimeRemaining ) )) : '--:--' );
$stateOfCharge = number_format( round( $attributes->soc, 2 ), 2, ',', '.');
$act_stateOfCharge=$stateOfCharge;
$stateOfChargeMax = number_format( round( $attributes->socMax, 2 ), 2, ',', '.');
$doorLockState = intval( $attributes->door_lock_state === 'SECURED' );
if($doorLockState == '1')
$doorLockState='CLOSED';
else
$doorLockState='OPEN';
$mileage = intval( $attributes->mileage );
$actline = $updateTime.";\t".$updateTime_timestamp.";\t".$electricRange.";\t\t".$chargingLevel.";\t\t\t\t".$stateOfCharge.";\t".$stateOfChargeMax.";\t".$chargingTimeRemaining.";\t".$mileage;
$lastline = $this->read_last_line();
if($lastline!=" " && $lastline!='')
{
$lastpower=0.0;
// extract the last and actual values
list($last_updatetime,$last_updatetime_timestamp,$last_electricRange,$last_chargingLevel,$last_stateOfCharge,$last_stateOfChargeMax,$last_chargingTimeRemaining,$last_mileage,$last_chargingpower,$last_consumption) = str_getcsv($lastline, ';');
// only to be symmetric to last line
list($act_updatetime, $act_updatetime_timestamp, $act_electricRange, $act_chargingLevel, $act_stateOfCharge, $act_stateOfChargeMax, $act_chargingTimeRemaining,$act_mileage) = str_getcsv($actline, ';');
// from german to english number format
$last_stateOfCharge=str_replace(',','.',$last_stateOfCharge);
$act_stateOfCharge=str_replace(',','.',$act_stateOfCharge);
// calc soc change
$socchange=$act_stateOfCharge-$last_stateOfCharge;
$elapsedtime=$act_updatetime_timestamp-$last_updatetime_timestamp;
$mileagechange=$act_mileage-$last_mileage;
if($elapsedtime!=0)
{
// convert to hours
$elapsedtime=($elapsedtime/1000)/3600;
// caculate charging
$chargingPower=$socchange/$elapsedtime;
// cut off after x digits
if($chargingPower>10)
$chargingPower=round($chargingPower,1);
else
$chargingPower=round($chargingPower,2);
if($chargingPower<=0)
if($last_chargingpower>0)
$chargingPower=$last_chargingpower;
else
$chargingPower='-.--';
// calculate consumption
if($mileagechange>0)
{
$consumption=(($socchange*(-1))/$mileagechange)*100;
if($consumption>10)
$consumption=round($consumption,1);
else
$consumption=round($consumption,2);
if($consumption<=0)
{
if($last_consumption>0)
$consumption=$last_consumption;
else
$consumption='-.--';
}
}
else
{
if($socchange<0)
{
// eg. while heating without driving
$consumption=99.9;
}
}
$actline = $actline.";\t".$chargingPower.";\t".$consumption."\r\n";
}
else
{
if($last_consumption>0)
$consumption=$last_consumption;
if($last_chargingpower>0)
$chargingPower=$last_chargingpower;
// should not be needed as if elapsedtime ==0 SOC should also be unchanged and not new line will be written to stats file
$actline = $actline.";\t".$chargingPower.";\t".$consumption."\r\n";
}
}
else
$actline = $actline.";\t--.--;\t--.--"."\r\n";
//simple debug output
//$doorLockState='>'.$lastline.'<';
// log changes (only if SOC has really be changed)
if($lastline!=$actline && $act_stateOfCharge!=$last_stateOfCharge && $mileage!=0)
{
$filehandle = fopen($this->stats_file,'a');
if($filehandle)
{
fputs($filehandle,$actline);
fclose($filehandle);
}
}
// Send Header
header('Access-Control-Allow-Origin: https://' . $_SERVER['SERVER_NAME'] );
header('Content-Type: application/json; charset=utf-8');
// Send JSON
die(
json_encode(
array(
'updateTime' => $updateTime,
'electricRange' => $electricRange,
'chargingLevel' => $chargingLevel,
'chargingActive' => $chargingActive,
'chargingTimeRemaining' => $chargingTimeRemaining,
'stateOfCharge' => $stateOfCharge,
'stateOfChargeMax' => $stateOfChargeMax,
'doorLockState' => $doorLockState,
'mileage' => $mileage,
'chargingClock' => $chargingClock,
'chargingPower' => $chargingPower,
'consumption' => $consumption
)
)
);
}
}
new Battery_API();
Code: Alles auswählen
<!DOCTYPE HTML>
<html manifest="battery.appcache">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Battery Status">
<link rel="preload" href="./fonts/advent-pro-100-reduced.woff2?2017-01-21-v3" as="font" type="font/woff2" crossorigin>
<link rel="apple-touch-icon" href="./img/apple-touch-icon.png?2017-01-21-v3" type="image/png">
<link rel="icon" href="img/favicon.ico?2017-01-21-v3" type="image/x-icon">
<title>i3 Status</title>
<style>
* {
border: 0;
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--color-white: rgb(254, 254, 254);
--color-white-light: rgba(254, 254, 254, .6);
--color-green: #509000;
--color-blue: #1c69d4;
--color-blue-light: #C7EEFF;
--color-red: #F90000;
--color-orange: #DC9012;
--color-purple: #f0f;
--color-cyan: #0ff;
--color-main-border: #222a32;
--color-page-gradient-from: #485563;
--color-page-gradient-to: #29323c;
}
@font-face {
font-family: 'Advent Pro';
font-style: normal;
font-weight: 100;
src: local('Advent Pro Thin'), local('AdventPro-Thin'), url('./fonts/advent-pro-100-reduced.woff2?2017-01-21-v3') format('woff2');
}
html {
height: 100%;
background: linear-gradient( to bottom right, var(--color-page-gradient-from), var(--color-page-gradient-to) );
color: var(--color-white);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
text-transform: uppercase;
text-align: center;
-webkit-font-smoothing: antialiased;
}
h2 {
font-size: 22px;
font-weight: 200;
line-height: 24px;
letter-spacing: 1px;
}
h4 {
color: var(--color-white-light);
font-size: 14px;
font-weight: 200;
line-height: 16px;
letter-spacing: 1px;
}
units {
font-size: 14px;
font-weight: 200;
line-height: 16px;
letter-spacing: 1px;
}
section {
margin-bottom: 20px;
flex-basis: 50%;
max-width: 50%;
}
header {
top: 0;
width: 100%;
padding: 45px 0 4px;
position: absolute;
}
footer {
width: 100%;
height: 30%;
bottom: 0;
position: absolute;
display: flex;
flex-flow: row wrap;
align-items: center;
justify-content: center;
}
header,
footer {
opacity: 1;
transition: opacity .25s;
}
header:empty,
footer:empty {
opacity: 0;
}
main {
top: 40%;
left: 50%;
width: 200px;
height: 200px;
position: absolute;
border: 12px solid var(--color-main-border);
border-radius: 100%;
overflow: hidden;
transform: translate(-50%, -50%);
box-shadow: 0 0 0 transparent;
transition: all 1.6s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.loading main {
display: flex;
align-items: center;
justify-content: center;
animation: pulse 1.7s infinite;
}
.loading main::after {
display: block;
content: 'Updating';
color: var(--color-white);
font-size: 22px;
font-weight: 200;
line-height: 24px;
letter-spacing: 1px;
}
.percent {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
z-index: 3;
display: flex;
align-items: center;
justify-content: center;
}
.percent::before {
content: attr(data-percent);
display: block;
font-family: 'Advent Pro';
font-size: 80px;
line-height: 80px;
font-weight: 100;
text-rendering: optimizeLegibility;
}
.percent::after {
top: 58px;
right: 20px;
position: absolute;
content: '%';
display: block;
font-size: 26px;
line-height: 26px;
font-weight: 100;
opacity: .6;
}
.water {
bottom: 0;
left: 0;
width: 100%;
position: absolute;
z-index: 2;
background: var(--color-green);
}
.wave {
width: 200%;
bottom: 100%;
display: none;
position: absolute;
}
.wave-back {
right: 0;
fill: var(--color-blue-light);
animation: wave-back 1.4s infinite linear;
}
.wave-front {
left: 0;
margin-bottom: -1px;
fill: var(--color-blue);
animation: wave-front .7s infinite linear;
}
@keyframes wave-front {
100% {
transform: translate(-50%, 0);
}
}
@keyframes wave-back {
100% {
transform: translate(50%, 0);
}
}
@keyframes pulse {
50% {
box-shadow: -5px 0 20px var(--color-purple), 5px 0 20px var(--color-cyan);
}
}
[data-percent^="0"] + .water,
[data-percent^="1"] + .water,
[data-percent^="2"] + .water {
background: var(--color-red);
}
[data-percent^="3"] + .water,
[data-percent^="4"] + .water {
background: var(--color-orange);
}
[data-percent="100"] + .water {
background: var(--color-green);
}
[data-charging="1"].water {
background: var(--color-blue);
}
[data-charging="1"] .wave {
display: block;
}
</style>
<script id="header-tmpl" type="text/x-dot-template">
<h2>
Mein i3 Status
</h2>
<h4>
{{=it.updateTime}}
</h4>
</script>
<script id="main-tmpl" type="text/x-dot-template">
<span class="percent" data-percent="{{=it.chargingLevel}}"></span>
<div class="water" data-charging="{{=it.chargingActive}}" style="height:{{=it.chargingLevel}}%">
<svg viewBox="0 0 560 20" class="wave wave-back">
<use xlink:href="#wave"></use>
</svg>
<svg viewBox="0 0 560 20" class="wave wave-front">
<use xlink:href="#wave"></use>
</svg>
</div>
</script>
<script id="footer-tmpl" type="text/x-dot-template">
<section>
<h4>
RANGE/AV CONS.
</h4>
<h2>
{{=it.electricRange}} <units>KM</units>/{{=it.consumption}} <units>KWH/...</units>
</h2>
</section>
<section>
<h4>
SOC ACT/SOC MAX
</h4>
<h2>
{{=it.stateOfCharge}}/{{=it.stateOfChargeMax}} <units>KWH</units>
</h2>
</section>
<section>
<h4>
CHARGED IN/AV POWER
</h4>
<h2>
{{=it.chargingTimeRemaining}} <units>H</units>/{{=it.chargingPower}} <units>KW</units>
</h2>
</section>
<section>
<h4>
FULLY CHARGED AT
</h4>
<h2>
{{=it.chargingClock}} <units>H</units>
</h2>
</section>
<section>
<h4>
CAR DOOR STATUS
</h4>
<h2>
{{=it.doorLockState}}
</h2>
</section>
<section>
<h4>
MILEAGE
</h4>
<h2>
{{=it.mileage}} <units>KM</units>
</h2>
</section>
</script>
</head>
<body class="loading">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" hidden>
<symbol id="wave">
<path d="M420,20c21.5-0.4,38.8-2.5,51.1-4.5c13.4-2.2,26.5-5.2,27.3-5.4C514,6.5,518,4.7,528.5,2.7c7.1-1.3,17.9-2.8,31.5-2.7c0,0,0,0,0,0v20H420z"></path>
<path d="M420,20c-21.5-0.4-38.8-2.5-51.1-4.5c-13.4-2.2-26.5-5.2-27.3-5.4C326,6.5,322,4.7,311.5,2.7C304.3,1.4,293.6-0.1,280,0c0,0,0,0,0,0v20H420z"></path>
<path d="M140,20c21.5-0.4,38.8-2.5,51.1-4.5c13.4-2.2,26.5-5.2,27.3-5.4C234,6.5,238,4.7,248.5,2.7c7.1-1.3,17.9-2.8,31.5-2.7c0,0,0,0,0,0v20H140z"></path>
<path d="M140,20c-21.5-0.4-38.8-2.5-51.1-4.5c-13.4-2.2-26.5-5.2-27.3-5.4C46,6.5,42,4.7,31.5,2.7C24.3,1.4,13.6-0.1,0,0c0,0,0,0,0,0l0,20H140z"></path>
</symbol>
</svg>
<header id="header"></header>
<main id="main"></main>
<footer id="footer"></footer>
<script src="./scripts/doT.min.js"></script>
<script>
const req = new XMLHttpRequest();
req.addEventListener( 'load', function() {
if ( this.status !== 200 ) {
return alert( 'Request failed: ' + this.status );
}
const json = JSON.parse( this.responseText );
[ 'header', 'main', 'footer' ].forEach( id => {
document.getElementById( id ).innerHTML = doT.template( document.getElementById( `${id}-tmpl` ).text )( json );
} );
document.getElementsByTagName( 'body' )[0].classList.remove( 'loading' );
} );
req.open( 'GET', './api/' );
req.send();
</script>
<script>
window.addEventListener( 'load', function( e ) {
window.applicationCache.addEventListener( 'updateready', function( e ) {
if ( window.applicationCache.status == window.applicationCache.UPDATEREADY ) {
window.applicationCache.swapCache();
window.location.reload();
}
}, false );
}, false );
</script>
</body>
</html>
Technikblog & Shop: http://www.elektrifiziert.net
PV 18,2kWp - BHKW EcoPower 1.0, 30kWh LiON Sunny Island System
BMW i3 60Ah (Verbrauch ca. 13,8kWh/100km; SW: I001-16-07-506)
PV 18,2kWp - BHKW EcoPower 1.0, 30kWh LiON Sunny Island System
BMW i3 60Ah (Verbrauch ca. 13,8kWh/100km; SW: I001-16-07-506)
Re: Habe mir eine WebApp für den Batterie-Status gebaut
Am webspace solls nicht liegen, hab da was bei hosteurope.endurance hat geschrieben: Das wäre ja zwecklos.
Als erstes braucht Du einen webspace mit PHP Umgebung. Gibt es für <10€/monat. Wenn Du daheim einen Rechner immer laufen hast kannst Du auch dort "hosten" - musst Dich dann aber mit nginx, Apache oder ISS .... beschäftigen. Wenn der Serverteil bei Dir läuft helfe ich auch gerne weiter.
-
- Vergleichbare Themen
-
-
Werden überhaupt noch reine AC Ladestationen gebaut?
von prof_einst » Fr 19. Apr 2024, 13:48 » in Öffentliche Lade-Infrastruktur -
Letzter Beitrag von SandroMa
So 5. Mai 2024, 20:29
-
Werden überhaupt noch reine AC Ladestationen gebaut?
-
-
Sitzheizung Status bleibt bei Fahrtbeginn gespeichert
von GoSonnE » So 6. Aug 2023, 09:55 » in e-208 - Allgemeine Themen -
Letzter Beitrag von Lockenfrosch
Do 31. Aug 2023, 20:04
-
Sitzheizung Status bleibt bei Fahrtbeginn gespeichert
-
-
go-e Charger cloud Status ‚Nicht verbunden‘
von nervzwergl » So 20. Aug 2023, 18:09 » in Ladeequipment -
Letzter Beitrag von Sinenomine
So 20. Aug 2023, 20:59
-
go-e Charger cloud Status ‚Nicht verbunden‘
-
-
Blinken der Status-LED: Was will sie mir sagen?
von Ruhr_Atoll » Mi 15. Nov 2023, 11:01 » in Ampera-e - Laden, Ladeequipment -
Letzter Beitrag von TomTomZoe
Do 16. Nov 2023, 19:03
-
Blinken der Status-LED: Was will sie mir sagen?
-
-
Status regelmäßiger OTA-Updates
von bm3 » Fr 12. Jan 2024, 10:26 » in Tesla - modellübergreifend -
Letzter Beitrag von bm3
Do 28. Mär 2024, 21:55
-
Status regelmäßiger OTA-Updates