Skip to content

Instantly share code, notes, and snippets.

@mountbatt
Last active March 24, 2023 06:01
Embed
What would you like to do?
Scriptable iOS widget that displays the status of your Renault ZOE on your iPhone and iPad.
/* Das Widget ist auf Grund von Beschwerden von Renault vorrübergehend entfernt */
/* Le widget a été temporairement supprimé suite à une plainte de Renault. */
/* The widget is temporarily removed due to complaints from Renault */
@RazvanGradinaru
Copy link

RazvanGradinaru commented Mar 27, 2022

Hello!
How can I solve this issue?
IMG_5865

@mountbatt
Copy link
Author

Try to add your VIN

@jumpjack
Copy link

jumpjack commented Apr 1, 2022

Maybe sniffing traffic of this Nissan app can help reversing engineering the SRP protocol?
https://play.google.com/store/apps/details?id=com.ideeo.nsnremote

@Tuedderbueddel
Copy link

Moin,

ich feiere das Widget seit einem Jahr. Seit dem letzten Update bekomme ich aber angehängte Fehlermeldung (SyntaxError: Invalid character '\u201e'). Ins Script habe ich an entsprechenden Stellen meinen Login (Mailadresse & PW) und die VIN geschrieben. Die Fahrzeugangaben stimmten alle von vornherein (Zoe 2, Akkugröße, etc.).

Hast du eine Idee, woran es noch liegen könnte?

Danke im Voraus!

Tuedderbueddel

IMG_2610

@mountbatt
Copy link
Author

mountbatt commented Apr 5, 2022

Moin,

ich feiere das Widget seit einem Jahr. Seit dem letzten Update bekomme ich aber angehängte Fehlermeldung (SyntaxError: Invalid character '\u201e'). Ins Script habe ich an entsprechenden Stellen meinen Login (Mailadresse & PW) und die VIN geschrieben. Die Fahrzeugangaben stimmten alle von vornherein (Zoe 2, Akkugröße, etc.).

Hast du eine Idee, woran es noch liegen könnte?

Danke im Voraus!

Tuedderbueddel

IMG_2610

@Tuedderbueddel Deutet auf falsche Anzahl Anführungszeichen hin. Schau mal in Zeile 7 oder etwas davor oder danach ob du dich da nicht vertan hast…

@Tuedderbueddel
Copy link

Tatsächlich, daran lag es! Bei Benutzer und PW war die deutsche Version der Anführungszeichen. Nach dem Ändern auf die englische funzt es nun. Vielen Dank und weiter so!

@druelie
Copy link

druelie commented Apr 30, 2022

Seit ein paar Tagen bekomme ich folgende Fehlermeldungen:

2022-04-30 17:52:15: Keychain cleared cause of action parameters
2022-04-30 17:52:15: gigyaPersonID (from keychain): undefined
2022-04-30 17:52:15: 1.: 200
2022-04-30 17:52:16: 3.: 200
2022-04-30 17:52:16: account_id (from keychain): undefined
2022-04-30 17:52:16: 4.: [object Object]
2022-04-30 17:52:16: Error on line 434:63: TypeError: undefined is not an object (evaluating 'apiResult.vehicleLinks[0].vehicleDetails.assets')

@druelie
Copy link

druelie commented Apr 30, 2022

Seit ein paar Tagen bekomme ich folgende Fehlermeldungen:

2022-04-30 17:52:15: Keychain cleared cause of action parameters 2022-04-30 17:52:15: gigyaPersonID (from keychain): undefined 2022-04-30 17:52:15: 1.: 200 2022-04-30 17:52:16: 3.: 200 2022-04-30 17:52:16: account_id (from keychain): undefined 2022-04-30 17:52:16: 4.: [object Object] 2022-04-30 17:52:16: Error on line 434:63: TypeError: undefined is not an object (evaluating 'apiResult.vehicleLinks[0].vehicleDetails.assets')

Das geht auch nur, wenn ich VIN hinzufüge.

@mountbatt
Copy link
Author

@dschuwa
Copy link

dschuwa commented May 2, 2022

Danke für das Update.
Leider funkt das Script bei mir weiterhin nicht. Ich hatte gehofft das sich das Problem mit dem Update löst. Bekomme weiterhin die Fehlermeldung: Error on line 451:56: TypeError: undefined is not an object (evaluating 'apiResult.vehicleLinks[carNumber].vehicleDetails').
FM

Kann das Script schon seit über 6 Monaten nicht mehr nutzen. Habe auch die VIN eintragen, Angaben zu Renault Login, ZOE und Akku passen alle.

Gibt es dafür eine Lösung oder eine Idee zur Lösung?

@mountbatt
Copy link
Author

mountbatt commented May 3, 2022

3.5.2022 – Kleines Update für alle, die mehrere Accounts bei gigya/Renault haben und keine Verbindung herstellen können. Probiert einfach in Zeile 30 die "accountNumber" des aktuellen Codes mal auf 1 statt 0 zu setzen.

https://gist.github.com/mountbatt/772e4512089802a2aa2622058dd1ded7?permalink_comment_id=3504088#gistcomment-3504088

@dschuwa

@dschuwa
Copy link

dschuwa commented May 3, 2022

3.5.2022 – Kleines Update für alle, die mehrere Accounts bei gigya/Renault haben und keine Verbindung herstellen können. Probiert einfach in Zeile 30 die "accountNumber" des aktuellen Codes mal auf 1 statt 0 zu setzen.

https://gist.github.com/mountbatt/772e4512089802a2aa2622058dd1ded7?permalink_comment_id=3504088#gistcomment-3504088

@dschuwa

Vielen Dank! Das ist die Lösung. Das Script funktioniert wieder.

@jumpjack
Copy link

About the PIN topic...
I found a datum in Renault servers response which looks like a PIN: you can find it into notification response:

data: Array(1)
0:
actionType: "COMMAND_RESPONSE"
commandResponse:
errorCategory:
categoryId: "00"
categoryType: "GENERIC_MSG"
status: "CANCELLED"
commandType: "HVAC_START"
kmrUserId: "yyyyyyy"               <<<<<<<<<===============  here
notifDate: "2022-06-15T15:36:27"
notificationId: "30371b21-fc0a-4fa6-9aab-5bda09cc11d3"
personId: "xxxxxxxxxxxxxxxx"
vin: "VFxxxxxxxxxxxxxxxx"

It's a 7 digits pin, "kmrUserId".

Maybe it is worth a try, but I don't know how/what to try...

This is my (buggy) method to query status of commands using notifications endpoint:

function manageNotification(type) {
	//https://api-wired-prod-1-euw1.wrd-aws.com/commerce/v1/persons/PPPPPPPPPPPPPPPPPPPP/notifications/kmr?country=IT&notificationId=d47296c9-6ab6-4192-9db1-4712010538fb
	notificationUrl = kamereonurl + "/commerce/v1/persons/" +
	personId.value +
	"/notifications/kmr" +
	"?apikey=" + KAMEREON_KEY +
	"&country=" + country.value;
	if (type === "last") {
		notificationUrl +=	"&notificationId=" + notificationId;
	} else {
		// raw url provides info on all previous notifications
	}

	notificationHeaders = {
		'apikey': KAMEREON_KEY,
        'x-gigya-id_token' :  JWT.value
	};
    notifRetry = 0;
	CommandCompleted = false;
	notifInterval = setInterval(getNotificationStatus, NOTIF_INT);
}


function getNotificationStatus(mex) {
console.log(">>>>>>",mex,notifRetry);
	notifRetry++;
	axios.get(
		notificationUrl,
		{
			headers: notificationHeaders
		}
	)
	.then(response => {
		pippo = response;
		if (response.data.length === 0) { // In case of empty response, if command has not yet executed,  retry some times
			if (!CommandCompleted) {
				if (notifRetry >= NOTIF_MAX) { // Give up after a given number of attempts
					if (notifInterval) clearInterval(notifInterval);
					console.log(notifRetry, notifInterval, "Given up.");
					output.value += "\nGiven up.\n";
				} else {
					// retry
					console.log("Attempt n. " + notifRetry +  ", please wait...");
					output.value += "\nAttempt n. " + notifRetry + ", please wait...\n";
				}
			} else { // Empty because completed ==> exit
                if (notifInterval) clearInterval(notifInterval);
                CommandCompleted = true;
				console.log("Already finished, exiting.");
			}
		} else {
			//console.log("POST result for notification n. " + notificationId + ": " + (JSON.stringify(response.data, null, 4)));
			try {
				if (mex === "last") {
					console.log(response.data[response.data.length-1].notifDate + ": " + response.data[response.data.length-1].commandType + ", " +  response.data[response.data.length-1].commandResponse.status);
					console.log(response);
					output.value += "\n" + response.data[response.data.length-1].notifDate + ": " + response.data[response.data.length-1].commandType + ", " +  response.data[response.data.length-1].commandResponse.status;
				} else {
					for (i=0; i < response.data.length; i++) {
						console.log(response.data[i].notifDate + ": " + response.data[i].commandType + ", " +  response.data[i].commandResponse.status);
					console.log(response);
						output.value += "\n" + response.data[i].notifDate + ": " + response.data[i].commandType + ", " +  response.data[i].commandResponse.status;
					}
				}
				if (response.data[response.data.length-1].commandResponse.status == "COMPLETED") {
                    if (notifInterval) clearInterval(notifInterval);
                    CommandCompleted = true;
					console.log("PROCESSING COMPLETED.");
                    output.value += "\nPROCESSING COMPLETED.";
				} else {
                    //CommandCompleted = false;
                    console.log("Waiting for command execution...");
                    output.value += "\nWaiting for command execution..."
				}
			} catch (e) {
				console.log("data error, raw response for attempt " + notifRetry + ":", response, e);
                output.value += "Data error, see console for details";
			}
		}
	})
	.catch(
	  function (error) {
		log.value += "\nERROR 005 for POST while managing notification n. " + notificationId + ":\n" + error + "\n";
		console.log("ERROR 005 for POST while  managing notification n. " + notificationId + ":\n" + error);
		console.log("POST ERROR 005:" ,error)
	  	login1.innerHTML = "ERR_POST";
		return ({ status: "POST ERROR 005 (Notifications)", data: response.data });
		}
	);
}

@mountbatt
Copy link
Author

@jumpjack what do you want to archive? I can’t remember that we talked about a PIN?!

@jumpjack
Copy link

There is an ongoing investigation to discover how to "crack" the Renault/Nissan SRP authentication, apparently required to run some endpoints involving door locking, windows management, engine starting and other things, all replying with "not authorized - error".

@Tuedderbueddel
Copy link

Gab‘s ein Update vom Widget? Ich bekomme seit heute angehängte Fehlermeldung.
E4995026-83C7-43B3-AD87-197D577A811C

@mountbatt
Copy link
Author

@Tuedderbueddel nein. Server ist nicht erreichbar. Siehe auch my Renault App…

@Tuedderbueddel
Copy link

Ah, dann ist es klar. Danke!

@wopfel
Copy link

wopfel commented Jul 1, 2022

When carNumber is an empty string, it is set to 0. In the next few code lines, the variable is decremented by 1, so it gets -1 then. A negative array index would be used then.

https://gist.github.com/mountbatt/772e4512089802a2aa2622058dd1ded7#file-zoe-widget-js-L449

Maybe this would fix it?

			if(carNumber == ""){ // fallback
				carNumber = 0;
			} else {
				// set correct carNumber to array (starts with 0)
				carNumber = carNumber - 1;
			}

@mountbatt
Copy link
Author

@wopfel Thanks … I updated the code …

@matzZz
Copy link

matzZz commented Aug 22, 2022

Hi, is there a solution to stop the charging at a specific % of charge?

@mountbatt
Copy link
Author

Hi, is there a solution to stop the charging at a specific % of charge?

Not with this widget! This widget only runs while you open it. And your iPhone decides when to refresh it. So no background activity / Monitoring etc is possible. This behavior is by design from Apple!

@codex-20
Copy link

Hi Mountbatt.

This is an awesome script. Many many thanks for taking the time to code it. I guess you’re a Zoe owner yourself!

My only suggestion would be to modify the code so that the language to be displayed is not hard coded in to the script but available to easily modify at the top with the rest of the user defined variables.

Thank you again in any case!

@RaMaHW
Copy link

RaMaHW commented Jan 16, 2023

Hi Mountbatt,
Thanks for the script, I have been using it successfully and frequently since almost the beginning.
FYI, I am currently getting some off values for the remaining charge. The attached result for a 52kWh ZOE. As far as I can tell from the log, the 13kWh is reported by the renault server.
8F6DD730-BE18-4A61-8023-6916F37EFA6D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment