Geniales iOS14 Homescreen Widget Script für SoC, Reichweite, usw.

AntwortenAntworten Options Options Arrow

Re: Geniales iOS14 Homescreen Widget Script für SoC, Reichweite, usw.

bill_lee
  • Beiträge: 67
  • Registriert: Sa 10. Nov 2018, 15:50
  • Hat sich bedankt: 7 Mal
  • Danke erhalten: 10 Mal
read
Super cool, läuft !
BMW i3s 120Ah Blue Ridge Mountain seit 03/2022
Hyundai Kona Elektro 150kw seit 09/2020
go-eCharger an CEE32
Anzeige

Re: Geniales iOS14 Homescreen Widget Script für SoC, Reichweite, usw.

Otte
  • Beiträge: 7
  • Registriert: Sa 2. Mai 2020, 21:46
  • Hat sich bedankt: 1 Mal
  • Danke erhalten: 15 Mal
read
AbRiNgOi hat geschrieben: Dieses Aktivierung benötigt die ein Paket aus dem Store https://www.bmw-connecteddrive.at/app/i ... kage_Store um 69 € pro Jahr oder funktioniert die app auch bei abgelaufenen Paketen oder ganz ohne ?

Und: Was kann die App dann mehr als die BMW App außer das diese am Start Bildschirm ist?
Das Script kann nichts mehr als die BMW App 😀 Eben nur der Vorteil, dass es direkt auf dem Homescreen angezeigt wird ohne das man die App öffnen muss. Also für den einen oder anderen nur ein unnützes Gimmick. Oder eben für mich ein doch nützliches Gimmick 😂

Es müssen meiner Meinung nach mind. die Remote Services gebucht sein. Da ich aber ein i3 aus 2015 habe sind dort eh einige Pakete dauerhaft dabei.
https://www.bmw-connecteddrive.at/app/i ... emoteOffer

Wichtig ist die Installationsanleitung, Schritt für Schritt einzuhalten. also Scriptable App installieren, GitHub Webseite mit Script mit Safari Browser öffnen, auf RAW oben rechts klicken, alles markierend in Zwischenablage kopieren, in Scriptable App auf Plus-Zeichen klicken und Script einfügen. Dann unten rechts auf den Play Button klicken. Dann Zugangsdaten für ConnectedDrive (email Adresse und Kennwort) eingeben.
Anschließend oben auf „Untitled Script“ klicken und einen Namen vergeben. Dann auf Done klicken.
Rest bzgl. Widget hinzufügen wie in der Anleitung angegeben, vorgehen.

Re: Geniales iOS14 Homescreen Widget Script für SoC, Reichweite, usw.

USER_AVATAR
  • pousa13
  • Beiträge: 712
  • Registriert: Mo 26. Jun 2017, 07:07
  • Wohnort: Erding
  • Hat sich bedankt: 37 Mal
  • Danke erhalten: 94 Mal
read
AbRiNgOi hat geschrieben:
pousa13 hat geschrieben: bei mir kommt eine Fehlermedlung
Error on line 290:59:
Expected value of type Date but got value of type null

kann da jemand was mit anfangen? hab mir scripten und programmierung so gar nichts am hut
wenn dein iPhone nicht auf df.dateFormat = 'dd.MM.yyyy HH:mm:ss Z' steht, funktionierts nicht. Sehr einfacher Programmierstil...
das Format ist dd.mm.yyyy ... inzwsichen kommt die meldung sogar schon wenn ich den code ins skript kopiere und play drücke ...
05/19 i3S 120Ah, Lila+Suite
04/91 520iA E34, Islandgrün Metallic
09/22 i4 40e, Brooklyn Grey

Re: Geniales iOS14 Homescreen Widget Script für SoC, Reichweite, usw.

USER_AVATAR
read
funzt super, danke für den Tipp!!

Re: Geniales iOS14 Homescreen Widget Script für SoC, Reichweite, usw.

Mola
  • Beiträge: 408
  • Registriert: Do 27. Dez 2018, 17:50
  • Hat sich bedankt: 28 Mal
  • Danke erhalten: 65 Mal
read
Echt klasse, vielen Dank!
Hab mal eben noch ein paar Infos ergänzt:-)
Dateianhänge
Bildschirmfoto 2020-11-20 um 07.17.04 (002).png

Re: Geniales iOS14 Homescreen Widget Script für SoC, Reichweite, usw.

ManfredI3
  • Beiträge: 142
  • Registriert: Mi 6. Nov 2019, 15:46
  • Wohnort: Bayern
  • Hat sich bedankt: 2 Mal
  • Danke erhalten: 30 Mal
read
Mola hat geschrieben:Echt klasse, vielen Dank!
Hab mal eben noch ein paar Infos ergänzt:-)
Würdest du das Skript teilen?
Danke:-)


Gesendet von iPhone mit Tapatalk

Re: Geniales iOS14 Homescreen Widget Script für SoC, Reichweite, usw.

Mola
  • Beiträge: 408
  • Registriert: Do 27. Dez 2018, 17:50
  • Hat sich bedankt: 28 Mal
  • Danke erhalten: 65 Mal
read
strings = {}
strings['de'] = {
'What to do ?':'Was ist zu tun ?',
'Start cabin heating':'Starte die Heizung',
'Flash lights':'Lichthupe',
'Make some noise':'Mach Lärm !',
'Lock the doors':'Türen verriegeln',
'Nothing, thank you.':'Nix, danke.',
'head the cabin':'den Innenraum heizen',
'flash the lights':'die Lichter anmachen',
'blow the horn':'die Hupe betätigen',
'lock the doors':'die Türen verriegeln',
'You want to ':'Du möchtest ',
'Just to make it clear':'Nur um sicherzugehen',
'Yes dude !':'Ja Man !',
'Noooooooo !':'Neiiiiiiin',
'Its done':'Erledigt',
'Your request to ':'Deine Anforderung für ',
' was sent.':' wurde gesendet.',
'Thank you !':'Dankeschön ;o)',
'Unable to login':'Login nicht möglich',
'You want to enter your credentials again ?':'Möchtest Du Deine Zugangsdaten noch mal eintragen?',
'Yes':'Ja',
'No, they are fine':'Nein, die sind ok',
'Save':'Speichern'
}

const userKey = Script.name()+'_cd_user'
const passKey = Script.name()+'_cd_pass'
const vinKey = Script.name()+'_vin'

if ((Keychain.contains(userKey)) && Keychain.contains(passKey)) {

let carData = await accumulateData()
const mainWdgData = await createWidget(carData) // build the normal widget login get the token vin and data
const widget = mainWdgData
const token = carData.token // we need the token and vin for later actions
const vin = carData.vin

if (config.runsInWidget) {
Script.setWidget(widget)
} else {
widget.presentMedium()
let useralert = new Alert()
useralert.title = localize('What to do ?')
useralert.addAction(localize('Start cabin heating'))
useralert.addAction(localize('Flash lights'))
useralert.addAction(localize('Make some noise'))
useralert.addAction(localize('Lock the doors'))
useralert.addCancelAction(localize('Nothing, thank you.'))
let action = await useralert.present()
switch (action) {
case 0:
proceedAction('head the cabin','RCN',token,vin)
break
case 1:
proceedAction('flash the lights','RLF',token,vin)
break
case 2:
proceedAction('blow the horn','RHB',token,vin)
break
case 3:
proceedAction('lock the doors','RDL',token,vin)
break
}
}
Script.complete()
} else {
await askForUsername()
await askForPassword()
}

function getPostBody(input) {
let result = ''
Object.keys(input).map((key)=>{
result = result + '&' + key + '=' + encodeURIComponent(input[key])
})
return result
}

function localize(text) {
let lng = Device.language()
if ((strings[lng]) && (strings[lng][text])) {
return strings[lng][text]
} else {
return text
}
}

async function accumulateData() {
let result = {}
let vin
let token = await getLoginToken()
if (token) {
result.token = token
vin = getVinbyParameterOrKeyChain()
if (vin === false) {
vin = await getVin(token)
}
if (vin) {
result.vin = vin
result.carImage = await fetchImage(vin)
let data = await getVehicleStatus(token,vin)
let dataSOC = await getVehicleStatusSOC(token,vin)
if (data) {
result.charging_status = data.charging_status
result.chargingSystemStatus = data.chargingSystemStatus
result.chargingTimeRemaining = data.chargingTimeRemaining
result.chargingLevelHv = data.chargingLevelHv
result.soc_hv_percent = data.soc_hv_percent
result.socmax = dataSOC.socmax
result.soc = dataSOC.soc
result.beRemainingRangeElectricKm = data.beRemainingRangeElectricKm
result.door_lock_state = data.door_lock_state
result.updateTime = data.updateTime
result.lat = data.gps_lat
result.lon = data.gps_lng
let nowDate = new Date()
let chg = await getlastCharge(token,vin,nowDate)
if (chg.latest === undefined) {
// maybe we have a new month and the uses hasnt chargd yet so go one month back
nowDate.setDate(1)
let month = nowDate.getMonth()
if (month === 0) { // Special in january go also one year back
nowDate.setMonth(11) //
nowDate.setFullYear(nowDate.getFullYear()-1)
} else {
nowDate.setMonth(nowDate.getMonth() - 1)
}
chg = await getlastCharge(token,vin,nowDate)
}
result.lastCharge = chg.latest

} else {
result.error = 'unable to fetch data'
}
} else {
result.error = 'unable to get your vin'
}
} else {
result.loginError = true
result.error = 'unable to login'
}
return result
}

async function createWidget(data) {
let bgColor = '#FFFFFF'
let fgColor = '#000000'

// if(Device.isUsingDarkAppearance()) {
bgColor = '#000000'
fgColor = '#FFFFFF'
// }
let chrglblCol = new Color(fgColor)
let widget = new ListWidget()
let canvas
widget.backgroundColor = new Color(bgColor, 1.0)
if (data.error === undefined) {
let carIconCell
let wideMode = false
if ((config.widgetFamily === 'medium') || (config.widgetFamily === 'large') || (config.runsInWidget === false)) {
let row = widget.addStack()
row.layoutHorizontally()
canvas = row.addStack()
canvas.layoutVertically()
row.addSpacer(8)
carIconCell = row.addStack()
wideMode = true
} else {
canvas = widget
}
// Battery stack
let batteryStack = canvas.addStack()
// Battery icon
let battIcon = SFSymbol.named('bolt.fill.batteryblock');
let battIconElement = batteryStack.addImage(battIcon.image)
battIconElement.imageSize = new Size(15, 15)
batteryStack.addSpacer(8)

// Set color based on charging state
if (data.charging_status == 'NOCHARGING') {
battIconElement.tintColor = new Color(fgColor)
} else {
chrglblCol = Color.blue()
battIconElement.tintColor = chrglblCol
}
let batteryText = batteryStack.addText(Math.floor(data.chargingLevelHv) + '% - ' + (Math.floor(data.soc*100)/100) + ' kWh')
batteryText.textColor = chrglblCol
batteryText.font = Font.systemFont(14)
canvas.addSpacer()


// Range stack
let rangeStack = canvas.addStack()
let rangeIcon = SFSymbol.named('gauge');
let rangeIconElement = rangeStack.addImage(rangeIcon.image)
rangeIconElement.imageSize = new Size(15, 15)
rangeIconElement.tintColor = new Color(fgColor)
rangeStack.addSpacer(8)
let rangeText = rangeStack.addText(Math.floor(data.beRemainingRangeElectricKm) +'km')
rangeText.textColor = new Color(fgColor)
rangeText.font = Font.systemFont(14)
canvas.addSpacer()


// MAXSOC stack
let maxsocStack = canvas.addStack()
let maxsocIcon = SFSymbol.named('bolt.fill.batteryblock');
let maxsocIconElement = maxsocStack.addImage(maxsocIcon.image)
maxsocIconElement.imageSize = new Size(15, 15)
maxsocIconElement.tintColor = new Color(fgColor)
maxsocStack.addSpacer(8)
let maxsocText = maxsocStack.addText('max ' + (Math.floor(data.socmax *100)/100) +' kWh')
maxsocText.textColor = new Color(fgColor)
maxsocText.font = Font.systemFont(14)
canvas.addSpacer()

// in wide Mode we will add the iamge on the right side
let carIconStack
let imgSize
let paddingBottom = -25
let paddingTrailing = 0
if (wideMode===true) {
imgSize = new Size(150, 150)
paddingBottom = -20
paddingTrailing = -20
carIconStack = carIconCell.addStack()
} else {
imgSize = new Size(100, 100)
carIconStack = canvas.addStack()
}


const carImageStack = carIconStack.addStack()
carIconStack.layoutHorizontally()
carImageStack.backgroundColor = new Color(bgColor, 1.0)
carImageStack.cornerRadius = 8
const wimg = carIconStack.addImage(data.carImage)
wimg.imageSize = imgSize
wimg.rightAlignImage()
wimg.url = 'https://maps.apple.com/?q=i3&ll='+data.lat+','+data.lon
carIconStack.setPadding(-40,0,paddingBottom,paddingTrailing)
canvas.addSpacer()


// Lock State Stack
let lockStack = canvas.addStack()
let lockIcon
if (data.door_lock_state === 'SECURED') {
lockIcon = SFSymbol.named('lock.circle');
} else {
lockIcon = SFSymbol.named('lock.open')
}
let lockIconElement = lockStack.addImage(lockIcon.image)
lockIconElement.imageSize = new Size(15, 15)
lockIconElement.tintColor = new Color(fgColor)
lockStack.addSpacer(8)
let lockText = lockStack.addText(data.door_lock_state)
lockText.textColor = new Color(fgColor)
lockText.font = Font.systemFont(14)
canvas.addSpacer()


// add the charging data if we are running in a wider mode
if (wideMode === true) {
if (data.lastCharge !== undefined) {
let chargeStack = widget.addStack()
let lchargeIcon = SFSymbol.named('bolt.car');
let lchargeIconElement = chargeStack.addImage(lchargeIcon.image)
lchargeIconElement.imageSize = new Size(15, 15)
lchargeIconElement.tintColor = new Color(fgColor)
chargeStack.addSpacer(8)

let chargeText = chargeStack.addText(data.lastCharge)
chargeText.font = Font.systemFont(14)
chargeText.textColor = new Color(fgColor)
widget.addSpacer()
}
}

// update stack
let updateStack = widget.addStack()
let chargingActive = (data.chargingSystemStatus === 'CHARGINGACTIVE')
let timeIcon = SFSymbol.named((chargingActive === true) ? 'timer' : 'clock');
let timeIconElement = updateStack.addImage(timeIcon.image)
timeIconElement.imageSize = new Size(15, 15)
timeIconElement.tintColor = chrglblCol
updateStack.addSpacer(8)


// Use the utc and convert to local time
let df = new DateFormatter()
let date

if (chargingActive === true) {
let now = new Date() // calculate the charging end time based on the remaining minutes
date = new Date(now.getTime()+parseInt(data.chargingTimeRemaining)*60000)
} else {
df.dateFormat = 'dd.MM.yyyy HH:mm:ss Z'
date = df.date(data.updateTime)
}

df.useShortDateStyle()
df.useShortTimeStyle()

let updateText = updateStack.addText(df.string(date))

updateText.textColor = chrglblCol // make it blue when the the car is charging

if (wideMode===false) { // in smallmode make the text even smaller
updateText.font = Font.systemFont(11)
} else {
updateText.font = Font.systemFont(14)
}
widget.addSpacer()


} else {
canvas = widget
canvas.addText(data.error)
if ((data.loginError === true) && (config.runsInWidget === false)) {
showReloginAlert()
}
}
return widget
}

async function proceedAction(question,actionType,token,vin) {
let proceedAlert = new Alert()
proceedAlert.title = localize('Just to make it clear')
proceedAlert.message = localize('You want to ') + localize(question) + '?'
proceedAlert.addAction(localize('Yes dude !'))
proceedAlert.addCancelAction(localize('Noooooooo !'))
let action = await proceedAlert.present()
if (action === 0) {
await performRemoteAction(actionType,token,vin)
proceedAlert = new Alert()
proceedAlert.title = localize('Its done')
proceedAlert.message = localize('Your request to ') + localize(question) + localize(' was sent.')
proceedAlert.addAction(localize('Thank you !'))
await proceedAlert.present()
}
}

async function showReloginAlert() {
let useralert = new Alert()
useralert.title = localize('Unable to login')
useralert.message = localize('You want to enter your credentials again ?')
useralert.addAction(localize('Yes'))
useralert.addCancelAction(localize('No, they are fine'))
let action = await useralert.present()
if (action === 0) {
await askForUsername()
await askForPassword()
}
}

async function askForUsername() {
let useralert = new Alert()
useralert.title = 'Connected Drive'
let cduser = useralert.addTextField('Username')
useralert.addAction(localize('Save'))
await useralert.present()
Keychain.set(userKey,useralert.textFieldValue(0))
}

async function askForPassword() {
let useralert = new Alert()
useralert.title = 'Connected Drive'
let cduser = useralert.addSecureTextField('Password')
useralert.addAction(localize('Save'))
await useralert.present()
Keychain.set(passKey,useralert.textFieldValue(0))
}

function getVehicleImage(vin) {
return new Promise(async (resolve,reject)=>{
let vehicleImageListUrl = 'https://www.bmw-connecteddrive.de/api/vehicle/image/v1/' + vin + '?startAngle=0&stepAngle=10&width=780'
let vehicleImageListRequest = new Request(vehicleImageListUrl)
vehicleImageListRequest.method = 'get'
vehicleImageListRequest.headers = {'Content-Type': 'application/json'}
let data = await vehicleImageListRequest.loadJSON()
if ((data) && (data.angleUrls)) {
resolve(data.angleUrls[5].url)
} else {
resolve(false)
}
})
}


function performRemoteAction(action,token,vin) {
return new Promise(async(resolve,reject)=>{
let remoteCommandUrl = 'https://www.bmw-connecteddrive.de/remot ... /rsapi/v1/' + vin + '/' + action

let remoteRequest = new Request(remoteCommandUrl)
remoteRequest.body = '{}'
remoteRequest.method = 'POST'
remoteRequest.headers = {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}
remoteRequest.loadJSON().then((data)=>{
resolve()})
.catch((e)=>{
console.error(e)
resolve()})

})
}

function getVehicleStatus(token,vin) {
return new Promise(async(resolve,reject)=>{
let vehicleDataUrl = 'https://www.bmw-connecteddrive.de/api/v ... ynamic/v1/' + vin + '?offset=-60'
let vehicleDataRequest = new Request(vehicleDataUrl)
vehicleDataRequest.method = 'get'
vehicleDataRequest.headers = {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}
let data = await vehicleDataRequest.loadJSON()
if (data) {
resolve(data.attributesMap)
} else {
resolve(false)
}
})
}

function getVehicleStatusSOC(token,vin) {
return new Promise(async(resolve,reject)=>{
let vehicleDataUrl = 'https://www.bmw-connecteddrive.de/api/v ... gation/v1/' + vin + ''
let vehicleDataRequest = new Request(vehicleDataUrl)
vehicleDataRequest.method = 'get'
vehicleDataRequest.headers = {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}
let data = await vehicleDataRequest.loadJSON()
if (data) {
resolve(data)
} else {
resolve(false)
}
})
}

function getVinbyParameterOrKeyChain() {
let parameters = args.widgetParameter
if (parameters != null && parameters.length > 0) {
console.log('using vin ' + parameters + ' from widget parameters')
return parameters
} else {
// try the keychain
if (!Keychain.contains(vinKey)) {
console.log('there is no stored vin')
return false
} else {
let vin = Keychain.get(vinKey)
console.log('using vin ' + vin +' from keychain')
return vin
}
}
}

function getVin(token) {
return new Promise(async(resolve,reject)=>{
console.log('using vin from cd')
let vehicleListUrl = 'https://www.bmw-connecteddrive.de/api/m ... e&brand=BM'
let vehicleListRequest = new Request(vehicleListUrl)
vehicleListRequest.method = 'get'
vehicleListRequest.headers = {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}
let list = await vehicleListRequest.loadJSON()
if (list.length > 0) {
let vin = list[0].vin
console.log(list[0])
Keychain.set(vinKey,vin)
resolve(vin)
} else {
console.log(list[0])
resolve(false)
}
})
}

function getlastCharge(token,vin,date) {
let result = {}
result.latest = undefined
return new Promise( async(resolve,reject)=>{
let df = new DateFormatter()
df.dateFormat = 'yyyy-MM'
let thisMonth = df.string(date) + '-01T00:00:00.000'
let chargeSessionUrl = 'https://cocoapi.bmwgroup.com/eadrax-chs ... sions?vin=' + vin + '&date=' + thisMonth
let vehicleChargeRequest = new Request(chargeSessionUrl)
vehicleChargeRequest.method = 'get'
vehicleChargeRequest.headers = {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}
let list = await vehicleChargeRequest.loadJSON()
if (list.chargingSessions !== undefined) {
result.nrj = list.chargingSessions.total

if ((list.chargingSessions.sessions) && (list.chargingSessions.sessions.length > 0)) {
let latest = list.chargingSessions.sessions[0]
result.latest = latest.title + '|' + latest.energyCharged
}
}
resolve(result)
})
}

async function getLoginToken() {
let user
let pwd
try {
if (!Keychain.contains(userKey)) {
console.error('missing parameters')
} else {
user = Keychain.get(userKey)
pwd = Keychain.get(passKey)
}
let url = 'https://customer.bmwgroup.com/gcdm/oauth/authenticate'
let post_data = {
'state': 'eyJtYXJrZXQiOiJkZSIsImxhbmd1YWdlIjoiZGUiLCJkZXN0aW5hdGlvbiI6ImxhbmRpbmdQYWdlIn0',
'username': user,
'client_id': 'dbf0a542-ebd1-4ff0-a9a7-55172fbfce35',
'password': pwd,
'redirect_uri': 'https://www.bmw-connecteddrive.com/app/ ... patch.html',
'response_type': 'token',
'scope': 'authenticate_user fupo',
'locale': 'DE-de'
}
let lRequest = new Request(url)
lRequest.method = 'POST'
lRequest.body = getPostBody(post_data)
let result = await lRequest.load()
let tokenUrl = lRequest.response.url
if (tokenUrl) {
let match = tokenUrl.match(/&access_token=([a-zA-z0-9]{0,})/)
if (match != null) {
let token = match[1]
return token
}
}

} catch(e) {
console.error(e)
}
return null
}


function fetchImage(vin) {
return new Promise (async(resolve,reject)=>{
let fm = FileManager.local()
let dir = fm.documentsDirectory()
let path = fm.joinPath(dir, vin + '.png')
if (fm.fileExists(path)) {
resolve(fm.readImage(path))
} else {
let carImageUrl = await getVehicleImage(vin)
let carImageRequest = new Request(carImageUrl)
let carImage = await carImageRequest.loadImage()
fm.writeImage(path, carImage)
resolve(carImage)
}
})
}

Re: Geniales iOS14 Homescreen Widget Script für SoC, Reichweite, usw.

Mola
  • Beiträge: 408
  • Registriert: Do 27. Dez 2018, 17:50
  • Hat sich bedankt: 28 Mal
  • Danke erhalten: 65 Mal
read
Gute Idee das hier rein zu pasten?🤪

Re: Geniales iOS14 Homescreen Widget Script für SoC, Reichweite, usw.

Mola
  • Beiträge: 408
  • Registriert: Do 27. Dez 2018, 17:50
  • Hat sich bedankt: 28 Mal
  • Danke erhalten: 65 Mal
read
Hab noch ein wenig korrigiert
Und Hintergrund dauerhaft schwarz
Keine Gewähr auf einwandfreies coding:-)
Dateianhänge
7AC58242-4589-4625-99B3-506F341F21A6.jpeg

Re: Geniales iOS14 Homescreen Widget Script für SoC, Reichweite, usw.

ManfredI3
  • Beiträge: 142
  • Registriert: Mi 6. Nov 2019, 15:46
  • Wohnort: Bayern
  • Hat sich bedankt: 2 Mal
  • Danke erhalten: 30 Mal
read
Super Danke!


Gesendet von iPhone mit Tapatalk
Anzeige
AntwortenAntworten

Zurück zu „i3 - Allgemeine Themen“

Gehe zu Profile
  • Vergleichbare Themen
    Antworten
    Zugriffe
    Letzter Beitrag