OpenEVSE Home Assistant Integration

For completeness, here’s the Home Assistant OpenEVSE sensor component. Let me know how you get on if you mange to test this:

I tried to implement that in my configuration.yaml file but it probably belongs in sensors.yaml because nothing showed up. I ran out of time to test, and now I’m in Munich waiting for my connecting flight.

Jake

I’m pretty sure you can added it to either file. The yaml syntax can be quite picky. See my openenergymonitor example where I added sensor info to confuguration,yaml. Probably not best practice but works for simple setups:

Hi Glyn,

I managed to get one of my OpenEVSE units working with the new firmware, I have Hassio loaded in docker, I installed emoncms with docker, and I have the OpenEVSE posting to emoncms. Not going to lie, I feel pretty accomplished right now.

However, what I really want to do is ‘see’ if the car is connected to the OpenEVSE as a condition for presence detection if I can. I can get empty icons by using your configuration example above, but I don’t have those parameters set up in my emoncms, and I’m not even sure if ‘status’ exists.

Any pointers?

1 Like

I have managed to get values in the icons now. I’ve been trying to convert the temperature to F, which I can see in emoncms, but not in hassio. Also, my car finished charging just about the same time I made the tweaks for the current reading, so I’m not sure if it’s correct or not.

Nice work @jakekooser, that sounds really great :+1:

It would be cool if you could share your .yaml setup and some screenshots to show off your setup.

I’ve finally got round to testing the OpenEVSE Home Assistant integration myself, however I’ve not been able to get it to work. I’ve entered the following into my config.yaml file, after a restart the metrics do not display and there are no errors in the logfile, any ideas?

  - platform: openevse
    host: 192.168.XX.XX
    monitored_variables:
      - status
      - usage_session
      - charge_time

Hi Glyn,

I am not sure if I ever got it working the way I wanted to with the IP address. I do know I enabled MQTT and just ended up connecting that to my MQTT server and it’s working fine.

This is what I have in my sensors.yaml file - I separated it out from the main configuration.yaml to save space.

### Smart car charging amps

- platform: mqtt

name: OpenEVSE amps

state_topic: "car/charging_amps"

unit_of_measurement: 'amps'

### Smart car connection status

- platform: mqtt

name: OpenEVSE status

state_topic: "car/status"

### OpenEVSE total KWh

- platform: mqtt

name: OpenEVSE KWh

state_topic: "openevse/kwh"

I use node-red to process the reading I get from the OpenEVSE like so:

I use a state-machine node to process the connection status from the EVSE to tell me on my phone (VIA MQTT message) if the car is charging, connected, or disconnected like so:

And that allows me to see a graph on a Lovelace history-graph card, and display the charging status and current as entities

image.png

Here’s what the graph looks like, I just came back from a dinner party and plugged the car in.

image.png

For me, it was easiest just to jump in with MQTT, since I really didn’t have much understanding of the Open Energy Monitor stuff. I still don’t really have a perfect grasp of MQTT, but it allowed me to get the information I wanted to see easily enough.

Hope this helps!

Jake

1 Like

Thanks, I agree MQTT is the best way to do this. I’ve just seen an open issue on HA github discussion the openevse component:

https://github.com/home-assistant/home-assistant/issues/12954

User TonyApuzzo has developed a MQTT based solution which looks very good:

That looks pretty nice as well. I haven’t really needed any more information than my setup gathers. I really need to sort out a node-red automation to remind me if the car is not plugged in.

The core Home Assistant OpenEVSE integration is now working again and compatible with all OpenEVSE WiFi since about 2017 V2.x, V3.x and V4.x :clap:

A few tweets are still needed e.g truncate energy readings! But the basic data is updating as sensors in HA

To enable it just add the following to your HA configuration.yaml and change the IP address to match your unit.

  - platform: openevse
    host: 192.168.86.242
    monitored_variables:
      - status
      - charge_time
      - rtc_temp
      - ir_temp
      - ambient_temp
      - usage_session
      - usage_total

Sensors will then appear in HA and can be put into UI cards in the usual way.

The integration is basic at the moment, but overtime more functions will be added e.g control.

If anyone wants to help with this effort here are some dev threads:


An alternative which could be used in conjunction is to embed the OpenEVSE UI inside a HA card e.g

1 Like

Not working for me, keep getting authentication error, how do you add the username/password when creating the integration? FW 4.1.0 and latest HA

Ooh this exactly what I need, where I can find this?

Try clicking that link - in blue. (I haven’t checked, but it might go to somewhere helpful.)

Yup I eventually found the config file hidden in his github, messing with it now :grinning_face_with_smiling_eyes:

Looks like that code is ancient in terms of Home Assistant, rest_cmd no longer exists and I have replaced it with rest_command like so:

rest_command:
  openevse_rapi:
    method: post
    authentication: digest
    username: !secret OPENEVSE_USERNAME
    password: !secret OPENEVSE_PASSWORD
    url: "{{ url | default('http://' ~ host ~ '/r?json=1&rapi=' ~ rapi, true) }}"

But authentication: digest is not recognized so I have removed it.
Basically just errors:

2021-09-08 20:38:27 ERROR (MainThread) [homeassistant.helpers.template] Template variable error: 'value_json' is undefined when rendering '{{- value_json.ret -}}'
2021-09-08 20:38:38 WARNING (MainThread) [homeassistant.helpers.template] Template variable warning: 'host' is undefined when rendering '{{ url | default('http://' ~ host ~ '/r?json=1&rapi=' ~ rapi, true) }}'
2021-09-08 20:38:38 WARNING (MainThread) [homeassistant.helpers.template] Template variable warning: 'rapi' is undefined when rendering '{{ url | default('http://' ~ host ~ '/r?json=1&rapi=' ~ rapi, true) }}'
2021-09-08 20:38:54 WARNING (MainThread) [homeassistant.components.rest.sensor] Empty reply found when expecting JSON data

I assume this worked ages ago but not since HA is updated so frequently.

I’m working on getting this to work too since the native integration is so limited.

Also the integration needs to be updated to the new rules on energy meters so it can easily be added to the Energy Dashboard. And ca we get the ability to turn on and off the schedule so we can charge on demand?

Yes there are many changes, I am going through them one by one but it’s hit and miss due to the intermittent wifi connection of the evse :frowning:
Would be nice if someone else can confirm if it works ok, here is the updated code:

# ---
# #
# # Control an OpenEVSE Electric Car Charging Station.
# #
# # There are unfortunately quite a few pre-requisites needed to work with
# # OpenEVSE.
# #
# ## REQUIREMENTS ##
# #
# # Version 2.9.1 of OpenEVSE WiFi module, both MQTT and REST APIs are
# # used because each provides different capabilities.
# #
# # You must have MQTT setup in Home Assistant.
# #
# # Required custom components:
# #   input_number (allows dynamic adjustment of min/max values)
# #   input_select (allows label/value to be separately set)
# #   rest_command (supports HTTP basic authentication required by OpenEVSE)
# #
# # Required secrets
# #   See '../secrets-example.yaml' for required secrets
# #
# # You must configure the OpenEVSE wifi control panel Services Tab:
# #   - Enable MQTT:  Checked
# #   - Host:         Your hassio IP address where the MQTT broker is running
# #   - Port:         MQTT broker port (1883)
# #   - Username:     MQTT username for OpenEVSE (e.g. "openevse")
# #   - Password:     MQTT password for OpenEVSE
# #   - Base-topic:   openevse (or edit secrets)
# #   - Voltage-topic: Unused
# #
# # Optional Solar PV divert (this is poorly tested as I don't have a PV setup)
# #   - Enable Solar PV divert: checked
# #   - Feed:          openevse_solar
# #
# # For RAPI Details, see:
# #   https://github.com/OpenEVSE/open_evse/blob/stable/firmware/open_evse/rapi_proc.h
# #

# # RAPI Service
# # When calling this, set service data for either the full URL in `url`
# # or set both `host` and `rapi` variables. `url` has precedence
# # This requires a rest_cmd that supports http basic auth
rest_command:
  openevse_rapi:
    method: post
    username: !secret OPENEVSE_USERNAME
    password: !secret OPENEVSE_PASSWORD
    url: "{{ url | default('http://' ~ host ~ '/r?json=1&rapi=' ~ rapi, true) }}"

# # Sensors
sensor:

  #Get OpenEVSE min/max current via REST
  - platform: rest
    name: "OpenEVSE RAPI $GC"
    resource: !secret OPENEVSE_REST_GET_CURRENT_CAPACITY_RANGE
    force_update: no
    authentication: basic
    username: !secret OPENEVSE_USERNAME
    password: !secret OPENEVSE_PASSWORD
    headers:
        User-Agent: Home Assistant
        Content-Type: application/json
    json_attributes: [ 'cmd', 'ret' ]
    value_template: >-
      {{ value_json.ret }}      

  # Get OpenEVSE Current Settings via REST
  - platform: rest
    name: "OpenEVSE RAPI $GE"
    resource: !secret OPENEVSE_R_GE
    force_update: no
    authentication: basic
    username: !secret OPENEVSE_USERNAME
    password: !secret OPENEVSE_PASSWORD
    headers:
        User-Agent: Home Assistant
        Content-Type: application/json
    json_attributes: [ 'cmd', 'ret' ]
    value_template: >-
      {{ value_json.ret }}
      
  - platform: rest
    name: "OpenEVSE RAPI $GG"
    resource: !secret OPENEVSE_REST_GET_CHARGING_CURRENT_AND_VOLTAGE
    force_update: yes
    authentication: basic
    username: !secret OPENEVSE_USERNAME
    password: !secret OPENEVSE_PASSWORD
    headers:
        User-Agent: Home Assistant
        Content-Type: application/json
    json_attributes: [ 'cmd', 'ret' ]
    value_template: >-
      {{ value_json.ret }}

  - platform: rest
    name: "OpenEVSE Status"
    resource: !secret OPENEVSE_R_STATUS
    force_update: no
    authentication: basic
    username: !secret OPENEVSE_USERNAME
    password: !secret OPENEVSE_PASSWORD
    headers:
        User-Agent: Home Assistant
        Content-Type: application/json
    json_attributes:
      - "mode"
      - "wifi_client_connected"
      - "srssi"
      - "ipaddress"
      - "emoncms_connected"
      - "packets_sent"
      - "packets_success"
      - "mqtt_connected"
      - "ohm_hour"
      - "free_heap"
      - "comm_sent"
      - "comm_success"
      - "amp"
      - "pilot"
      - "temp1"
      - "temp2"
      - "temp3"
      - "state"
      - "elapsed"
      - "wattsec"
      - "watthour"
      - "gfcicount"
      - "nogndcount"
      - "stuckcount"
      - "divertmode"
      - "solar"
      - "grid_ie"
      - "charge_rate"
      - "divert_update"
    value_template: >-
      {{- value_json.state -}}

  # The RAPI REST Sensor is very unreliable, so filter it
  - platform: filter
    name: "OpenEVSE Session Energy"
    entity_id: sensor.openevse_session_energy_raw
    filters:
      - filter: outlier
        window_size: 6
        radius: 0.5

  # Template Sensors
  - platform: template
    sensors:
      openevse_session_energy_raw:
        friendly_name: "OpenEVSE Session Energy (unfiltered)"
        icon_template: mdi:gauge
        device_class: energy
        unit_of_measurement: 'kWh'
        value_template: >-
          {% set wattsec = state_attr('sensor.openevse_status', 'wattsec') | float(-1) %}
          {% if wattsec >= 0 %}
            {{ (wattsec / 3600000)|round(2) }}
          {% else %}
            unknown
          {% endif %}

      # Charging current from REST API
      openevse_current_now_rest:
        unique_id: openevse_current_now_rest
        friendly_name: "OpenEVSE Current Now (REST)"
        icon_template: mdi:current-ac
        device_class: current
        unit_of_measurement: 'A'
        value_template: >-
          {{- states('sensor.openevse_rapi_gg') |
              regex_replace('^\$OK ([0-9]+) (-?[0-9]+).*', '\\1') | float(0) / 1000 -}}

      # Combine both MQTT and REST sensors since neither ends up being super reliable
      openevse_current_now:
        unique_id: openevse_current_now
        friendly_name: "OpenEVSE Current Now"
        icon_template: mdi:current-ac
        device_class: current
        unit_of_measurement: 'A'
        value_template: >-
          {{-
            ([
              state_attr('sensor.openevse_status','charge_rate') | float(0),
              states('sensor.openevse_current_now_mqtt') | float(0)
            ] | max)
          -}}

      # Combine both MQTT and REST sensors since neither ends up being super reliable
      openevse_pilot:
        unique_id: openevse_pilot
        friendly_name: "OpenEVSE Pilot"
        icon_template: mdi:current-ac
        device_class: current
        unit_of_measurement: 'A'
        value_template: >-
          {{-
            ([
              state_attr('sensor.openevse_status','pilot') | float(0),
              states('sensor.openevse_pilot_mqtt') | float(0)
            ] | max)
          -}}

      openevse_pilot_rest:
        friendly_name: "OpenEVSE Pilot (REST)"
        icon_template: mdi:current-ac
        device_class: current
        unit_of_measurement: 'A'
        value_template: "{{ state_attr('sensor.openevse_status', 'pilot') | float(-1) }}" 

      openevse_state:
        friendly_name: "OpenEVSE State"
        icon_template: >-
          {%- if state_attr('sensor.openevse_status', 'state') | regex_match('^[01]$') -%}
            mdi:power-plug-off
          {%- elif state_attr('sensor.openevse_status', 'state') | regex_match('^2$') -%}
            mdi:car-electric
          {%- elif state_attr('sensor.openevse_status', 'state') | regex_match('^3$') -%}
            mdi:battery-charging
          {%- elif state_attr('sensor.openevse_status', 'state') | regex_match('^([456789]|10)$') -%}
            mdi:battery-alert
          {%- elif state_attr('sensor.openevse_status', 'state') | regex_match('^254$') -%}
            mdi:sleep
          {%- elif state_attr('sensor.openevse_status', 'state') | regex_match('^255$') -%}
            mdi:power-off
          {%- else -%}
            mdi:battery-unknown
          {%- endif -%}
        value_template: >-
          {%- if state_attr('sensor.openevse_status', 'state') | regex_match('^[01]$') -%}
            Not Connected
          {%- elif state_attr('sensor.openevse_status', 'state') | regex_match('^2$') -%}
            Connected
          {%- elif state_attr('sensor.openevse_status', 'state') | regex_match('^3$') -%}
            Charging
          {%- elif state_attr('sensor.openevse_status', 'state') | regex_match('^([456789]|10)$') -%}
            Error ({{- state_attr('sensor.openevse_status', 'state') -}})
          {%- elif state_attr('sensor.openevse_status', 'state') | regex_match('^254$') -%}
            Sleeping
          {%- elif state_attr('sensor.openevse_status', 'state') | regex_match('^255$') -%}
            Disabled
          {%- else -%}
            Unknown ({{- state_attr('sensor.openevse_status', 'state') -}})
          {%- endif -%}
    

  - platform: mqtt
    name: "OpenEVSE Temp 2"
    state_topic: !secret OPENEVSE_T_TEMP2
    unit_of_measurement: '°F'
    device_class: temperature
    value_template: >-
      {%- macro C2F(temperature) -%}
        {% set tmp = (((temperature *9) /50.0) + 32) %}
        {{- "%0.2f" % tmp -}}
      {%- endmacro -%}
      {{- C2F(value|float)|float -}}

  - platform: mqtt
    name: "OpenEVSE Current Now (MQTT)"
    icon: mdi:current-ac
    state_topic: !secret OPENEVSE_T_AMP
    device_class: current
    unit_of_measurement: "A"
    value_template: >-
      {{- (value|float / 1000.0) | round(1) -}}

  - platform: mqtt
    name: "OpenEVSE Pilot (MQTT)"
    icon: mdi:current-ac
    state_topic: !secret OPENEVSE_T_PILOT
    device_class: current
    unit_of_measurement: "A"
    value_template: >-
      {{- value|int -}}

  - platform: mqtt
    name: "OpenEVSE Total Energy"
    unique_id: openevse_total_energy_01
    icon: mdi:gauge-full
    state_topic: !secret OPENEVSE_T_WH
    state_class: total_increasing
    device_class: energy
    unit_of_measurement: kWh
    value_template: >-
      {{- value|float / 1000 | round(2) -}}

# Switches
switch:
  - platform: template
    switches:
      openevse_enable:
        friendly_name: "OpenEVSE Enable"
        turn_off:
          - service: rest_command.openevse_rapi
            data:
              url: !secret OPENEVSE_REST_DISABLE
        turn_on:
          - service: rest_command.openevse_rapi
            data:
              url: !secret OPENEVSE_REST_ENABLE
        icon_template: >-
          {%- if state_attr('sensor.openevse_status', 'state') | regex_match('^[0123]$') -%}
            mdi:power-on
          {%- else -%}
            mdi:power-off
          {%- endif -%}
        value_template: >-
          {{- state_attr('sensor.openevse_status', 'state') | regex_match('^[0123]$') -}}

      openevse_divertmode:
        friendly_name: "OpenEVSE Eco Divert"
        turn_off:
          - service: mqtt.publish
            data:
              topic: !secret OPENEVSE_T_DIVERT
              retain: true
              payload: 1
        turn_on:
          - service: mqtt.publish
            data:
              topic: !secret OPENEVSE_T_DIVERT
              retain: true
              payload: 2
        icon_template: >-
          {%- if state_attr('sensor.openevse_status', 'divertmode') | regex_match('^2$') -%}
            mdi:solar-power
          {%- else -%}
            mdi:power-off
          {%- endif -%}
        value_template: >-
          {{- state_attr('sensor.openevse_status', 'divertmode') | regex_match('^2$') -}}
      
input_boolean:
  # When true causes pilot current changes to be persisted into the OpenEVSE
  # firmware (otherwise saving pilot current is temporary) when this is OFF
  # then the volatile parameter is sent when changing the current
  openevse_save_current:
    name: "OpenEVSE Persist Current"
    initial: on
    icon: mdi:sync

input_number:
  openevse_pilot:
    name: "OpenEVSE Current"
    unit_of_measurement: "A"
    min: 10
    max: 32
    initial: 16
    step: 1

# Requires use of custom input_select component

input_select:  
  openevse_service_level:
    name: OpenEVSE Service Level
    options:
      - "A"
      - "1"
      - "2"
    icon: mdi:target

script:
  set_openevse_max_current:
    icon: mdi:content-save
    alias: OpenEVSE Set Pilot
    sequence:
      - service: rest_command.openevse_rapi
        data:
          host: !secret OPENEVSE_HOST
          rapi: "{{ '$SC ' ~ (states('input_number.openevse_pilot')|int) ~ (' V' if not is_state('input_boolean.openevse_save_current', 'on')) }}"

automation openevse:
  - alias: Update OpenEVSE Min/Max Current
    id: update_openevse_min_max_current
    trigger:
      platform: state
      entity_id: sensor.openevse_rapi_gc
    condition:
      condition: template
      value_template: >-
        {{ state_attr('sensor.openevse_rapi_gc', 'ret') | regex_match('^\$OK [0-9]+ +[0-9]+') }}
    action:
      - service: input_number.set_value
        data_template:
          entity_id: input_number.openevse_pilot
          # Restrict the min to 6A - 10A
          min: >-
            {%- set ret = state_attr('sensor.openevse_rapi_gc', 'ret') -%}
            {%- set rgx = '(?<=^\$OK) +([0-9]+) +([0-9]+)' -%}
            {%- set v = ret | regex_findall_index(rgx) -%}
            {{- [10, [6, v[0]|int]|max] | min -}}
          # Restrict the max to 32A even if L2 returns >32A
          max: >-
            {%- set ret = state_attr('sensor.openevse_rapi_gc', 'ret') -%}
            {%- set rgx = '(?<=^\$OK )([0-9]+) +([0-9]+)' -%}
            {%- set v = ret | regex_findall_index(rgx) -%}
            {{- [32, v[1]|int]|min -}}

  - id: update_input_number_openevse_pilot
    alias: Update input_number OpenEVSE Current
    description: >-
      Reflect changes made to pilot current on OpenEVSE back to the input_number
      slider position.
    trigger:
      platform: state
      entity_id: sensor.openevse_pilot
    condition:
      condition: template
      value_template: >-
        {{
           (states('sensor.openevse_pilot')|int >= state_attr('input_number.openevse_pilot', 'min')|int)
           and
           (states('sensor.openevse_pilot')|int <= state_attr('input_number.openevse_pilot', 'max')|int)
        }}
    action:
      - service: input_number.set_value
        data:
          entity_id: input_number.openevse_pilot
          value: "{{ states('sensor.openevse_pilot')|int }}"
    
  - alias: Update OpenEVSE Service Level
    id: update_openevse_service_level
    description: >-
      Reflect changes made to service level on the OpenEVSE back to Home Assistant.
    trigger:
      platform: mqtt
      topic: !secret OPENEVSE_T_ISL
    action:
      service: input_select.select_option
      data_template:
        entity_id: input_select.openevse_service_level
        option: >-
          {%- for option in state_attr("input_select.openevse_service_level", "options") -%}
            {%- if trigger.payload == option -%}
              {{- state_attr("input_select.openevse_service_level", 'options')[loop.index - 1] -}}
            {%- endif -%}
          {%- endfor -%}

  - alias: Set OpenEVSE Service Level
    id: set_openevse_service_level
    trigger:
      platform: state
      entity_id: input_select.openevse_service_level
    action:
      service: rest_command.openevse_rapi
      data_template:
        host: !secret OPENEVSE_HOST
        rapi: >-
          {%- for option in state_attr("input_select.openevse_service_level", "options") -%}
            {%- if is_state("input_select.openevse_service_level", option) -%}
              {{- '$SL+' ~ state_attr("input_select.openevse_service_level", 'options')[loop.index - 1] -}}
            {%- endif -%}
          {%- endfor -%}

FYI I would strongly advise against using RAPI as it is deprecated in the v4 firmware.

If you are missing anything thing from the WiFi API raise a ticket in the repo and it can probably be added.

You also should make use of the WebSocket as you will get much faster feedback and don’t have to poll the WiFi module all the time.

Also, also you can auto discover OpenEVSE units via mDNS or MQTT. Although this may need more of a ‘proper’ integration, I haven’t looked in to the details of Home Assistant yet.

Basically looking for these


So if anyone knows how to convert from RAPI to HTTP feel free to share :slight_smile:

I have started doing some documentation of the v4 API which might help you, there are also some examples of the API calls, but in short:

  • status can be retrieved from the /status endpoint as you are doing, and as I mentioned could be updated via /ws websocket
  • OpenEVSE Enable should be implemented using the /override endpoint
  • OpenEVSE Eco divert should use set the charge_mode config
  • OpenEVSE Current should use the /claims endpoint, although there is a bug in the current release that I have fixed on the jeremypoulter/issue9 branch
1 Like