Better Range Planning

Live data from Hare in ABRP

Hare’s internal range estimates are waaaay off when towing. Roughly off by a factor of 2. Since we’d rather not get stuck because we’ve run out of batteries, we’ve been experimenting with other routing tools. One that comes highly recommended is A Better Route Planner. One key feature is that it lets you plan assuming different settings.

Plan settings view

For our purposes, the most important is the Reference consumption. The Rivian dashboard displays the power consumption in mi/kWh. We found that it was typically around 0.80 while towing Tourtoise at highway speed, so that suggested 1000/0.80 = 1250 Wh/mi. Plugging that into the box resulted in pretty good agreement between what ABRP thought our charge was and what it actually was.

So. Pretty good estimate. Nice. Blog post over, right?

Hah.

The ABRP API

A Better Route Planner has the ability to use telemetry from your actual vehicle to improve the routing. Sadly, Rivian integration doesn’t exist (yet?). So let’s figure out how to send it data using the Telemetry API! You need to actually email the ABRP folks to get an API key, but they were quite obliging.

You’ll also want to generate a data token for your vehicle. Because I want to calibrate the consumption separately for Hare alone and Tourtoise and Hare together, I’ve created two vehicles and generated keys for each.

On the Your vehicles screen
tap “Live data”
Live data screen
From Live data, tap “Link Generic”
and copy the UUID token

The Rivian API

Sadly, Rivian doesn’t expose a documented public API. But there is an API, and happily, some enterprising folks have done the work of figuring it out, which lets me import the data into Home Assistant as described in the Data Collection post.

The Glue

In Home Assistant’s configuration.yaml file, I added a template sensor to populate the telemetry data:

sensor:
  - platform: template
    sensors:
      rivian_abrp_telemetry:
        value_template: >
          {
              "utc":{{ utcnow().timestamp() | int}},
              "soc":{{ states("sensor.rivian_energy_storage_charger_adjusted_soc") }},
              "lat":{{ state_attr('device_tracker.rivian_telematics_gnss_position', 'latitude') }},
              "lon":{{ state_attr('device_tracker.rivian_telematics_gnss_position', 'longitude') }},
              "is_charging":{{ is_state('binary_sensor.rivian_energy_storage_charger_vehicle_charger_state', 'on') and 1 or 0 }},
              "is_parked":{{ is_state('sensor.rivian_dynamics_propulsion_status_prndl', 'Park') and 1 or 0 }},
              "odometer":{{ float(states('sensor.rivian_dynamics_odometer_value')) * 1.609 }},
              "est_battery_range":{{ float(states('sensor.rivian_energy_storage_vehicle_energy_vehicle_range')) * 1.609 }},
              "car_model":"rivian:r1s:20:135:other"
            }

And a rest_command to actually post the data, with the right token based on whether it’s in Towing mode or not:

rest_command:
  abrp_telemetry:
    url: https://api.iternio.com/1/tlm/send
    #url: http://localhost:8000/1/tlm/send
    method: POST
    headers:
      authorization: "APIKEY <MY API KEY FROM THE EMAIL>"
    content_type:  'application/json; charset=utf-8'
    payload: >
      {
        "tlm":{{ states('sensor.rivian_abrp_telemetry') }},
        "token":"{{ is_state('sensor.rivian_dynamics_modes_drive_mode', 'Towing') and '<TOKEN FOR TOURTOISE AND HARE>' or '<TOKEN FOR HARE>' }}"
      }

To test this, I swapped in the localhost url line above and ran this handy little one-liner on my Home Assistant machine that pretended to be an HTTP server:

while true; do printf 'HTTP/1.1 200 OK\n' | nc -Nl 8000; done

This let me capture some samples of the data I’d send, which I then tested in Postman to make sure I got a success response.

Finally, I want to send data whenever Hare is either charging or moving (with a little grace period after it stops), so I set up an automation:

alias: ABRP Telemetry
description: "Send Rivian Telemetry to ABRP"
trigger:
  - platform: time_pattern
    seconds: /5
condition:
  - condition: or
    conditions:
      - condition: state
        entity_id: >-
          binary_sensor.rivian_energy_storage_charger_status_vehicle_charger_status
        state: "on"
      - condition: state
        entity_id: binary_sensor.rivian_use_state
        state: "on"
      - condition: not
        conditions:
          - condition: state
            entity_id: binary_sensor.rivian_use_state
            state: "off"
            for:
              hours: 0
              minutes: 1
              seconds: 0
action:
  - service: rest_command.abrp_telemetry
    data: {}
mode: single

Success!