Peugeot Citroen DS cars - Connected Car

I’m currently exploring more efficient methods to manage the preconditioning process of my vehicle. At the moment, I have implemented this feature as a REST switch. However, I’m facing a delay issue: it takes several minutes for the preconditioning state to update to ‘Enabled’ after the command is sent.

I’m considering an idea to improve this. My thought is to introduce an intermediate state, ‘Pending’, which would represent the period from when the command is issued to when it is actually activated in the car. This way, there would be a clear indication that the command has been sent but not yet executed.

Has anyone in the community tackled a similar challenge or have any suggestions on how to implement this more effectively? Insights on better state management or alternative approaches would be greatly appreciated.

Thank you in advance for your help and ideas!

The way I’ve tackled the issue is that I use the custom mushroom-template-card which enables custom secondary statuses. Then using the code below it displays the status and color of the button using the combination of toggle status and preconditioning status.

image

Blockquote
- type: custom:mushroom-template-card
primary: e2008 Preconditioning
secondary: |2-
{% set prestatus=states(‘sensor.e2008_preconditioning_status’) %}
{% set toggle=states(entity) %}
{% if prestatus==‘Disabled’ and toggle==‘off’ %}
Off
{% elif prestatus==‘Disabled’ and toggle==‘on’ %}
On - Starting
{% elif prestatus==‘Enabled’ and toggle==‘on’ %}
On - Enabled
{% elif prestatus==‘Finished’ and toggle==‘on’ %}
On - Finished
{% else %}
Off
{% endif %}
icon: mdi:fan
entity: input_boolean.e2008_preconditioning_toggle
layout: vertical
tap_action:
action: toggle
icon_color: |2-
{% set prestatus=states(‘sensor.e2008_preconditioning_status’) %}
{% set toggle=states(entity) %}
{% if prestatus==‘Disabled’ and toggle==‘off’ %}
primary
{% elif prestatus==‘Disabled’ and toggle==‘on’ %}
orange
{% elif prestatus==‘Enabled’ and toggle==‘on’ %}
green
{% elif prestatus==‘Finished’ and toggle==‘on’ %}
green
{% else %}
grey
{% endif %}

1 Like

Since somtime I didn’t have any error like this in the protocol without changing anything.
Thanks

Did someone already include Battery SOH into an entity?
I am struggling and can’t get it to work.

@drogfild
I’ve built a little on @sebu’s solution on the precondition process.
Here’s a guide on how I have set it up:

image
image
ha-precon-fan
image
image

Requirements:

  • Mushroom
  • card-mod

It is fully possible to do this without mushroom or card-mod, but that is what I have used.

Configuration:

configuration.json

In the configuration, I have set up a switch to activate the preconditioning. I also have an input boolean used for keeping track of the current toggle status.

command_line:
  - switch:
      name: "e2008 climate"
      command_on: curl -s "http://<PSACC_SLUG>:5000/preconditioning/<YOUT_VIN>/1"
      command_off: curl -s "http://<PSACC_SLUG>:5000/preconditioning/<YOUR_VIN>/0"

input_boolean:
  e2008_preconditioning_toggle:
    name: e2008 preconditioning toggle
    icon: mdi:fan
    initial: off

sensor.yaml

On sensors, I have created specific sensors that have more advanced preconditioning status. They give you statuses such as Off, Starting, Enabled and Finished. They mainly use the toggle to determine the starting state. I have also created a similar sensor just for getting the status color, optionally if not needed. The reason for creating a new sensor for this is that it makes it a bit easier to work with on different dashboard cards.

- platform: rest
  name: peugeot_e2008
  resource: http://<PSACC_SLUG>:5000/get_vehicleinfo/<YOUT_VIN>?from_cache=1
  scan_interval: 60
  timeout: 30
  value_template: "OK"
  json_attributes:
    - energy
    - battery
    - environment
    - doors_state
    - timed_odometer
    - preconditionning

- platform: template
  sensors:
    e2008_preconditioning_status:
      friendly_name: "e2008 Preconditioning Status"
      value_template: '{{ states.sensor.peugeot_e2008.attributes["preconditionning"]["air_conditioning"]["status"] }}'
    e2008_preconditioning_advance_status:
      friendly_name: "e2008 Preconditioning Advance Status"
      value_template: >
        {% set prestatus=states("sensor.e2008_preconditioning_status") %}
        {% set toggle=states("input_boolean.e2008_preconditioning_toggle") %}
        {% if prestatus=="Disabled" and toggle=="off" %}
          {% set newstatus="Off" %}
        {% elif prestatus=="Disabled" and toggle=="on" %}
          {% set newstatus="Starting" %}
        {% elif prestatus=="Enabled" %}
          {% set newstatus="Running" %}
        {% elif prestatus=="Finished" %}
          {% set newstatus="Finished" %}
        {% else %}
          {% set newstatus="Off" %}
        {% endif %}
        {{ newstatus }}
    e2008_preconditioning_advance_status_color:
      friendly_name: "e2008 Preconditioning Status Color"
      value_template: >
        {% set prestatus=states("sensor.e2008_preconditioning_status") %}
        {% set toggle=states("input_boolean.e2008_preconditioning_toggle") %}
        {% if prestatus=="Disabled" and toggle=="off" %}
          {% set color="primary" %}
        {% elif prestatus=="Disabled" and toggle=="on" %}
          {% set color="yellow" %}
        {% elif prestatus=="Enabled" %}
          {% set color="green" %}
        {% elif prestatus=="Finished" %}
          {% set color="purple" %}
        {% else %}
          {% set color="primary" %}
        {% endif %}
        {{ color }}

automations.yaml

Here, I have set up two automations: one to ensure the toggle turns off if not done manually after 15 minutes or after preconditioning is finished, and an automation to run the switch based on the input_boolean toggle.

- id: '1701847387643'
  alias: Reset e2008 preconditioning toggle
  trigger:
  - platform: state
    entity_id: sensor.e2008_preconditioning_status
    to: Finished
  - platform: state
    entity_id: input_boolean.e2008_preconditioning_toggle
    to: 'on'
    for:
      minutes: 15
  action:
  - service: input_boolean.turn_off
    entity_id: input_boolean.e2008_preconditioning_toggle
- id: '1701847387644'
  alias: e2008 preconditioning switch
  trigger:
  - platform: state
    entity_id: input_boolean.e2008_preconditioning_toggle
  action:
    service: "{% if states('input_boolean.e2008_preconditioning_toggle') == 'on' %}\n
      \ switch.turn_on\n{% else %}\n  script.turn_off\n{% endif %}\n"
    entity_id: switch.e2008_clim

Dashboard configuration:

This will use the mushroom template card. It will display the current status and time since the change. When clicked, it will open more info (a toggle for turning preconditioning on/off). The icon color will also change based on the status, and the fan icon will animate if the status is set to Running.

type: custom:mushroom-template-card
icon: mdi:fan
primary: Preconditioning
secondary: >
  {% set status = states("sensor.e2008_preconditioning_advance_status") %}

  {% set last_changed =
  states.sensor.e2008_preconditioning_advance_status.last_changed%}

  {{status}} {{' - ' + relative_time(last_changed) if last_changed is not none
  else ''}}
entity: input_boolean.e2008_preconditioning_toggle
tap_action:
  action: more-info
icon_color: |
  {{states("sensor.e2008_preconditioning_advance_status_color")}}
card_mod:
  style: |
    @keyframes rotating {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }
    ha-state-icon { 
      {{'animation: rotating 2s linear infinite;' if is_state('sensor.e2008_preconditioning_advance_status', 'Running') }}
    }

Hope this helps you with setting up your own preconditioning. I wanted to expand it to include conditions for the car to activate preconditioning, such as checking the battery level and if the doors are locked. However, I don’t have access to the door state for my car, so I can only perform a battery check (>50%).

2 Likes

Trying to install this, but getting the following error at configuration…

And you are 150% sure that your cendentials are fine? Same for the country ID and brand?

yes, giving a wrong email address returns the following:
image

The password is correct, i even logged out of the app on my phone and signed in again, the password was accepted.

Perhaps i need to do some manual configuration in a YAML file or something? or?

Nope, you don’t. You install it and do the two steps for authentication. This is it

Hi,

Does anyone knows how to plot / record trip information in HA using PSA- car controller

Never mind, I did it
It will upload all the charge and trip data to Google sheets as well

Could you show us how you did it too? :wink:

1 Like

Hi , tried to log in for the first time, this gives me the next error

Traceback (most recent call last):
File “/usr/local/lib/python3.9/dist-packages/psa_car_controller/web/view/config_views.py”, line 133, in connectPSA
res = firstLaunchConfig(app_name, email, password, countrycode)
File “/usr/local/lib/python3.9/dist-packages/psa_car_controller/psa/setup/app_decoder.py”, line 95, in firstLaunchConfig
psacc.connect(client_email, client_password)
File “/usr/local/lib/python3.9/dist-packages/psa_car_controller/psacc/application/psa_client.py”, line 35, in connect
self.manager.init_with_user_credentials_realm(user, password, self.realm)
File “/usr/local/lib/python3.9/dist-packages/psa_car_controller/psa/oauth.py”, line 26, in init_with_user_credentials_realm
self._token_request(self._grant_password_request_realm(login, password, realm), True)
File “/usr/local/lib/python3.9/dist-packages/oauth2_client/credentials_manager.py”, line 205, in _token_request
CredentialManager._handle_bad_response(response)
File “/usr/local/lib/python3.9/dist-packages/oauth2_client/credentials_manager.py”, line 87, in _handle_bad_response
raise OAuthError(HTTPStatus(response.status_code), error.get(‘error’), error.get(‘error_description’))
oauth2_client.credentials_manager.OAuthError: 400 - server_error : Internal Server Error

Tried diferent countrys and brands but they give different errors.

Thanx

Hello,
I could already use the apllication for month. Now I have the same error message as Peter.
Can someone help please.

This issue is logged in Change to the PSA API - Internal Error 400 · Issue #733 · flobz/psa_car_controller · GitHub . There is currently a complicated workaround around available in topic >> Change to the PSA API - Internal Error 400 · Issue #733 · flobz/psa_car_controller · GitHub .

I wasn’t able to get the workaround working using Windows 11. It always failed in “panda wheel build” during the installation of PSA car controller. In the discussion there was somebody pointing the way to do it with virtual machine (VirtualBox) and Ubuntu, so using that approach I was able to get the workaround working.

@vktpla: how did you manage that?

I used a few sensors/helpers and automations to achieve this

First I created few input_datetime helpers to track the trip start time, trip end time, and duration.
Few input_number / sensors for tracking consumption, kwh used, battery start, battery end %, odometer start km, odometer end km.

Then I created an automation to track the start of the trip.
It will be triggered when moving_status changes from False to true, or if odometer value changes.

Once its triggered I will set a counter to 1 to identify that trip is started, and will start trip again only if this counter is reset.

If this condition matches, I will set current time as trip_start time, current battery level as trip start level, and current odometer as trip start odometer.

alias: Trip_start_parameters_set
description: ""
trigger:
  - platform: state
    entity_id:
      - sensor.e2008_odometer
    id: "1"
  - platform: state
    entity_id:
      - sensor.e2008_battery_level
    id: "2"
  - platform: state
    entity_id:
      - sensor.moving_status
    from: "False"
    to: "True"
    id: "3"
condition:
  - condition: state
    entity_id: counter.trip_start_counter
    state: "0"
  - condition: template
    value_template: >-
      {{ trigger.from_state.state != "unavailable" and trigger.to_state.state !=
      "unavailable" }}
  - condition: state
    entity_id: sensor.e2008_preconditioning_status
    state: Disabled
  - condition: state
    entity_id: sensor.e2008_charging_status
    state: Disconnected
action:
  - service: input_datetime.set_datetime
    data:
      timestamp: "{{ as_timestamp(now()) }}"
    target:
      entity_id: input_datetime.trip_start_time
  - if:
      - condition: trigger
        id:
          - "1"
    then:
      - service: input_number.set_value
        data:
          value: >-
            {{ states.sensor.e2008.attributes["timed_odometer"]["mileage"] |
            float - 0.1 }}
        target:
          entity_id: input_number.trip_start_odometer
    else:
      - service: input_number.set_value
        data:
          value: >-
            {{ states.sensor.e2008.attributes["timed_odometer"]["mileage"] |
            float }}
        target:
          entity_id: input_number.trip_start_odometer
  - if:
      - condition: trigger
        id:
          - "2"
    then:
      - service: input_number.set_value
        data:
          value: >-
            {{ (states.sensor.e2008.attributes["energy"][0]["level"] | int) - 1
            }}
        target:
          entity_id: input_number.trip_start_battery_level
    else:
      - service: input_number.set_value
        data:
          value: "{{ states.sensor.e2008.attributes[\"energy\"][0][\"level\"] }}"
        target:
          entity_id: input_number.trip_start_battery_level
  - service: input_text.set_value
    data:
      value: >-
        {{ states.sensor.e2008_position.state.split(", ")[1] | replace("[","") |
        replace("]","") | float() }} , {{
        states.sensor.e2008_position.state.split(", ")[0] | replace("[","") |
        replace("]","") | float() }}
    target:
      entity_id: input_text.trip_start_position
  - service: counter.set_value
    data:
      value: 1
    target:
      entity_id: counter.trip_start_counter
  - service: notify.mobile_app_pixel_7_pro
    data:
      message: Trip started at {{ states.input_datetime.trip_start_time.state }}

mode: single

Something like this

and end the trip like this

alias: trip_end_parameters_set
description: ""
trigger:
  - platform: template
    value_template: >-
      {{ now() > ((states.sensor.e2008_odometer.last_updated | as_local) +
      timedelta(minutes=5)) }} 
condition:
  - condition: template
    value_template: "{{ states.sensor.e2008_odometer.state != \"unavailable\" }}"
  - condition: state
    entity_id: counter.trip_start_counter
    state: "1"
  - condition: template
    value_template: >-
      {{ now() > ((states.sensor.e2008_odometer.last_updated | as_local) +
      timedelta(minutes=5)) }} 
action:
  - service: counter.reset
    data: {}
    target:
      entity_id: counter.trip_start_counter
  - service: input_datetime.set_datetime
    data:
      timestamp: >-
        {{ (as_timestamp(states.sensor.e2008_odometer.last_updated | as_local))
        }}
    target:
      entity_id: input_datetime.trip_end_time
  - service: input_number.set_value
    data:
      value: "{{ states.sensor.e2008.attributes[\"energy\"][0][\"level\"] }}"
    target:
      entity_id: input_number.trip_end_battery_level
  - service: input_number.set_value
    data:
      value: "{{ states.sensor.e2008.attributes[\"timed_odometer\"][\"mileage\"] }}"
    target:
      entity_id: input_number.trip_end_odometer
  - service: input_text.set_value
    data:
      value: >-
        {{ states.sensor.e2008_position.state.split(", ")[1] | replace("[","") |
        replace("]","") | float() }} , {{
        states.sensor.e2008_position.state.split(", ")[0] | replace("[","") |
        replace("]","") | float() }}
    target:
      entity_id: input_text.trip_end_position
  - service: google_sheets.append_sheet
    data:
      config_entry: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      data:
        Start time: "{{ states.input_datetime.trip_start_time.state }}"
        End time: "{{ states.input_datetime.trip_end_time.state }}"
        Duration: "{{ states.sensor.e2008_trip_duration.state }}"
        start latitude: >-
          {{ states.input_text.trip_start_position.state.split(",")[1] |
          replace("[","") | replace("]","") | float() }}
        start longitude: >-
          {{ states.input_text.trip_start_position.state.split(",")[0] |
          replace("[","") | replace("]","") | float() }}
        end latitude: >-
          {{ states.input_text.trip_end_position.state.split(",")[1] |
          replace("[","") | replace("]","") | float() }}
        end longtitude: >-
          {{ states.input_text.trip_end_position.state.split(",")[1] |
          replace("[","") | replace("]","") | float() }}
        Average Speed: "{{ states.sensor.e2008_trip_average_speed.state}}"
        Start Odometer: "{{ states.input_number.trip_start_odometer.state }}"
        End odometer: "{{ states.input_number.trip_end_odometer.state }}"
        Total km: "{{ states.sensor.e2008_trip_total_km.state }}"
        Start Battery level: "{{ states.input_number.trip_start_battery_level.state }}"
        End Battery level: "{{ states.input_number.trip_end_battery_level.state }}"
        Total battery used: "{{ states.sensor.e2008_trip_total_battery_used.state }}"
        Trip consumption: "{{ states.sensor.e2008_trip_consumption.state }}"
  - service: notify.mobile_app_pixel_7_pro
    data:
      message: >-
        Trip completed at {{ states.input_datetime.trip_end_time.state }} {{-
        '\n' -}} Trip KM {{ states.sensor.e2008_trip_total_km.state | round (2)
        }} km {{- '\n' -}} Trip duration {{
        states.sensor.e2008_trip_duration.state }} {{- '\n' -}} Trip Consumption
        {{ states.sensor.e2008_trip_consumption.state | round (2) }} kwh/100km
mode: single

You need to install google sheets integration to add to google sheets and there are good documentations available on how to do that.

Similiarly you can do for charging data also

Now you can make a glance card for these sensors or helpers to display these values in dashboard

2 Likes

@vktpla First of all, very nice what you’ve created so far. Most probably you already took a look at this but since the PSA controller is already keeping track on charging- and trip data, would it not be possible get those values into HA without the need of calculating them?

Im also still looking how I can change the price per kWH.

Yes. They already in process of pushing some changes to get charge/trip via api calls. Hope it will be available soon

I saw your post in discussions at github. Looks great!
Could you please share your car sensor/card/automations-config?