Coder Social home page Coder Social logo

mypyllant's Introduction

myPyllant

PyPI GitHub Workflow Status

A Python library to interact with the API behind the myVAILLANT app ((and branded versions of it, such as the MiGo app from Saunier Duval). Needs at least Python 3.10.

Warning

If you're using sensoAPP or the multiMATIC app, this library won't work for you. Try the pymultiMATIC library instead and check Vaillant's website for more information.

This integration is not affiliated with Vaillant, the developers take no responsibility for anything that happens to your devices because of this library.

myPyllant

Install and Test

Warning

You need at least Python 3.10

pip install myPyllant

..or use Docker, if you just want to use it as a CLI tool:

docker run -ti ghcr.io/signalkraft/mypyllant:latest python3 -m myPyllant.export user password brand --country country

Usage

Exporting Data about your System

python3 -m myPyllant.export user password brand --country country
# See python3 -m myPyllant.export -h for more options and a list of countries

The --data argument exports historical data of the devices in your system. Without this keyword, information about your system will be exported as JSON.

Exporting Energy Reports

python3 -m myPyllant.report user password brand --country country
# Wrote 2023 report to energy_data_2023_ArothermPlus_XYZ.csv
# Wrote 2023 report to energy_data_2023_HydraulicStation_XYZ.csv

Writes a report for each heat generator, by default for the current year. You can provide --year to select a different year.

Using the API in Python

#!/usr/bin/env python3

import argparse
import asyncio
import logging
from datetime import datetime, timedelta

from myPyllant.api import MyPyllantAPI
from myPyllant.const import ALL_COUNTRIES, BRANDS, DEFAULT_BRAND

parser = argparse.ArgumentParser(description="Export data from myVaillant API   .")
parser.add_argument("user", help="Username (email address) for the myVaillant app")
parser.add_argument("password", help="Password for the myVaillant app")
parser.add_argument(
    "brand",
    help="Brand your account is registered in, i.e. 'vaillant'",
    default=DEFAULT_BRAND,
    choices=BRANDS.keys(),
)
parser.add_argument(
    "--country",
    help="Country your account is registered in, i.e. 'germany'",
    choices=ALL_COUNTRIES.keys(),
    required=False,
)
parser.add_argument(
    "-v", "--verbose", help="increase output verbosity", action="store_true"
)


async def main(user, password, brand, country):
    async with MyPyllantAPI(user, password, brand, country) as api:
        async for system in api.get_systems():
            print(await api.set_set_back_temperature(system.zones[0], 18))
            print(await api.quick_veto_zone_temperature(system.zones[0], 21, 5))
            print(await api.cancel_quick_veto_zone_temperature(system.zones[0]))
            setpoint = 10.0 if system.control_identifier.is_vrc700 else None
            print(
                await api.set_holiday(
                    system,
                    datetime.now(system.timezone),
                    datetime.now(system.timezone) + timedelta(days=7),
                    setpoint,  # Setpoint is only required for VRC700 systems
                )
            )
            print(await api.cancel_holiday(system))
            if system.domestic_hot_water:
                print(await api.boost_domestic_hot_water(system.domestic_hot_water[0]))
                print(await api.cancel_hot_water_boost(system.domestic_hot_water[0]))
                print(
                    await api.set_domestic_hot_water_temperature(
                        system.domestic_hot_water[0], 46
                    )
                )


if __name__ == "__main__":
    args = parser.parse_args()
    if args.verbose:
        logging.basicConfig(level=logging.DEBUG)
    asyncio.run(main(args.user, args.password, args.brand, args.country))

Tested Configurations

See https://github.com/signalkraft/mypyllant-component/blob/main/README.md#tested-setups

Contributing

Warning

You need at least Python 3.10

I'm happy to accept PRs, if you run the pre-commit checks and test your changes:

git clone https://github.com/signalkraft/myPyllant.git
cd myPyllant
python3 -m venv .venv
source .venv/bin/activate
pip install -r dev-requirements.txt
pip install -e .
pre-commit install
pytest

Supporting new Countries

The myVAILLANT app uses Keycloak and OIDC for authentication, with a realm for each country and brand. There is a script to check which countries are supported:

python3 -m myPyllant.tests.find_countries

Copy the resulting dictionary into src/myPyllant/const.py

Contributing Test Data

Because the myVAILLANT API isn't documented, you can help the development of this library by contributing test data:

python3 -m myPyllant.tests.generate_test_data -h
python3 -m myPyllant.tests.generate_test_data username password brand --country country

..or use Docker:

docker run -v $(pwd)/test_data:/build/src/myPyllant/tests/json -ti ghcr.io/signalkraft/mypyllant:latest python3 -m myPyllant.tests.generate_test_data username password brand --country country

With docker, the results will be put into test_data/.

You can then either create a PR with the created folder, or zip it and attach it to an issue.

Acknowledgements

mypyllant's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mypyllant's Issues

caching login token?

I want to write a simple rest API that uses mypyllant. In the app I would like to avoid superfluous login calls and would like to login once then use the token until it expires and then login again. Because of the asyncio setup this appears to be not that easy. In particular getting an API object from a login in one event loop does not allow it to be reused in another event loop. I will be using flask for the rest API and flask creates a separate eventloop for each request. How can I reuse the api object instead of logging in for every request?

Energy Report not working

Hi,

python3 -m myPyllant.report username password vaillant --country austria gives me only an error (same errer as with real user/pass):
Traceback (most recent call last): File "/usr/lib/python3.10/runpy.py", line 196, in _run_module_as_main return _run_code(code, main_globals, None, File "/usr/lib/python3.10/runpy.py", line 86, in _run_code exec(code, run_globals) File "/usr/local/lib/python3.10/dist-packages/myPyllant/report.py", line 46, in <module> asyncio.run(main(**kwargs)) File "/usr/lib/python3.10/asyncio/runners.py", line 37, in run raise ValueError("a coroutine was expected, got {!r}".format(main)) ValueError: a coroutine was expected, got <async_generator object main at 0x7fc013982540>

Same with the docker version: docker run -ti ghcr.io/signalkraft/mypyllant:latest python3 -m myPyllant.report username password vaillant --country austria
Traceback (most recent call last): File "<frozen runpy>", line 198, in _run_module_as_main File "<frozen runpy>", line 88, in _run_code File "/build/src/myPyllant/report.py", line 46, in <module> asyncio.run(main(**kwargs)) File "/usr/local/lib/python3.11/asyncio/runners.py", line 190, in run return runner.run(main) ^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/asyncio/runners.py", line 89, in run raise ValueError("a coroutine was expected, got {!r}".format(coro)) ValueError: a coroutine was expected, got <async_generator object main at 0x7fdd1ead6260>

is there a problem with the energy report at the moment?

regards

Test data from my heat pump

Hi,

I'm using the home assistant integration. Great work, thanks for developing this.

I dumped the test_data from our heat pump. It's s simple installation, nothing funky.

The myVaillant app can show the start/stops for pump and hydraulics. Have you seen them in the API-resonse? I would love to have them in home assistant.
test_data.zip

Regards,
Robert

How to properly contribute API data?

Hi there,

my Vaillant system comes with these components:

  • sensocomfort VRC 702
  • aguaflow (hot drinking water using the heating water tank as a heat source)
  • auroflow (solar thermal heating the water tank)
  • flexotherm (heat pump heating the water tank)

The home assistant integration is missing several sensors that are present in the myVaillant app.

I'd love to help adding those to your library. Can you add a guide on how to identify the strings you need for that and how to do the MITM thing?

No Entities - Arotherm Plus, UniTower, Sensonet, VR921

Sadly myVaillant integration gives me 0 entities...

2023-06-06 19:57:20.205 ERROR (MainThread) [homeassistant] Error doing job: Unclosed client session
2023-06-06 19:57:20.233 ERROR (MainThread) [homeassistant] Error doing job: Unclosed connector
2023-06-06 19:57:21.891 ERROR (MainThread) [custom_components.mypyllant] Unexpected error fetching myVAILLANT data: 'index'
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 258, in _async_refresh
self.data = await self._async_update_data()
File "/config/custom_components/mypyllant/init.py", line 140, in _async_update_data
data = [
File "/config/custom_components/mypyllant/init.py", line 140, in
data = [
File "/usr/local/lib/python3.10/site-packages/myPyllant/api.py", line 256, in get_systems
system = System(
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 158, in init
self.zones = [Zone(system_id=self.id, **z) for z in self._merged_zones]
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 158, in
self.zones = [Zone(system_id=self.id, **z) for z in self._merged_zones]
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 187, in _merge_object
state = [c for c in self.state.get(obj_name, []) if c["index"] == idx][0]
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 187, in
state = [c for c in self.state.get(obj_name, []) if c["index"] == idx][0]
KeyError: 'index'
2023-06-06 19:57:22.157 ERROR (MainThread) [custom_components.mypyllant] Unexpected error fetching myVAILLANT data: 'index'
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 258, in _async_refresh
self.data = await self._async_update_data()
File "/config/custom_components/mypyllant/init.py", line 157, in _async_update_data
async for system in await self.hass.async_add_executor_job(
File "/usr/local/lib/python3.10/site-packages/myPyllant/api.py", line 256, in get_systems
system = System(
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 158, in init
self.zones = [Zone(system_id=self.id, **z) for z in self._merged_zones]
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 158, in
self.zones = [Zone(system_id=self.id, **z) for z in self._merged_zones]
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 187, in _merge_object
state = [c for c in self.state.get(obj_name, []) if c["index"] == idx][0]
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 187, in
state = [c for c in self.state.get(obj_name, []) if c["index"] == idx][0]
KeyError: 'index'
2023-06-06 19:57:22.419 ERROR (MainThread) [custom_components.mypyllant] Unexpected error fetching myVAILLANT data: 'index'
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 258, in _async_refresh
self.data = await self._async_update_data()
File "/config/custom_components/mypyllant/init.py", line 178, in _async_update_data
async for system in await self.hass.async_add_executor_job(
File "/usr/local/lib/python3.10/site-packages/myPyllant/api.py", line 256, in get_systems
system = System(
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 158, in init
self.zones = [Zone(system_id=self.id, **z) for z in self._merged_zones]
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 158, in
self.zones = [Zone(system_id=self.id, **z) for z in self._merged_zones]
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 187, in _merge_object
state = [c for c in self.state.get(obj_name, []) if c["index"] == idx][0]
File "/usr/local/lib/python3.10/site-packages/myPyllant/models.py", line 187, in
state = [c for c in self.state.get(obj_name, []) if c["index"] == idx][0]
KeyError: 'index'
2023-06-06 19:57:22.427 WARNING (MainThread) [custom_components.mypyllant.binary_sensor] No system data, skipping binary sensors
2023-06-06 19:57:22.429 WARNING (MainThread) [custom_components.mypyllant.sensor] No system data, skipping sensors
2023-06-06 19:57:22.431 WARNING (MainThread) [custom_components.mypyllant.sensor] No hourly data, skipping sensors
2023-06-06 19:57:22.432 WARNING (MainThread) [custom_components.mypyllant.sensor] No daily data, skipping sensors
2023-06-06 19:57:22.434 WARNING (MainThread) [custom_components.mypyllant.climate] No system data, skipping climate
2023-06-06 19:57:22.436 WARNING (MainThread) [custom_components.mypyllant.water_heater] No system data, skipping water hearer

Test data for flexotherm exclusive@vrc700

15d5d4d8373bec8a39f148f8a9fe52fed7418743.zip

Thanks for making this! It works and I get all kinds of info from the API. When calling python3 -m myPyllant.export xxx yyy vaillant --country germany > test.json I get this error (in addition to a filled test.json):

Could not get MPC data
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/dist-packages/myPyllant/api.py", line 1052, in get_mpc
    response = await self.aiohttp_session.get(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/aiohttp/client.py", line 691, in _request
    await raise_for_status(resp)
  File "/usr/local/lib/python3.11/dist-packages/myPyllant/api.py", line 102, in on_raise_for_status
    response.raise_for_status()
  File "/usr/local/lib/python3.11/dist-packages/aiohttp/client_reqrep.py", line 1060, in raise_for_status
    raise ClientResponseError(
aiohttp.client_exceptions.ClientResponseError: 404, message='Resource Not Found', url=URL('https://api.vaillant-group.com/service-connected-control/vrc700/v1/hem/031a815a-0586-4fbf-8985-5af8428ca7ea/mpc')

Let me know if I can provide more info!

Missing value for field "is_cooling_allowed"

Before submitting a new issue

  • I'm using the latest version of myPyllant
  • I've checked known issues
  • I'm sure this problem is about the myPyllant library. For issues with the home assistant component, open an issue here

Problem description

Hi,

i've written a short script for my smart home that pulls data from myVaillant to save them to Influx using the myPyllant libary.
Before the 1st of march everything works fine but now the libary throws the following error:

dacite.exceptions.MissingValueError: missing value for field "is_cooling_allowed"

Seems like the expected value is not longer included in the vaillant api.

The relevant part of my code looks like this:

async with MyPyllantAPI(user, password, brand, country) as api:
     async for system in api.get_systems():`

Stack Trace:

`During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\USERNAME\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\myPyllant\export.py", line 94, in <module>
    asyncio.run(main(**kwargs))
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2288.0_x64__qbz5n2kfra8p0\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2288.0_x64__qbz5n2kfra8p0\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.11_3.11.2288.0_x64__qbz5n2kfra8p0\Lib\asyncio\base_events.py", line 654, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "C:\Users\USERNAME\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\myPyllant\export.py", line 59, in main
    async for system in api.get_systems(
  File "C:\Users\USERNAME\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\myPyllant\api.py", line 321, in get_systems
    system = System.from_api(
             ^^^^^^^^^^^^^^^^
  File "C:\Users\USERNAME\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\myPyllant\models.py", line 324, in from_api
    system.circuits = [
                      ^
  File "C:\Users\USERNAME\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\myPyllant\models.py", line 325, in <listcomp>
    Circuit.from_api(system_id=system.id, **c)
  File "C:\Users\USERNAME\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\myPyllant\models.py", line 122, in from_api
    return from_dict(
           ^^^^^^^^^^
  File "C:\Users\USERNAME\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\dacite\core.py", line 76, in from_dict
    raise MissingValueError(field.name)
dacite.exceptions.MissingValueError: missing value for field "is_cooling_allowed"`

Amount of Cycles aroTherm

Hey 👋 ,
is there any chance the amount of cycles could be added as an entity? Problem is, I tried to setup mitm with IOS and my MAC but the certificate pinning prevents me from posting the correct API. The MyVaillant app can show the amount of cycles and working hours. The API seems to be something like this (which I only found because they use instana as an observability solution): https://api.vaillant-group.com/service-connected-control/end-user-app-api/v1/hem/SOME-ID-NOT-SURE-IF-SENSITIVE/mpc

sending commands via command line using docker

great work so far! :-)
Using the following command line to get system information works fine for me:

docker run -ti ghcr.io/signalkraft/mypyllant:latest python3 -m myPyllant.export myUser myPassword vaillant --country germany

but (and sorry for this question) I wasn't able to manage sending commands. I tried e.g.:

docker run -ti ghcr.io/signalkraft/mypyllant:latest python3 -m myPyllant.api.set_zone_heating_operating_mode myUser myPassword vaillant --country germany --zone Wohnzimmer --mode NIGHT

maybe it's because im not that familiar with python. I would be very happy if you could give me the correct prompt for the obviously wrong example above.

Problem with COPY and REFERENCE in models.py

In models.py you use:
state = [c for c in self.state.get(obj_name, []) if c["index"] == idx][0]
This will give you a reference to the original object.
The command del state["index"] will delete the field index in the original object and the program will crash.

To fix the problem you should use:
state = dict([c for c in self.state.get(obj_name, []) if c["index"] == idx][0])
This will give you a copy of the original object.

My solution:

def _merge_object(self, obj_name) -> Iterator[dict]:
    indexes = [o["index"] for o in self.configuration.get(obj_name, [])]
    for idx in indexes:
        configuration = dict([
            c for c in self.configuration.get(obj_name, []) if c["index"] == idx
        ][0])
        state = dict([c for c in self.state.get(obj_name, []) if c["index"] == idx][0])
        properties = dict([
            c for c in self.properties.get(obj_name, []) if c["index"] == idx
        ][0])
        del state["index"]
        del properties["index"]
        logger.debug(
            f"Merging {obj_name} into results: {json.dumps(configuration, indent=2)}"
        )
        logger.debug(json.dumps(state, indent=2))
        logger.debug(json.dumps(properties, indent=2))
        merged = dict(**state, **properties, **configuration)			
        yield merged

Probleme mit der API - Error fetching myVAILLANT data: 403, message='Forbidden'

Hallo,
Ich habe MyVaillant nun in meinen HomeAsisstant integriert und den HUB eingerichtet,
hier kann ich mich auch mit meinen Zugangsdaten Authentifizieren.

Aber ich bekomme eine Fehlermeldung, Error fetching myVAILLANT data: 403, message='Forbidden' sobald ich auf die Kunden API des Server zugreifen möchte.

Es geht um diese URL:
https://api.vaillant-group.com/service-connected-control/end-user-app-api/v1/systems/None/meta-info/time-zone

Hier noch ein Logfileauszug

MyVailland Debug Log

2024-02-07 10:42:53.789 DEBUG (MainThread) [custom_components.mypyllant] Starting mypyllant component v0.7.2 (library 0.7.7) with homeassistant 2024.1.6, dacite 1.8.1, and aiohttp 3.9.3
2024-02-07 10:42:53.790 DEBUG (MainThread) [custom_components.mypyllant] Creating API and logging in with [my@mailadress(mailto:my@mailadress) in realm germany
2024-02-07 10:42:57.981 DEBUG (MainThread) [custom_components.mypyllant] Refreshing SystemCoordinator
2024-02-07 10:42:57.981 DEBUG (MainThread) [custom_components.mypyllant] Starting async update data for SystemCoordinator
2024-02-07 10:42:57.981 DEBUG (MainThread) [custom_components.mypyllant] Waiting 119s until token refresh for my@mailadress
2024-02-07 10:42:58.314 ERROR (MainThread) [custom_components.mypyllant] Error fetching myVAILLANT data: 403, message='Forbidden', url=URL('https://api.vaillant-group.com/service-connected-control/end-user-app-api/v1/systems/None/meta-info/time-zone')
2024-02-07 10:42:58.315 DEBUG (MainThread) [custom_components.mypyllant] Finished fetching myVAILLANT data in 0.334 seconds (success: False)
2024-02-07 10:42:58.315 DEBUG (MainThread) [custom_components.mypyllant] Refreshing DailyDataCoordinator
2024-02-07 10:42:58.315 DEBUG (MainThread) [custom_components.mypyllant] Starting async update data for DailyDataCoordinator
2024-02-07 10:42:58.315 DEBUG (MainThread) [custom_components.mypyllant] Waiting 119s until token refresh for my@mailadresst
2024-02-07 10:42:58.436 ERROR (MainThread) [custom_components.mypyllant] Error fetching myVAILLANT data: 403, message='Forbidden', url=URL('https://api.vaillant-group.com/service-connected-control/end-user-app-api/v1/systems/None/meta-info/time-zone')
2024-02-07 10:42:58.436 DEBUG (MainThread) [custom_components.mypyllant] Finished fetching myVAILLANT data in 0.121 seconds (success: False)
2024-02-07 10:42:58.444 WARNING (MainThread) [custom_components.mypyllant.binary_sensor] No system data, skipping binary sensors
2024-02-07 10:42:58.444 WARNING (MainThread) [custom_components.mypyllant.calendar] No system data, skipping calendar entities
2024-02-07 10:42:58.444 WARNING (MainThread) [custom_components.mypyllant.climate] No system data, skipping climate
2024-02-07 10:42:58.444 WARNING (MainThread) [custom_components.mypyllant.datetime] No system data, skipping date time entities
2024-02-07 10:42:58.445 WARNING (MainThread) [custom_components.mypyllant.number] No system data, skipping number entities
2024-02-07 10:42:58.445 WARNING (MainThread) [custom_components.mypyllant.sensor] No system data, skipping sensors
2024-02-07 10:42:58.445 DEBUG (MainThread) [custom_components.mypyllant.sensor] Daily data: None
2024-02-07 10:42:58.445 WARNING (MainThread) [custom_components.mypyllant.sensor] No daily data, skipping sensors
2024-02-07 10:42:58.445 WARNING (MainThread) [custom_components.mypyllant.switch] No system data, skipping switch entities
2024-02-07 10:42:58.445 WARNING (MainThread) [custom_components.mypyllant.water_heater] No system data, skipping water heater

Specify minimum Python version

Good morning!

I tried running the tests but I was getting the following error;

  File "/Users/-/GitHub/myPyllant/tests/generate_test_data.py", line 31, in main
    from myPyllant.api import API_URL_BASE, MyPyllantAPI
  File "/Users/-/GitHub/myPyllant/src/myPyllant/api.py", line 10, in <module>
    from myPyllant.models import (
  File "/Users/-/GitHub/myPyllant/src/myPyllant/models.py", line 58, in <module>
    class Zone(BaseModel):
  File "/Users/-/GitHub/myPyllant/src/myPyllant/models.py", line 69, in Zone
    humidity: float | None
TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'

After some digging I found this is due to | operator being a Python 3.10 feature, and I was running it with Python 3.9 (as hinted here in the setup.cfg)

Do you mind specifying the supported Python version somewhere?

Very cool project!

ERROR TWO DAYS AGO

It hasn't connected to the system for two days and it gives these errors.

Este error se originó a partir de una integración personalizada.

Logger: custom_components.mypyllant
Source: custom_components/mypyllant/init.py:179
Integration: myVAILLANT (documentation, issues)
First occurred: 12:12:06 (1 occurrences)
Last logged: 12:12:06

Unexpected error fetching myVAILLANT data: 1 validation error for Zone heating_state value is not a valid enumeration member; permitted: 'IDLE', 'HEATING_UP' (type=type_error.enum; enum_values=[<ZoneHeatingState.IDLE: 'IDLE'>, <ZoneHeatingState.HEATING_UP: 'HEATING_UP'>])
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 283, in _async_refresh
self.data = await self._async_update_data()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/mypyllant/init.py", line 179, in _async_update_data
async for system in await self.hass.async_add_executor_job(
File "/usr/local/lib/python3.11/site-packages/myPyllant/api.py", line 227, in get_systems
system = System(
^^^^^^^
File "/usr/local/lib/python3.11/site-packages/myPyllant/models.py", line 123, in init
self.zones = [Zone(system_id=self.id, **z) for z in self._raw_zones]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/myPyllant/models.py", line 123, in
self.zones = [Zone(system_id=self.id, **z) for z in self._raw_zones]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "pydantic/main.py", line 341, in pydantic.main.BaseModel.init
pydantic.error_wrappers.ValidationError: 1 validation error for Zone
heating_state
value is not a valid enumeration member; permitted: 'IDLE', 'HEATING_UP' (type=type_error.enum; enum_values=[<ZoneHeatingState.IDLE: 'IDLE'>, <ZoneHeatingState.HEATING_UP: 'HEATING_UP'>])

Incorrect setpoint temperature in ECO mode

Hello
I'm using the latest version of Myvaillant.
When the outside temperature passes a threshold set in the thermostat (TE cut-off threshold), the heating is switched off, the set temperature drops to zero and the ECO mode is displayed on the display.
Using Services and mypyllant.generate_test_data development tools, I can see the set temperature going to zero.
desiredRoomTemperatureSetpointHeating: 0
desiredRoomTemperatureSetpoint: 0
But the "sensor.my_home_zone_1_circuit_0_desired_temperature" sensor is at 18°.
If I turn off the heating "sensor.my_home_zone_1_circuit_0_desired_temperature" goes to 0°
I am attaching the 3 files for analysis.
Thank you

Mode ECO.txt
Mode normal.txt
Mode OFF.txt
Capture d’écran 2024-05-16 113533
Screenshot_2024-05-16-11-08-30-20_6b53f0dc5cbd7a58da62b9cf6081846f

Error Requested Range Not Satisfiable

I was using a previous version of myPyllant (few month ago).
I notice that my script (working fine since a few months) was unable to read data since 2months.

The error was for the call async for device_data in api.get_data_by_device(system.devices[0]):
aiohttp.client_exceptions.ClientResponseError: 416, message='Requested Range Not Satisfiable', url=URL('https://api.vaillant-group.com/service-connected-control/end-user-app-api/v1/emf/v2/xxxxxxxx/devices/xxxxxxxxxx/buckets?resolution=DAY&operationMode=DOMESTIC_HOT_WATER&energyType=CONSUMED_ELECTRICAL_ENERGY&startDate=2018-11-23T00:00:00Z&endDate=2024-05-12T02:13:01Z')

I upgrade to the last version and run the script with the same error.
I want to get the field device_data.extra_fields['total_consumption'] to send the total energy consumed since the boiler installation.

Support for VRC700

3 days ago Vaillant added support for VRC700 to their apps, after this I was able to migrate to MyVaillant.

But this library is not working, I'm getting the following error:

aiohttp.client_exceptions.ClientResponseError: 404, message='Resource Not Found', url=URL('https://api.vaillant-group.com/service-connected-control/end-user-app-api/v1/systems/<system_id>/vrc700')

So it looks like something special is happening for the vrc700 control identifier.

I tried to use mitmproxy to figure out what is happening with the app, but when doing that I can't use the app (authentication fails). Can you explain how you used mitmproxy?

Additional Properties

Hi thanks for this module, I was wondering are you able to retrieve additional parameters like flowrate and the return flow temp? Also I can see the heat curve setting but cannot change it, is it possible so that it can be changed. I have the arotherm heatpump. Appreciate your feedback

set_dhw_circulation_time_program doesn´t work

Hi,
i tried the service to set the time_programm to my circulation_pump. After push the button (response "green") but no data was change. I waited some minutes but nothing changed.
can u help me a bit?

Login issue in Java

Hello,
I'm trying to do a reverse engineering of the login in Java, but I don't know why, I get 403 on the second request instead of 302 (I put a quick main):

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.CookieOrigin;
import org.apache.http.cookie.CookieSpec;
import org.apache.http.cookie.CookieSpecProvider;
import org.apache.http.cookie.MalformedCookieException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.cookie.DefaultCookieSpec;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

public class MyVaillantMain {
	
	
	static class EasyCookieSpec extends DefaultCookieSpec {
	    @Override
	    public void validate(Cookie arg0, CookieOrigin arg1) throws MalformedCookieException {
	        //allow all cookies 
	    }
	}

	static class EasySpecProvider implements CookieSpecProvider {
	    @Override
	    public CookieSpec create(HttpContext context) {
	        return new EasyCookieSpec();
	    }
	}
	

    private final static String CLIENT_ID = "myvaillant";
    private final static String AUTHENTICATE_URL = "https://identity.vaillant-group.com/auth/realms/vaillant-italy-b2c/protocol/openid-connect/auth";
    private final static String LOGIN_URL = "https://identity.vaillant-group.com/auth/realms/vaillant-italy-b2c/login-actions/authenticate";

    public static void main(String[] args) throws Exception {
        String generatedCode[] = generateCode();
        
        Registry<CookieSpecProvider> r = RegistryBuilder.<CookieSpecProvider>create()
                .register("easy", new EasySpecProvider())
                .build();

        RequestConfig requestConfig = RequestConfig.custom()
                .setCookieSpec("easy")
                .build();
        
        BasicCookieStore cookieStore = new BasicCookieStore();
        CloseableHttpClient httpclient = HttpClientBuilder.create()
        		.setDefaultRequestConfig(requestConfig)
                .setDefaultCookieStore(cookieStore)
                .setDefaultCookieSpecRegistry(r)
                .setDefaultRequestConfig(requestConfig)
                .disableRedirectHandling()
                .build();
        
        URI uri = new URIBuilder(AUTHENTICATE_URL).addParameter("client_id", CLIENT_ID)
                .addParameter("redirect_uri", "enduservaillant.page.link://login").addParameter("response_type", "code")
                .addParameter("code", "code_challenge").addParameter("code_challenge_method", "S256")
                .addParameter("code_challenge", generatedCode[1]).build();
        HttpGet httpGet = new HttpGet(uri.toString());
        try {
            CloseableHttpResponse response = (CloseableHttpResponse) httpclient.execute(httpGet);
            String login_html = EntityUtils.toString(response.getEntity());
            // String login_html = new BasicResponseHandler().handleResponse(response);
            response.close();
            Pattern pattern = Pattern.compile(LOGIN_URL + "\\?([^\\\"]*)");
            Matcher matcher = pattern.matcher(login_html);
            if (matcher.find()) {
                String username = "username";
                String password = "password";
                String loginUrl = matcher.group().replace("&amp;", "&");

                HttpPost authPost = new HttpPost(loginUrl);
                List<NameValuePair> params = new ArrayList<NameValuePair>();
                params.add(new BasicNameValuePair("username", username));
                params.add(new BasicNameValuePair("password", password));
                params.add(new BasicNameValuePair("credentialId", ""));
                authPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
                
                HttpPost authPost2 = new HttpPost(loginUrl);
                authPost2.setHeader("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
                authPost2.setHeader("content-type", "application/x-www-form-urlencoded");
                authPost2.setHeader("origin", null);
                authPost2.setHeader("user-agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 16_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Mobile/15E148 Safari/604.1");
                authPost2.setHeader("accept-language","it-it");
                StringBuilder json = new StringBuilder();
                json.append("");
                json.append("username="+username+"&");
                json.append("password="+password+"&");
                json.append("credentialId=");
                json.append("");
                authPost2.setEntity(new StringEntity(json.toString()));

                response = httpclient.execute(authPost);
                response.getAllHeaders();
                //System.out.println(EntityUtils.toString(response.getEntity()));
                System.out.println(response.getStatusLine().getStatusCode());
                response.close();
            }
        } finally {

            httpclient.close();

        }

    }

    private static String[] generateCode() throws NoSuchAlgorithmException {
        String code_verifier = shuffle(RandomStringUtils.randomAlphabetic(64) + RandomStringUtils.randomNumeric(64));
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(code_verifier.getBytes(StandardCharsets.UTF_8));

        String b64 = Base64.getUrlEncoder().encodeToString(hash);
        String code_challenge = b64.replace("=", "");

        return new String[] { code_verifier, code_challenge };
    }

    public static String shuffle(String input) {
        List<Character> characters = new ArrayList<Character>();
        for (char c : input.toCharArray()) {
            characters.add(c);
        }
        StringBuilder output = new StringBuilder(input.length());
        while (characters.size() != 0) {
            int randPicker = (int) (Math.random() * characters.size());
            output.append(characters.remove(randPicker));
        }
        return output.toString();
    }

}

cannot log-in into the integration

once I enter my credentials and hit send, it saves, than deletes the credentials and nothing happens. does anyone have a clue where to look at why the home assistant integration behaves that way?

New test data for aroTHERM plus + multiMATIC 700 + sensoNET VR 921

Before submitting a new issue

  • I'm using the latest version of myPyllant
  • I've checked known issues
  • I'm sure this problem is about the myPyllant library. For issues with the home assistant component, open an issue here

My setup

  • Vaillant aroTHERM plus VWL 75/6 A S2 R1 6,7 kW
  • Vaillant uniSTOR plus VIH RW 300/3 BR R1
  • multiMATIC 700
  • sensoNET VR 921

test data

06311a0a4a60d0b43e225afc4e99996fd2b12d23.zip

Problem description

I am providing this test data for my setup because even with the latest version, where I do have mpc and rts enabled I still can't see all sensors. mpc and rts both create 404 errors in the log, but when I run the service mypyllant.generate_test_data (output here: signalkraft/mypyllant-component#119 (comment) ) I can see the values for operationTime and onOffCycles. So it should be possible to fetch those data, even if the mpc and rtc endpoint return 404.

Unknown state after changing opereting mode to "Cooling for several days"

Good morning,
the integration worked fine until i changed the operating mode from winter to summer. In particular, for the summer period, I have to set the operating mode to "Cooling for several days". Since that day, the sensors of entity "SENSOconfort" are in an unknown state. I attach the log below.

Thanks a lot.

log.txt

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.