Skip to content

Update to new carwings URL and API#58

Merged
joeshaw merged 2 commits intojoeshaw:masterfrom
adrianreber:2025-07-02-new-api
Jul 4, 2025
Merged

Update to new carwings URL and API#58
joeshaw merged 2 commits intojoeshaw:masterfrom
adrianreber:2025-07-02-new-api

Conversation

@adrianreber
Copy link
Copy Markdown
Contributor

This includes a new backend URL and a new encryption algorithm.

Possibly fixes #57

@KecskeTech

This comment was marked as off-topic.

@adrianreber adrianreber force-pushed the 2025-07-02-new-api branch from d154d6f to 6e6bfe2 Compare July 2, 2025 15:00
@KecskeTech

This comment was marked as off-topic.

@andig

This comment was marked as off-topic.

@KecskeTech

This comment was marked as off-topic.

joeshaw
joeshaw previously requested changes Jul 2, 2025
Comment thread carwings.go Outdated
Comment thread carwings.go Outdated
Comment thread carwings.go Outdated
Comment thread carwings.go Outdated
Comment thread carwings.go
BatteryRemainingAmount string
BatteryRemainingAmountWH string
BatteryRemainingAmountKWH string
BatteryRemainingAmountKWH []string
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give some more context for this change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this change I get:

ERROR: json: cannot unmarshal array into Go struct field .BatteryStatus.BatteryRemainingAmountKWH of type string

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is contained in the array value in the JSON?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get: "BatteryRemainingAmountkWH":[]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am on the fence about whether to switch to []string or just make it json.RawMessage. @andig do you use this field at all in evcc?

It's been my experience that the Nissan API is very sloppy especially around empty values, so I don't have confidence that this will always been an array. Using json.RawMessage would make this degrade gracefully but not be particularly useful to consumers of the API.

@adrianreber adrianreber force-pushed the 2025-07-02-new-api branch from 6e6bfe2 to 5f47894 Compare July 3, 2025 07:54
@adrianreber
Copy link
Copy Markdown
Contributor Author

I addressed your feedback.

But the bigger problem is, I found out that it only works if you have an existing ~/.carwings-session file. With this PR it is not possible to login yet. Without an existing session I get:

HTTP/1.1 200 OK
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
Date: Thu, 03 Jul 2025 07:56:20 GMT
Server: Apache

29
{"status":404,"message":"INVALID PARAMS"}
0



ERROR: received status code 404 (INVALID PARAMS)

So there is still something missing. It works, however, if there is a session file.

@adrianreber adrianreber marked this pull request as draft July 3, 2025 07:58
@docolli
Copy link
Copy Markdown

docolli commented Jul 3, 2025

When using Blowfish, the password was encrypted with the baseprm, that was received in session init from carwings API.
Now with your AES code, the baseprm value seems no longer in use. Why? Shouldn't the password still be encrypted with the baseprm value?

Blowfish code: encpw, err := encrypt(password, initResp.Baseprm)
AES code:      encpw, err := encrypt(password)

@adrianreber
Copy link
Copy Markdown
Contributor Author

When using Blowfish, the password was encrypted with the baseprm, that was received in session init from carwings API. Now with your AES code, the baseprm value seems no longer in use. Why? Shouldn't the password still be encrypted with the baseprm value?

Blowfish code: encpw, err := encrypt(password, initResp.Baseprm)
AES code:      encpw, err := encrypt(password)

I removed the code around the baseprm value because that value can neither be used as a key nor as an IV. AES returns:

panic: cipher.NewCBCEncrypter: IV length must equal block size

or

ERROR: crypto/aes: invalid key size 22

Also, I am relatively sure it worked yesterday. Not completely sure, however.

I also took a look at the other implementations in Java and Python and non of those implementations seem to use the baseprm value anymore. It is replaced by this key:

public static final String PS = "H9YsaE6mr3jBEsAaLC4EJRjn9VXEtTzV";

From what I saw they AES key needs to be 32 bytes and the initialization vector 16 bytes. Maybe there is a new API to get one of those values.

@docolli
Copy link
Copy Markdown

docolli commented Jul 3, 2025

I am struggeling with the carwings implementation in perl for FHEM. So I tested and learned a lot today. Finally I was able to get data by using curl with the correct parameters.
From this work I can confirm, that Baseprm is no longer needed to encrypt the password, just iv and ps are sufficient !

Here is how you can get carwings data from Nissan by using curl and the AES256 encrypted password:

Sample password used here is 1234567890 and the user name is carwings@github.com.
Use your own password and user name for testing data aquisition with curl. 😉

If you encrypt the sample password correctly (for example use https://www.devglan.com/online-tools/aes-encryption-decryption),
then you get yzRXyArQz3lfNVxno+9KAw==

Now you can use curl (replace password with your encrypted password and use your Nissan user name ):

curl -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36" -H "Content-Type: application/x-www-form-urlencoded" -d "Password=yzRXyArQz3lfNVxno+9KAw==&RegionCode=NE&initial_app_str=9s5rfKVuMrT03RtzajWNcA&UserId=carwings@github.com&custom_sessionid=" -X POST https://gdcportalgw.its-mo.com/api_v250205_NE/gdc/UserLoginRequest.php

FYI: Here is the perl code for password encryption. Note: padding the password was no longer necessary.

package main;
use strict;
use warnings;
use POSIX;
use Crypt::CBC;
use MIME::Base64;

print STDERR "Start AES enryption\n";

my $CW_IV = "xaX4ui2PLnwqcc74";
my $CW_PS = "H9YsaE6mr3jBEsAaLC4EJRjn9VXEtTzV";

my $test_pw = "1234567890";
# PW 1234567890 -> (AES256) -> yzRXyArQz3lfNVxno+9KAw==
# Encrypted PW for comparision derived from https://www.devglan.com/online-tools/aes-encryption-decryption

my $txt = $test_pw;
my $enc = "";

my $cipher = Crypt::CBC->new(
    -cipher      => 'Crypt::Rijndael',
    -header      => 'none',
    -key         => $CW_PS,
    -iv          => $CW_IV,
    -padding     => 'standard',
    -keysize     => 32,    # 256-Bit-Schlüssel
    -pbkdf       => 'none',  # no usage of PBKDF !
);

$enc = $cipher->encrypt($txt);
	
my $encpw = MIME::Base64::encode_base64($enc, '');
print STDERR "Encoded: " . $encpw . "\n";

@joeshaw
Copy link
Copy Markdown
Owner

joeshaw commented Jul 3, 2025

@adrianreber Thank you for doing the updates, the code itself looks good to me now. (With the one possible tweak on BatteryRemainingAmountKWH.) I'll wait for your followup on the initial login. One thing you might want to try is playing around with the User-Agent that gets sent and how it compares to other implementations. It's been my experience that sometimes certain ones are filtered out.

As I've mentioned elsewhere, I live in the US and this API is no longer available here so I've gotta rely on y'all to test changes and I appreciate that you do so.

@joeshaw joeshaw dismissed their stale review July 3, 2025 14:35

Blockers fixed.

@joeshaw
Copy link
Copy Markdown
Owner

joeshaw commented Jul 3, 2025

@docolli

Note: padding the password was no longer necessary.

I believe the implementation you're using is doing the padding for you:

my $cipher = Crypt::CBC->new(
    -cipher      => 'Crypt::Rijndael',
    -header      => 'none',
    -key         => $CW_PS,
    -iv          => $CW_IV,
    -padding     => 'standard',
    -keysize     => 32,    # 256-Bit-Schlüssel
    -pbkdf       => 'none',  # no usage of PBKDF !
);

Regarding your curl -- was that specific User-Agent necessary or do others work? Does the default curl one not work?

@adrianreber
Copy link
Copy Markdown
Contributor Author

@docolli Thanks for your example. But I cannot make it work on my side. Not sure. I can still login in the official web interface. Using different user agents also doesn't change anything. I will do some more experiments, but I am not sure why it doesn't work any more.

@joeshaw Concerning BatteryRemainingAmountKWH. I am happy to change it to whatever makes most sense.

@docolli
Copy link
Copy Markdown

docolli commented Jul 3, 2025

I was just copying the user agent, that your are using. 😉
Originally the perl code is was modifying didn't had a user agent set.

@adrianreber where does this ~/.carwings-session file come from? I as as I can see this is not created by this go package.

If your are brave you might want to use the AES256 webservice I have linked to encrypt your personal Nissan password.
Like I did this for my test 1234567890:
grafik

Then you can use curl with this encrypted password and your user name to test if Nissan API gives you reasonable data back.

I used this Web service to implement AES256 in this little perl program, that is shown in my previous post. After I managed to get my code to output the same encrypted value for 1234567890, then I used my real password to encrypt it with my perl code. Then I tested this with curl.

Note: I needed to deactive the use of pbkdf to get the correct encrypted password. Maybe this is also necessary for the go-code.

Does your go code encrypts 1234567890 to yzRXyArQz3lfNVxno+9KAw== ???

@adrianreber
Copy link
Copy Markdown
Contributor Author

I was just copying the user agent, that your are using. 😉 Originally the perl code is was modifying didn't had a user agent set.

@adrianreber where does this ~/.carwings-session file come from? I as as I can see this is not created by this go package.

Yes, it is created by this Go application: https://github.com/joeshaw/carwings/blob/master/carwings.go#L530

If your are brave you might want to use the AES256 webservice I have linked to encrypt your personal Nissan password. Like I did this for my test 1234567890: grafik

Then you can use curl with this encrypted password and your user name to test if Nissan API gives you reasonable data back.

I used this Web service to implement AES256 in this little perl program, that is shown in my previous post. After I managed to get my code to output the same encrypted value for 1234567890, then I used my real password to encrypt it with my perl code. Then I tested this with curl.

Note: I needed to deactive the use of pbkdf to get the correct encrypted password. Maybe this is also necessary for the go-code.

Does your go code encrypts 1234567890 to yzRXyArQz3lfNVxno+9KAw== ???

Yes: yzRXyArQz3lfNVxno+9KAw==. I tried your perl code and it creates the same password string as the Go code. Seeing that your perl code creates the same result as my changes to the Go code was helpful. So that seems correct. It still doesn't work with curl or with the Go code. I also tried it from another IP address, but that also didn't change anything. Maybe I tried it too often with my account and it is now blocked accessing the API for some time. I don't know.

I will retry it next week again. Hopefully it will work again.

@docolli
Copy link
Copy Markdown

docolli commented Jul 3, 2025

When that has happened to me in the past, I used the Nissan App on my mobile to grab battery SOC, it always said my login has ended and I need to log in again.

After sucessfull login from the APP, most of the time the connection from my code was working again. Give it try! 😉

@docolli
Copy link
Copy Markdown

docolli commented Jul 3, 2025

Comparing the output of your dev-branch in debug-mode to the parameters of my successful curl request (see above) shows me, that you are missing parameters inital_app_str and custom_sessionid !
I have added this to the code, however, it still fails (of course I've used my credentials 😉):

carwings -username carwings@github.com -password 1234567890 -region NE -debug battery
Logging into Carwings...
Error loading session from /.carwings-session: open /.carwings-session: The system cannot find the file specified.
POST /api_v250205_NE/gdc/UserLoginRequest.php HTTP/1.1
Host: gdcportalgw.its-mo.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36
Content-Length: 139
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip

Password=yzRXyArQz3lfNVxno%2B9KAw%3D%3D&RegionCode=NE&UserId=carwings%40github.com&custom_sessionid=&initial_app_str=9s5rfKVuMrT03RtzajWNcA

HTTP/1.1 200 OK
Connection: close
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Date: Thu, 03 Jul 2025 18:46:28 GMT
Server: Apache

2d
{"status":"-2010","message":"INVALID PARAMS"}
0

ERROR: received status code -2010 (INVALID PARAMS)

But I see that there might be an encoding problem.

encPassword = yzRXyArQz3lfNVxno%2B9KAw%3D%3D but should be yzRXyArQz3lfNVxno+9KAw==
userID = carwings%40github.com but should be carwings@github.com

I don't know currently how to fix this encodig problem, but maybe somebody can help.

@docolli
Copy link
Copy Markdown

docolli commented Jul 4, 2025

Further testing with curl I realized that it must be the URL encoding. If I use curl to send data in verbose mode it tells me it has sent 131 bytes of data, however the current carwings exe tell me it has sent 139 bytes of data (content-length).
This is exactly the difference between "native" and "URL encoded data!

Password=yzRXyArQz3lfNVxno+9KAw==&RegionCode=NE&initial_app_str=9s5rfKVuMrT03RtzajWNcA&UserId=carwings@github.com&custom_sessionid=
Password=yzRXyArQz3lfNVxno%2B9KAw%3D%3D&RegionCode=NE&UserId=carwings%40github.com&custom_sessionid=&initial_app_str=9s5rfKVuMrT03RtzajWNcA

But I still don't know what to do in go code to prevent that URL encoding. Need to do testing in debug mode with original "blowfish" carwings.exe, if this also sends URL encoded data... But I am busy this weekend!

This includes a new backend URL and a new encryption algorithm.

Signed-off-by: Adrian Reber <adrian@lisas.de>
@adrianreber
Copy link
Copy Markdown
Contributor Author

@docolli Thanks once more for your feedback. Now the curl command finally works. Using the app didn't help immediately, but right now I was able to finally log back into the API using curl and my changes. The encoding part is correct (see https://en.wikipedia.org/wiki/Percent-encoding#Reserved_characters for a possible explanation).

@adrianreber adrianreber force-pushed the 2025-07-02-new-api branch from 5f47894 to 535d8c3 Compare July 4, 2025 07:24
@adrianreber adrianreber marked this pull request as ready for review July 4, 2025 07:24
@adrianreber
Copy link
Copy Markdown
Contributor Author

@joeshaw Ready for review. Let me know if I should change the BatteryRemainingAmountKWH handling.

@docolli
Copy link
Copy Markdown

docolli commented Jul 4, 2025

SORRY!!! It works. My mistake, I've used the wrong email address 🙈🙈🙈🤣
I can also get data with the last commit!!!

Please accept the pull request, so we can get this issue fixed in evcc.


Old:
Testet your last commit, however I was not able to get data with carwings.exe. Still the error {"status":"-2010","message":"INVALID PARAMS"}

@joeshaw joeshaw force-pushed the 2025-07-02-new-api branch from 89a6639 to a0e44c1 Compare July 4, 2025 17:35
@joeshaw
Copy link
Copy Markdown
Owner

joeshaw commented Jul 4, 2025

@adrianreber We can leave the BatteryRemainingAmountKWH as it is in your PR and we'll adjust it if we find that other people have problems with it. Thanks again!

@joeshaw joeshaw merged commit 1708e34 into joeshaw:master Jul 4, 2025
1 check passed
@adrianreber adrianreber deleted the 2025-07-02-new-api branch July 5, 2025 08:42
@docolli
Copy link
Copy Markdown

docolli commented Jul 5, 2025

Sorry to step in again! However, after checking more detailed today, I am realizing, that despite I am getting now a JSON data response with -debug activated, it seems that I am gettting more or less always the same JSON structure, no matter if I use batteryor cabintemp. Some Numbers (IDs) change, but I am not able to find the requested value ind the data.

If I use carwings.exe without -debug, I am getting no value answer, but ERROR: open /.carwings-session: Access is denied.
Can someone please help, I am using carwings.exe on a Windows 11 and I am sure I have write rights in the carwings.exe path. Where does carwings.exe tries to create the .carwings-session file?

@docolli
Copy link
Copy Markdown

docolli commented Jul 5, 2025

Okay, I see in the code, that session file is located at ~\.carwings-session, which is NOT windows compatible. 😉
Changing this file location fixed for me this error on windows. Now I can fully confirm Carwings.exe works again!

Logging into Carwings...
Getting latest retrieved battery status...
Battery status as of 2025-07-05 11:18:00 +0200 CEST:
  Capacity: 196 / 240 (82%) 27.8kWh
  Cruising range: 119 miles (115 miles with AC)
  Plug-in state: not connected
  Charging status: not charging
  Time to full:
    Level 1 charge: 9h0m0s
    Level 2 charge: 3h30m0s
    Level 2 at 6 kW: 3h0m0s

@joeshaw
Copy link
Copy Markdown
Owner

joeshaw commented Jul 5, 2025

@docolli Please open a separate issue (or pull request) for the Windows file name thing. It would be appreciated to track that as a bug.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New API endpoint URL

5 participants