jgriss / FusionSolarPy

A basic client to the Huawei Fusion Solar cloud interface for solar power plants
MIT License
30 stars 12 forks source link

Loggin issues #22

Closed Jesus-M closed 7 months ago

Jesus-M commented 8 months ago

Hi!

I've been using the API for few months (thanks for the work, it's great!!) and for the last couple of days it was failing. I tried to check the problem (I am not a developer, so my capabilities are very limited)and saw this:

I was using this line in the client.py file: client = FusionSolarClient("XXXXXXX", "YYYYYY", huawei_subdomain="region02eu5")

And I was getting this error:

Traceback (most recent call last):
File "/cinemateka/scripts/PVDailyYield.py", line 7, in <module>
client = FusionSolarClient("XXXXXXX", "YYYYYY", huawei_subdomain="region02eu5")
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 230, in __init__
self._configure_session()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 350, in _configure_session
raise AuthenticationException("Invalid response received. Please check the correct Huawei subdomain.")
fusion_solar_py.exceptions.AuthenticationException: Invalid response received. Please check the correct Huawei subdomain.

So I changed the subdomain to: client = FusionSolarClient("XXXXXXX", "YYYYYY", huawei_subdomain="uni002eu5") (uni002eu5 is the translated domain when I open the web portal, even when I actually call https://region02eu5.fusionsolar.huawei.com).

And the error changed to:

Traceback (most recent call last):
File "/usr/lib64/python3.9/site-packages/requests/models.py", line 971, in json
return complexjson.loads(self.text, **kwargs)
File "/usr/lib64/python3.9/json/__init__.py", line 346, in loads
return _default_decoder.decode(s)
File "/usr/lib64/python3.9/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib64/python3.9/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 6 column 1 (char 5)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/cinemateka/scripts/PVDailyYield.py", line 9, in <module>
client = FusionSolarClient("XXXXXXX", "YYYYYY", huawei_subdomain="uni002eu5")
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 230, in __init__
self._configure_session()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 337, in _configure_session
self._login()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 162, in wrapper
result = func(self, *args, **kwargs)
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 319, in _login
if r.json()["errorMsg"]:
File "/usr/lib64/python3.9/site-packages/requests/models.py", line 975, in json
raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
requests.exceptions.JSONDecodeError: Expecting value: line 6 column 1 (char 5)

Then I saw the issue #12 and tried using region02eu5:

Traceback (most recent call last):
File "/cinemateka/scripts/PVDailyYield.py", line 8, in <module>
client = FusionSolarClient("XXXXXXX", "YYYYYY", huawei_subdomain="region03eu5")
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 230, in __init__
self._configure_session()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 350, in _configure_session
raise AuthenticationException("Invalid response received. Please check the correct Huawei subdomain.")
fusion_solar_py.exceptions.AuthenticationException: Invalid response received. Please check the correct Huawei subdomain.

So, any idea?

Thanks in advance!

Jesus-M commented 8 months ago

Continuing with the tests, I changed my password just in case, and trying with the old one:

Traceback (most recent call last):
File "/cinemateka/scripts/PVDailyYield.py", line 8, in <module>
client = FusionSolarClient("XXXXXXX", "YYYYYY", huawei_subdomain="region02eu5")
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 230, in __init__
self._configure_session()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 337, in _configure_session
self._login()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 162, in wrapper
result = func(self, *args, **kwargs)
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 327, in _login
raise AuthenticationException(
fusion_solar_py.exceptions.AuthenticationException: Failed to login into FusionSolarAPI: Login failed. Enter the correct username and password.

So that's fine, wrong password, it's expected it fails. But trying with the new one, same error:

Traceback (most recent call last):
File "/cinemateka/scripts/PVDailyYield.py", line 8, in <module>
client = FusionSolarClient("XXXXXXX", "ZZZZZZZ", huawei_subdomain="region02eu5")
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 230, in __init__
self._configure_session()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 350, in _configure_session
raise AuthenticationException("Invalid response received. Please check the correct Huawei subdomain.")
fusion_solar_py.exceptions.AuthenticationException: Invalid response received. Please check the correct Huawei subdomain.
karstenBriksoft commented 8 months ago

i think they changed a bit more than just introducing a new sub-domain. The login endpoint is now v3 (/unisso/v3/validateUser.action) and it expects an RSA encrypted version of the password. The Javascript code on the webpage downloads the public key, then encrypts the password with it. I've tried to replicate the encryption using Python's Crypto library, but i'm getting different results during encryption and thus can't login with the encrypted password.

jgriss commented 8 months ago

Hi @Jesus-M

I just double checked my own installation in my Homeassitant setup and reran the unittests. For me, everything is still working using my "old" subdomain (I'm on region01eu5).

It might be that they are currently deploying a new version of their webinterface - which would probably mean a larger development effort to fix this.

Unfortunately, I currently cannot access the uni002eu5 subdomain, as I'm always redirected to "my" region01eu5 one.

May I just ask you which version of the package you are currently using?

Jesus-M commented 8 months ago

There is a maintenance of the server announced for this evening, and for me the connectivity today is terrible (app and web portal), so yeah, maybe they are in the middle of a deployment and it is not impacting everybody, yet.

I was on 0.014 and updated to 0.0.17 yesterday. No evident difference.

jgriss commented 8 months ago

In that case, as soon as your connection is working / stable again, could you try with the region02eu5 subdomain again and let my know if the error persists?

The login code was changed quite a bit in 0.0.16 and a new error log would make it easier for me to pinpoint the issue.

Also, you can increase the log level using python's logging library:


import logging

# add this before calling anything else
logging.basicConfig(level=logging.DEBUG)
Jesus-M commented 8 months ago

Maintenance window is until tomorrow 8:00. I will try after that and will increase the log level.

Thanks

Jesus-M commented 8 months ago

ok, I just checked and I have two servers, on running 0.0.14 and the other one was updated yesterday to 0.0.17.

0.0.14 output:

Login failed: None
Traceback (most recent call last):
File "/home/jesusm/bin/PVDailyYield.py", line 8, in <module>
client = FusionSolarClient("XXXXXXXXX", "ZZZZZZZZZ", huawei_subdomain="region02eu5")
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 82, in __init__
self._login()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 124, in _login
raise AuthenticationException(
fusion_solar_py.exceptions.AuthenticationException: Failed to login into FusionSolarAPI: None

and 0.0.17:

Traceback (most recent call last):
File "/cinemateka/scripts/PVDailyYield.py", line 8, in <module>
client = FusionSolarClient("XXXXXXXXX", "ZZZZZZZZZ", huawei_subdomain="region02eu5")
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 230, in __init__
self._configure_session()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 350, in _configure_session
raise AuthenticationException("Invalid response received. Please check the correct Huawei subdomain.")
fusion_solar_py.exceptions.AuthenticationException: Invalid response received. Please check the correct Huawei subdomain.

and 0.0.17 with extra debug level:

DEBUG:fusion_solar_py.client:Logging into Huawei Fusion Solar API
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): eu5.fusionsolar.huawei.com:443
DEBUG:urllib3.connectionpool:https://eu5.fusionsolar.huawei.com:443 "POST /unisso/v2/validateUser.action?decision=1&service=https%3A%2F%2Fregion02eu5.fusionsolar.huawei.com%2Funisess%2Fv1%2Fauth%3Fservice%3D%2Fnetecowebext%2Fhome%2Findex.html%23%2FLOGIN HTTP/1.1" 200 None
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): region02eu5.fusionsolar.huawei.com:443
DEBUG:urllib3.connectionpool:https://region02eu5.fusionsolar.huawei.com:443 "GET /rest/neteco/web/organization/v2/company/current?_=1704376075376 HTTP/1.1" 302 0
DEBUG:urllib3.connectionpool:https://region02eu5.fusionsolar.huawei.com:443 "GET /unisess/v1/auth?service=%2Frest%2Fneteco%2Fweb%2Forganization%2Fv2%2Fcompany%2Fcurrent%3F_%3D1704376075376 HTTP/1.1" 302 0
DEBUG:urllib3.connectionpool:https://eu5.fusionsolar.huawei.com:443 "GET /unisso/login.action?service=https%3A%2F%2Fregion02eu5.fusionsolar.huawei.com%2Funisess%2Fv1%2Fauth%3Fservice%3D%252Frest%252Fneteco%252Fweb%252Forganization%252Fv2%252Fcompany%252Fcurrent%253F_%253D1704376075376&decision=1 HTTP/1.1" 200 None
Traceback (most recent call last):
File "/cinemateka/scripts/PVDailyYield.py", line 13, in <module>
client = FusionSolarClient("XXXXXXXXX", "ZZZZZZZZZ", huawei_subdomain="region02eu5")
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 230, in __init__
self._configure_session()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 350, in _configure_session
raise AuthenticationException("Invalid response received. Please check the correct Huawei subdomain.")
fusion_solar_py.exceptions.AuthenticationException: Invalid response received. Please check the correct Huawei subdomain.

and finally, 0.0.17, debug and using uni002eu5:

DEBUG:fusion_solar_py.client:Logging into Huawei Fusion Solar API
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): uni002eu5.fusionsolar.huawei.com:443
DEBUG:urllib3.connectionpool:https://uni002eu5.fusionsolar.huawei.com:443 "POST /unisso/v2/validateUser.action?decision=1&service=https%3A%2F%2Funi002eu5.fusionsolar.huawei.com%2Funisess%2Fv1%2Fauth%3Fservice%3D%2Fnetecowebext%2Fhome%2Findex.html%23%2FLOGIN HTTP/1.1" 302 22
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): eu5.fusionsolar.huawei.com:443
DEBUG:urllib3.connectionpool:https://eu5.fusionsolar.huawei.com:443 "GET /unisso/login.action HTTP/1.1" 200 None
Traceback (most recent call last):
File "/usr/lib64/python3.9/site-packages/requests/models.py", line 971, in json
return complexjson.loads(self.text, **kwargs)
File "/usr/lib64/python3.9/json/__init__.py", line 346, in loads
return _default_decoder.decode(s)
File "/usr/lib64/python3.9/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib64/python3.9/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 6 column 1 (char 5)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/cinemateka/scripts/PVDailyYield.py", line 13, in <module>
client = FusionSolarClient("XXXXXXXXX", "ZZZZZZZZZ", huawei_subdomain="uni002eu5")
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 230, in __init__
self._configure_session()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 337, in _configure_session
self._login()
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 162, in wrapper
result = func(self, *args, **kwargs)
File "/usr/lib64/python3.9/site-packages/fusion_solar_py/client.py", line 319, in _login
if r.json()["errorMsg"]:
File "/usr/lib64/python3.9/site-packages/requests/models.py", line 975, in json
raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
requests.exceptions.JSONDecodeError: Expecting value: line 6 column 1 (char 5)

Tomorrow, after the maintenance, I will make more tests.

Thanks

Jesus-M commented 8 months ago

Well, maintenance is over, app and web portal seem to be stable and responding well, the API is still failing.

What I have observed, but don't know if this is new or not, is that there are different domains being redirected at different stages when using the web portal:

  1. I access to region02eu5 (https://region02eu5.fusionsolar.huawei.com)
  2. During the log in process it changes to eu5 (https://eu5.fusionsolar.huawei.com/unisso/login.action)
  3. Once authenticated, the dashboard is in uni002eu5

I hope this helps. Thanks

Jesus-M commented 8 months ago

Were you able to get any findings on this?

Thanks

jgriss commented 8 months ago

Hi @Jesus-M

Unfortunately, this change apparently doesn't affect my account. Therefore, I currently have now way to replicate the issue on my side.

Could you maybe do the following:

import time

# assuming "client" is your fusion_solar_py client object
r = client._session.get(
            url=f"https://region02eu5.fusionsolar.huawei.com/rest/neteco/web/organization/v2/company/current",
            params={"_": round(time.time() * 1000)},
        )

response_text = r.content.decode()

with open("response.html", "w") as writer:
  writer.write(response_text)

Nevertheless, since I cannot reproduce these issues using my account, I'll probably will only be able to fix this, once my account is moved as well.

Jesus-M commented 8 months ago

I tried, but I am not getting anything, but probably I am doing something wrong.

The use I am making is very basic, I just have a cron work to get the daily yield at the end of the day, making a simple call. The two lines for the client is to try with the 'old' domain or the new one.

#!/usr/bin/python3

import datetime
from fusion_solar_py.client import FusionSolarClient

# log into the API - with proper credentials...
#client = FusionSolarClient("uuuuuuuuu", "ppppppppp", huawei_subdomain="uni002eu5")
client = FusionSolarClient("uuuuuuuuu", "ppppppppp", huawei_subdomain="region02eu5")

# get the stats
stats = client.get_power_status()

# print all stats
print("{},{:.2f}".format(datetime.datetime.today().strftime("%Y-%m-%d,%H:%M:%S"),stats.total_power_today_kwh))

# log out - just in case
client.log_out()

So I modified the script with what I understood from your request:

#!/usr/bin/python3

import time
import datetime
from fusion_solar_py.client import FusionSolarClient

# log into the API - with proper credentials...
#client = FusionSolarClient("uuuuuuuuu", "ppppppppp", huawei_subdomain="uni002eu5")
client = FusionSolarClient("uuuuuuuuu", "ppppppppp", huawei_subdomain="region02eu5")

r = client._session.get(
url=f"https://region02eu5.fusionsolar.huawei.com/rest/neteco/web/organization/v2/company/current",
params={"_": round(time.time() * 1000)},
)

response_text = r.content.decode()

with open("response.html", "w") as writer:
writer.write(response_text)

# get the stats
stats = client.get_power_status()

# print all stats
#print("{},{:.2f}".format(datetime.datetime.today().strftime("%Y-%m-%d,%H:%M:%S"),stats.total_power_today_kwh))

# log out - just in case
client.log_out()

The problem is that it fails when FusionSolarClient is called, and hence no html output is generated.

Did I do anything wrong?

Would it help if you can use my credentials for testing?

Thanks

jgriss commented 7 months ago

Hi @Jesus-M

Yes - that would allow me to create proper tests for this new API and see how much work it is to fix this.

If you don't mind, you could share your credentials privately with me via e-mail: johannes.griss@meduniwien.ac.at

Jesus-M commented 7 months ago

sent

jgriss commented 7 months ago

Hi @Jesus-M

Thanks a lot for sharing your credentials! I wouldn't have been able to fix this without them.

That said, we were lucky and it seems that only the login procedure changed. This is now supported in v0.0.19 (already on PyPi). In order for everything to work, you have to set the huawei_subdomain to uni002eu5.

Beware though that other features may also be affected by this new version that are not covered by my tests. Therefore, in case you experience any additional issues, please don't hesitate to post a new Issue.

Jesus-M commented 7 months ago

Tested ... and getting an error:

TypeError: 'DeprecationWarning' object is not callable

Looking around, it seems the data has changed, what it used to be total_power_today_kwh as a float, is now energy_today_kwh as a string.

Apart from that, it is working fine. Thanks!

jgriss commented 7 months ago

Hi @Jesus-M

This may actually be (partly) deliberately. We depecrated total_power_today_kwh and total_power_kwh as these were renamed to energy_today_kwh and energy_kwh.

Issue with string values in PowerStatus is fixed in v0.0.20 (available within the next few minutes).

Thanks a lot!