Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Skripten / Logik
    4. Bastellösung: Polestar Ladezustand via Tibber App API

    NEWS

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    • Monatsrückblick - April 2025

    Bastellösung: Polestar Ladezustand via Tibber App API

    This topic has been deleted. Only users with topic management privileges can see it.
    • Mirko Hufnagel
      Mirko Hufnagel @Stroell last edited by

      @stroell super, danke. Genau was ich gesucht habe.

      1 Reply Last reply Reply Quote 0
      • Sebastian Löb
        Sebastian Löb @Stroell last edited by

        @stroell vielen Dank für die Inspiration. Ich habe ein paar Deiner "unschönen" Punkte mit folgendem Script gelöst:

        • Token speichern und bei jedem Request den alten Token versuchen
        • Wenn ein 401 - unauthorized kommt: neuen Token anfragen
        • Rudimentäres Error-Handling
        const request = require('request');
        
        // Configuration Constants
        const EMAIL = 'YOUR_TIBBER_EMAIL';
        const PASSWORD = 'YOUR_TIBBER_PASSWORD';
        const IOBROKER_SOC_STATE = '0_userdata.0.Polestar.SoC'; // Replace with your actual state name
        const IOBROKER_LASTSEEN_STATE = '0_userdata.0.Polestar.LastSeen'; // Replace with your actual state name
        const IOBROKER_TOKEN_STATE = '0_userdata.0.Polestar.Token'; // Replace with your actual state name
        
        // Function to save the token to ioBroker state
        function saveTokenToState(token) {
            setState(IOBROKER_TOKEN_STATE, token, true);
        }
        
        // Function to get the token from ioBroker state
        function getTokenFromState() {
            return getState(IOBROKER_TOKEN_STATE).val;
        }
        
        // Function to perform the authentication request and get a token
        function authenticate(callback) {
            const authOptions = {
                uri: 'https://app.tibber.com/login.credentials',
                method: 'POST',
                json: {
                    "@type": "login",
                    "email": EMAIL,
                    "password": PASSWORD
                }
            };
        
            request(authOptions, (error, authResponse, authBody) => {
                if (!error && authResponse.statusCode === 200) {
                    const authToken = authBody.token;
                    console.log('Authentication Successful - Token:');
                    console.log(authBody.token);
                    // Store the authToken in ioBroker variable or state
                    saveTokenToState(authToken);
                    callback(null, authToken);
                } else {
                    console.error('Authentication Error:', error);
                    callback(error);
                }
            });
        }
        
        // Function to perform the data request
        function fetchData(token, callback) {
            const dataOptions = {
                uri: 'https://app.tibber.com/v4/gql',
                method: 'POST',
                headers: {
                    'Authorization': `Bearer ${token}`
                },
                json: {
                    query: `
                        {
                            me {
                                homes {
                                    electricVehicles {
                                        lastSeen
                                        battery {
                                            percent
                                        }
                                    }
                                }
                            }
                        }
                    `
                }
            };
        
            request(dataOptions, (error, dataResponse, dataBody) => {
                if (!error && dataResponse.statusCode === 200) {
                    const soc = dataBody.data.me.homes[0].electricVehicles[0].battery.percent;
                    const lastSeen = dataBody.data.me.homes[0].electricVehicles[0].lastSeen;
                    console.log('Data Request Successful - SOC:');
                    console.log(soc);
                    console.log('Last Seen:');
                    console.log(lastSeen);
        
                    // Update ioBroker states
                    setState(IOBROKER_SOC_STATE, soc, true);
                    setState(IOBROKER_LASTSEEN_STATE, lastSeen, true);
        
                    callback(null, soc, lastSeen);
                } else if (error && dataResponse.statusCode === 401) {
                    console.error('Token expired. Attempting to re-authenticate...');
        
                    // Perform the login to obtain a new token
                    authenticate((err, newToken) => {
                        if (err) {
                            console.error('Error obtaining a new token:', err);
                            callback(err);
                        } else {
                            // Use the new token to retry the data request
                            fetchData(newToken, callback);
                        }
                    });
                } else {
                    console.error('Data Fetch Error:', error);
                    callback(error);
                }
            });
        }
        
        // Main logic
        function main() {
            // Get the current token from ioBroker state
            const currentToken = getTokenFromState();
        
            // Check if the current token is valid
            if (!currentToken) {
                console.error('Token is not available. Attempting to obtain a new token...');
                
                // If the token is not available, obtain a new token
                authenticate((err, newToken) => {
                    if (err) {
                        console.error('Error obtaining a new token:', err);
                    } else {
                        // Store the newToken in ioBroker variable or state
                        saveTokenToState(newToken);
        
                        // Use the new token to fetch data
                        fetchData(newToken, (err, soc, lastSeen) => {
                            if (err) {
                                console.error('Error fetching data with new token:', err);
                            } else {
                                console.log('Data Fetch Successful with New Token - SOC:');
                                console.log(soc);
                                console.log('Last Seen:');
                                console.log(lastSeen);
                            }
                        });
                    }
                });
            } else {
                // If the token is available, use it to fetch data
                fetchData(currentToken, (err, soc, lastSeen) => {
                    if (err) {
                        console.error('Error fetching data:', err);
                    } else {
                        console.log('Data Fetch Successful - SOC:');
                        console.log(soc);
                        console.log('Last Seen:');
                        console.log(lastSeen);
                    }
                });
            }
        }
        
        // Run the main logic
        main();
        
        

        Es werden aktuell drei Datenpunkte benötigt:

        • SoC: Zahl
        • LastSeen: String
        • Token: String
        1 Reply Last reply Reply Quote 0
        • K
          kptkip last edited by

          @Stroell Nachdem im neuen Javascript-Adapter (>?8.0) das JS-Script nicht mehr funktioniert, habe ich unter Hilfestellung einiger Forumsmitglieder eine Adaption davon zusammen gestellt

          Zu finden hier:
          https://forum.iobroker.net/topic/74930/plötzlich-referenceerror-request-is-not-defined/62?_=1717264899677

          G 1 Reply Last reply Reply Quote 0
          • G
            Gantenbein @kptkip last edited by

            @kptkip Danke für deine Mühe, aber ich muss gestehen mir gelingt es nicht das zu verstehen und irgendwie das Script zu ändern.
            Könntest du, für Dummies wie mich, hier das komplette Script mit Soc und Lastsseen posten oder auch das ganze XML?

            K 1 Reply Last reply Reply Quote 0
            • K
              kptkip @Gantenbein last edited by

              @gantenbein

              Die Beschreibung von @Stroell oben folgen.

              1. Da ist das XML für das Blockly-Script enthalten. Also Blockly erstzellen und XML importieren. Dort musst Du die User-Datenpunkte angeben, in die die Werte eingetragen werden sollen.
              2. im Blockly muss im Block "Javascript-Funktion" das Javascript aus meinem Link eingefügt werden.
              3. Im Script musst Du Deine Tibber-Credentials eingeben und den User-Datenpunkt für das JSON-Output.
              G 1 Reply Last reply Reply Quote 0
              • G
                Gantenbein @kptkip last edited by Gantenbein

                @kptkip Danke, so geht es.
                Ich hatte versucht das von dir verlinkte Skript in das blockly, passend zu dem JSON von Sebastian Löb, einzufügen. Das ist gescheitert weil die Datenpunkte (BatteryPercent & LastSeen) direkt aus dem Skript geschrieben wurden. Mit der jetzigen Änderungen muss das wieder aus dem Blockly gemacht werden.

                Aber es ergibt sich, für mich, eine weitere Frage: zeitweise sehe ich einen Timeout ("timeout of 2000ms exceeded"). Könnte man das irgendwie abfangen bzw. relaxter einstellen?

                S 1 Reply Last reply Reply Quote 0
                • S
                  Stephan74 @Gantenbein last edited by

                  @gantenbein
                  Hi !
                  Fuunktioniert die Lösung bei dir noch ?
                  Bei mir ist seit gestern Funkstille.
                  Im Status steht folgendes:
                  {"errors":[{"message":"This operation has been blocked as a potential Cross-Site Request Forgery (CSRF). Please either specify a 'content-type' header (with a type that is not one of application/x-www-form-urlencoded, multipart/form-data, text/plain) or provide a non-empty value for one of the following headers: x-apollo-operation-name, apollo-require-preflight\n","extensions":{"code":"BAD_REQUEST","stacktrace":["BadRequestError: This operation has been blocked as a potential Cross-Site Request Forgery (CSRF). Please either specify a 'content-type' header (with a type that is not one of application/x-www-form-urlencoded, multipart/form-data, text/plain) or provide a non-empty value for one of the following headers: x-apollo-operation-name, apollo-require-preflight",""," at new GraphQLErrorWithCode (file:///app/node_modules/@apollo/server/dist/esm/internalErrorClasses.js:7:9)"," at new BadRequestError (file:///app/node_modules/@apollo/server/dist/esm/internalErrorClasses.js:75:9)"," at preventCsrf (file:///app/node_modules/@apollo/server/dist/esm/preventCsrf.js:29:11)"," at ApolloServer.executeHTTPGraphQLRequest (file:///app/node_modules/@apollo/server/dist/esm/ApolloServer.js:507:17)"," at process.processTicksAndRejections (node:internal/process/task_queues:95:5)"]}}]}

                  G 1 Reply Last reply Reply Quote 0
                  • G
                    Gantenbein @Stephan74 last edited by

                    @stephan74
                    Hi,

                    nein die Lösung ist derzeit nicht mehr funktionsfähig.
                    Polestar hat wohl die API angepasst und das hatte Änderungen bei Tibber zur Folge. Der Fakt wird im Polestar-Forum diskutiert, derzeit scheint aber niemand eine Lösung zu haben.
                    Meine Kenntnisse genügen leider nicht um das Skript anzupassen. Ich muss warten bis jemand ein copy&paste Lösung für Tibber, oder besser den Polestarserver zur Verfügung stellt.

                    G 1 Reply Last reply Reply Quote 0
                    • G
                      garagenbier78 @Gantenbein last edited by garagenbier78

                      @gantenbein Bei mir funktionierts mit folgender Änderung in der Funktion wieder:

                       httpGet(uri, { bearerAuth: tokenFromLogin, timeout : 5000, headers: {'content-type' : 'application/json' }}, (error, response) => {
                                  if (!error) {
                                      //log(response.statusCode);
                                      // log(response.data);  // uncomment for debug purposes
                                      //log(response.headers); // uncomment for debug purposes
                                      output = response.data;
                      
                                      setState( dpJsonResponse, output);
                                  } else {
                                      log(error, 'error');
                                  }
                              });    
                      

                      Tibber scheint jetzt keine "leeren" Header mehr zu akzeptieren, daher die Änderung in der ersten Zeile.
                      Hab gleich noch den Timeout erhöht, da die 2000ms bei mir auch immer mal wieder zu Fehlern im Log geführt haben.

                      G 1 Reply Last reply Reply Quote 0
                      • G
                        Gantenbein @garagenbier78 last edited by Gantenbein

                        @garagenbier78
                        Super, danke dir - mit der kleinen Änderung läuft es wieder! Der SoC (batterypercent) ist wieder aktuell.

                        Allerdings hat sich die Bedeutung von "lastseen" geändert. Zuvor war es der Zeitpunkt der letzten Meldung Auto->Server, jetzt ist es einfach der Zeitpunkt der Datenabfrage - also eher ein "lastcall". Der in der Tibber-App angezeigte Zeitstempel ist aber korrekt, wird aber wohl vom Script nicht mehr richtig ausgelesen. Das ist insofern schade, weil man nun bei Kommunikationsstörungen nicht mehr weiß ob der SoC ein aktueller oder veralteter Wert ist.

                        Habe bei mir übrigens das Timeout auf 8000ms erhöht, erst dann kommt sicher die Verbindung zustande. Bei kleineren Werten (weniger als 7tms) hat es immer 'mal wieder gehakt.

                        S 1 Reply Last reply Reply Quote 0
                        • S
                          Stephan74 @Gantenbein last edited by

                          @gantenbein Hallo !
                          Könnt ihr eventuell den ganzen Request hier einstellen, damit ich das in Blockly importieren kann.
                          Ich bin nämlich was diese Programmierung ein "DAU".
                          Kann nur Siemens S5 oder S7 programmieren.....

                          G 1 Reply Last reply Reply Quote 0
                          • G
                            Gantenbein @Stephan74 last edited by Gantenbein

                            @stephan74

                            Du musst nur die eine Zeile ändern wie von @garagenbier78 gepostet.
                            Also den httpget um "timeout : 5000, headers: {'content-type' : 'application/json' }" ergänzen - das war's schon.

                            S 1 Reply Last reply Reply Quote 0
                            • S
                              Stephan74 @Gantenbein last edited by

                              @gantenbein Super ! Vielen Dank !
                              Kleine Änderung große Auswirkung.
                              Dank eurer Lösung funktioniert das alles wieder einwandfrei.

                              G 1 Reply Last reply Reply Quote 0
                              • G
                                Gantenbein @Stephan74 last edited by

                                @stephan74
                                ... mit Ausnahme von "lastSeen" - die Defintion hat Tibber selbst geändert ohne eine Ersatzgröße ein zu führen.

                                1 Reply Last reply Reply Quote 0
                                • First post
                                  Last post

                                Support us

                                ioBroker
                                Community Adapters
                                Donate

                                619
                                Online

                                31.6k
                                Users

                                79.5k
                                Topics

                                1.3m
                                Posts

                                7
                                15
                                40985
                                Loading More Posts
                                • Oldest to Newest
                                • Newest to Oldest
                                • Most Votes
                                Reply
                                • Reply as topic
                                Log in to reply
                                Community
                                Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
                                The ioBroker Community 2014-2023
                                logo