Habe mir eine WebApp für den Batterie-Status gebaut

AntwortenAntworten Options Options Arrow

Re: Habe mir eine WebApp für den Batterie-Status gebaut

USER_AVATAR
read
Schade :doof: verstehe kein Wort, hätte aber auch gern so ne App. Gibts da irgendwie ne Anleitung für Doofe?
BMW i3 von 03.2014 bis 12.2016 Bild
BMW i3 (94Ah) seit 12.2016 Bild
Model ☰ reserviert am 31.3.16
Anzeige

Re: Habe mir eine WebApp für den Batterie-Status gebaut

i3fahrer
  • Beiträge: 467
  • Registriert: So 8. Jan 2017, 15:28
  • Wohnort: Hamburg
  • Hat sich bedankt: 36 Mal
  • Danke erhalten: 16 Mal
read
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 ;)
Meine T-Shirts für E-MOBILISTEN

BMW i3 Fluid Black / 60 Ah / Since 2016

Re: Habe mir eine WebApp für den Batterie-Status gebaut

USER_AVATAR
read
DANKE - vor allem dafür den Stein hier ins Rollen gebracht zu haben
2017-01-29_14h52_08.png
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)

Re: Habe mir eine WebApp für den Batterie-Status gebaut

USER_AVATAR
read
Knobi hat geschrieben:Gibts da irgendwie ne Anleitung für Doofe?
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.
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)

Re: Habe mir eine WebApp für den Batterie-Status gebaut

i3fahrer
  • Beiträge: 467
  • Registriert: So 8. Jan 2017, 15:28
  • Wohnort: Hamburg
  • Hat sich bedankt: 36 Mal
  • Danke erhalten: 16 Mal
read
endurance hat geschrieben:DANKE - vor allem dafür den Stein hier ins Rollen gebracht zu haben
Sehr gerne.
endurance hat geschrieben: Wird so langsam eng (Einheiten habe ich schon kleiner dargestellt).
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.
Meine T-Shirts für E-MOBILISTEN

BMW i3 Fluid Black / 60 Ah / Since 2016

Re: Habe mir eine WebApp für den Batterie-Status gebaut

USER_AVATAR
read
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)

Re: Habe mir eine WebApp für den Batterie-Status gebaut

i3fahrer
  • Beiträge: 467
  • Registriert: So 8. Jan 2017, 15:28
  • Wohnort: Hamburg
  • Hat sich bedankt: 36 Mal
  • Danke erhalten: 16 Mal
read
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.
Meine T-Shirts für E-MOBILISTEN

BMW i3 Fluid Black / 60 Ah / Since 2016

Re: Habe mir eine WebApp für den Batterie-Status gebaut

USER_AVATAR
read
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

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;	--.-
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.
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)

Re: Habe mir eine WebApp für den Batterie-Status gebaut

USER_AVATAR
read
systematic 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
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 gefunden
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>

2017-01-29_22h20_25.png
Das war's dann aber für's erste - nächste Woche nicht im Lande.
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)

Re: Habe mir eine WebApp für den Batterie-Status gebaut

USER_AVATAR
read
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.
Am webspace solls nicht liegen, hab da was bei hosteurope.
BMW i3 von 03.2014 bis 12.2016 Bild
BMW i3 (94Ah) seit 12.2016 Bild
Model ☰ reserviert am 31.3.16
Anzeige
AntwortenAntworten

Zurück zu „i3 - Laden, Ladeequipment“

Gehe zu Profile
  • Vergleichbare Themen
    Antworten
    Zugriffe
    Letzter Beitrag