Open rob3r7 opened 12 months ago
It seems that the entire payload is encrypted:
e.data = a.a.encryptHex(JSON.stringify(e.data), h))
with a new encryption key per request ex.: "webDK6Xl15mzc2RW"
I think they are using the standard CryptoJS
CryptoJS.AES.encrypt("Message", "Secret Passphrase").ciphertext
as seen in their source:
encryptHex: function(e, t) { return i(e, t).ciphertext.toString() }
Hi, same problem.
[09:36:41] INFO: Login to iSolarCloud using gateway https://gateway.isolarcloud.eu ... Error: appkey is incorrect 'er_invalid_appkey' Usage: GoSungrow api login [flags]
The key not change: 93D72E60331ABDCDC7B39ADC2D1F32B3
| --appkey | | GOSUNGROW_APPKEY | SunGrow: api application key. | 93D72E60331ABDCDC7B39ADC2D1F32B3 |
| | | | | * |
| --host | | GOSUNGROW_HOST | SunGrow: Provider API URL. | https://gateway.isolarcloud.eu
Regards
I have the same Problem.
[13:45:06] INFO: Login to iSolarCloud using gateway https://gateway.isolarcloud.eu ... Error: appkey is incorrect 'er_invalid_appkey'
| --appkey | | GOSUNGROW_APPKEY | SunGrow: api application key. | 93D72E60331ABDCDC7B39ADC2D1F32B3
ERROR: appkey is incorrect 'er_invalid_appkey' s6-rc: info: service legacy-services: stopping s6-rc: info: service legacy-services successfully stopped s6-rc: info: service legacy-cont-init: stopping s6-rc: info: service legacy-cont-init successfully stopped s6-rc: info: service fix-attrs: stopping s6-rc: info: service fix-attrs successfully stopped s6-rc: info: service s6rc-oneshot-runner: stopping s6-rc: info: service s6rc-oneshot-runner successfully stopped
Hallo, same problem, with the same error messages...
Same problem
Same problem with the Australian server as well. I did upgrade my firmware and noticed I got a Session Expired error in the app which forced me to log in again
Same here, issue appears to start last week
https://augateway.isolarcloud.eu/
[3.0.7] - 2023-09-04
Tried
init: false Init: true hassio_role: default host_pid: true
Hi,
I have the same Problem.
[20:55:36] INFO: Login to iSolarCloud using gateway https://gateway.isolarcloud.eu ... Error: appkey is incorrect 'er_invalid_appkey' Usage: GoSungrow api login [flags]
Examples: GoSungrow api login
Flags: Use "GoSungrow help flags" for more info.
Additional help topics:
ERROR: appkey is incorrect 'er_invalid_appkey' s6-rc: info: service legacy-services: stopping s6-rc: info: service legacy-services successfully stopped s6-rc: info: service legacy-cont-init: stopping s6-rc: info: service legacy-cont-init successfully stopped s6-rc: info: service fix-attrs: stopping s6-rc: info: service fix-attrs successfully stopped s6-rc: info: service s6rc-oneshot-runner: stopping s6-rc: info: service s6rc-oneshot-runner successfully stopped
It also says: | --token-expiry | | GOSUNGROW_TOKEN_EXPIRY | SunGrow: last login. | 2023-11-19T16:14:03
same problem, the same error
As @BTDrink mentioned the payload is encrypted. The session_key (ie. "webDK6Xl15mzc2RW") is generated on client side. It is then encrypted with public key (rsaEncryption (PKCS 1)) and send in the header:
var _ = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNA ...."
var h = e.randomKey;
return c.a.isUndefinedOrNull(h) || (e.headers["x-random-secret-key"] = v.a.sgEncrypt(h, _),
Same issue, following for a fix
Same Problem here
Same problme here - appkex 93D72E60331ABDCDC7B39ADC2D1F32B3 is used
[07:02:06] INFO: Login to iSolarCloud using gateway https://gateway.isolarcloud.eu ... Error: appkey is incorrect 'er_invalid_appkey' Usage: GoSungrow api login [flags]
Examples: GoSungrow api login
same here
Same here. I digged into the web interface's source code, and found they use an other key, which is called WebAppKey in the current repo here: https://github.com/MickMake/GoSungrow/blob/391253aaadd2cae9df32c95bec9e6b9bbf83f4d6/iSolarCloud/WebAppService/getMqttConfigInfoByAppkey/data.go#L17
If I use this appkey "B0455FBE7AA0328DB57B59AA729F05D8" than I get an other error... about encryption. They probably changed the encryption method.
./GoSungrow api login
Error: unknown error 'Request is not encrypted'
Usage:
GoSungrow api login [flags]
Examples:
GoSungrow api login
Flags: Use "GoSungrow help flags" for more info.
Additional help topics:
ERROR: unknown error 'Request is not encrypted'
same problem here. Hoping for a fix ... 😳 @MickMake
same problem, waiting for the correction.. thx
Body is encrypted using the randomKey and standard AES in ECB mode with PKCS7 padding. Decryption is done using the same key and parameters.
The x-random-secret-key
is the randomKey encrypted using the secretKey (app and web seem to have different keys) with RSA and pkcs1.
The secretKey seems to be related to x-access-key
as changing one will break the encryption.
The randomKey is random generated by using a prefix (web
or and
) based on if you are using web or the android app. Guessing that ios would be ios
but can't verify that atm.
Hope that helps bringing the project back to working.
Got the functions done in NodeJS but not in Go at this time:
import * as CryptoJS from "crypto-js";
import NodeRSA from "node-rsa";
export function randomKey() {
return "and" + randomString(13);
}
export function encryptAES<T>(data: T, key: string): string {
const d = CryptoJS.enc.Utf8.parse(JSON.stringify(data));
const k = CryptoJS.enc.Utf8.parse(key);
return CryptoJS.AES.encrypt(d, k, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
})
.ciphertext.toString()
.toUpperCase();
}
export function decryptAES<T>(data: string, key: string): T {
const d = CryptoJS.format.Hex.parse(data);
const k = CryptoJS.enc.Utf8.parse(key);
const dec = CryptoJS.AES.decrypt(d, k, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return JSON.parse(CryptoJS.enc.Utf8.stringify(dec)) as T;
}
export function encryptRSA(publicKey: string, value: string): string {
const key = new NodeRSA();
key.setOptions({ encryptionScheme: "pkcs1" });
key.importKey(publicKey, "pkcs8-public-pem");
return key.encrypt(value, "base64");
}
The key values for the app are:
const ACCESS_KEY = "kme8xdq4fp88wps563qd5d57vw6jxrf4"
const APP_KEY = "3A51762ED80A39AD3DF3DB3CE6767884"
const SECRET_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlcwFJfpjOsy5U6KBpDEC9ZU_sgjD4AQ_Io0MuuGmQq8wdeLoozOdXRlkyZ2GovikSa6IXMkJ25NeChWGwBDTsnXuvZ3JIFqiTNt5eMtb42u2iHumWtv7fsjj17FFknOIIVzUMPBJ3eIb2"
Additionally there is the x-limit-obj
header that needs to be send on all non-login requests. It is constructed by encrypting the user_id
with the secretKey using the same RSA method used for the x-random-secret-key
header
It looks like there was an app key that allowed non-encrypted requests, but it's gone now. Some kind of integration got discontinued on their end perhaps? This is some terrible practice from Sungrow... I guess they want to charge money for integration with their api
Keys found for the web interface:
X_ACCESS_KEY = "9grzgbmxdsp3arfmmgq347xjbza4ysps"
SECRET_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkecphb6vgsBx4LJknKKes-eyj7-RKQ3fikF5B67EObZ3t4moFZyMGuuJPiadYdaxvRqtxyblIlVM7omAasROtKRhtgKwwRxo2a6878qBhTgUVlsqugpI_7ZC9RmO2Rpmr8WzDeAapGANfHN5bVr7G7GYGwIrjvyxMrAVit_oM4wIDAQAB'
APP_KEY = 'B0455FBE7AA0328DB57B59AA729F05D8'
is there an easy patch path here? Changing definitely sees the change in error message, happy to test, as I can replicate this, just crawling through their .js tome
Same issue. Following for fix. Please!
Same error here, pls explain way around
Hi, Same issue It would be very generous to fix this issue. Sorry my Go knowhow is too bad to do it on my own. Thank you
Same issue here
Here we go. A first minimal MVP. The api_key_param
is updated with each request before encryption.
import json
import random
import string
from base64 import b64decode, b64encode, urlsafe_b64decode
from datetime import datetime
import requests
from Crypto.Cipher import AES, PKCS1_v1_5
from Crypto.PublicKey import RSA
from Crypto.Util.Padding import pad, unpad
def encrypt_hex(data_str, key):
cipher = AES.new(key.encode("UTF-8"), AES.MODE_ECB)
date_byte = cipher.encrypt(pad(data_str.encode("UTF-8"), 16))
return date_byte.hex()
def decrypt_hex(data_hex_str, key):
cipher = AES.new(key.encode("UTF-8"), AES.MODE_ECB)
text = unpad(cipher.decrypt(bytes.fromhex(data_hex_str)), 16).decode("UTF-8")
return json.loads(text)
public_key = RSA.import_key(
urlsafe_b64decode(
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkecphb6vgsBx4LJknKKes-eyj7-RKQ3fikF5B67EObZ3t4moFZyMGuuJPiadYdaxvRqtxyblIlVM7omAasROtKRhtgKwwRxo2a6878qBhTgUVlsqugpI_7ZC9RmO2Rpmr8WzDeAapGANfHN5bVr7G7GYGwIrjvyxMrAVit_oM4wIDAQAB"
)
)
cipher = PKCS1_v1_5.new(public_key)
def encrypt_RSA(data_str):
ciphertext = cipher.encrypt(data_str.encode("UTF-8"))
return b64encode(ciphertext).decode("UTF-8")
def random_word(length):
characters = string.ascii_lowercase + string.ascii_uppercase + string.digits
random_word = "".join(random.choice(characters) for _ in range(length))
return random_word
def get_data(url, data):
random_key = "web" + random_word(13)
data["api_key_param"] = {
"timestamp": int(datetime.now().timestamp() * 1000),
"nonce": random_word(32),
}
data["appkey"] = "B0455FBE7AA0328DB57B59AA729F05D8"
data_str = json.dumps(data, separators=(",", ":"))
data_hex = encrypt_hex(data_str, random_key)
headers = {
"content-type": "application/json;charset=UTF-8",
"sys_code": "200",
"x-access-key": "9grzgbmxdsp3arfmmgq347xjbza4ysps",
}
headers["x-random-secret-key"] = encrypt_RSA(random_key)
response = requests.post(url, data=data_hex, headers=headers)
return decrypt_hex(response.text, random_key)
token = get_data(
"https://gateway.isolarcloud.eu/v1/userService/login",
{
"user_account": "XXXXX@XXXXX.ch",
"user_password": "XXXXXXXX",
},
)["result_data"]["token"]
get_data(
"https://gateway.isolarcloud.eu/v1/commonService/queryMutiPointDataList",
{
"ps_key": "XXXXXXX_14_1_2",
"points": "p13003",
"start_time_stamp": "20231108000000",
"end_time_stamp": "20231109000000",
"token": token,
},
)
@0SkillAllLuck: Grüsse nach Bern!
Here is another working Python example of how to use query the new Sungrow API. Sadly, I forgot to refresh the page while working and didn't see the post of @rob3r7 ...
The scope of the code below is very similar to his contribution, with some extra additions:
You will see that the post method has a isFormData flag. The code associated with that flag being true is reverse engineered based on what I was able to make out of minified Sungrow encryption code. I do not know a call for which the flag should be set to true, so in that particular case there may be some issues...
import base64
import string
import random
from typing import Optional
import time
import json
import requests
from cryptography.hazmat.primitives import serialization, asymmetric, padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
# TODO: Getting these values directly from the files by the Sungrow API is better than hardcoding them...
LOGIN_RSA_PUBLIC_KEY: asymmetric.rsa.RSAPublicKey = serialization.load_pem_public_key(b"-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJRGV7eyd9peLPOIqFg3oionWqpmrjVik2wyJzWqv8it3yAvo/o4OR40ybrZPHq526k6ngvqHOCNJvhrN7wXNUEIT+PXyLuwfWP04I4EDBS3Bn3LcTMAnGVoIka0f5O6lo3I0YtPWwnyhcQhrHWuTietGC0CNwueI11Juq8NV2nwIDAQAB\n-----END PUBLIC KEY-----")
APP_RSA_PUBLIC_KEY: asymmetric.rsa.RSAPublicKey = serialization.load_pem_public_key(bytes("-----BEGIN PUBLIC KEY-----\n" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkecphb6vgsBx4LJknKKes-eyj7-RKQ3fikF5B67EObZ3t4moFZyMGuuJPiadYdaxvRqtxyblIlVM7omAasROtKRhtgKwwRxo2a6878qBhTgUVlsqugpI_7ZC9RmO2Rpmr8WzDeAapGANfHN5bVr7G7GYGwIrjvyxMrAVit_oM4wIDAQAB".replace("-", "+").replace("_", "/") + "\n-----END PUBLIC KEY-----", 'utf8'))
ACCESS_KEY = "9grzgbmxdsp3arfmmgq347xjbza4ysps"
APP_KEY = "B0455FBE7AA0328DB57B59AA729F05D8"
def encrypt_rsa(value: str, key: asymmetric.rsa.RSAPublicKey) -> str:
# Encrypt the value
encrypted = key.encrypt(
value.encode(),
asymmetric.padding.PKCS1v15(),
)
return base64.b64encode(encrypted).decode()
def encrypt_aes(data: str, key: str):
key_bytes = key.encode('utf-8')
data_bytes = data.encode('utf-8')
# Ensure the key is 16 bytes (128 bits)
if len(key_bytes) != 16:
raise ValueError("Key must be 16 characters long")
cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend())
encryptor = cipher.encryptor()
padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(data_bytes) + padder.finalize()
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
return encrypted_data.hex()
def decrypt_aes(data: str, key: str):
key_bytes = key.encode('utf-8')
# Ensure the key is 16 bytes (128 bits)
if len(key_bytes) != 16:
raise ValueError("Key must be 16 characters long")
encrypted_data = bytes.fromhex(data)
cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend())
decryptor = cipher.decryptor()
decrypted_padded_data = decryptor.update(encrypted_data) + decryptor.finalize()
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
decrypted_data = unpadder.update(decrypted_padded_data) + unpadder.finalize()
return decrypted_data.decode('utf-8')
def generate_random_word(length: int):
char_pool = string.ascii_letters + string.digits
random_word = ''.join(random.choice(char_pool) for _ in range(length))
return random_word
class SungrowScraper:
def __init__(self, username: str, password: str):
self.baseUrl = "https://www.isolarcloud.com"
# TODO: Set the gateway during the login procedure
self.gatewayUrl = "https://gateway.isolarcloud.eu"
self.username = username
self.password = password
self.session: "requests.Session" = requests.session()
self.userToken: "str|None" = None
def login(self):
self.session = requests.session()
resp = self.session.post(
f"{self.baseUrl}/userLoginAction_login",
data={
"userAcct": self.username,
"userPswd": encrypt_rsa(self.password, LOGIN_RSA_PUBLIC_KEY),
},
headers={
"_isMd5": "1"
},
timeout=60,
)
self.userToken = resp.json()["user_token"]
return self.userToken
def post(self, relativeUrl: str, jsn: "Optional[dict]"=None, isFormData=False):
userToken = self.userToken if self.userToken is not None else self.login()
jsn = dict(jsn) if jsn is not None else {}
nonce = generate_random_word(32)
# TODO: Sungrow also adjusts for time difference between server and client
# This is probably not a must though. The relevant call is:
# https://gateway.isolarcloud.eu/v1/timestamp
unixTimeMs = int(time.time() * 1000)
jsn["api_key_param"] = {"timestamp": unixTimeMs, "nonce": nonce}
randomKey = "web" + generate_random_word(13)
userToken = self.userToken
userId = userToken.split('_')[0]
jsn["appkey"] = APP_KEY
if "token" not in jsn:
jsn["token"] = userToken
jsn["sys_code"] = 200
data: "dict|str"
if isFormData:
jsn["api_key_param"] = encrypt_aes(json.dumps(jsn["api_key_param"]), randomKey)
jsn["appkey"] = encrypt_aes(jsn["appkey"], randomKey)
jsn["token"] = encrypt_aes(jsn["token"], randomKey)
data = jsn
else:
data = encrypt_aes(json.dumps(jsn, separators=(",", ":")), randomKey)
resp = self.session.post(
f"{self.gatewayUrl}{relativeUrl}",
data=data,
headers={
"x-access-key": ACCESS_KEY,
"x-random-secret-key": encrypt_rsa(randomKey, APP_RSA_PUBLIC_KEY),
"x-limit-obj": encrypt_rsa(userId, APP_RSA_PUBLIC_KEY),
"content-type": "application/json;charset=UTF-8"
}
)
return decrypt_aes(resp.text, randomKey)
s = SungrowScraper("MY_USERNAME", "MY_PASSWORD")
resp = s.post(
"/v1/powerStationService/getPsListNova",
jsn={
"share_type_list": ["0", "1", "2"]
}
)
print(resp)
@Pistro @rob3r7 Thank you for the effort. But what do I have to do with these codes? Unfortunately I have no idea...
It would be great if someone could explain it to me step by step
same problem since 3 days.... Still no fix?
Here is another working Python example of how to use query the new Sungrow API. Sadly, I forgot to refresh the page while working and didn't see the post of @rob3r7 ...
The scope of the code below is very similar to his contribution, with some extra additions:
- I have added a call to log in to the portal (this requires a different RSA key)
- My x-limit-obj adds the RSA encrypted user id, which is required for some calls, as also mentioned by @0SkillAllLuck
You will see that the post method has a isFormData flag. The code associated with that flag being true is reverse engineered based on what I was able to make out of minified Sungrow encryption code. I do not know a call for which the flag should be set to true, so in that particular case there may be some issues...
import base64 import string import random from typing import Optional import time import json import requests from cryptography.hazmat.primitives import serialization, asymmetric, padding from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend # TODO: Getting these values directly from the files by the Sungrow API is better than hardcoding them... LOGIN_RSA_PUBLIC_KEY: asymmetric.rsa.RSAPublicKey = serialization.load_pem_public_key(b"-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJRGV7eyd9peLPOIqFg3oionWqpmrjVik2wyJzWqv8it3yAvo/o4OR40ybrZPHq526k6ngvqHOCNJvhrN7wXNUEIT+PXyLuwfWP04I4EDBS3Bn3LcTMAnGVoIka0f5O6lo3I0YtPWwnyhcQhrHWuTietGC0CNwueI11Juq8NV2nwIDAQAB\n-----END PUBLIC KEY-----") APP_RSA_PUBLIC_KEY: asymmetric.rsa.RSAPublicKey = serialization.load_pem_public_key(bytes("-----BEGIN PUBLIC KEY-----\n" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkecphb6vgsBx4LJknKKes-eyj7-RKQ3fikF5B67EObZ3t4moFZyMGuuJPiadYdaxvRqtxyblIlVM7omAasROtKRhtgKwwRxo2a6878qBhTgUVlsqugpI_7ZC9RmO2Rpmr8WzDeAapGANfHN5bVr7G7GYGwIrjvyxMrAVit_oM4wIDAQAB".replace("-", "+").replace("_", "/") + "\n-----END PUBLIC KEY-----", 'utf8')) ACCESS_KEY = "9grzgbmxdsp3arfmmgq347xjbza4ysps" APP_KEY = "B0455FBE7AA0328DB57B59AA729F05D8" def encrypt_rsa(value: str, key: asymmetric.rsa.RSAPublicKey) -> str: # Encrypt the value encrypted = key.encrypt( value.encode(), asymmetric.padding.PKCS1v15(), ) return base64.b64encode(encrypted).decode() def encrypt_aes(data: str, key: str): key_bytes = key.encode('utf-8') data_bytes = data.encode('utf-8') # Ensure the key is 16 bytes (128 bits) if len(key_bytes) != 16: raise ValueError("Key must be 16 characters long") cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend()) encryptor = cipher.encryptor() padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data_bytes) + padder.finalize() encrypted_data = encryptor.update(padded_data) + encryptor.finalize() return encrypted_data.hex() def decrypt_aes(data: str, key: str): key_bytes = key.encode('utf-8') # Ensure the key is 16 bytes (128 bits) if len(key_bytes) != 16: raise ValueError("Key must be 16 characters long") encrypted_data = bytes.fromhex(data) cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend()) decryptor = cipher.decryptor() decrypted_padded_data = decryptor.update(encrypted_data) + decryptor.finalize() unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() decrypted_data = unpadder.update(decrypted_padded_data) + unpadder.finalize() return decrypted_data.decode('utf-8') def generate_random_word(length: int): char_pool = string.ascii_letters + string.digits random_word = ''.join(random.choice(char_pool) for _ in range(length)) return random_word class SungrowScraper: def __init__(self, username: str, password: str): self.baseUrl = "https://www.isolarcloud.com" # TODO: Set the gateway during the login procedure self.gatewayUrl = "https://gateway.isolarcloud.eu" self.username = username self.password = password self.session: "requests.Session" = requests.session() self.userToken: "str|None" = None def login(self): self.session = requests.session() resp = self.session.post( f"{self.baseUrl}/userLoginAction_login", data={ "userAcct": self.username, "userPswd": encrypt_rsa(self.password, LOGIN_RSA_PUBLIC_KEY), }, headers={ "_isMd5": "1" }, timeout=60, ) self.userToken = resp.json()["user_token"] return self.userToken def post(self, relativeUrl: str, jsn: "Optional[dict]"=None, isFormData=False): userToken = self.userToken if self.userToken is not None else self.login() jsn = dict(jsn) if jsn is not None else {} nonce = generate_random_word(32) # TODO: Sungrow also adjusts for time difference between server and client # This is probably not a must though. The relevant call is: # https://gateway.isolarcloud.eu/v1/timestamp unixTimeMs = int(time.time() * 1000) jsn["api_key_param"] = {"timestamp": unixTimeMs, "nonce": nonce} randomKey = "web" + generate_random_word(13) userToken = self.userToken userId = userToken.split('_')[0] jsn["appkey"] = APP_KEY if "token" not in jsn: jsn["token"] = userToken jsn["sys_code"] = 200 data: "dict|str" if isFormData: jsn["api_key_param"] = encrypt_aes(json.dumps(jsn["api_key_param"]), randomKey) jsn["appkey"] = encrypt_aes(jsn["appkey"], randomKey) jsn["token"] = encrypt_aes(jsn["token"], randomKey) data = jsn else: data = encrypt_aes(json.dumps(jsn, separators=(",", ":")), randomKey) resp = self.session.post( f"{self.gatewayUrl}{relativeUrl}", data=data, headers={ "x-access-key": ACCESS_KEY, "x-random-secret-key": encrypt_rsa(randomKey, APP_RSA_PUBLIC_KEY), "x-limit-obj": encrypt_rsa(userId, APP_RSA_PUBLIC_KEY), "content-type": "application/json;charset=UTF-8" } ) return decrypt_aes(resp.text, randomKey) s = SungrowScraper("MY_USERNAME", "MY_PASSWORD") resp = s.post( "/v1/powerStationService/getPsListNova", jsn={ "share_type_list": ["0", "1", "2"] } ) print(resp)
Did you fix it? Is there any way you could explain what to do to get it to work again?
Same issue, following for a fix.
Same issue.... :(
same issue here, following for a fix. thanks in advance to whoever is working on it.
Following for a fix :(
Same here. Following for a fix!
Same here. Following for a fix!
I'm using the ModbusTCP2MQTT now, with my Sungrow SG5.0RT inverter, and it's working fine.... If you can't wait the fix, try it...
I'm using the ModbusTCP2MQTT now, with my Sungrow SG5.0RT inverter, and it's working fine.... If you can't wait the fix, try it...
I might be the issue here but I can't figure out how to properly set it up :-/
I created an issue for it, hopeing someone could help: https://github.com/MatterVN/ModbusTCP2MQTT/issues/68
Unfortunately, I have the same problem when connecting to the https://gateway.isolarcloud.eu server. I'm waiting for the plugin to be fixed.
Thank you for your work!
i have the same issue, is there any update or solution yet? @MickMake
Hello everyone, I haven't received any data for a few days.
A solution to the problem hasn't been found yet when I read through the posts.
It would be nice if a solution could be found. There is quite a lot of time involved in the integration.
Thanks!
@KLucky13, @Pistro: can your python scripts be changed such they have the same command and result interface as GoSungrow's queryMutiPointDataList, for querying the points? That would be great.
@KLucky13, @Pistro: can your python scripts be changed such they have the same command and result interface as GoSungrow's queryMutiPointDataList, for querying the points? That would be great.
I have nothing to do with this integration I'm afraid, just a user like the rest :-)
@KLucky13, @Pistro: can your python scripts be changed such they have the same command and result interface as GoSungrow's queryMutiPointDataList, for querying the points? That would be great.
I've been actively following this discussion because our company has developed an internal Python library to work with the Sungrow API. Like many of you, we encountered challenges when the Sungrow API was updated. To contribute to this community, I've shared our solution here, inspired by the useful tips from @0SkillAllLuck, which were instrumental in resolving our issues.
While I'm enthusiastic about this project, my current schedule prevents me from being directly involved in integrating our approach with the larger project, especially as my expertise in Go is quite limited. However, I'm more than willing to answer any questions or clarify any aspects of our Python code if you find it confusing or need more insight.
Looks like @MickMake has no time to deal with this project. My Go skill are none existent.
@Pistro what do you think, how much work would it be to transform your Python code into a proper HA component? My Python skills are okay, however I never made a ha component, only modified an existing one.
I'm using the ModbusTCP2MQTT now, with my Sungrow SG5.0RT inverter, and it's working fine.... If you can't wait the fix, try it...
for the time being I am using this addon with direct access to SH6.0RT, which works perfectly fine. I hope we will have a fixed for his asap.
Unfortunatly, the addon above can only be used in combination with only 1 converter. I have 2, so I would to choose between one, which is not really interesting
I'm using the ModbusTCP2MQTT now, with my Sungrow SG5.0RT inverter, and it's working fine.... If you can't wait the fix, try it... https://github.com/MatterVN/ModbusTCP2MQTT
for the time being I am using this addon with direct access to SH6.0RT, which works perfectly fine. I hope we will have a fixed for his asap.
I am also using it since yesterday with a SG8.0RT and it seems it's working (even though I dont have much sun at this time of the year where I am :D )
Since tonight I get with
https://gateway.isolarcloud.eu
in HA the following error:ERROR: appkey is incorrect 'er_invalid_appkey
Checking with
https://portaleu.isolarcloud.com/
I realized that the appkey could have changed toB0455FBE7AA0328DB57B59AA729F05D8
(at least I find this key when searching for the termappkey
) .When doing a direct request at
/v1/userService/login
at least I don't get any more an invalid_appkey error but now anRequest is not encrypted
error.When looking at the source of
https://portaleu.isolarcloud.com/#/dashboard
there is the following function:e.data.set("appkey", a.a.encryptHex(e.data.get("appkey"), h))
Did Sungrow changed the API access? How to deal with this change?