Integration of Polestar 2

This might take us somewhere…

https://www.loopybunny.co.uk/polestar/canbus_overview.html

Current best way:

  • “Install” Car Stats Viewer

  • Create a webhook to push information from “Car Stats Viewer” to HA

Following the hint above about the Car Stats Viewer app, which I had completely missed, I have been able to get information about my Polestar 2 in to HA. A lot of the info is in German (as the app is from Germany), but this is the process.

  1. Sign up for a Test Track. The current list of tracks is here: CarStatsViewer Informationen - Polestar Wiki - Polestar Club (Google Translate will work on that page). Use the Google Account attached to your car.

  2. Wait. I got a notification when I was added to the list, but you may not. Either way, you have to wait at least 24 hours before you can download the app. If you do get a notification, wait another 24 hours.

  3. On your desktop, use the link supplied to accept the invitation and then use the option within Google Play to install the app on to your car. This shouldn’t take long.

  4. In the car, you can find the app and configure it appropriately.

  5. In Home Assistant, create a new template file:

templete:
  - trigger:
      - platform: webhook
        local_only: false
        webhook_id: averysecurecode
    sensor:
    
      - name: "Polestar 2 Altitude"
        state: "{{ trigger.json.alt }}"
        unit_of_measurement: 'm'       
        icon: mdi:altimeter
      - name: "Polestar 2 Temp"
        state: "{{ trigger.json.ambientTemperature }}"
        icon: mdi:thermometer
        unit_of_measurement: 'c'       
      - name: "Polestar 2 Battery Level"
        state: "{{ trigger.json.batteryLevel }}"
        icon: mdi:battery
        unit_of_measurement: 'w'       
      - name: "Polestar 2 Charge Port Connected"
        state: "{{ trigger.json.chargePortConnected }}"
        icon: mdi:ev-plug-type2
      - name: "Polestar 2 Ignition State"
        state: "{{ trigger.json.ignitionState }}"
        icon: mdi:car-key       
      - name: "Polestar 2 Latitude"
        state: "{{ trigger.json.lat }}"
        icon: mdi:latitude
      - name: "Polestar 2 Longitude"
        state: "{{ trigger.json.lon }}"
        icon: mdi:longitude
      - name: 'Polestar 2 Current Power'
        state: "{{ trigger.json.power }}"
        unit_of_measurement: 'mW'
        icon : mdi:speedometer
      - name: "Polestar 2 Gear"
        state: "{{ trigger.json.selectedGear }}"
        icon: mdi:car
      - name: 'Polestar 2 Speed m/s'
        state : "{{trigger.json.speed}}"
        unit_of_measurement: 'm/s'
        icon: mdi:car-speed-limiter
      - name: 'Polestar 2 SOC'
        unit_of_measurement: '%'
        state: "{{ trigger.json.stateOfCharge }}"
        icon : mdi:car-electric
      - name: "Polestar 2 Timestamp"
        state: "{{ trigger.json.timestamp }}"
        icon: mdi:clock-time-eight-outline

Change the webhook_id to something of your own. Remember that you will have to type it in on the car, so make it easy to type. I usually use Word.Word.Word (three words with a period between).
The above is based on a post in the German forum, but the values were incorrect, so I have adjusted them.

  1. Do a refresh of template entities in Developer tools, and verify you have a number of sensors starting Polestar2 with an unknown value.

  2. Back to your car. The URL you need to enter in the webhook depends on how you are exposing HA to the internet.
    If it is direct, then enter the value thus:

https://hostname:8123/api/webhook/averysecurecode

If you are using Nabu Casa, then you need to enable it in the Home Assistant Cloud config. It will be called something like "Trigger Update Coordinator (template) and choose to enable it. This will give you a URL. You need to get that URL to the car. It is a bit long to type, so what I did was email it to a webmail account, then open the account using Vivaldi browser and then copy and paste the URL in to the app.
Make sure that you enable the API and enable location tracking etc.

Once values are coming back to HA, you can do some more work in HA…

To use the location information, create a new Automation - just create the automation, switch to YAML view and then copy and paste:

alias: "Polestar: CSV Location"
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.polestar_2_latitude
      - sensor.polestar_2_longitude
condition: []
action:
  - service: device_tracker.see
    data:
      dev_id: polestar_csv
      gps:
        - "{{ states('sensor.polestar_2_latitude') }}"
        - "{{ states('sensor.polestar_2_longitude') }}"
mode: single

That is a very basic entry, it can be expanded with other information such as the battery charge.

Conversion of some values to something more friendly?


sensor:
  - platform: template
    sensors:          
        polestar2_speed_mph:
             value_template: "{{ (states('sensor.polestar_2_speed_m_s') | float * 2.237) | round(1)}}"
             unit_of_measurement: "mph"
             friendly_name: "Polestar 2 speed MPH"

  - platform: template
    sensors:            
        polestar2_charge:
            value_template: "{{ (states('sensor.polestar_2_soc') | float * 100 )}}"
            unit_of_measurement: "%"
            friendly_name: "Polestar 2 Charge"
           
  - platform: template
    sensors:          
        polestar2_power_kw:          
            value_template: "{{ (states('sensor.polestar_2_current_power') | float / 10000000) | round(2) }}"
            unit_of_measurement: "kw"
            friendly_name: "Polestar 2 Power kw"

Then finally a card to just show everything.

type: entities
entities:
  - entity: sensor.polestar_2_altitude
  - entity: sensor.polestar_2_temp
  - entity: sensor.polestar_2_charge_port_connected
  - entity: sensor.polestar_2_ignition_state
    secondary_info: last-changed
  - entity: sensor.polestar_2_latitude
  - entity: sensor.polestar_2_longitude
  - entity: sensor.polestar_2_current_power
  - entity: sensor.polestar_2_gear
    secondary_info: last-changed
  - entity: sensor.polestar_2_speed_m_s
  - entity: sensor.polestar_2_soc
  - entity: sensor.polestar_2_timestamp
  - entity: sensor.polestar2_speed_mph
    icon: mdi:car-speed-limiter
  - entity: sensor.polestar2_charge
    icon: mdi:car-electric
  - entity: sensor.polestar2_power_kw
    icon: mdi:speedometer

Map card works well, particularly with the auto_fit value.
Mini Graph Card is good for the altitude value.

Only bit I am missing is converting the timestamp to human readable format, but I have an open question elsewhere on this forum on how to do that.

1 Like

So for anyone else following along, the last entry in the template for the timestamp should be changed to this:

      - name: "Polestar 2 Timestamp"
        state: "{{ trigger.json.timestamp | multiply(1/1000) | timestamp_local }}"
        icon: mdi:clock-time-eight-outline

That will convert the last date and time of the update to something readable.

You can also add this line:

      - name: "Polestar 2 Last Update"
        state: "{{ trigger.json.timestamp | multiply(1/1000) | timestamp_local }}"
        icon: mdi:clock-time-eight-outline
        device_class: timestamp

Which will give you a read out of the time the last update occured.

This is great, but somewhat complicated. HA in the car already exposes some of these sensors across the HA implementations (like location), could HA in the car be updated to expose all the same sensors that car stats viewer exposes?

As I understod it, it already does, but since you cannot get into the app settings in the car anymore ( after complain from google ) you cannot enable them. I thought this was only a temporary change but I seams permanent.

I would like to contribute on top of already excellent post by @scriven33

Changes:

  • introduce binary_sensor for charge port
  • extra attributes
  • unique_id (so you can personalize in the UI)
template:
  - trigger:
      - platform: webhook
        webhook_id: VERY_SECRET_ENDPOINT_THAT_IS_OPEN_TO_THE_INTERNET
        local_only: false
    unique_id: polestar_webhook
    binary_sensor:
      - name: "Polestar 2 Charge Port Connected"
        state: "{{ trigger.json.chargePortConnected }}"
        icon: mdi:ev-plug-type2
        unique_id: p2_charge_port_connected
    sensor:
      - name: "Polestar 2 Altitude"
        state: "{{ float(trigger.json.alt) if trigger.json.alt is defined }}"
        availability: "{{ trigger.json.alt is defined }}"
        unit_of_measurement: 'm'       
        icon: mdi:altimeter
        unique_id: p2_altitude
      - name: "Polestar 2 Latitude"
        state: "{{ float(trigger.json.lat) if trigger.json.lat is defined  }}"
        availability: "{{ trigger.json.lat is defined }}"
        icon: mdi:latitude
        unique_id: p2_latitude
      - name: "Polestar 2 Longitude"
        state: "{{ float(trigger.json.lon) if trigger.json.lon is defined  }}"
        availability: "{{ trigger.json.lon is defined }}"
        icon: mdi:longitude
        unique_id: p2_longitude
      - name: "Polestar 2 Temp"
        state: "{{ trigger.json.ambientTemperature }}"
        icon: mdi:thermometer
        unit_of_measurement: '°C'
        unique_id: p2_ambient_temperature     
      - name: "Polestar 2 Battery Level"
        state: "{{ trigger.json.batteryLevel | float(0) / 1000 }}"
        icon: mdi:battery
        unit_of_measurement: 'kWh'
        device_class: energy
        unique_id: p2_battery_level
      - name: "Polestar 2 Ignition State"
        state: "{{ trigger.json.ignitionState }}"
        icon: mdi:car-key
        unique_id: p2_ignition_state
      - name: 'Polestar 2 Current Power'
        state: "{{ trigger.json.power | multiply(1/1000000) }}"
        unit_of_measurement: 'kW'
        icon : mdi:speedometer
        device_class: power
        unique_id: p2_current_power
      - name: "Polestar 2 Gear"
        state: "{{ trigger.json.selectedGear }}"
        icon: mdi:car
        unique_id: p2_gear
      - name: 'Polestar 2 Speed km/s'
        state : "{{trigger.json.speed | float(0) * 3.6 }}"
        unit_of_measurement: 'km/h'
        icon: mdi:car-speed-limiter
        unique_id: p2_speed
      - name: 'Polestar 2 SOC'
        unit_of_measurement: '%'
        state: "{{ trigger.json.stateOfCharge | float(0) * 100 }}"
        icon : mdi:car-electric
        device_class: battery
        unique_id: p2_soc
      - name: "Polestar 2 Timestamp"
        state: "{{ trigger.json.timestamp | multiply(1/1000) | timestamp_local }}"
        icon: mdi:clock-time-eight-outline
        device_class: timestamp
        unique_id: p2_timestamp

Automation to update location:

alias: "Polestar: CSV Location"
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.polestar_2_latitude
condition:
  - condition: not
    conditions:
      - condition: or
        conditions:
          - condition: state
            entity_id: sensor.polestar_2_latitude
            state: unavailable
          - condition: state
            entity_id: sensor.polestar_2_longitude
            state: unavailable
        alias: Any will do
    alias: Neither Latitude or Longitude can be Unavailable
action:
  - service: device_tracker.see
    data:
      gps:
        - "{{ states('sensor.polestar_2_latitude') }}"
        - "{{ states('sensor.polestar_2_longitude') }}"
      dev_id: polestar
mode: single
1 Like

I would like to contribute on top of already excellent post by @scriven33

Those are some great modifications. Thanks for updating my work - mine was very much early stages and needed cleaning up, as I knew it could be done better.

I prefer the sensor for the Charge Port Connected, as it states True, which I think is more true to life.
Your modifications also allowed me to remove some of the additional templates I created as well.
Are you on the International Polestar Forum? If so, you might want to post your mods on there as well.

Not an active user there.

Feel free to link from there to here :wink: (thus avoiding duplication)

I just want to leave this here:

It seems some heros have developed exactly what we are all looking for, but for the Volvo environment. I think it would just be a matter of branching this to Polestar and we’re all set (with a rock solid integration not relying on 3rd party integrations and intermediary apps).

Perhaps someone here could port it? Alternatively help me petition the creator to help branch their creation to the Polestar environment!

Cheers
//S

AFAIK we do not share the same backend

Do you have access to your car information through a webpage ? Like volvo’s do ?

Clearly there is an API since companies like Tibber can develop functionalities towards it (Polestar Smartladdning – Ladda din Polestar smart ⚡️ Tibber), but it’s likely not public.

The only access I have, as you well know, is through the Polestar App.

edit: my thought was the Polestar API likely shares a similar internal structure to the VCC API.

If you just need the SoC, checkout here

2 Likes

A new version of Car Stats Viewer has been released, although it isn’t on all of the tracks. New functionality in the API, but the old one works as before.

Hi @scriven33
I am using your templates and stuff in Home Assistant. They mostly work great but I don’t seem to get any Lat Long or Altitude data. I’ve checked ABRP is working and the blue connection icon shows in CSV. Any ideas?

Have you enabled the option in CSV in the car to send that information? I am sure it is a toggle (not with the car right now).

Seeing this makes me sad that Polestar doesn’t offer the same things for us. The platforms are the same but the backends are different, so this wont work for us. Look at all the things they get directly with the Volvo API:

VEHICLES_URL = “https://api.volvocars.com/connected-vehicle/v1/vehicles
VEHICLE_DETAILS_URL = “https://api.volvocars.com/connected-vehicle/v1/vehicles/{0}
WINDOWS_STATE_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/windows
CLIMATE_START_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/commands/climatization-start
CLIMATE_STOP_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/commands/climatization-stop
LOCK_STATE_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/doors
CAR_LOCK_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/commands/lock
CAR_UNLOCK_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/commands/unlock
RECHARGE_STATE_URL = “https://api.volvocars.com/energy/v1/vehicles/{0}/recharge-status
ODOMETER_STATE_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/odometer
LOCATION_STATE_URL = “https://api.volvocars.com/location/v1/vehicles/{0}/location
TYRE_STATE_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/tyres
ENGINE_STATE_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/engine-status
BATTERY_CHARGE_STATE_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/battery-charge-level
FUEL_STATE_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/fuel
STATISTICS_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/statistics
ENGINE_DIAGNOSTICS_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/engine
VEHICLE_DIAGNOSTICS_URL = “https://api.volvocars.com/connected-vehicle/v2/vehicles/{0}/diagnostics

I’m jealous.

I’m going to try to host my own test version of CSV and then push to HA, guess that’s our best bet today.

This works pretty well. I have added a Charger Status indicator to my EV page, and I’m calculating the estimated SOC based on how many kWh were added to the starting battery level and divided by the battery capacity.

At home, when plugging in the charger before locking the car, the data gets sent to HA properly. When done charging, unlocking the car will get the information sent to HA.

The left battery bar is the estimated SOC, the right bar is the actual data coming from the car. I took the screenshot after going out to the post office, but they did match when the car first reported its level back.

2 Likes

I tried this setup via tibber (free account without energy contract), works pretty good!

Similar solution via bash script has already been mentioned above:

1 Like

How does Tibber obtain the information? Does it have a connection to the Polestar API?