hultenvp / solis-sensor

HomeAssistant integration for the SolisCloud PV Monitoring portal via SolisCloud API
Apache License 2.0
191 stars 42 forks source link

Soliscloud API support #18

Closed hawkeye100 closed 2 years ago

hawkeye100 commented 2 years ago

As far as I can tell, I have followed the instructions - files downloaded and put in the correct place on my HA install including the config in configuration.yaml. Checked via File Editor as well as Samba share. HA is 2021.10.6 on Virtualbox vdi. [Other changes on HA are OK including adding integrations from UI].

When I check the config, I get the following:

Platform error sensor.solis - Integration 'solis' not found.

The full log shows:

File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 185, in handle_call_service await hass.services.async_call( File "/usr/src/homeassistant/homeassistant/core.py", line 1491, in async_call task.result() File "/usr/src/homeassistant/homeassistant/core.py", line 1526, in _execute_service await handler.job.target(service_call) File "/usr/src/homeassistant/homeassistant/components/hassio/init.py", line 585, in async_handle_core_service raise HomeAssistantError( homeassistant.exceptions.HomeAssistantError: The system cannot restart because the configuration is not valid: Platform error sensor.solis - Integration 'solis' not found.

I am afraid this does not mean much to me as a HA newbie (and rusty programmer).

I have created a new account in m.ginlong.com and checked it logs on OK to web interface. Those credentials are in the config. (this is because Solis moved my main account to soliscloud.com recently)

Any help (in layman's terms) would be much appreciated. Thanks

jonla76 commented 2 years ago

...answering my own question. There is a post above that points you towards finding the station ID. However, if anyone find this post because it has the error in it and it looking for the answer, I hope this helps.

If you are using soliscloud.com and you see an error like this when trying to restart HA. "_nvalid config for [sensor.solis]: expected int for dictionary value @ data['portal_plantid']. Got"

You are possibly using a short PlantID which might have letters in it. To find the Station ID, open www.soliscloud.com, open your plant page and then check the URL. eg https://www.soliscloud.com/#/station/stationdetail_1?id=1111111222222233333334444444

Use the number at the end of the URL in your configuration.yaml.

@hultenvp Thanks again. Do you accept donations?

Kaya2017 commented 2 years ago

I bought an SDM230 energy meter. I connected this via the pulse output and also via an RS485 module (Modbus). That works great. The Soliscloud doesn't want what I want. My energyboard looks good now. And it's all integrated. maybe I can help with this option. I'm annoyed by the Soliscloud Api. 1 2 3 .

terppatyyppi commented 1 year ago

Hello all, I' having trouble getting past the Soliscloud credentials on this Home assistant HACS integration.

I do have the API key and secret, as well as the station ID and the portal URL...but despite all of that being in the right place in the UI install, it will not get me past this -> "Cannot login with provided URL and credentials". The UI only asks for a username or e-mail, but no password is ever asked for.

Can someone help me please? Would really appreciate it.

abu717 commented 1 year ago

@terppatyyppi

I had the same problem and tried many times before I got it working.

First of all make sure you enter 'https://www.soliscloud.com:13333' (without dash at the end) as the URL and select SolisCloud as platform.

Second, I had to use the long version of the plant ID that you can copy from the URL when logged into the SolisCloud and viewing your plant.

image

Third, it seemed like my inverter had to be online before I got it to work. No success when I tried late evening and dark outside.

I tried at least 20 times before I got it working.

jorgesm-itsia commented 1 year ago

Hi. ¿Could someone share an example in Postman or PHP?

I get the same error always: {"success":true,"code":"1","msg":"数据异常 请联系管理员","data":null}

This is my php script:

<?php

$keyID = 'xxxxxx';
$keySecret = 'xxxxxx';
$body = '{"pageNo":1,"pageSize":10}';

$contentMD5 = base64_encode(md5($body, true));

$contentType = "application/json";
$endPoint = "/v1/api/userStationList";

$gmdate = gmdate("D, j M Y H:i:s") . " GMT";

$cadena = "POST\n{$contentMD5}\n{$contentType}\n{$gmdate}\n{$endPoint}";
$signature = base64_encode(hash_hmac('sha1', $cadena , $keySecret, false));

$authorization = "API " . $keyID . ":" . $signature;

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://www.soliscloud.com:13333/v1/api/userStationList',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS => $body,
  CURLOPT_HTTPHEADER => array(
    'Content-MD5: ' . $contentMD5,
    'Authorization: ' . $authorization,
    'Content-Type: application/json;charset=UTF-8',
    'Date: ' . $gmdate
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo "<br><br>" . $response;

thanks in advance!

adamcable commented 1 year ago

Hey @jorgesm-itsia in case it helps, here's your code updated to make it work :) (it's just the base64 encoding that I had to change...)

`<?php $keyID = 'xxxxxx'; $keySecret = 'xxxxxx';

$body = '{"pageNo":1,"pageSize":10}';

$contentMD5 = base64_encode(md5($body, true));

$contentType = "application/json"; $endPoint = "/v1/api/userStationList";

$gmdate = gmdate("D, j M Y H:i:s") . " GMT";

$cadena = "POST\n{$contentMD5}\n{$contentType}\n{$gmdate}\n{$endPoint}"; $signature = base64_encode(pack('H*',hash_hmac('sha1', $cadena , $keySecret, false)));

$authorization = "API_" . $keyID . ":" . $signature;

$curl = curl_init();

$headers = array( 'Content-MD5: ' . $contentMD5, 'Authorization: ' . $authorization, 'Content-Type: ' . $contentType, 'Date: ' . $gmdate );

curl_setopt_array($curl, array( CURLOPT_URL => 'https://www.soliscloud.com:13333/v1/api/userStationList', CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => $body, CURLOPT_HTTPHEADER => $headers, ));

$response = curl_exec($curl);

curl_close($curl); echo "

" . $response;`

jorgesm-itsia commented 1 year ago

Thank you for the response @adamcable !!

Could you please put the complete code with the changes?

I don`t see the content of the var $headers.

adamcable commented 1 year ago

Thank you for the response @adamcable !!

Could you please put the complete code with the changes?

I don`t see the content of the var $headers.

Apologies, don't think I uploaded the code properly - should be there now :)

jorgesm-itsia commented 1 year ago

@adamcable still not working for me. The response is the same. I don't see the use of hex_to_base64() in the code.

thanks!

adamcable commented 1 year ago

@adamcable still not working for me. The response is the same. I don't see the use of hex_to_base64() in the code.

thanks!

Fixed again ;) It's just the base64 encoding line that needed fixing from the original code...

$signature = base64_encode(pack('H*',hash_hmac('sha1', $cadena , $keySecret, false)));

jorgesm-itsia commented 1 year ago

thank you @adamcable.

I only put my $keyID and my $keySecret in your example but still not work. :( Always the same error. If I put a bad Key or secret the response is: ...appid invalid... So it's not a problem with my count.

FlexxFR commented 1 year ago

ok, finally it works :) It was not the update, still the same "no invertors found" message.

My solution was to rename the plant name in SolidCloud., The plant id remained the same after this update (checked) but now it works.

I did the data transfer, enabled the API, but this integration never worked for SolisCloud. (I did all the other usual tips) I never edited anything in the SolisCloud app before (as all was previously done in m.ginling).

Maybe it helps somebody.

jamesnunn commented 5 months ago

ok, finally it works :) It was not the update, still the same "no invertors found" message.

My solution was to rename the plant name in SolidCloud., The plant id remained the same after this update (checked) but now it works.

I did the data transfer, enabled the API, but this integration never worked for SolisCloud. (I did all the other usual tips) I never edited anything in the SolisCloud app before (as all was previously done in m.ginling).

Maybe it helps somebody.

Yes this helped me - renaming the plant under Modify Information. Would never have tried this, so well done for tinkering.

ekicimustafa commented 4 months ago

My codes in the postman pre-request script section:

var key_id = "**"; var secret = CryptoJS.enc.Hex.parse('*****');

var url = 'https://www.soliscloud.com:13333/v1/api/userStationList'; var body = '{"pageNo":1, "pageSize":10}'; var contentMD5 = CryptoJS.enc.Base64.stringify(CryptoJS.MD5(body)); var contentType = 'application/json'; var now = new Date(); var date = now.toUTCString();

var encryptStr = 'POST\n' + contentMD5 + '\n' + contentType + '\n' + date + '\n/v1/api/userStationList'; var hmac = CryptoJS.HmacSHA1(encryptStr, secret); var sign = CryptoJS.enc.Base64.stringify(hmac); var authorization = 'API ' + key_id + ':' + sign;

var headers = { 'Content-MD5': contentMD5, 'Content-Type': contentType, 'Date': date, 'Authorization': authorization

};

console.log(date); console.log(postman); // Doğru kullanım, postman nesnesini konsola yazdırır postman.setNextRequest("Next Request Name"); // Postman çalışma akışını ayarlar pm.sendRequest({ url: url, method: 'POST', header: headers, body: { mode: 'raw', raw: body } }, function (err, res) { if (err) { console.log(err); return; } console.log(res); });

but it doesn't work and I get a return like this:

"timestamp": 1707556424115, "status": 403, "error": "Forbidden", "message": "Date can not be empty", "path": "/v1/api/userStationList"

How can I solve this problem? No matter what I tried, it didn't work.

adamcable commented 4 months ago

Managed to get your code to work if I removed the CryptoJS.enc.Hex.parse() function call and just used my secret as a string

Pixilated23 commented 2 months ago

Hi all , so far i despair of building the api url.

API URL: https://www.soliscloud.com:13333/ KEY: xxxxxxx1395dfb35bxxxxxxxxx ID: 1xxxxxxxxxxx677236060

Something like: https://www.soliscloud.com:13333/api/xxxx?id=..... ?

Thank you for any help !

adamcable commented 2 months ago

You cannot access the API by querystring alone. You need to pass it authentication headers and a body of data too. There's PHP and JavaScript examples included here, or I'm sure a quick google will help you find you what you need in your language of choice.

Pixilated23 commented 2 months ago

Thank you ,

for other solar clouds i was able to call a single URL like the following for solaxcloud in powershell easily:

Invoke-RestMethod "https://www.solaxcloud.com/proxyApp/proxy/api/getRealtimeInfo.do?tokenId=xxxxxxxxx7379820653"

Too bad that it is not possible here.

ekicimustafa commented 2 months ago

Managed to get your code to work if I removed the CryptoJS.enc.Hex.parse() function call and just used my secret as a string

Hi Bro, i did it what you said but the same error continues.

key id and secret are not real values

Here is my code: var key_id = "123445676890212343212";

var secret = '2801d02r62y69sju9l29164a2x5tt4qq';

var url = 'https://www.soliscloud.com:13333/v1/api/userStationList'; var body = '{"pageNo":1, "pageSize":10}'; var contentMD5 = CryptoJS.enc.Base64.stringify(CryptoJS.MD5(body)); var contentType = 'application/json'; var now = new Date(); var date = now.toUTCString();

var encryptStr = 'POST\n' + contentMD5 + '\n' + contentType + '\n' + date + '\n/v1/api/userStationList'; var hmac = CryptoJS.HmacSHA1(encryptStr, secret); var sign = CryptoJS.enc.Base64.stringify(hmac); var authorization = 'API ' + key_id + ':' + sign;

var headers = { 'Content-MD5': contentMD5, 'Content-Type': contentType, 'Date': date, 'Authorization': authorization

};

console.log(date); console.log(postman); // Doğru kullanım, postman nesnesini konsola yazdırır postman.setNextRequest("Next Request Name"); // Postman çalışma akışını ayarlar pm.sendRequest({ url: url, method: 'POST', header: headers, body: { mode: 'raw', raw: body } }, function (err, res) { if (err) { console.log(err); return; } console.log(res); });

adamcable commented 2 months ago

That exact code works fine for me (with my credentials). What error are you seeing?

Pixilated23 commented 1 month ago

**Hi all, i have translated the code to Powershell. I get the following response when running the script:

success code msg data


True 0 success @{stationStatusVo=; page=; mpptSwitch=0}

Does anyone know how i can get more results with my request ? Thank you for any help !

Powershell Script:**

$key_id = "xxxxxx" # Überprüfen Sie diesen Wert $secret = 'xxxxxxxxxx' # Überprüfen Sie diesen Wert $url = 'https://www.soliscloud.com:13333/v1/api/userStationList' $body = '{"pageNo":1, "pageSize":10}' $contentMD5 = [Convert]::ToBase64String((New-Object System.Security.Cryptography.MD5CryptoServiceProvider).ComputeHash([Text.Encoding]::UTF8.GetBytes($body))) $contentType = 'application/json'

$date = (Get-Date).ToUniversalTime().ToString("R")

$encryptStr = "POSTn$contentMD5n$contentTypen$daten/v1/api/userStationList"

$hmacsha1 = New-Object System.Security.Cryptography.HMACSHA1 $hmacsha1.Key = [Text.Encoding]::UTF8.GetBytes($secret) $signatureBytes = $hmacsha1.ComputeHash([Text.Encoding]::UTF8.GetBytes($encryptStr)) $sign = [Convert]::ToBase64String($signatureBytes) $authorization = "API $key_id`:$sign"

Write-Output "API Key: $key_id" Write-Output "Content-MD5: $contentMD5" Write-Output "Date: $date" Write-Output "Authorization: $authorization"

$headers = @{ 'Content-MD5' = $contentMD5 'Content-Type' = $contentType 'Date' = $date 'Authorization' = $authorization }

try { $response = Invoke-RestMethod -Uri $url -Method Post -Headers $headers -Body $body $response } catch { Write-Error $.Exception.Message if ($.Exception.Response) { $stream = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($stream) $reader.BaseStream.Position = 0 $responseBody = $reader.ReadToEnd() Write-Output $responseBody } }

adamcable commented 1 month ago

Hi @Pixilated23, you just need a few changes:

$encryptStr = "POST`n$contentMD5`n$contentType`n$date`n/v1/api/userStationList" $authorization = "API_$key_id`:$sign" $response = Invoke-WebRequest -Uri $url -Method Post -Headers $headers -SkipHeaderValidation -Body $body

You'll need to run this in Powershell 7 as for some reason Powershell 5 rewrites the "Date" header to make it "standards compliant", but therefore messes up the authorisation verification.

Then I see RawContent with the full returned payload.

Hope that helps :)

Adam

Pixilated23 commented 1 month ago

HI @adamcable ,

thank you ! Its working fine ,

i get the following result:

Content : {"success":true,"code":"0","msg":"success","data":{"stationStatusVo":{"all":1,"normal":1,"fault":0, "offline":0,"building":0,"mppt":0},"page":{"records":[{"id":"1298491919449477426","dataTimestamp":" 17…

Is there any possibility to see the Batterie Power ?

Its missing in the results.

Thank you for any help , BR

adamcable commented 1 month ago

If you make a call to '/v1/api/inverterDetail' instead of '/v1/api/userStationList' And pass in the ID, e.g. '{"id":130867521794763xxxx}'

Then you receive 'batteryCapacitySoc' which is a percentage, which you just need to multiply with your battery storage.

Pixilated23 commented 1 month ago

Thank you , i tried using: $Body = '{"pageNo":1, "pageSize":10,"Id":"1298491919449477426"}'

I use the id from the request before.

i m getting the following result:

{"success":true,"code":"R0002","msg":"The inverter No operation permission or no longer exists","data":null}

Thank you

adamcable commented 1 month ago

I suspect 'Id' should be 'id', and you don't need 'pageNo' or 'pageSize'

Pixilated23 commented 1 month ago

Thank you ,

With : $url = 'https://www.soliscloud.com:13333/v1/api/inverterDetail' $encryptStr = "POSTn$contentMD5n$contentTypen$daten/v1/api/inverterDetail"

$Body = '{,"id":"1298491919449477426"}' Im getting: {"success":true,"code":"R0002","msg":"The inverter No operation permission or no longer exists","data":null}

adamcable commented 1 month ago

Your JSON payload isn't valid with the comma at the front

LucidityCrash commented 1 month ago

You are not passing the right id in and you need to pass in the inverter serial number too. Look at the file test/soliscloud_test.py in the repo it has a bunch of manual calls to the api and there are some comments about what the parameters in the body need to be

On Thu, 6 Jun 2024, 08:07 Pixilated23, @.***> wrote:

Thank you ,

With : $Body = '{,"id":"1298491919449477426"}' Im getting: {"success":true,"code":"R0002","msg":"The inverter No operation permission or no longer exists","data":null}

— Reply to this email directly, view it on GitHub https://github.com/hultenvp/solis-sensor/issues/18#issuecomment-2151556242, or unsubscribe https://github.com/notifications/unsubscribe-auth/ALWZJKZVWBUHT4MSXBUT6QDZGADBLAVCNFSM5GL6QQF2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMJVGE2TKNRSGQZA . You are receiving this because you were mentioned.Message ID: @.***>

Pixilated23 commented 1 month ago

Still not working: I took the information from the solis-clod side for serial number and id:

$Body = '{"sn":"010F92235310037","id":"1E0C65"}'

{"success":true,"code":"1","msg":"Communication error. Please refresh and try again later","data":null}

Where can i find the correct id and sn ?

Thank you ,

LucidityCrash commented 1 month ago

id looks very wrong ... it should be this bit (red) from your url bar when you are looking at the web ui

image

Pixilated23 commented 1 month ago

its working now , thank you very much for any help !