Nach dem OTA-Update auf P2.14.3 zeigt die Polestar-API für Home Assistant noch immer P2.13 an.
Hat noch jemand das Phänomen?
Alle sonstigen Einträge (SoC, Kilometer usw.) sind aktuell.
Nach dem OTA-Update auf P2.14.3 zeigt die Polestar-API für Home Assistant noch immer P2.13 an.
Hat noch jemand das Phänomen?
Alle sonstigen Einträge (SoC, Kilometer usw.) sind aktuell.
Ja, hier. Alles stimmt bis auf die Version und dessen Veröffentlichungsdatum. Aber solange alles andere stimmt, soll es mich nicht weiter stören.
Hab zwar keinen HA, aber auch meine API Anbindung zeigt nach dem Update noch die 2.13.1 vom 9.12.23.
Seid ihr nach dem Update schon damit gefahren?
Ich noch nicht. Vielleicht liegt’s daran.
Ja, hat trotzdem nicht geholfen.
Heute Morgen ist der Eintrag aktuell. Allerdings wurde auch die Instanz meines Servers neu gestartet.
Keine Ahnung, ob es daran lag.
Passt vielleicht nicht 100%-ig in dieses Thema, aber ich weiß nicht, wohin damit: im Laufe des Abends ist mein Fahrzeug aus meinem Konto verschwunden. Gemerkt hab ich das durch ein Home-Assistant-Update, aber ich kann mir nicht vorstellen, dass das der Auslöser ist.
Geht’s noch jemandem so? Vielleicht nur ein temporärer Ausfall?
For what it’s worth: nachdem hier keine Selbstheilung eingetreten war, hab ich heute den Support angeschrieben. Gab gerade zwei E-Mails: eine, dass mein Polestar aus meinem Konto entfernt wurde, und eine zweite, dass er wieder hinzugefügt wurde. Der Support schrieb dann noch
Sollte diese Verbindung in Zukunft wieder getrennt werden, dann melden Sie sich schnellstmöglich wieder bei uns, damit wir uns das mal anschauen können.
Sollte das also noch bei jemand anderem passieren, schnell dem Support Bescheid geben - es scheint sich um einen Bug zu handeln.
Hallo zusammen.
Kann hier mal jemand eine Rest-Api Abfrage teilen, wie ich sie direkt im Browser nutzen könnte, um Daten zu meinem Fahrzeug zu bekommen? Danke dafür.
Wahrscheinlich wird das irgendwie so aussehen:
https://api.polestar.com/bridge?auth=a4309eba1dc7903ed858392ed5c85067&user=xy@mail.de
So einfach ist das nicht. Du musst dich erst authentifizieren und dir einen AuthToken holen, bevor du dir per GraphQl query tatsächlich Daten holen kannst.
Die Diskussion könnte ein guter Start für dich sein: Polestar API Down: New API Endpoint "/mystar-v2" · Issue #96 · leeyuentuen/polestar_api · GitHub
Hat das Thema jemand schon in NodeRed gelöst? ich bin zu dumm dafür und habe es leider noch nicht geschafft.
So, hab es nun selbst zusammen gebracht - Für alle die auch gerne NodeRed verwenden wollen:
[{"id":"fd45e125eed8839a","type":"function","z":"8f1e61194be045a8","name":"getAllCarData","func":"// Code added here will be run once\n// whenever the node is started.\n\nclass Polestar {\n #credentials = {\n email: null,\n password: null,\n }\n\n #token = {\n access: null,\n refresh: null,\n expires: null,\n }\n\n #vehicle = {\n vin: null,\n id: null,\n }\n\n constructor(email, password) {\n if (!email || !password) {\n throw new Error(\"Email and password must be provided\")\n }\n this.#credentials.email = email\n this.#credentials.password = password\n }\n\n async login() {\n const { pathToken, cookie } = await this.#getLoginFlowTokens()\n const tokenRequestCode = await this.#performLogin(pathToken, cookie)\n const apiCreds = await this.#getApiToken(tokenRequestCode)\n await this.#storeToken(apiCreds)\n if (!await this.#checkAuthenticated()) {\n throw new Error(\"Login failed, token is not valid\")\n }\n }\n\n async #storeToken(token) {\n const apiCreds = token\n\n const tokenExpiryTime = new Date()\n const secondsToAdd = apiCreds.expires_in\n tokenExpiryTime.setSeconds(tokenExpiryTime.getSeconds() + (secondsToAdd - 120))\n\n this.#token.access = apiCreds.access_token\n this.#token.refresh = apiCreds.refresh_token\n this.#token.expires = tokenExpiryTime\n }\n\n async #checkAuthenticated() {\n if (!this.#token.access || !this.#token.refresh || !this.#token.expires) {\n throw new Error(\"Not logged in\")\n }\n if (this.#token.expires < new Date()) {\n const apiCreds = await this.#refreshToken()\n this.#storeToken(apiCreds)\n }\n\n if (await this.#checkTokenValidity()) {\n return true\n } else {\n throw new Error(\"Token is not valid or refresh token has expired\")\n }\n\n }\n\n async #refreshToken() {\n console.log(\"Refreshing token\")\n const response = await axios.post(\n \"https://pc-api.polestar.com/eu-north-1/auth\",\n \"{\\\"query\\\":\\\"\\\\n query refreshAuthToken($token: String!) {\\\\n refreshAuthToken(token: $token) {\\\\n access_token\\\\n expires_in\\\\n id_token\\\\n refresh_token\\\\n }\\\\n }\\\\n\\\",\\\"variables\\\":{\\\"token\\\":\\\"\" + this.#token.refresh + \"\\\"}}\",\n {\n headers: {\n \"cache-control\": \"no-cache\",\n \"content-type\": \"application/json\",\n \"Authorization\": \"Bearer \" + this.#token.access,\n pragma: \"no-cache\",\n },\n maxRedirects: 0,\n validateStatus: function (status) {\n return true\n },\n }\n )\n const data = await response.data\n const apiCreds = data.data.refreshAuthToken\n return {\n access_token: apiCreds.access_token,\n refresh_token: apiCreds.refresh_token,\n expires_in: apiCreds.expires_in,\n }\n }\n\n async #checkTokenValidity() {\n const response = await axios.get(\n \"https://pc-api.polestar.com/eu-north-1/my-star/?query=query%20introspectToken(%24token%3A%20String!)%20%7B%0A%20%20introspectToken(token%3A%20%24token)%20%7B%0A%20%20%20%20active%0A%20%20%20%20__typename%0A%20%20%7D%0A%7D&operationName=introspectToken&variables=%7B%22token%22%3A%22\" + this.#token.access + \"%22%7D\",\n {\n headers: {\n \"cache-control\": \"no-cache\",\n \"content-type\": \"application/json\",\n \"Authorization\": \"Bearer \" + this.#token.access,\n pragma: \"no-cache\",\n },\n maxRedirects: 0,\n validateStatus: function (status) {\n return true\n },\n }\n )\n const data = await response.data.data\n if (!data.introspectToken || !data.introspectToken.active) {\n return false\n } else {\n return true\n }\n }\n\n async #performLogin(pathToken, cookie) {\n const response = await axios.post(\n \"https://polestarid.eu.polestar.com/as/\" + pathToken + \"/resume/as/authorization.ping?client_id=polmystar\",\n {\n \"pf.username\": this.#credentials.email,\n \"pf.pass\": this.#credentials.password,\n },\n {\n headers: {\n \"cache-control\": \"no-cache\",\n \"content-type\": \"application/x-www-form-urlencoded\",\n pragma: \"no-cache\",\n cookie: cookie,\n },\n maxRedirects: 0,\n validateStatus: function (status) {\n return true\n },\n }\n )\n\n const redirectUrl = response.headers.location\n const regex = /code=([^&]+)/\n const match = redirectUrl.match(regex)\n const tokenRequestCode = match ? match[1] : null\n\n return tokenRequestCode\n }\n\n async #getLoginFlowTokens() {\n const response = await axios.get(\n \"https://polestarid.eu.polestar.com/as/authorization.oauth2?response_type=code&client_id=polmystar&redirect_uri=https%3A%2F%2Fwww.polestar.com%2Fsign-in-callback&scope=openid%20profile%20email%20customer:attributes%20customer:attributes:write\",\n {\n headers: {\n \"cache-control\": \"no-cache\",\n pragma: \"no-cache\",\n },\n referrerPolicy: \"strict-origin-when-cross-origin\",\n body: null,\n maxRedirects: 0,\n method: \"GET\",\n validateStatus: function (status) {\n return true\n },\n }\n )\n const data = await response\n const redirectUrl = response.headers.location\n const regex = /resumePath=(\\w+)/\n const match = redirectUrl.match(regex)\n const pathToken = match ? match[1] : null\n const cookies = response.headers[\"set-cookie\"]\n const cookie = cookies[0].split('; ')[0] + \";\"\n return {\n pathToken: pathToken,\n cookie: cookie\n }\n }\n\n async #getApiToken(tokenRequestCode) {\n const response = await axios.get(\n \"https://pc-api.polestar.com/eu-north-1/auth/?query=query%20getAuthToken(%24code%3A%20String!)%20%7B%0A%20%20getAuthToken(code%3A%20%24code)%20%7B%0A%20%20%20%20id_token%0A%20%20%20%20access_token%0A%20%20%20%20refresh_token%0A%20%20%20%20expires_in%0A%20%20%7D%0A%7D%0A&operationName=getAuthToken&variables=%7B%22code%22%3A%22\" + tokenRequestCode + \"%22%7D\",\n {\n headers: {\n \"cache-control\": \"no-cache\",\n \"content-type\": \"application/json\",\n pragma: \"no-cache\",\n },\n maxRedirects: 0,\n validateStatus: function (status) {\n return true\n },\n }\n )\n const data = await response.data\n const apiCreds = data.data.getAuthToken\n return {\n access_token: apiCreds.access_token,\n refresh_token: apiCreds.refresh_token,\n expires_in: apiCreds.expires_in,\n }\n }\n\n async getVehicles() {\n if (!await this.#checkAuthenticated()) {\n throw new Error(\"Not authenticated\")\n }\n const response = await axios.get(\n \"https://pc-api.polestar.com/eu-north-1/my-star/?query=query%20getCars%20%7B%0A%20%20getConsumerCarsV2%20%7B%0A%20%20%20%20vin%0A%20%20%20%20internalVehicleIdentifier%0A%20%20%20%20modelYear%0A%20%20%20%20content%20%7B%0A%20%20%20%20%20%20model%20%7B%0A%20%20%20%20%20%20%20%20code%0A%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%20%20__typename%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20images%20%7B%0A%20%20%20%20%20%20%20%20studio%20%7B%0A%20%20%20%20%20%20%20%20%20%20url%0A%20%20%20%20%20%20%20%20%20%20angles%0A%20%20%20%20%20%20%20%20%20%20__typename%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20__typename%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20__typename%0A%20%20%20%20%7D%0A%20%20%20%20hasPerformancePackage%0A%20%20%20%20registrationNo%0A%20%20%20%20deliveryDate%0A%20%20%20%20currentPlannedDeliveryDate%0A%20%20%20%20__typename%0A%20%20%7D%0A%7D&operationName=getCars&variables=%7B%7D\",\n {\n headers: {\n \"cache-control\": \"no-cache\",\n \"content-type\": \"application/json\",\n \"Authorization\": \"Bearer \" + this.#token.access,\n pragma: \"no-cache\",\n },\n maxRedirects: 0,\n }\n )\n if (!response.data.data.getConsumerCarsV2) {\n throw new Error(\"No vehicles found\")\n }\n const vehicles = response.data.data.getConsumerCarsV2\n return vehicles\n }\n\n async setVehicle(vin) {\n if (!await this.#checkAuthenticated()) {\n throw new Error(\"Not authenticated\")\n }\n const vehicles = await this.getVehicles()\n let vehicle = null\n if (vin) {\n vehicle = vehicles.find((vehicle) => vehicle.vin === vin)\n } else {\n vehicle = vehicles[0]\n }\n if (!vehicle) {\n throw new Error(\"Vehicle not found\")\n }\n this.#vehicle.vin = vehicle.vin\n this.#vehicle.id = vehicle.internalVehicleIdentifier\n\n return this.#vehicle\n }\n\n async getBattery() {\n if (!await this.#checkAuthenticated()) {\n throw new Error(\"Not authenticated\")\n }\n\n if (!this.#vehicle.vin) {\n throw new Error(\"No vehicle selected\")\n }\n\n const response = await axios.get(\n \"https://pc-api.polestar.com/eu-north-1/mystar-v2?query=query%20GetBatteryData(%24vin%3A%20String!)%20%7B%0A%20%20getBatteryData(vin%3A%20%24vin)%20%7B%0A%20%20%20%20averageEnergyConsumptionKwhPer100Km%0A%20%20%20%20batteryChargeLevelPercentage%0A%20%20%20%20chargerConnectionStatus%0A%20%20%20%20chargingCurrentAmps%0A%20%20%20%20chargingPowerWatts%0A%20%20%20%20chargingStatus%0A%20%20%20%20estimatedChargingTimeMinutesToTargetDistance%0A%20%20%20%20estimatedChargingTimeToFullMinutes%0A%20%20%20%20estimatedDistanceToEmptyKm%0A%20%20%20%20estimatedDistanceToEmptyMiles%0A%20%20%20%20eventUpdatedTimestamp%20%7B%0A%20%20%20%20%20%20iso%0A%20%20%20%20%20%20unix%0A%20%20%20%20%20%20__typename%0A%20%20%20%20%7D%0A%20%20%20%20__typename%0A%20%20%7D%0A%7D&operationName=GetBatteryData&variables=%7B%22vin%22%3A%22\" + this.#vehicle.vin + \"%22%7D\",\n {\n headers: {\n \"cache-control\": \"no-cache\",\n \"content-type\": \"application/json\",\n \"Authorization\": \"Bearer \" + this.#token.access,\n pragma: \"no-cache\",\n },\n maxRedirects: 0,\n }\n )\n const data = await response.data.data.getBatteryData\n return data\n }\n\n async getOdometer() {\n if (!await this.#checkAuthenticated()) {\n throw new Error(\"Not authenticated\")\n }\n\n if (!this.#vehicle.vin) {\n throw new Error(\"No vehicle selected\")\n }\n\n const response = await axios.get(\n \"https://pc-api.polestar.com/eu-north-1/mystar-v2?query=query%20GetOdometerData(%24vin%3A%20String!)%20%7B%0A%20%20getOdometerData(vin%3A%20%24vin)%20%7B%0A%20%20%20%20averageSpeedKmPerHour%0A%20%20%20%20eventUpdatedTimestamp%20%7B%0A%20%20%20%20%20%20iso%0A%20%20%20%20%20%20unix%0A%20%20%20%20%20%20__typename%0A%20%20%20%20%7D%0A%20%20%20%20odometerMeters%0A%20%20%20%20tripMeterAutomaticKm%0A%20%20%20%20tripMeterManualKm%0A%20%20%20%20__typename%0A%20%20%7D%0A%7D&operationName=GetOdometerData&variables=%7B%22vin%22%3A%22\" + this.#vehicle.vin + \"%22%7D\",\n {\n headers: {\n \"cache-control\": \"no-cache\",\n \"content-type\": \"application/json\",\n \"Authorization\": \"Bearer \" + this.#token.access,\n pragma: \"no-cache\",\n },\n maxRedirects: 0,\n }\n )\n const data = await response.data.data.getOdometerData\n return data\n }\n}\n\n\n\nconst polestar = new Polestar(\"DEINE EMAIL\", \"DEIN PASSWORD\");\nawait polestar.login();\nconst vehicles = await polestar.getVehicles();\nawait polestar.setVehicle(vehicles[0].vin);\nconst batt = await polestar.getBattery();\nconst odo = await polestar.getOdometer();\n\nmsg.odo = odo;\nmsg.vehicles = vehicles;\nmsg.bat = batt;\nmsg.payload = vehicles[0].content.model.name;\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"axios","module":"axios"}],"x":700,"y":200,"wires":[["cd43d91816497c1e","5667b87be88d9f5d","34467ccf11dbacf2","a0aec7100a98d524","d5f544c8916f6ffd","9737340fef439758","377e31a58cc94d81","d59067120006bbd4"]]},{"id":"bf870353702a3547","type":"inject","z":"8f1e61194be045a8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":440,"y":200,"wires":[["fd45e125eed8839a"]]},{"id":"cd43d91816497c1e","type":"debug","z":"8f1e61194be045a8","name":"debug 31","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":980,"y":120,"wires":[]},{"id":"71042cdc04822d04","type":"debug","z":"8f1e61194be045a8","name":"Soc","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1350,"y":200,"wires":[]},{"id":"5667b87be88d9f5d","type":"function","z":"8f1e61194be045a8","name":"SoC","func":"msg.payload = msg.bat.batteryChargeLevelPercentage;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":950,"y":200,"wires":[["71042cdc04822d04"]]},{"id":"a0aec7100a98d524","type":"function","z":"8f1e61194be045a8","name":"Verbrauch/100km","func":"msg.payload = msg.bat.averageEnergyConsumptionKwhPer100Km\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":990,"y":300,"wires":[["00ba973a886207f7"]]},{"id":"d5f544c8916f6ffd","type":"function","z":"8f1e61194be045a8","name":"Steckerstatus","func":"msg.payload = msg.bat.chargerConnectionStatus\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":980,"y":350,"wires":[["d4237b28ebf84db4"]]},{"id":"34467ccf11dbacf2","type":"function","z":"8f1e61194be045a8","name":"Distance","func":"msg.payload = msg.bat.estimatedDistanceToEmptyKm\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":960,"y":250,"wires":[["2ca76bfddd6b8b9e"]]},{"id":"9737340fef439758","type":"function","z":"8f1e61194be045a8","name":"Restladezeit","func":"msg.payload = msg.bat.estimatedChargingTimeToFullMinutes;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":970,"y":400,"wires":[["97cfe32175d2262d"]]},{"id":"377e31a58cc94d81","type":"function","z":"8f1e61194be045a8","name":"Kilometerstand","func":"msg.payload = msg.odo.odometerMeters / 1000;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":980,"y":450,"wires":[["4fc69f5975c3c82c"]]},{"id":"d59067120006bbd4","type":"function","z":"8f1e61194be045a8","name":"Durchschnitsgeschwindigkeit","func":"msg.payload = msg.odo.averageSpeedKmPerHour;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1020,"y":500,"wires":[["fa41b75b538a01b1"]]},{"id":"2ca76bfddd6b8b9e","type":"debug","z":"8f1e61194be045a8","name":"Reichweite","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1370,"y":250,"wires":[]},{"id":"00ba973a886207f7","type":"debug","z":"8f1e61194be045a8","name":"Durchschnitsverbrauch","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1400,"y":300,"wires":[]},{"id":"d4237b28ebf84db4","type":"debug","z":"8f1e61194be045a8","name":"Steckerstatus","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1380,"y":350,"wires":[]},{"id":"4fc69f5975c3c82c","type":"debug","z":"8f1e61194be045a8","name":"Kilometerstand","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1380,"y":450,"wires":[]},{"id":"fa41b75b538a01b1","type":"debug","z":"8f1e61194be045a8","name":"Durchschnitsgeschwindigkeit","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1420,"y":500,"wires":[]},{"id":"97cfe32175d2262d","type":"debug","z":"8f1e61194be045a8","name":"Restladezeit","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1370,"y":400,"wires":[]}]
Hey @bausi2k
Das funktioniert perfekt !!!
Vielen herzlichen Dank für das Publizieren
Andi
ich bekomme aktuell einen Fehler (seit heute Nacht) / irgendwas mit dem Passwort, hat das Thema noch jemand?
Ja, Polestar hat wieder was geändert
Hier gibt es schon einen Fix, es hat sich wohl unter anderem die Client ID geändert:
jetzt muss nur wer noch die JS API updaten… oder ich muss mir eine HA Instanz hochziehen
Ist es wirklich nur die client_ID, die sich geändert hat und dass man sich auf polestar.com einloggen und die neuen Terms & Conditions durchwinken muss?
Also so einfach ist es wohl nicht.
Man verbessere mich, wenn ich das nicht richtig kapiert habe:
Es reicht NICHT sich manuell auf polestar.com einzuloggen und die terms & conditions abzunicken.
Das gleiche muss man anscheinend über die API realisieren. Möglicherweise nur einmal.
Wie das auszusehen hat, muss ich erstmal verstehen.
Korrekt @Sipple ! Es sind mehrere Änderungen, die Umgesetzt werden müssen. Unter anderem die neue Client ID und das Akzeptieren der T&Cs.
Über die Tibber API funktioniert es weiterhin …
Kanal & Gruppe
Chatten.
Für Small Talk nutzen wir den Telegram-Messenger.
Hier kannst du unserer Gruppe beitreten:
Offizielle Website
Informieren.
Community
Diskutieren.