Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate Weishaupt API instead of using WEM Portal #9

Closed
simonstamm opened this issue Apr 11, 2021 · 53 comments
Closed

Integrate Weishaupt API instead of using WEM Portal #9

simonstamm opened this issue Apr 11, 2021 · 53 comments
Labels
enhancement New feature or request

Comments

@simonstamm
Copy link

simonstamm commented Apr 11, 2021

Hey @erikkastelec,

a while ago I switched to (currently undocumented?) API endpoints instead of scraping the web interface, because it was more reliable. Unfortunately a lot of informations are currently missing (or I was not able to find out the correct endpoint) which are visible in the WEM Portal. Maybe you're able to dig deeper into the API, because there it's also possible to change the temperature and stuff like that very easily (which helps with #7).

Headers
User-Agent: WeishauptWEMApp
Accept: application/json
X-Api-Version: 2.0.0.0

Login Endpoint
URL: https://www.wemportal.com/app/Account/Login
Request: POST
Body:

Name:<username>
PasswordUTF8:<password>
AppID:de.weishaupt.wemapp
AppVersion:2.0.2
ClientOS:Android

Get all devices attached to the account
You'll use these module indexes within the next request.
URL: https://www.wemportal.com/app/Device/Read
Request: GET

Getting data from device
URL: https://www.wemportal.com/app/DataAccess/Read
Request: POST
Body:

{
    "DeviceID": <0000>,
    "Modules": [
        {
            "ModuleIndex": 1,
            "ModuleType": 3,
            "Parameters": [
                {
                    "ParameterID": "Warmwassertemperatur"
                }
            ]
        }
    ]
}

List all values which can be updated and retrieved
That will fix the problem mentioned from you in #7 because there is a parameter called IsWriteable where you receive the name of the parameter which can be updated (also with e.g. MinValue and MaxValue).
URL: https://www.wemportal.com/app/EventType/Read
Request: POST
Body:

DeviceID:<0000>
ModuleIndex:1
ModuleType:3
@erikkastelec erikkastelec added the enhancement New feature or request label Apr 11, 2021
@erikkastelec
Copy link
Owner

I tested it out and it works well. How did you manage to get the endpoints of the API? I tried using mitm proxy, but I couldn't get ssl cert to work with the wem app.

I Will try to implement it soon and if it works as expected it can be added as official Homeassistant integrations, which was previously not possible, due to web scraping.

@simonstamm
Copy link
Author

simonstamm commented Apr 12, 2021

You can download the Android app and decompile it with e.g. the Bytecode Viewer. Then you see e.g. in ./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/dataaccess/OHDataAccessRepository.java something like:

    public void getData(final DataAccessGetRequest dataAccessGetRequest, final WebResultCallback<DataAccessGetResult> webResultCallback, final CacheFetchType cacheFetchType) {
        this.post("DataAccess/Read", dataAccessGetRequest, DataAccessGetResult.class, webResultCallback, cacheFetchType);
    }

    public void postData(final DataAccessPostRequest dataAccessPostRequest, final WebResultCallback<DataAccessPostResult> webResultCallback) {
        this.post("DataAccess/Write", dataAccessPostRequest, DataAccessPostResult.class, webResultCallback, CacheFetchType.FETCH_WEB_ONLY);
    }

With that way you will be able to get all endpoints.

POST

./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/statistics/OHStatisticsRepository.java:19:        this.post("Statistics/Read", statisticsReadRequest, StatisticsReadResult.class, webResultCallback, cacheFetchType);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/statistics/OHStatisticsRepository.java:24:        this.post("Statistics/Read", statisticsReadRequestWithJobID, StatisticsReadResult.class, webResultCallback, cacheFetchType);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/statistics/OHStatisticsRepository.java:29:        this.post("Statistics/Refresh", statisticsRefreshRequest, StatisticsRefreshResult.class, webResultCallback, cacheFetchType);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/circuittimes/OHCircuitTimesRepository.java:19:        this.post("CircuitTimes/Read", circuitTimesGetRequest, CircuitTimesGetResult.class, webResultCallback, cacheFetchType);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/circuittimes/OHCircuitTimesRepository.java:24:        this.post("CircuitTimes/Read", circuitTimesGetRequestWithJobID, CircuitTimesGetResult.class, webResultCallback, cacheFetchType);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/circuittimes/OHCircuitTimesRepository.java:29:        this.post("CircuitTimes/Write", circuitTimesPostRequest, CircuitTimesPostResult.class, webResultCallback, CacheFetchType.FETCH_WEB_ONLY);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/circuittimes/OHCircuitTimesRepository.java:34:        this.post("CircuitTimes/Refresh", circuitTimesRefreshRequest, CircuitTimesRefreshResult.class, webResultCallback, CacheFetchType.FETCH_WEB_ONLY);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/dataaccess/OHDataAccessRepository.java:19:        this.post("DataAccess/Read", dataAccessGetRequest, DataAccessGetResult.class, webResultCallback, cacheFetchType);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/dataaccess/OHDataAccessRepository.java:24:        this.post("DataAccess/Read", dataAccessGetRequestWithJobID, DataAccessGetResult.class, webResultCallback, cacheFetchType);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/dataaccess/OHDataAccessRepository.java:29:        this.post("DataAccess/Write", dataAccessPostRequest, DataAccessPostResult.class, webResultCallback, CacheFetchType.FETCH_WEB_ONLY);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/dataaccess/OHDataAccessRepository.java:34:        this.post("DataAccess/Refresh", dataAccessRefreshRequest, DataAccessRefreshResult.class, webResultCallback, CacheFetchType.FETCH_WEB_ONLY);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/account/OHAccountRepository.java:19:        this.post("Account/Login", loginRequest, LoginResult.class, webResultCallback, CacheFetchType.FETCH_WEB_ONLY);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/account/OHAccountRepository.java:24:        this.post("Account/Logout", CacheFetchType.FETCH_WEB_ONLY);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/eventtype/OHEventTypeRepository.java:19:        this.post("EventType/Read", eventTypeReadRequest, EventTypeReadResult.class, webResultCallback, cacheFetchType);
./de/weishaupt/wemapp/dal/web/implementations/okhttp/repositories/devicestatus/OHDeviceStatusRepository.java:21:        this.post("DeviceStatus/Read", deviceStatusGetRequest, DeviceStatusGetResult.class, webResultCallback, cacheFetchType);

In addition to that ./de/weishaupt/wemapp/presentation/datahelpers/DeviceHelper.java is extremely helpful in finding out the parameters.

Thanks for looking into it and your effort @erikkastelec! :)

@dm82m
Copy link
Contributor

dm82m commented Jun 16, 2021

@erikkastelec any updates here?

@erikkastelec
Copy link
Owner

I managed to implement a basic version of this but did not finish and test everything yet, as I had quite a few things on my plate. Unfortunately, not all the data is available through the API, so web scraping will still be needed, to get some of it.

I think that the best approach would be to keep the web scraping part and use the API to update some of the data more often.

I will get to it when I am done with exams but can push the code if you want to take a look at it.

@erikkastelec
Copy link
Owner

I finished the basic functionality, but still need to implement the logic for changing some of the settings, which are available in the app. (#7)

I need a few days to find any bugs that were missed and will make a release then. If any of you want to test it, you can manually add it to custom_components folder in the Homeassistant config.

@dm82m
Copy link
Contributor

dm82m commented Aug 17, 2021

Thanks Erik! Why are u using web and app together? Will there be a way to let the user define if he prefers scraping or app services? I guess I will test it out tomorrow. Best, Dirk

@erikkastelec
Copy link
Owner

Amount of data available via the API is quite limited. For majority of users API should suffice, so I will implement an option to turn off the webscraping.

@dm82m
Copy link
Contributor

dm82m commented Sep 4, 2021

tested this version but it seems there is an dependency problem:

Logger: homeassistant.config
Source: custom_components/wemportal/wemportalapi.py:14
Integration: wemportal (documentation, issues)
First occurred: 21:45:06 (1 occurrences)
Last logged: 21:45:06

Platform error: sensor
Traceback (most recent call last):
  File "/srv/homeassistant/lib/python3.8/site-packages/homeassistant/config.py", line 891, in async_process_component_config
    platform = p_integration.get_platform(domain)
  File "/srv/homeassistant/lib/python3.8/site-packages/homeassistant/loader.py", line 524, in get_platform
    cache[full_name] = self._import_platform(platform_name)
  File "/srv/homeassistant/lib/python3.8/site-packages/homeassistant/loader.py", line 529, in _import_platform
    return importlib.import_module(f"{self.pkg_path}.{platform_name}")
  File "/usr/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 783, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/home/homeassistant/.homeassistant/custom_components/wemportal/sensor.py", line 43, in <module>
    from .wemportalapi import WemPortalApi
  File "/home/homeassistant/.homeassistant/custom_components/wemportal/wemportalapi.py", line 14, in <module>
    from fuzzywuzzy import fuzz
ModuleNotFoundError: No module named 'fuzzywuzzy'

@erikkastelec
Copy link
Owner

Did you restart the Homeassistant completely. I think that restarting from the UI does not install the missing dependencies.

@dm82m
Copy link
Contributor

dm82m commented Sep 4, 2021

but there is no new dependency in manifest.json, maybe you missed to put it into the PR

@erikkastelec
Copy link
Owner

Fuzzywuzzy is the new dependency listed in the manifest. Check out the dev branch.

@dm82m
Copy link
Contributor

dm82m commented Sep 4, 2021

ah, I was on that one here and it wasn't containing the manifest change:
image

@Hagiman
Copy link

Hagiman commented Dec 2, 2021

I finished the basic functionality, but still need to implement the logic for changing some of the settings, which are available in the app. (#7)

I need a few days to find any bugs that were missed and will make a release then. If any of you want to test it, you can manually add it to custom_components folder in the Homeassistant config.

Hi Erik,
Thanks for your great Plugin! I dont see it clearly from your post. Does you testversion already cointain a possibility to change value in the wem portal such as Warmwassertemperatur oder trigger a Warmwasser Pusch?

Thanks in advance
Steffen

@erikkastelec
Copy link
Owner

Currently API is used only for gathering data, but I plan to implement this in the future #7

@iridium2001
Copy link

iridium2001 commented Feb 7, 2022

Hi @simonstamm and all,

can someone point me to the right syntax for writing parameters to the API?
I tried the following and get a Status "200 ok" answer, but it is not changing the value:

[EDIT: In fact it was changing the value: the example below sets the value actually to 0, which was not visisble with the boolean WW-Push, but e.g. with temperature values. Using "NumericalValue" instead of just "Value" and removing the "" around the number were needed.]

POST https://www.wemportal.com/app/DataAccess/Write

{ "DeviceID": ####, "Modules": [ { "ModuleIndex": 0, "ModuleType": 3, "Parameters": [ { "ParameterID": "WW-Push", "Value": "1.0" } ] } ] }

@iridium2001
Copy link

iridium2001 commented Feb 11, 2022

After some trial and error - this works:

{ "DeviceID": ####, "Modules": [ { "ModuleIndex": 0, "ModuleType": 3, "Parameters": [ { "ParameterID": "WW-Push", "NumericValue": 1.0 } ] } ] }

And analogous with "NormalWW" to set the target water temperature.

@Hagiman
Copy link

Hagiman commented Feb 12, 2022 via email

@iridium2001
Copy link

I'm afraid my python skills are not that great that I'd dare to make an addition to the code from @erikkastelec. So I hope that more experienced people can make use of this information. :-)
For me, it would be really helpful, if these controls (WW-Push, NormalWW, AbsenkWW) would be working. And maybe the switch for the overall mode (Heizen, Standby).
Floor heatings in modern houses don't need sophisticated scheduling or permanent adjustment of temperature anyway. So I don't need to get all controls that the device offers.
If these 3-4 parameters were accessible, this would already allow to do 99% of what I would like to do from within Home Assistant. E.g. avoiding unneccessary warm water production during absence from home, by simply lowering the target temperature. Or triggering a WW-Push when returning home.

@erikkastelec
Copy link
Owner

I see that there is quite some interest in this functionality, so I will try to add this by the end of the week. If anyone else is already working on this, please let me know.

@iridium2001
Copy link

@erikkastelec this is great news! Tonight I'll try to figure out how the API write call handles the enums.

@Hagiman
Copy link

Hagiman commented Feb 14, 2022 via email

@iridium2001
Copy link

iridium2001 commented Feb 14, 2022

Regarding other parameters:

I could set the mode "Betriebsart" (which is labeled as "data type 1") in the same way as the water temperature or the push function. This is in ModuleType 2 and has the following values:

"Betriebsart",
1 = Standby
2 = Zeitprogramm 1
3 = Zeitprogramm 2
4 = Zeitprogramm 3
5 = Sommer
6 = Komfort
7 = Normal
8 = Absenk

https://www.wemportal.com/app/DataAccess/Write
{
    "DeviceID": ####,
    "Modules": [
        {
            "ModuleIndex": 0,
            "ModuleType": 2,
            "Parameters": [
                {
                    "ParameterID": "Betriebsart",
                    "NumericValue": 4
                }
            ]
        }
    ]
}

My wishlist for accessible controls in HA would be:

from ModuleType 3:
WW-Push > Allowed Values 0 - 1, higher values will be accepted, but not sure if this practically works
NormalWW > Allowed Values 0 - 100, integer - decimal can be used but is ignored
AbsenkWW > as above. For both (NormalWW and AbsenkWW) practically range 10-70 would be absolutely sufficient.

and from ModuleType 2:
Betriebsart > Allowed Values 1 - 8, see above. Would be great if they have a "friendly name" in HA and not only numbers.

Some people might like these as well. Personally I would not touch "Komfort/Normal/Absenk" temperatures in HA. This is for setting temperature levels for all heating programs. Also located in ModuleType 2:
Komfort > Allowed Values 5-35
Normal > as above
Absenk > as above

As far as I can tell, this is basically all I what can be usefully adjusted via the API. It might be possible to set certain time-related functions for vacation and party-mode, but this might be tricky and (once the above is implemented) can be done also via HA if needed.

@reyntjensw
Copy link

Hi @erikkastelec I'm working on a similar integration for Homey but I'm having issues with reading the temperatures.
I went over the calls listed above in your startpost, but I just cannot get the room temperature for example.

Could you point me in the right direction?

@erikkastelec
Copy link
Owner

If you already managed to implement login than you can send a GET request to /device/read to get all the available modules.

By sending a POST request to /EventType/Read you can get parameters that are present inside a specific module.

{
    "DeviceID": deviceID,
    "ModuleIndex": 1,
    "ModuleType": 3
}

After that you can request data by sending a POST request to /DataAccess/Read . Example for room temperature (ModuleType and ModuleIndex can change depending on the device, so you should not hardcode anything):

{
    "DeviceID": deviceID,
    "Modules": [
        {
            "ModuleIndex": 1,
            "ModuleType": 2,
            "Parameters": [
                {
                    "ParameterID": "Raumtemperatur"
                }
            ]
        }
    ]
}

If you need anything else let me know. I can also send you a Postman file export if you want.

@reyntjensw
Copy link

@erikkastelec that is indeed what I am seeing as well, but good to know that it is not related to me :)

@erikkastelec
Copy link
Owner

Is anyone else experiencing any issues with the WEM mobile app?
I spent a few hours looking for issues in the API calls I make and realized that can't update anything from the mobile app either. Data also isn't being updated.

I first encountered this issue on Friday and I am stuck waiting for them to fix it.

@dm82m
Copy link
Contributor

dm82m commented Mar 20, 2022

I do not use the mobile app

@Hagiman
Copy link

Hagiman commented Mar 20, 2022 via email

@iridium2001
Copy link

iridium2001 commented Mar 20, 2022

For me the Android app also works fine.
@erikkastelec: maybe you did something that Weishaupt didn't like and they locked your account? Can you log into the Wemportal website manually?

@erikkastelec
Copy link
Owner

Everything worked fine on the website, but when I used the API or mobile app the values were not updating, but all the response codes were still 200 OK

It started to work today at 7:30. Lost quite a few hours wondering what I am doing wrong when calling the API, but everything works fine now.
weishaupt

@Hagiman
Copy link

Hagiman commented Mar 31, 2022 via email

@erikkastelec
Copy link
Owner

erikkastelec commented Mar 31, 2022

I am just finishing a few things. Integration should be ready for testing in a day or two and then released through HACS after a week or two of testing. All the settings that are available through the mobile app will be available in this integration.

@Hagiman
Copy link

Hagiman commented Apr 1, 2022 via email

@erikkastelec
Copy link
Owner

This will take a few more days. Last few days changing any value inside the app does nothing or the change happens a few hours later and I can't test anything.

@erikkastelec
Copy link
Owner

erikkastelec commented Apr 8, 2022

There is a possibility that API calls are limited. For me the API starts working on Monday and stops on Tuesday.
I guess setting api_scan_interval to 1 min generates too many API calls.
Anyone else experiencing this?
image

@iridium2001
Copy link

Will try to test this on the weekend

@erikkastelec
Copy link
Owner

Integration is ready for testing. For anyone not familiar with the process:

  • replace existing files under config/custom_components/wemportal with files from dev branch
  • look at the README for changes in configuration

@dm82m
Copy link
Contributor

dm82m commented Apr 13, 2022

What exactly was changed?

@erikkastelec
Copy link
Owner

erikkastelec commented Apr 13, 2022

All the values that can be changed via WEM mobile app can now be changed inside Homeassistant.

@Hagiman
Copy link

Hagiman commented Apr 17, 2022 via email

@erikkastelec
Copy link
Owner

You have to remove wemportal config under sensors and put this somewhere in your yaml configuration file:

wemportal:
    #scan_interval: 1800
    #api_scan_interval: 300
    #language: en
    #mode: both
    username: your_username
    password: your_password

@Hagiman
Copy link

Hagiman commented Apr 17, 2022 via email

@erikkastelec
Copy link
Owner

This information was in the README of development branch.
For other things I didn't really provide any instructions, as it is quite straight forward inside Homeassistant.

If you go to https://your_homeassistant_ip/config/entities and search for wemportal you should get all the available entities for this integration. All the entities of type number of select are used for changing settings. This way you should be able to find all the entities you want to include into your dashboard.

@iridium2001
Copy link

Hello @erikkastelec , thanks for your effort! Currently I only see these "select" entities and no "number" entities. Is this expected or do I need to configure something differently?
image

@erikkastelec
Copy link
Owner

Entities depend on the type of device that you have. This could be normal. Are you missing any settinngs that are available in wem app?

@erikkastelec
Copy link
Owner

Integration seems to work fine, so I made a new release (you can install it via HACS store now).
I will be closing this issue. If you encounter any problems or have any questions, open a new issue.

@Hagiman
Copy link

Hagiman commented Oct 11, 2022 via email

@Hagiman
Copy link

Hagiman commented Oct 11, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants