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.
Files changed (30) hide show
  1. {pyvicare-2.58.0 → pyvicare-2.59.0}/PKG-INFO +1 -1
  2. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCare.py +1 -1
  3. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareAbstractOAuthManager.py +14 -1
  4. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareBrowserOAuthManager.py +10 -6
  5. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareCachedService.py +8 -1
  6. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareDeviceConfig.py +1 -1
  7. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareOAuthManager.py +9 -6
  8. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareService.py +1 -1
  9. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareUtils.py +11 -0
  10. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareVentilationDevice.py +20 -0
  11. {pyvicare-2.58.0 → pyvicare-2.59.0}/pyproject.toml +1 -1
  12. {pyvicare-2.58.0 → pyvicare-2.59.0}/LICENSE +0 -0
  13. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/Feature.py +0 -0
  14. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareDevice.py +0 -0
  15. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareElectricalEnergySystem.py +0 -0
  16. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareFloorHeating.py +0 -0
  17. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareFuelCell.py +0 -0
  18. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareGateway.py +0 -0
  19. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareGazBoiler.py +0 -0
  20. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareHeatCurveCalculation.py +0 -0
  21. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareHeatPump.py +0 -0
  22. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareHeatingDevice.py +0 -0
  23. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareHybrid.py +0 -0
  24. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareOilBoiler.py +0 -0
  25. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCarePelletsBoiler.py +0 -0
  26. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareRadiatorActuator.py +0 -0
  27. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareRepeater.py +0 -0
  28. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/PyViCareRoomSensor.py +0 -0
  29. {pyvicare-2.58.0 → pyvicare-2.59.0}/PyViCare/__init__.py +0 -0
  30. {pyvicare-2.58.0 → pyvicare-2.59.0}/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyViCare
3
- Version: 2.58.0
3
+ Version: 2.59.0
4
4
  Summary: Library to communicate with the Viessmann ViCare API.
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -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('ViCare')
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('ViCare')
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 AbstractViCareOAuthManager
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('ViCare')
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=VIESSMANN_SCOPE, code_challenge_method='S256')
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('ViCare')
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('ViCare')
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 AbstractViCareOAuthManager
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('ViCare')
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=VIESSMANN_SCOPE, code_challenge_method='S256')
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('ViCare')
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"])
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "PyViCare"
3
- version = "2.58.0"
3
+ version = "2.59.0"
4
4
  description = "Library to communicate with the Viessmann ViCare API."
5
5
  authors = [
6
6
  "Simon Gillet <mail+pyvicare@gillet.ninja>"
File without changes
File without changes
File without changes