PyViCare 2.58.0__tar.gz → 2.59.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PKG-INFO +1 -1
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCare.py +1 -1
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareAbstractOAuthManager.py +14 -1
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareBrowserOAuthManager.py +10 -6
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareCachedService.py +8 -1
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareDeviceConfig.py +1 -1
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareOAuthManager.py +9 -6
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareService.py +1 -1
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareUtils.py +11 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareVentilationDevice.py +20 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/pyproject.toml +1 -1
- {pyvicare-2.58.0 → pyvicare-2.59.0}/LICENSE +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/Feature.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareDevice.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareElectricalEnergySystem.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareFloorHeating.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareFuelCell.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareGateway.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareGazBoiler.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareHeatCurveCalculation.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareHeatPump.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareHeatingDevice.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareHybrid.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareOilBoiler.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCarePelletsBoiler.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareRadiatorActuator.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareRepeater.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareRoomSensor.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/__init__.py +0 -0
- {pyvicare-2.58.0 → pyvicare-2.59.0}/README.md +0 -0
|
@@ -9,7 +9,7 @@ from PyViCare.PyViCareOAuthManager import ViCareOAuthManager
|
|
|
9
9
|
from PyViCare.PyViCareService import ViCareDeviceAccessor, ViCareService
|
|
10
10
|
from PyViCare.PyViCareUtils import PyViCareInvalidDataError
|
|
11
11
|
|
|
12
|
-
logger = logging.getLogger(
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
13
|
logger.addHandler(logging.NullHandler())
|
|
14
14
|
|
|
15
15
|
|
|
@@ -9,12 +9,20 @@ from PyViCare import Feature
|
|
|
9
9
|
from PyViCare.PyViCareUtils import (PyViCareCommandError,
|
|
10
10
|
PyViCareDeviceCommunicationError,
|
|
11
11
|
PyViCareInternalServerError,
|
|
12
|
+
PyViCareNotPaidForError,
|
|
12
13
|
PyViCareRateLimitError)
|
|
13
14
|
|
|
14
|
-
logger = logging.getLogger(
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
15
16
|
logger.addHandler(logging.NullHandler())
|
|
16
17
|
|
|
17
18
|
API_BASE_URL = 'https://api.viessmann-climatesolutions.com/iot/v2'
|
|
19
|
+
AUTHORIZE_URL = 'https://iam.viessmann-climatesolutions.com/idp/v3/authorize'
|
|
20
|
+
TOKEN_URL = 'https://iam.viessmann-climatesolutions.com/idp/v3/token'
|
|
21
|
+
|
|
22
|
+
SCOPE_IOT = "IoT"
|
|
23
|
+
SCOPE_USER = "User"
|
|
24
|
+
SCOPE_OFFLINE_ACCESS = "offline_access"
|
|
25
|
+
SCOPE_INTERNAL = "internal"
|
|
18
26
|
|
|
19
27
|
|
|
20
28
|
class AbstractViCareOAuthManager:
|
|
@@ -41,6 +49,7 @@ class AbstractViCareOAuthManager:
|
|
|
41
49
|
self.__handle_expired_token(response)
|
|
42
50
|
self.__handle_rate_limit(response)
|
|
43
51
|
self.__handle_device_communication_error(response)
|
|
52
|
+
self.__handle_not_paid_for(response)
|
|
44
53
|
self.__handle_server_error(response)
|
|
45
54
|
return response
|
|
46
55
|
except TokenExpiredError:
|
|
@@ -65,6 +74,10 @@ class AbstractViCareOAuthManager:
|
|
|
65
74
|
if ("errorType" in response and response["errorType"] == "DEVICE_COMMUNICATION_ERROR"):
|
|
66
75
|
raise PyViCareDeviceCommunicationError(response)
|
|
67
76
|
|
|
77
|
+
def __handle_not_paid_for(self, response):
|
|
78
|
+
if ("errorType" in response and response["errorType"] == "PACKAGE_NOT_PAID_FOR"):
|
|
79
|
+
raise PyViCareNotPaidForError(response)
|
|
80
|
+
|
|
68
81
|
def __handle_server_error(self, response):
|
|
69
82
|
if ("statusCode" in response and response["statusCode"] >= 500):
|
|
70
83
|
raise PyViCareInternalServerError(response)
|
|
@@ -7,17 +7,21 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
|
7
7
|
from authlib.common.security import generate_token
|
|
8
8
|
from authlib.integrations.requests_client import OAuth2Session
|
|
9
9
|
|
|
10
|
-
from PyViCare.PyViCareAbstractOAuthManager import
|
|
10
|
+
from PyViCare.PyViCareAbstractOAuthManager import (
|
|
11
|
+
AUTHORIZE_URL,
|
|
12
|
+
SCOPE_IOT,
|
|
13
|
+
SCOPE_OFFLINE_ACCESS,
|
|
14
|
+
SCOPE_USER,
|
|
15
|
+
TOKEN_URL,
|
|
16
|
+
AbstractViCareOAuthManager,
|
|
17
|
+
)
|
|
11
18
|
from PyViCare.PyViCareUtils import (PyViCareBrowserOAuthTimeoutReachedError,
|
|
12
19
|
PyViCareInvalidCredentialsError)
|
|
13
20
|
|
|
14
|
-
logger = logging.getLogger(
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
15
22
|
logger.addHandler(logging.NullHandler())
|
|
16
23
|
|
|
17
|
-
AUTHORIZE_URL = 'https://iam.viessmann-climatesolutions.com/idp/v3/authorize'
|
|
18
|
-
TOKEN_URL = 'https://iam.viessmann-climatesolutions.com/idp/v3/token'
|
|
19
24
|
REDIRECT_PORT = 51125
|
|
20
|
-
VIESSMANN_SCOPE = ["IoT User", "offline_access"]
|
|
21
25
|
AUTH_TIMEOUT = 60 * 3
|
|
22
26
|
|
|
23
27
|
|
|
@@ -50,7 +54,7 @@ class ViCareBrowserOAuthManager(AbstractViCareOAuthManager):
|
|
|
50
54
|
def __execute_browser_authentication(self):
|
|
51
55
|
redirect_uri = f"http://localhost:{REDIRECT_PORT}"
|
|
52
56
|
oauth_session = OAuth2Session(
|
|
53
|
-
self.client_id, redirect_uri=redirect_uri, scope=
|
|
57
|
+
self.client_id, redirect_uri=redirect_uri, scope=[SCOPE_IOT, SCOPE_USER, SCOPE_OFFLINE_ACCESS], code_challenge_method='S256')
|
|
54
58
|
code_verifier = generate_token(48)
|
|
55
59
|
authorization_url, _ = oauth_session.create_authorization_url(AUTHORIZE_URL, code_verifier=code_verifier)
|
|
56
60
|
|
|
@@ -8,9 +8,11 @@ from PyViCare.PyViCareService import (ViCareDeviceAccessor, ViCareService,
|
|
|
8
8
|
from PyViCare.PyViCareUtils import (PyViCareDeviceCommunicationError,
|
|
9
9
|
PyViCareInvalidDataError,
|
|
10
10
|
PyViCareInternalServerError,
|
|
11
|
+
PyViCareNotPaidForError,
|
|
12
|
+
PyViCareNotSupportedFeatureError,
|
|
11
13
|
ViCareTimer)
|
|
12
14
|
|
|
13
|
-
logger = logging.getLogger(
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
14
16
|
logger.addHandler(logging.NullHandler())
|
|
15
17
|
|
|
16
18
|
|
|
@@ -44,6 +46,11 @@ class ViCareCachedService(ViCareService):
|
|
|
44
46
|
|
|
45
47
|
try:
|
|
46
48
|
data = self.fetch_all_features()
|
|
49
|
+
except PyViCareNotPaidForError as e:
|
|
50
|
+
logger.error("Viessmann API denied access (PACKAGE_NOT_PAID_FOR). Features unavailable: %s", e)
|
|
51
|
+
if self.__cache is not None:
|
|
52
|
+
return self.__cache
|
|
53
|
+
raise PyViCareNotSupportedFeatureError("PACKAGE_NOT_PAID_FOR")
|
|
47
54
|
except (PyViCareDeviceCommunicationError, PyViCareInternalServerError) as e:
|
|
48
55
|
if self.__cache is not None:
|
|
49
56
|
logger.warning("API error, returning stale cache: %s", e)
|
|
@@ -17,7 +17,7 @@ from PyViCare.PyViCareElectricalEnergySystem import ElectricalEnergySystem
|
|
|
17
17
|
from PyViCare.PyViCareGateway import Gateway
|
|
18
18
|
from PyViCare.PyViCareVentilationDevice import VentilationDevice
|
|
19
19
|
|
|
20
|
-
logger = logging.getLogger(
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
21
|
logger.addHandler(logging.NullHandler())
|
|
22
22
|
|
|
23
23
|
|
|
@@ -8,17 +8,20 @@ import requests
|
|
|
8
8
|
from authlib.common.security import generate_token
|
|
9
9
|
from authlib.integrations.requests_client import OAuth2Session
|
|
10
10
|
|
|
11
|
-
from PyViCare.PyViCareAbstractOAuthManager import
|
|
11
|
+
from PyViCare.PyViCareAbstractOAuthManager import (
|
|
12
|
+
AUTHORIZE_URL,
|
|
13
|
+
SCOPE_IOT,
|
|
14
|
+
SCOPE_USER,
|
|
15
|
+
TOKEN_URL,
|
|
16
|
+
AbstractViCareOAuthManager,
|
|
17
|
+
)
|
|
12
18
|
from PyViCare.PyViCareUtils import (PyViCareInvalidConfigurationError,
|
|
13
19
|
PyViCareInvalidCredentialsError)
|
|
14
20
|
|
|
15
|
-
logger = logging.getLogger(
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
16
22
|
logger.addHandler(logging.NullHandler())
|
|
17
23
|
|
|
18
|
-
AUTHORIZE_URL = 'https://iam.viessmann-climatesolutions.com/idp/v3/authorize'
|
|
19
|
-
TOKEN_URL = 'https://iam.viessmann-climatesolutions.com/idp/v3/token'
|
|
20
24
|
REDIRECT_URI = "vicare://oauth-callback/everest"
|
|
21
|
-
VIESSMANN_SCOPE = ["IoT User"]
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
class ViCareOAuthManager(AbstractViCareOAuthManager):
|
|
@@ -55,7 +58,7 @@ class ViCareOAuthManager(AbstractViCareOAuthManager):
|
|
|
55
58
|
oauth sessions object
|
|
56
59
|
"""
|
|
57
60
|
oauth_session = OAuth2Session(
|
|
58
|
-
self.client_id, redirect_uri=REDIRECT_URI, scope=
|
|
61
|
+
self.client_id, redirect_uri=REDIRECT_URI, scope=[SCOPE_IOT, SCOPE_USER], code_challenge_method='S256')
|
|
59
62
|
code_verifier = generate_token(48)
|
|
60
63
|
authorization_url, _ = oauth_session.create_authorization_url(AUTHORIZE_URL, code_verifier=code_verifier)
|
|
61
64
|
logger.debug("Auth URL is: %s", authorization_url)
|
|
@@ -5,7 +5,7 @@ from typing import Any, List
|
|
|
5
5
|
from PyViCare.PyViCareAbstractOAuthManager import AbstractViCareOAuthManager
|
|
6
6
|
from PyViCare.PyViCareUtils import PyViCareNotSupportedFeatureError
|
|
7
7
|
|
|
8
|
-
logger = logging.getLogger(
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
9
|
logger.addHandler(logging.NullHandler())
|
|
10
10
|
|
|
11
11
|
def readFeature(entities, property_name):
|
|
@@ -105,6 +105,17 @@ class PyViCareInvalidDataError(Exception):
|
|
|
105
105
|
pass
|
|
106
106
|
|
|
107
107
|
|
|
108
|
+
class PyViCareNotPaidForError(Exception):
|
|
109
|
+
def __init__(self, response):
|
|
110
|
+
extended_payload = response.get("extendedPayload", {})
|
|
111
|
+
monetization = extended_payload.get("monetization", "Unknown")
|
|
112
|
+
|
|
113
|
+
msg = f"Feature not available: {monetization}"
|
|
114
|
+
|
|
115
|
+
super().__init__(self, msg)
|
|
116
|
+
self.message = msg
|
|
117
|
+
|
|
118
|
+
|
|
108
119
|
class PyViCareDeviceCommunicationError(Exception):
|
|
109
120
|
def __init__(self, response):
|
|
110
121
|
extended_payload = response.get("extendedPayload", {})
|
|
@@ -230,10 +230,26 @@ class VentilationDevice(Device):
|
|
|
230
230
|
def getSupplyTemperature(self) -> float:
|
|
231
231
|
return float(self.getProperty("ventilation.sensors.temperature.supply")["properties"]["value"]["value"])
|
|
232
232
|
|
|
233
|
+
@handleNotSupported
|
|
234
|
+
def getExhaustTemperature(self) -> float:
|
|
235
|
+
return float(self.getProperty("ventilation.sensors.temperature.exhaust")["properties"]["value"]["value"])
|
|
236
|
+
|
|
237
|
+
@handleNotSupported
|
|
238
|
+
def getExtractTemperature(self) -> float:
|
|
239
|
+
return float(self.getProperty("ventilation.sensors.temperature.extract")["properties"]["value"]["value"])
|
|
240
|
+
|
|
233
241
|
@handleNotSupported
|
|
234
242
|
def getSupplyHumidity(self) -> int:
|
|
235
243
|
return int(self.getProperty("ventilation.sensors.humidity.supply")["properties"]["value"]["value"])
|
|
236
244
|
|
|
245
|
+
@handleNotSupported
|
|
246
|
+
def getExhaustHumidity(self) -> int:
|
|
247
|
+
return int(self.getProperty("ventilation.sensors.humidity.exhaust")["properties"]["value"]["value"])
|
|
248
|
+
|
|
249
|
+
@handleNotSupported
|
|
250
|
+
def getExtractHumidity(self) -> int:
|
|
251
|
+
return int(self.getProperty("ventilation.sensors.humidity.extract")["properties"]["value"]["value"])
|
|
252
|
+
|
|
237
253
|
@handleNotSupported
|
|
238
254
|
def getVolatileOrganicCompounds(self) -> int:
|
|
239
255
|
return int(self.getProperty("ventilation.sensors.volatileOrganicCompounds")["properties"]["value"]["value"])
|
|
@@ -282,6 +298,10 @@ class VentilationDevice(Device):
|
|
|
282
298
|
def getHeatExchangerFrostProtectionActive(self) -> bool:
|
|
283
299
|
return "off" != str(self.getProperty("ventilation.heatExchanger.frostprotection")["properties"]["status"]["value"])
|
|
284
300
|
|
|
301
|
+
@handleNotSupported
|
|
302
|
+
def getHeatRecoveryEfficiency(self) -> float:
|
|
303
|
+
return float(self.getProperty("ventilation.heating.recovery")["properties"]["value"]["value"])
|
|
304
|
+
|
|
285
305
|
@handleNotSupported
|
|
286
306
|
def getSupplyVolumeFlow(self) -> int:
|
|
287
307
|
return int(self.getProperty("ventilation.volumeFlow.current.input")["properties"]["value"]["value"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|