[OBSOLETE] BMW ConnectedDrive

FYI: Home Assistant has BMW ConnectedDrive integration that works with US cars, BMW i3 electric cars, and more:

Maybe this helps

Hi Tom,

I’ve been trying to figure out how to read the variables Charge Time Remaining and State of Charge (%age), as I’d love to tie this in with Echo Speaks to announce when my i3 has finished charging. I’m completely stumped though after looking at the code.

Is this an easy thing to implement please?

Thanks
Guy

I’ve had a look at some of the documentation for the i3 - it looks like the attribute “chargingTimeRemaining” and “chargingStatus” would help.

I tried looking at where I could include this in the code, but couldnt see - is there a way to enable the device type handler to store a status which contains the readout from “chargingTimeRemaining” please?

The documented API is here - it looks as though these 2 variables from part of the broader suite of responses used in the existing code (e.g. Mileage):

Would be great to get this working please :slight_smile:

1 Like

Hey all,

There are two very different APIs at work here. Older models (apparently pre late 2017 and post late 2017). Not to mention the different regions.

SmartThings new integration only works with i model cars and cars manufactured after late 2017 (my best guess).

So while YES, I could update the app to work with these newer cars and multiple regions. I neither have the cars/accessibility to other countries or time to write the integration.

If anyone else wants to fork my code to add support for these models/regions, feel free- We can work together on pull requests.

Just wanted to check in and see if anyone has been able to get this integration to work with the BMW i3 in the US. The comment in the posting from last year suggests it only works outside of US/Canada.

Thanks,
Dan

I tried fiddling with it but didn’t get it to work before I lost interest :slight_smile:

HomeAssistant has similar integration using the API calls and although it worked, it never seemed to be worthwhile. BMW made changes a while back so you’d only be able to retrieve the car’s position when it was parked, so you couldn’t use that for position tracking. You could still get the state of some things, like whether the doors were locked or the windows are down, but that didn’t seem that useful to me.

What I miss is the old Smartthings app that ran in the car and detected when you got close to home so you could open the garage, etc.

Hi all,

I’ve spent a bit of time today on this, as I have a BMW i3 but charge it away from home (round the corner from where I live). I wanted to integrate this into WebCoRE so that when the car was reaching a certain charge level I would get a notification through Alexa to let me know.

I’ve altered the DTH to include the stateofChargeHV variable which reports the battery level as a % (customised DTH below).

Now the DTH reports the charge level as an integer, therefore opening up the possibility to conduct an action based on this value.

I hope this is useful for some of you.

Best wishes,
Guy

/**
 *  BMW ConnectedDrive Car
 *
 *  Copyright 2018 Tom Beech
 *
 */
 
metadata {
	definition (name: "BMW ConnectedDrive Car", namespace: "ibeech", author: "ibeech") {
		capability "Lock"
    capability "Image Capture"
    capability "Refresh"
    capability "Polling"
    capability "Thermostat Fan Mode"
    capability "Light"
    
    attribute "Battery","number"
    
    command "startVentalation"
    command "flashLights"
   
	}

	simulator {
		// TODO: define status and reply messages here
	}

	tiles(scale: 2) {
    
	tiles {

        carouselTile("carImage", "device.image", action: "Image Capture.take", width: 3, height: 2) { }
			     
       	valueTile("carDetails", "device.carDetails", width: 3, height: 2) {
    		  state "val", label:'${currentValue}', defaultState: true
		}
        
        valueTile("Battery", "device.Battery", width: 3, height: 2) {
    		  state "val", label:'${currentValue}', defaultState: true
		}
        
        valueTile("carService", "device.carService", width: 6, height: 2) {
    		  state "val", label:'${currentValue}', defaultState: true
		}
        
        standardTile("lockCar", "lock", width: 2, height: 2) {
				state "default", label:'Lock', action:"lock", icon:"st.Home.home3", backgroundColor:"#79b821"
			}
        
        standardTile("unlockCar", "unlock", width: 2, height: 2) {
				state "default", label:'Unlock', action:"unlock", icon:"st.Home.home3", backgroundColor:"#79b821"
			}
        
        standardTile("flashLightButton", "flashLights", width: 2, height: 2) {
				state "default", label:'Flash', action:"flashLights", icon:"st.Lighting.light11", backgroundColor:"#79b821"
			}      
        
        standardTile("startVentalationButton", "startVentalation", width: 2, height: 2) {
				state "default", label:'Ventilate', action:"startVentalation", icon:"st.Home.home1", backgroundColor:"#79b821"
			}            
                        
    	standardTile("refresh", "refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
				state("default", label:'refresh', action:"refresh", icon:"st.secondary.refresh-icon")
			}

        main "startVentalationButton"
        details([ "carImage", "carDetails", "carService", "lockCar", "unlockCar", "flashLightButton", "startVentalationButton", "refresh"])
    }        
}
}

def refresh() {

	def serviceInfo = parent.getCarServicenfo(device.deviceNetworkId)
    
def string = device.displayName;
string += "\r\rMiles: ${serviceInfo.attributesMap.mileage} " 
string += "Charge: ${serviceInfo.attributesMap.chargingLevelHv} %" 
	sendEvent(name: "carDetails", value: string);
   
   	def integer = device.displayName;
integer = "${serviceInfo.attributesMap.chargingLevelHv}"
sendEvent(name: "Battery", value: integer);

string = "Location: ${getCarLocation(serviceInfo.attributesMap.gps_lat, serviceInfo.attributesMap.gps_lng)}"
serviceInfo.vehicleMessages.cbsMessages.each{msg -> 
    def info1 = formatServiceInfo(msg);
    if(info1 != "") string += "\r\n${info1}";
}
sendEvent(name: "carService", value: string);    
captureCarImage()
}

def getCarLocation(lat, lon) {

	def params = [
		uri: "https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lon}&key=AIzaSyD-nz_0qxBlvGj0WZjpbHfnA-rQG4anMtM"
	]	
log.debug params

httpGet(params) {response ->    
    log.debug "${response.data}"
    
		return response.data.results.formatted_address[0]
	}
}

def formatServiceInfo(msg){
	              
	if(msg == null) return "";

if(msg.unitOfLengthRemaining != "") {
	return "${msg.text}: ${msg.status}. Due ${msg.date} or ${msg.unitOfLengthRemaining}"
    } else {
    	return "${msg.text}: ${msg.status}. Due ${msg.date}"
}
}

def poll() {
	refresh()
checkStatus()
}

def lock() {
	log.debug "Locking"
parent.lockDoors(device.deviceNetworkId)
}

def unlock() {
	log.debug "Unlocking"
parent.unlockDoors(device.deviceNetworkId)
}

def startVentalation() {
	log.debug "Starting Ventilation"
parent.ventilate(device.deviceNetworkId)
}

def fanOn() {
	log.debug "fanOn hit"
	startVentalation();
}
def fanCirculate() {
	log.debug "fanCirculate hit"
	startVentalation();
}
def fanAuto() {
	log.debug "fanAuto hit"
	startVentalation();
}

def flashLights() {
	log.debug "Flashing lights ${device.deviceNetworkId}"
parent.flashLights(device.deviceNetworkId)
}
def on() {
	log.debug "Light.on hit"
flashLights()    
}
def off() {
	log.debug "Light.off hit. Unable to turn lights off as they only flash"
}

def getImageName() {
return java.util.UUID.randomUUID().toString().replaceAll('-','')
}

def captureCarImage() {
if(state.PicTaken) {
	// Pic was already taken, all done.    
} else {
    log.debug "Aquring image of car"
    def params = [
        uri: parent.getCarImageUrl(device.deviceNetworkId)
    ]

    try {
        httpGet(params) { response ->
            // we expect a content type of "image/jpeg" from the third party in this case
            if (response.status == 200 && response.headers.'Content-Type'.contains("image/png")) {
                def imageBytes = response.data
                if (imageBytes) {
                    def name = getImageName()
                    try {
                        storeImage(name, imageBytes)
                        state.PicTaken = true
                    } catch (e) {
                        log.error "Error storing image ${name}: ${e}"
                    }

                }
            } else {
                log.error "Image response not successful or not a jpeg response"
            }
        }
    } catch (err) {
        log.debug "Error making request: $err"
    }
}
}

// parse events into attributes
def parse(String description) {
	log.debug "Virtual siwtch parsing '${description}'"
}

Any chance there’s a similar code available for those of us in the USA? I see where its been requested but it appears to be a dead end.

Hi @Guy1992 - i tried setting up this smartapp and your modified device handler becuase I also have an i3 so was hoping to see battey level etc. But the smartapp is failing on the login with the BMW credentials. Just wondering if yours is still working for you? Or the the smartapp is just dead now. Thanks, David

Hi David,

I’ve actually moved to Hubitat now, however it should still work on SmartThings (unless their migration to the ‘New App’ now means that the SmartApp can’t initialise).

Please can you have a go at removing the app in the ST mobile app, then re-installing with ‘live logging’ open in IDE and post a copy of the output when you go through the login process?

Cheers
Guy

PS - the ported SmartApp and DTH for Hubitat Elevation can be found here: https://github.com/mayhew132/HubitatElevation/tree/main/BMWConnectedDrive

Thanks Guy

This is what I get in the log when I put in my BMW credentials in the smartapp:

ddc17bf0-48c8-4109-874a-698752d405a5 14:42:20:error java.lang.IndexOutOfBoundsException: index is out of range 0…-1 (index = 0)

ddc17bf0-48c8-4109-874a-698752d405a5 14:42:20:debug Request was successful, 302

ddc17bf0-48c8-4109-874a-698752d405a5 14:41:36:debug mainPage

ddc17bf0-48c8-4109-874a-698752d405a5 14:41:36:debug mainPage

Hi David,

I’ve just tested this and it looks like the transition to the SmartThings New App has killed the initialisation of this SmartApp therefore it is no longer working.

As an aside, it works absolutely fine porting the code over to Hubitat Elevation so there is a possibility of integration there. I’m glad I made the move away from ST last week as it seems there are a number of SmartApps which no longer work with the updated app.

Best wishes,
Guy

Sorry to dig up an old thread, but I am using Hubitat now too and getting the same error when entering credentials into the App

“java.lang.IndexOutOfBoundsException: index is out of range 0…-1 (index = 0)”

Any chance there could be an easy fix for this one or has this officially been killed by BMW? :slight_smile:

Hi,

Is there any new driver for the ST Edge Drivers ?

Thank you,
Daniel