kamiKAC / leaf2mqtt

Pull in data from the Nissan Connect platform for the LEAF and publish over MQTT
GNU General Public License v3.0
9 stars 5 forks source link

Docker Pulls Docker Image Version (latest by date) GitHub Actions Workflow Status

leaf2mqtt

:warning: olderCanada and olderUSA support may break at anytime because Nissan keep changing the API key. Thank you Nissan for working against your customers.

:warning: If you're not using the Leaf frequently, stop the container or drastically reduce the update frequency, or you could well end up with a flat 12V battery.

This works for my Canadian made in 2018 LEAF 40kWh. It was also reported to be working on a newer than May 2019 Leaf.

You must have a working MQTT broker on your LAN.

Should work with multiple Leafs, but it is untested. Please open an issue with feedback if possible.

Setup

Home Assistant add-on

Click the icon below to add this repository to your Home Assistant instance or follow the procedure highlighted on the Home Assistant website.

Install leaf2mqtt add-on repo.

Pre-built images

You can use pre-built images from here: https://hub.docker.com/r/kamikac/leaf2mqtt

tag example: kamikac/leaf2mqtt:latest

Building the image

docker build --tag leaf2mqtt .

     -- OR --

cp local_settings.env.tmpl local_settings.env
docker-compose build

Running the image

Parameter Optional Description
LEAF_USERNAME No Your NissanConnect username
LEAF_PASSWORD No Your NissanConnect password
LEAF_TYPE No newerThanMay2019, olderCanada, olderUSA, olderEurope, olderAustralia or olderJapan
MQTT_HOST No IP or hostname of your mqtt broker. Localhost or 127.0.0.1 will not work when using Docker, use real host LAN ip
MQTT_PORT Yes Port of your mqtt broker. Default is 1883
MQTT_USERNAME Yes Your mqtt username
MQTT_PASSWORD Yes Your mqtt password
MQTT_BASE_TOPIC Yes The root MQTT topic for leaf2mqtt. Default is "leaf"
UPDATE_INTERVAL_MINUTES Yes Time between automatic status refresh. Default is 60
CHARGING_UPDATE_INTERVAL_MINUTES* Yes Time between automatic status refresh when charging. Default is 15
COMMAND_ATTEMPTS Yes Number of attempts for any command regardless of success or failure. Since some of the Nissan apis are unreliable, I recommend a value of 5. Default is 1.
LOG_LEVEL Yes The log verbosity used by leaf2mqtt. Default is "Warning"

Example:

docker run --restart always -e LEAF_USERNAME="myusername@somewhere.com" -e LEAF_PASSWORD="Some P4ssword!" -e LEAF_TYPE="newerThanMay2019" -e MQTT_HOST=192.168.1.111 -e UPDATE_INTERVAL_MINUTES=1440 -e COMMAND_ATTEMPTS=5 --name leaf2mqtt leaf2mqtt

     -- OR --

Edit local_settings.env
docker-compose up -d

:information_source:* The CHARGING_UPDATE_INTERVAL_MINUTES value will only be used after the ongoing UPDATE_INTERVAL_MINUTES is elapsed and the Leaf is charging.

Status and Commands

In these examples, the MQTT_BASE_TOPIC is set to the default (leaf).

General

Status

Topic Type Description
leaf/{vin}/nickname String The reported nickname of the leaf
leaf/{vin}/vin String The reported vin of the leaf
leaf/{vin}/lastErrorDateTimeUtc Iso8601 UTC The datetime of the last failed command execution or status query
leaf/{vin}/json String A json representation of all general status

Commands

Topic Payload Description
leaf/{vin}/command update Request an update for all status

Battery

Status

Topic Type Description
leaf/{vin}/battery/percentage Integer The last reported battery charge of the leaf
leaf/{vin}/battery/connected Boolean True if the leaf is reported as currently connected. False otherwise
leaf/{vin}/battery/charging Boolean True if the leaf is reported as currently charging. False otherwise
leaf/{vin}/battery/capacity Double The reported total capacity of the battery
leaf/{vin}/battery/chargingSpeed String can be one of None, Slow, Normal or Fast
leaf/{vin}/battery/cruisingRangeAcOffKm Integer Range left with climate off in kilometers as estimated by the Leaf
leaf/{vin}/battery/cruisingRangeAcOffMiles Integer Range left with climate off in miles as estimated by the Leaf
leaf/{vin}/battery/cruisingRangeAcOnKm Integer Range left with climate on in kilometers as estimated by the Leaf
leaf/{vin}/battery/cruisingRangeAcOnMiles Integer Range left with climate on in miles as estimated by the Leaf
leaf/{vin}/battery/timeToFullTrickleInMinutes String The reported time (H:MM:SS.mmmmmm) to fully charge when trickling (~1kw)
leaf/{vin}/battery/timeToFullL2InMinutes String The reported time (H:MM:SS.mmmmmm) to fully charge when charging in half speed L2 (~3kw)
leaf/{vin}/battery/timeToFullL2_6kwInMinutes String The reported time (H:MM:SS.mmmmmm) to fully charge when charging in full speed L2 (~6kw)
leaf/{vin}/battery/lastUpdatedDateTimeUtc Iso8601 UTC The datetime when the last battery values were updated
leaf/{vin}/battery/lastReceivedDateTimeUtc Iso8601 UTC The datetime when leaf2mqtt received the last battery values
leaf/{vin}/battery/json String A json representation of all battery status

Commands

Topic Payload Description
leaf/{vin}/command/battery update Request an update for all battery status
leaf/{vin}/command/battery startCharging Request the Leaf to start charging

Climate

Status

Topic Type Description
leaf/{vin}/climate/cabinTemperatureC Double The reported cabin temperature in Celsius
leaf/{vin}/climate/cabinTemperatureF Double The reported cabin temperature in Fahrenheit
leaf/{vin}/climate/runningStatus Boolean True if the Leaf is reporting the HVAC as running. False otherwise
leaf/{vin}/climate/lastReceivedDateTimeUtc Iso8601 UTC The datetime when leaf2mqtt received the last climate values
leaf/{vin}/climate/json String A json representation of all climate status

Commands

Topic Payload Description
leaf/{vin}/command/climate update Request an update for all climate status
leaf/{vin}/command/climate start Request the Leaf to start climate control
leaf/{vin}/command/climate startC XY Request the Leaf to start climate control at XY Celsius
leaf/{vin}/command/climate startF XY Request the Leaf to start climate control at XY Fahrenheit
leaf/{vin}/command/climate stop Request the Leaf to stop climate control

Stats

{TimeRange} must be daily or monthly.

Status

Topic Type Description
leaf/{vin}/stats/{TimeRange}/targetDate Iso8601 The reported target date of the stats
leaf/{vin}/stats/{TimeRange}/travelTimeHours double The reported time traveled in hours during specified time range
leaf/{vin}/stats/{TimeRange}/travelDistanceMiles double The reported miles traveled during specified time range
leaf/{vin}/stats/{TimeRange}/travelDistanceKilometers double The reported kilometers traveled during specified time range
leaf/{vin}/stats/{TimeRange}/milesPerKwh double The reported miles per kWh during specified time range
leaf/{vin}/stats/{TimeRange}/kilometersPerKwh double The reported kilometers per kWh during specified time range
leaf/{vin}/stats/{TimeRange}/kwhUsed double The reported kWh consumption during specified time range
leaf/{vin}/stats/{TimeRange}/kwhPerMiles double The reported kWh consumption per miles during specified time range
leaf/{vin}/stats/{TimeRange}/kwhPerKilometers double The reported kWh consumption per km during specified time range
leaf/{vin}/stats/{TimeRange}/co2ReductionKg double The reported number of co2 in Kg saved during specified time range
leaf/{vin}/stats/{TimeRange}/tripsNumber int The reported number of trips during specified time range
leaf/{vin}/stats/{TimeRange}/kwhGained Double The reported total regen in kWh during specified time range
leaf/{vin}/stats/{TimeRange}/lastReceivedDateTimeUtc Iso8601 UTC The datetime when leaf2mqtt received the last stats values
leaf/{vin}/stats/json String A json representation of all stats

Commands

Topic Payload Description
leaf/{vin}/command/stats/{TimeRange} update YYYY-MM-DD HH:MM:SS Request an update for daily or monthly stats. Date must respect Iso8601

Location

Status

Topic Type Description
leaf/{vin}/location/latitude String The reported last known location's latitude in decimal degrees
leaf/{vin}/location/longitude String The reported last known location's longitude in decimal degrees
leaf/{vin}/location/coordinates String The reported last known location (combined latitude and longitude in decimal degrees)
leaf/{vin}/location/lastReceivedDateTimeUtc Iso8601 UTC The datetime when leaf2mqtt received the last location values
leaf/{vin}/location/json String A json representation of all location status

Commands

Topic Payload Description
leaf/{vin}/command/location update Request an update for the last known location

Cockpit Status

Status

Topic Type Description
leaf/{vin}/cockpitStatus/totalMileage Double The total mileage from the vehicle. The unit (km or miles) depends on the regional area.
leaf/{vin}/cockpitStatus/lastReceivedDateTimeUtc Iso8601 UTC The datetime when leaf2mqtt received the last cockpit status values
leaf/{vin}/cockpitStatus/json String A json representation of all cockpit status

Commands

Topic Payload Description
leaf/{vin}/command/cockpitStatus update Request an update for the cockpit status

:information_source: The status and commands for the first Leaf in the account are also supported by using the same topic without the {vin}.

:warning: Not all status and commands are supported for a given leaf type due to Carwings, NissanConnectNA or NissanConnect api limitations.

Home Assistant Integration

Sensor examples

mqtt:
  sensor:
    - name: leaf_battery_level
      # Since VIN is not specified, it will represent the state from the first vehicle in the account.
      state_topic: "leaf/battery/percentage"
      unit_of_measurement: "%"
      device_class: battery

    - name: leaf_battery_last_updated
      # Since VIN is not specified, it will represent the state from the first Leaf in the account.
      state_topic: "leaf/battery/lastUpdatedDateTimeUtc"
      device_class: timestamp

    - name: leaf_battery_last_received
      # You can specify the vin if you prefer or if you have more than one Leaf.
      state_topic: "leaf/XXXXXSOMEXVINXXXXX/battery/lastReceivedDateTimeUtc"
      device_class: timestamp

Recommended Battery Status Update Script

In Home Assistant, calling a script like this - service: script.some_script_name within another script or automation will actually stop the execution of the calling script until script.some_script_name terminates, unlike using script.turn_on. Knowing this, you can ensure you have the latest state for your leaf before continuing an automation using a script like this:

update_leaf_battery:
  # Using queued will ensure you do not update twice at the same time and will prevent
  # subsequent invocations from asking an update right away because of the while's conditions.
  # All the callers will also wait for the result.
  mode: queued
  sequence:
    - repeat:
        while:
          # Used with the sensors in the section above, this condition will
          # ensure we continue until the states are really updated.
          # It will also prevent subsequent calls from unnecessarily requesting
          # an update before the current state is 10 minutes old.
          - >
            {{ as_timestamp(now()) -
              as_timestamp(states('sensor.leaf_battery_last_updated')) > 600 }}

          # We also stop the loop after 4 tries since Nissan servers can send the same
          # old data many times in a row. I think this happens when the state did not really changed
          # or the Leaf is unreachable. 
          - "{{ repeat.index <= 4 }}"

        sequence:
          # We publish the update command for the car. 
          # You can also ommit the VIN to target the first Leaf in the account.
          # You can also request update for every state for one Leaf by removing the /battery section
          - service: mqtt.publish
            data:
              topic: "leaf/XXXXXSOMEXVINXXXXX/command/battery"
              payload: "update"
          # We now wait until we actually have received a response or if we timed out.
          # This does not mean that the received data is the latest. This is why
          # we check sensor.leaf_battery_last_updated in the while condition.
          - wait_for_trigger:
              - platform: state
                entity_id: sensor.leaf_battery_last_received
            timeout: 600
    # Let's have a cool down to give time to Home Assistant to update all the states.
    - delay: "00:00:10"

OpenHAB integration

Once you configure and start application you can configure OpenHAB to get data for your Leaf. Example config file is here and documentation is here

Credits