Tesla Wall Connector Status (Gen 3 with Wi-Fi) (Amps, Voltage, health, and contact sensor - "is my car plugged in")

Hubitat Tesla owners.

For folks with a Tesla Charger.... this driver checks to see if your Tesla is plugged in by directly interrogating the wall charger - Super simple...

Have a great day!

/**
 *  Tesla Plug
 *
 *  Copyright 2022 Paul Nielsen
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 *  Change History:
 *
 *    Date        Who             Info            Version
 *    ----        ---             ----            -------
 *    2022-09-13  Paul Nielsen    Created         0.0
 * 
 */
import groovy.json.JsonSlurper

metadata {
    definition (name: "Tesla Plug", namespace: "PMFN", author: "Paul Nielsen") {
        capability "Sensor"
        capability "Contact Sensor"
        command "refresh"
        
        attribute 'voltage', 'number'
        attribute 'current', 'number'
        attribute 'status', 'string'
    }

    preferences {
        input(name: "deviceIP", type: "string", title:"Device IP Address", description: "Enter IP Address of your Tesla Home Charger", required: true, displayDuringSetup: true)
    }
}

def initialize() {
    runEvery1Minute('refresh')
    
}

def parse(String description) {
    log.debug(description)
    def msg = parseLanMessage(description)   
    def bodyString = new groovy.json.JsonSlurper().parseText(msg.body)
    //log.debug(bodyString)
    def volts = bodyString.'grid_v'
    def amps = bodyString.'vehicle_current_a'
    sendEvent(name: "voltage", value: volts)
    sendEvent(name: "current", value: amps)
    log.debug(bodyString.'vehicle_connected')
    if (bodyString.'vehicle_connected') {
        close() 
    } else {
        open()
    }
    

}

def refresh() {
    getCmd()
}

def open(){
	sendEvent(name: "contact", value: "open")
}

def close(){
	sendEvent(name: "contact", value: "closed")
}

def getCmd() {
    def localDevicePort = "80"
    def path = "/api/1/vitals" 
    def body = ""
    def method = "GET"
    def deviceContent = "application/json"
    //if(deviceBody) body = deviceBody
    def headers = [:] 
    headers.put("HOST", "${deviceIP}:${localDevicePort}")
    headers.put("Content-Type", deviceContent)

    try {
        def hubAction = new hubitat.device.HubAction(
            method: method,
            path: path,
            body: body,
            headers: headers
            )
        //log.debug hubAction
        sendEvent(name: "status", value: "healthy")
        return hubAction
    }
    catch (Exception e) {
        log.debug "getCmd exception ${e} on ${hubAction}"
        sendEvent(name: "status", value: "unhealthy")
    }  
}
5 Likes

Link?

Sorry... I'm trying to figure that out. Forgot how to post code.

If you want to post the code directly here, use the </> pre-formatted text button.

So that the code will show up like this.

/**
 *  Tesla Plug
 *
 *  Copyright 2022 Paul Nielsen
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 *  Change History:
 *
 *    Date        Who             Info            Version
 *    ----        ---             ----            -------
 *    2022-09-13  Paul Nielsen    Created         0.0
 * 
 */
import groovy.json.JsonSlurper

metadata {
    definition (name: "Tesla Plug", namespace: "PMFN", author: "Paul Nielsen") {
        capability "Sensor"
        capability "Contact Sensor"
        command "refresh"
        
        attribute 'voltage', 'number'
        attribute 'current', 'number'
        attribute 'status', 'string'
    }

    preferences {
        input(name: "deviceIP", type: "string", title:"Device IP Address", description: "Enter IP Address of your Tesla Home Charger", required: true, displayDuringSetup: true)
    }
}

def initialize() {
    runEvery1Minute('refresh')
    
}

def parse(String description) {
    log.debug(description)
    def msg = parseLanMessage(description)   
    def bodyString = new groovy.json.JsonSlurper().parseText(msg.body)
    //log.debug(bodyString)
    def volts = bodyString.'grid_v'
    def amps = bodyString.'vehicle_current_a'
    sendEvent(name: "voltage", value: volts)
    sendEvent(name: "current", value: amps)
    log.debug(bodyString.'vehicle_connected')
    if (bodyString.'vehicle_connected') {
        close() 
    } else {
        open()
    }
    

}

def refresh() {
    getCmd()
}

def open(){
	sendEvent(name: "contact", value: "open")
}

def close(){
	sendEvent(name: "contact", value: "closed")
}

def getCmd() {
    def localDevicePort = "80"
    def path = "/api/1/vitals" 
    def body = ""
    def method = "GET"
    def deviceContent = "application/json"
    //if(deviceBody) body = deviceBody
    def headers = [:] 
    headers.put("HOST", "${deviceIP}:${localDevicePort}")
    headers.put("Content-Type", deviceContent)

    try {
        def hubAction = new hubitat.device.HubAction(
            method: method,
            path: path,
            body: body,
            headers: headers
            )
        //log.debug hubAction
        sendEvent(name: "status", value: "healthy")
        return hubAction
    }
    catch (Exception e) {
        log.debug "getCmd exception ${e} on ${hubAction}"
        sendEvent(name: "status", value: "unhealthy")
    }  
}
2 Likes

Thank you

1 Like

Nice, that's pretty neat. For me personally electric vehicles appear to be much worse for the environment. I read it takes 100-300 barrels of oil to fully produce a complete battery assembly for a Tesla. Car looks to be tremendous fun to drive, however if you're in Cali, people are being asked to not charge their cars, how the hell do you get to work?

Solar panels -> Free “fuel”!

We charge our EV once a week, so it wouldn’t be much of an issue for us, but that said, we don’t have much traffic to deal with here.

Electric is great for slow or stop and go traffic - doesn’t use much electricity while stopped, compared to gas cars!

2 Likes

Hi @nielsen411 . Thanks for doing this. I added a couple of lines to get the last charge session in Wh.
def last = bodyString.'session_energy_wh'
sendEvent(name: "last charge", value: last + ' Wh')

Nice. Thank you. I will add that to the next version.

You can also do some math on it and display Kwh.
def last = (bodyString.'session_energy_wh')/1000
sendEvent(name: "last charge", value: last + ' Kwh')

It would be great if there was some way to turn the charging on and off.

Version 1.0. Thanks @Wounded

/**
 *  Tesla Plug
 *
 *  Copyright 2022 Paul Nielsen
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 *  Change History:
 *
 *    Date        Who             Info            Version        Notes:
 *    ----        ---             ----            -------        ------
 *    2022-09-13  Paul Nielsen    Created         0.0            
 *    2022-09-16  Paul Nielsen    Update          1.0            Added new Fields (Thanks @wounded) and debug toggle
 *
 */
import groovy.json.JsonSlurper

metadata {
    definition (name: "Tesla Plug", namespace: "PMFN", author: "Paul Nielsen") {
        capability "Sensor"
        capability "Contact Sensor"
        command "refresh"
        
        attribute 'voltage', 'number'
        attribute 'current', 'number'
        attribute 'status', 'string'
        attribute 'last_charge', 'string'
        attribute 'charge_status', 'string'
    }

    preferences {
        input(name: "deviceIP", type: "string", title:"Device IP Address", description: "Enter IP Address of your Tesla Home Charger", required: true, displayDuringSetup: true)
        input name: 'loggingEnabled', type: 'bool', title: '<b>Enable Logging?</b>', description: '<div><i>Automatically disables after 30 minutes.</i></div><br>', defaultValue: false
    }
}

def initialize() {
    runEvery1Minute('refresh')
    if (settings.loggingEnabled) runIn(1800, disableLogging)

}

def parse(String description) {
    logDebug "Debug: JSON Message: '${description}'"
    def msg = parseLanMessage(description)   
    def bodyString = new groovy.json.JsonSlurper().parseText(msg.body)
    //log.debug(bodyString)
    def volts = bodyString.'grid_v'
    def amps = bodyString.'vehicle_current_a'
    def last = bodyString.'session_energy_wh'/1000
    def chargeStatus = bodyString.'evse_state'
    def chargeStatus2
    switch(chargeStatus){
    case 1 : chargeStatus2 = "Not Connected";
        break;
    case 4 : chargeStatus2 = "Finished";
        break;
    case 11 : chargeStatus2 = "Charging";
        break;
    default: chargeStatus2 = "Unknown"
    }
    sendEvent(name: "charge_status", value: chargeStatus2)
    sendEvent(name: "last_charge", value: last + ' KWh')
    sendEvent(name: "voltage", value: volts)
    sendEvent(name: "current", value: amps)
    logDebug "Debug: charge status: '${chargeStatus2}'"
    if (bodyString.'vehicle_connected') {
        close() 
    } else {
        open()
    }
    

}

def refresh() {
    getCmd()
}

def open(){
	sendEvent(name: "contact", value: "open")
}

def close(){
	sendEvent(name: "contact", value: "closed")
}

def getCmd() {
    def localDevicePort = "80"
    def path = "/api/1/vitals" 
    def body = ""
    def method = "GET"
    def deviceContent = "application/json"
    def headers = [:] 
    headers.put("HOST", "${deviceIP}:${localDevicePort}")
    headers.put("Content-Type", deviceContent)

    try {
        def hubAction = new hubitat.device.HubAction(
            method: method,
            path: path,
            body: body,
            headers: headers
            )
        logDebug "Debug: hubAction '${hubAction}'"
        sendEvent(name: "status", value: "healthy")
        return hubAction
    }
    catch (Exception e) {
        logDebug "Debug: getCmd exception '${e}' on '${hubAction}'"
        sendEvent(name: "status", value: "unhealthy")
    }  
}

void disableLogging() {
	log.info 'Logging disabled'
	device.updateSetting('loggingEnabled',[value:'false',type:'bool'])
}

void logDebug(str) {
    if (loggingEnabled) {
        log.debug str
    }
}

Thank you for this @nielsen411
On line 65 there's an additional evse_state of 9 = "Charging Stopped". I've seen this state returned when the Wall Charger is plugged in and waiting to be charged (ex: when not charging during peak-hours, waiting to start by departure time, or just before/after a "Finished" state).

    case 9 : chargeStatus2 = "Charging Stopped";
        break;

Keep up the great work. Thanks again!

I tried to paste the code as an "App Code" which gave me a warning that it might contain driver code. Error code reads: Metadata Error:Please check if you inadvertently pasted driver code into apps code window on line 25

I did not get any issues with pasting into as Driver Code instead and successfully saved.

you can also use the tesla car app. that can tell you if the car is plugged in..

ie..

Hi. This is a device driver. Install the driver code, add a new virtual device (the name of this driver), open the device, and set the IP... you should be good to go after that...

Is there anyway to "Refresh" status of Virtual Device using Tesla Plug driver as an "Action" in RM? Device is not listed to choose?

1 Like

Like @msaeri, I would also be interested if there is a way to query the status of the Virtual Device I created using this driver. The device status can be updated from the device page using the "Refresh" button, however, the values do not update themselves. Because of this, none of the values function as Triggers (although they can be selected as such), and conditionals never evaluate using current values, only "stale" values as of the last time they were manually Refreshed by the user. Having this device available in the drop-down list of devices that can be "Refreshed" as an Action in RM would at least allow some way to automate this, even if the Virtual Device doesn't "push" updates to HE.

(I realize this driver hasn't been updated in a couple of years - thank you, Paul Nielsen! - but it still functions, doesn't depend on the cloud / Tesla API, and reports everything I need to know).

I found this driver and have the same issue as "Teslio" with refresh updating.
I added the buttons below to my main dashboard:
image
Is there a way to add a function to the driver whereby I could add another button to perform a manually triggered refresh action? Groovy looks easy enough to work with, it understanding how the API works. Like, can I add a manual refresh trigger button.

The way I’ve done this in the past for different but similar purposes requires 3 steps.

Step 1: Create a virtual button
Step 2: Create a Rule Machine (RM) rule that is triggered by the virtual button. The rule actions send the Refresh() command to the device driver (actuator).
Step 3: Add the Virtual button to a dashboard. Will run the refresh via the RM rule when pressed.