PyViCare 2.59.0__tar.gz → 2.60.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 (32) hide show
  1. {pyvicare-2.59.0 → pyvicare-2.60.0}/PKG-INFO +1 -1
  2. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCare.py +42 -7
  3. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareAbstractOAuthManager.py +34 -3
  4. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareDeviceConfig.py +35 -3
  5. pyvicare-2.60.0/PyViCare/PyViCareRoomControl.py +136 -0
  6. pyvicare-2.60.0/PyViCare/PyViCareRoomSensor.py +132 -0
  7. {pyvicare-2.59.0 → pyvicare-2.60.0}/pyproject.toml +1 -1
  8. pyvicare-2.59.0/PyViCare/PyViCareRoomSensor.py +0 -17
  9. {pyvicare-2.59.0 → pyvicare-2.60.0}/LICENSE +0 -0
  10. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/Feature.py +0 -0
  11. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareBrowserOAuthManager.py +0 -0
  12. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareCachedService.py +0 -0
  13. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareDevice.py +0 -0
  14. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareElectricalEnergySystem.py +0 -0
  15. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareFloorHeating.py +0 -0
  16. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareFuelCell.py +0 -0
  17. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareGateway.py +0 -0
  18. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareGazBoiler.py +0 -0
  19. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareHeatCurveCalculation.py +0 -0
  20. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareHeatPump.py +0 -0
  21. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareHeatingDevice.py +0 -0
  22. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareHybrid.py +0 -0
  23. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareOAuthManager.py +0 -0
  24. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareOilBoiler.py +0 -0
  25. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCarePelletsBoiler.py +0 -0
  26. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareRadiatorActuator.py +0 -0
  27. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareRepeater.py +0 -0
  28. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareService.py +0 -0
  29. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareUtils.py +0 -0
  30. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/PyViCareVentilationDevice.py +0 -0
  31. {pyvicare-2.59.0 → pyvicare-2.60.0}/PyViCare/__init__.py +0 -0
  32. {pyvicare-2.59.0 → pyvicare-2.60.0}/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyViCare
3
- Version: 2.59.0
3
+ Version: 2.60.0
4
4
  Summary: Library to communicate with the Viessmann ViCare API.
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -6,8 +6,9 @@ from PyViCare.PyViCareBrowserOAuthManager import ViCareBrowserOAuthManager
6
6
  from PyViCare.PyViCareCachedService import ViCareCachedService
7
7
  from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig
8
8
  from PyViCare.PyViCareOAuthManager import ViCareOAuthManager
9
+ from PyViCare.PyViCareRoomControl import RoomControl
9
10
  from PyViCare.PyViCareService import ViCareDeviceAccessor, ViCareService
10
- from PyViCare.PyViCareUtils import PyViCareInvalidDataError
11
+ from PyViCare.PyViCareUtils import PyViCareInvalidDataError, PyViCareNotSupportedFeatureError
11
12
 
12
13
  logger = logging.getLogger(__name__)
13
14
  logger.addHandler(logging.NullHandler())
@@ -46,23 +47,57 @@ class PyViCare:
46
47
 
47
48
  data = installations['data']
48
49
  self.installations = Wrap(data)
49
- self.devices = list(self.__extract_devices())
50
+ self.all_devices = list(self.__extract_all_devices())
51
+ self.devices = [d for d in self.all_devices
52
+ if d.device_type in self.SUPPORTED_DEVICE_TYPES]
53
+ self.__enrichZigbeeDevices()
50
54
 
51
- def __extract_devices(self):
55
+ SUPPORTED_DEVICE_TYPES = [
56
+ "heating", "zigbee", "vitoconnect", "electricityStorage",
57
+ "tcu", "ventilation", "roomControl",
58
+ ]
59
+
60
+ def __extract_all_devices(self):
52
61
  for installation in self.installations:
53
62
  for gateway in installation.gateways:
54
63
  for device in gateway.devices:
55
- if device.deviceType not in ["heating", "zigbee", "vitoconnect", "electricityStorage", "tcu", "ventilation"]:
56
- continue # we are only interested in heating, photovoltaic, electricityStorage, and ventilation devices
57
-
58
64
  accessor = ViCareDeviceAccessor(
59
65
  installation.id, gateway.serial, device.id)
60
66
  service = self.__buildService(accessor, device.roles)
61
67
 
62
- logger.info("Device found: %s", device.modelId)
68
+ logger.info("Device found: %s (type=%s)", device.modelId, device.deviceType)
63
69
 
64
70
  yield PyViCareDeviceConfig(service, device.id, device.modelId, device.status, device.deviceType, device.roles)
65
71
 
72
+ def __enrichZigbeeDevices(self):
73
+ """Enrich Zigbee devices with sensor data from RoomControl.
74
+
75
+ Viessmann moved temperature/humidity data from physical Zigbee
76
+ sensors to the RoomControl virtual device. This reverses that
77
+ mapping by cross-referencing RoomControl actors with Zigbee
78
+ device IDs.
79
+ """
80
+ devices_by_id = {device_config.device_id: device_config for device_config in self.devices}
81
+
82
+ for device_config in self.devices:
83
+ if device_config.device_type != "roomControl":
84
+ continue
85
+
86
+ room_control = RoomControl(device_config.service)
87
+ try:
88
+ actor_map = room_control.buildActorRoomMap()
89
+ except (KeyError, IndexError, PyViCareNotSupportedFeatureError):
90
+ logger.debug("Could not build actor map for %s", device_config.getModel(), exc_info=True)
91
+ continue
92
+
93
+ for device_id, room_id in actor_map.items():
94
+ zigbee_config = devices_by_id.get(device_id)
95
+ if zigbee_config is None:
96
+ continue
97
+ zigbee_config.setRoomControlEnrichment(room_control, room_id)
98
+ logger.info("Enriched %s with room %s data from %s",
99
+ zigbee_config.device_id, room_id, device_config.getModel())
100
+
66
101
 
67
102
  class DictWrap(object):
68
103
  def __init__(self, d):
@@ -44,7 +44,9 @@ class AbstractViCareOAuthManager:
44
44
  def get(self, url: str) -> Any:
45
45
  try:
46
46
  logger.debug(self.__oauth)
47
- response = self.__oauth.get(f"{API_BASE_URL}{url}", timeout=31).json()
47
+ raw_response = self.__oauth.get(f"{API_BASE_URL}{url}", timeout=31)
48
+ self.__raise_on_non_json_error(raw_response)
49
+ response = raw_response.json()
48
50
  logger.debug("Response to get request: %s", response)
49
51
  self.__handle_expired_token(response)
50
52
  self.__handle_rate_limit(response)
@@ -58,6 +60,21 @@ class AbstractViCareOAuthManager:
58
60
  except InvalidTokenError:
59
61
  self.renewToken()
60
62
  return self.get(url)
63
+ except OSError as e:
64
+ raise PyViCareInternalServerError(
65
+ {"statusCode": 0,
66
+ "message": str(e),
67
+ "viErrorId": "n/a"}) from e
68
+
69
+ def __raise_on_non_json_error(self, response):
70
+ """Guard against non-JSON error responses (e.g. 502 HTML pages from API gateway)."""
71
+ if response.status_code >= 500:
72
+ content_type = response.headers.get('content-type', '')
73
+ if 'application/json' not in content_type:
74
+ raise PyViCareInternalServerError(
75
+ {"statusCode": response.status_code,
76
+ "message": f"Non-JSON {response.status_code} response",
77
+ "viErrorId": "n/a"})
61
78
 
62
79
  def __handle_expired_token(self, response):
63
80
  if ("error" in response and response["error"] == "EXPIRED TOKEN"):
@@ -82,6 +99,13 @@ class AbstractViCareOAuthManager:
82
99
  if ("statusCode" in response and response["statusCode"] >= 500):
83
100
  raise PyViCareInternalServerError(response)
84
101
 
102
+ extended = response.get("extendedPayload", {})
103
+ if isinstance(extended, dict) and extended.get("code") in ("500", "502", "503"):
104
+ raise PyViCareInternalServerError(
105
+ {"statusCode": int(extended["code"]),
106
+ "message": extended.get("reason", ""),
107
+ "viErrorId": response.get("viErrorId", "n/a")})
108
+
85
109
  def __handle_command_error(self, response):
86
110
  if not Feature.raise_exception_on_command_failure:
87
111
  return
@@ -106,8 +130,10 @@ class AbstractViCareOAuthManager:
106
130
  headers = {"Content-Type": "application/json",
107
131
  "Accept": "application/vnd.siren+json"}
108
132
  try:
109
- response = self.__oauth.post(
110
- f"{API_BASE_URL}{url}", data, headers=headers).json()
133
+ raw_response = self.__oauth.post(
134
+ f"{API_BASE_URL}{url}", data, headers=headers)
135
+ self.__raise_on_non_json_error(raw_response)
136
+ response = raw_response.json()
111
137
  self.__handle_expired_token(response)
112
138
  self.__handle_rate_limit(response)
113
139
  self.__handle_command_error(response)
@@ -118,3 +144,8 @@ class AbstractViCareOAuthManager:
118
144
  except InvalidTokenError:
119
145
  self.renewToken()
120
146
  return self.post(url, data)
147
+ except OSError as e:
148
+ raise PyViCareInternalServerError(
149
+ {"statusCode": 0,
150
+ "message": str(e),
151
+ "viErrorId": "n/a"}) from e
@@ -4,6 +4,7 @@ import re
4
4
 
5
5
  from PyViCare.PyViCareFloorHeating import FloorHeating, FloorHeatingChannel
6
6
  from PyViCare.PyViCareFuelCell import FuelCell
7
+ from PyViCare.PyViCareRoomControl import RoomControl
7
8
  from PyViCare.PyViCareGazBoiler import GazBoiler
8
9
  from PyViCare.PyViCareHeatingDevice import HeatingDevice
9
10
  from PyViCare.PyViCareHeatPump import HeatPump
@@ -22,7 +23,7 @@ logger.addHandler(logging.NullHandler())
22
23
 
23
24
 
24
25
  class PyViCareDeviceConfig:
25
- # pylint: disable=too-many-arguments,too-many-positional-arguments
26
+ # pylint: disable=too-many-arguments,too-many-positional-arguments,too-many-instance-attributes
26
27
  def __init__(self, service, device_id, device_model, status, device_type=None, roles=None):
27
28
  self.service = service
28
29
  self.device_id = device_id
@@ -30,6 +31,8 @@ class PyViCareDeviceConfig:
30
31
  self.status = status
31
32
  self.device_type = device_type
32
33
  self.roles = roles if roles is not None else []
34
+ self._room_control = None
35
+ self._room_id = None
33
36
 
34
37
  def asGeneric(self):
35
38
  return HeatingDevice(self.service)
@@ -62,7 +65,18 @@ class PyViCareDeviceConfig:
62
65
  return FloorHeatingChannel(self.service)
63
66
 
64
67
  def asRoomSensor(self):
65
- return RoomSensor(self.service)
68
+ sensor = RoomSensor(self.service)
69
+ if self._room_control is not None:
70
+ sensor.setRoomControl(self._room_control, self._room_id)
71
+ return sensor
72
+
73
+ def asRoomControl(self):
74
+ return RoomControl(self.service)
75
+
76
+ def setRoomControlEnrichment(self, room_control, room_id):
77
+ """Store RoomControl enrichment data to apply when creating a RoomSensor."""
78
+ self._room_control = room_control
79
+ self._room_id = room_id
66
80
 
67
81
  def asRepeater(self):
68
82
  return Repeater(self.service)
@@ -113,6 +127,7 @@ class PyViCareDeviceConfig:
113
127
  (self.asRadiatorActuator, r"E3_RadiatorActuator", ["type:radiator"]),
114
128
  (self.asFloorHeating, r"Smart_zigbee_fht_main|E3_FloorHeatingCircuitDistributorBox", ["type:fhtMain"]),
115
129
  (self.asFloorHeatingChannel, r"Smart_zigbee_fht_channel", ["type:fhtChannel"]),
130
+ (self.asRoomControl, r"E3_RoomControl|Smart_RoomControl", ["type:virtual;smartRoomControl"]),
116
131
  (self.asRoomSensor, r"E3_RoomSensor", ["type:climateSensor"]),
117
132
  (self.asRepeater, r"E3_Repeater", ["type:repeater"]),
118
133
  (self.asGateway, r"E3_TCU41_x04", ["type:gateway;TCU100"]),
@@ -125,11 +140,28 @@ class PyViCareDeviceConfig:
125
140
  for (creator_method, type_name, roles) in device_types:
126
141
  if re.search(type_name, self.device_model) or self.service.hasRoles(roles):
127
142
  logger.info("detected %s %s", self.device_model, creator_method.__name__)
128
- return creator_method()
143
+ device = creator_method()
144
+ if isinstance(device, (GazBoiler, HeatPump)) and not isinstance(device, Hybrid):
145
+ if self._isHybridByFeatures():
146
+ logger.info("upgrading %s to Hybrid based on API features", self.device_model)
147
+ return self.asHybridDevice()
148
+ return device
129
149
 
130
150
  logger.info("Could not auto detect %s. Use generic device.", self.device_model)
131
151
  return self.asGeneric()
132
152
 
153
+ def _isHybridByFeatures(self):
154
+ """Check API features to detect hybrid devices (both burners and compressors)."""
155
+ try:
156
+ features = self.service.fetch_all_features()
157
+ feature_names = [f["feature"] for f in features.get("data", [])]
158
+ has_burners = any(f.startswith("heating.burners") for f in feature_names)
159
+ has_compressors = any(f.startswith("heating.compressors") for f in feature_names)
160
+ return has_burners and has_compressors
161
+ except (KeyError, TypeError, AttributeError, OSError):
162
+ logger.debug("Could not fetch features for hybrid detection of %s", self.device_model)
163
+ return False
164
+
133
165
  def get_raw_json(self):
134
166
  return self.service.fetch_all_features()
135
167
 
@@ -0,0 +1,136 @@
1
+ import logging
2
+ from typing import Any, cast
3
+
4
+ from PyViCare.PyViCareDevice import Device
5
+ from PyViCare.PyViCareUtils import PyViCareNotSupportedFeatureError, handleNotSupported, handleAPICommandErrors
6
+
7
+ logger = logging.getLogger(__name__)
8
+ logger.addHandler(logging.NullHandler())
9
+
10
+
11
+ class RoomControl(Device):
12
+ """Viessmann RoomControl virtual device.
13
+
14
+ Aggregates room sensor data and heating programs.
15
+ Used to enrich physical Zigbee devices with room data.
16
+ """
17
+
18
+ @handleNotSupported
19
+ def getAvailableRooms(self) -> list[str]:
20
+ return cast(list[str], self.service.getProperty("rooms")["properties"]["enabled"]["value"])
21
+
22
+ def getRoomActorIds(self, room_id: str) -> list[str]:
23
+ """Return list of actor device IDs for a room."""
24
+ try:
25
+ actors = self.service.getProperty(f"rooms.{room_id}")["properties"]["actors"]["value"]
26
+ return [str(a["deviceId"]) for a in actors]
27
+ except (PyViCareNotSupportedFeatureError, KeyError):
28
+ return []
29
+
30
+ def getRoomName(self, room_id: str) -> str | None:
31
+ try:
32
+ return str(self.service.getProperty(f"rooms.{room_id}")["properties"]["name"]["value"])
33
+ except (PyViCareNotSupportedFeatureError, KeyError):
34
+ return None
35
+
36
+ def getRoomType(self, room_id: str) -> str | None:
37
+ try:
38
+ return str(self.service.getProperty(f"rooms.{room_id}")["properties"]["type"]["value"])
39
+ except (PyViCareNotSupportedFeatureError, KeyError):
40
+ return None
41
+
42
+ # --- Sensors ---
43
+
44
+ def getRoomTemperature(self, room_id: str) -> float:
45
+ return float(self.service.getProperty(f"rooms.{room_id}.sensors.temperature")["properties"]["value"]["value"])
46
+
47
+ def getRoomHumidity(self, room_id: str) -> float:
48
+ return float(self.service.getProperty(f"rooms.{room_id}.sensors.humidity")["properties"]["value"]["value"])
49
+
50
+ def getRoomCO2(self, room_id: str) -> int:
51
+ return int(self.service.getProperty(f"rooms.{room_id}.sensors.co2")["properties"]["value"]["value"])
52
+
53
+ def getRoomCondensationRisk(self, room_id: str) -> bool:
54
+ return bool(self.service.getProperty(f"rooms.{room_id}.condensationRisk")["properties"]["value"]["value"])
55
+
56
+ # --- Operating state ---
57
+
58
+ def getRoomOperatingStateLevel(self, room_id: str) -> str:
59
+ return str(self.service.getProperty(f"rooms.{room_id}.operating.state")["properties"]["level"]["value"])
60
+
61
+ def getRoomOperatingStateDemand(self, room_id: str) -> str:
62
+ return str(self.service.getProperty(f"rooms.{room_id}.operating.state")["properties"]["demand"]["value"])
63
+
64
+ def getRoomOperatingStateReason(self, room_id: str) -> str:
65
+ return str(self.service.getProperty(f"rooms.{room_id}.operating.state")["properties"]["reason"]["value"])
66
+
67
+ # --- Heating programs ---
68
+
69
+ def getRoomNormalHeatingTemperature(self, room_id: str) -> float:
70
+ return float(self.service.getProperty(f"rooms.{room_id}.operating.programs.normalHeating")["properties"]["temperature"]["value"])
71
+
72
+ @handleAPICommandErrors
73
+ def setRoomNormalHeatingTemperature(self, room_id: str, temperature: float) -> None:
74
+ self.service.setProperty(f"rooms.{room_id}.operating.programs.normalHeating", "setTemperature",
75
+ {"targetTemperature": temperature})
76
+
77
+ def getRoomReducedHeatingTemperature(self, room_id: str) -> float:
78
+ return float(self.service.getProperty(f"rooms.{room_id}.operating.programs.reducedHeating")["properties"]["temperature"]["value"])
79
+
80
+ @handleAPICommandErrors
81
+ def setRoomReducedHeatingTemperature(self, room_id: str, temperature: float) -> None:
82
+ self.service.setProperty(f"rooms.{room_id}.operating.programs.reducedHeating", "setTemperature",
83
+ {"targetTemperature": temperature})
84
+
85
+ def getRoomComfortHeatingTemperature(self, room_id: str) -> float:
86
+ return float(self.service.getProperty(f"rooms.{room_id}.operating.programs.comfortHeating")["properties"]["temperature"]["value"])
87
+
88
+ @handleAPICommandErrors
89
+ def setRoomComfortHeatingTemperature(self, room_id: str, temperature: float) -> None:
90
+ self.service.setProperty(f"rooms.{room_id}.operating.programs.comfortHeating", "setTemperature",
91
+ {"targetTemperature": temperature})
92
+
93
+ # --- Schedule ---
94
+
95
+ def getRoomSchedule(self, room_id: str) -> dict[str, Any]:
96
+ props = self.service.getProperty(f"rooms.{room_id}.schedule")["properties"]
97
+ return {
98
+ "active": props["active"]["value"],
99
+ "mon": props["entries"]["value"]["mon"],
100
+ "tue": props["entries"]["value"]["tue"],
101
+ "wed": props["entries"]["value"]["wed"],
102
+ "thu": props["entries"]["value"]["thu"],
103
+ "fri": props["entries"]["value"]["fri"],
104
+ "sat": props["entries"]["value"]["sat"],
105
+ "sun": props["entries"]["value"]["sun"],
106
+ }
107
+
108
+ # --- Quick modes ---
109
+
110
+ def getRoomManualTillNextScheduleActive(self, room_id: str) -> bool:
111
+ return bool(self.service.getProperty(
112
+ f"rooms.{room_id}.quickmodes.manualTillNextSchedule")["properties"]["active"]["value"])
113
+
114
+ @handleAPICommandErrors
115
+ def activateRoomManualTillNextSchedule(self, room_id: str, temperature: float) -> None:
116
+ self.service.setProperty(f"rooms.{room_id}.quickmodes.manualTillNextSchedule", "activate",
117
+ {"temperature": temperature})
118
+
119
+ @handleAPICommandErrors
120
+ def deactivateRoomManualTillNextSchedule(self, room_id: str) -> None:
121
+ self.service.setProperty(f"rooms.{room_id}.quickmodes.manualTillNextSchedule", "deactivate", {})
122
+
123
+ # --- Mapping ---
124
+
125
+ def buildActorRoomMap(self) -> dict[str, str]:
126
+ """Build a mapping of actor device ID -> room ID."""
127
+ actor_map: dict[str, str] = {}
128
+ try:
129
+ rooms = self.getAvailableRooms()
130
+ except PyViCareNotSupportedFeatureError:
131
+ return actor_map
132
+
133
+ for room_id in rooms:
134
+ for actor_id in self.getRoomActorIds(room_id):
135
+ actor_map[actor_id] = room_id
136
+ return actor_map
@@ -0,0 +1,132 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, TYPE_CHECKING
4
+
5
+ from PyViCare.PyViCareDevice import ZigbeeBatteryDevice
6
+ from PyViCare.PyViCareUtils import handleNotSupported, handleAPICommandErrors
7
+
8
+ if TYPE_CHECKING:
9
+ from PyViCare.PyViCareRoomControl import RoomControl
10
+
11
+
12
+ class RoomSensor(ZigbeeBatteryDevice):
13
+
14
+ _room_control: RoomControl | None = None
15
+ _room_id: str | None = None
16
+
17
+ def setRoomControl(self, room_control: RoomControl, room_id: str) -> None:
18
+ """Enrich this sensor with data from a RoomControl device."""
19
+ self._room_control = room_control
20
+ self._room_id = room_id
21
+
22
+ def _getRoomContext(self) -> tuple[RoomControl, str]:
23
+ """Return (room_control, room_id), raising if not enriched."""
24
+ if self._room_control is None or self._room_id is None:
25
+ raise KeyError("roomControl")
26
+ return self._room_control, self._room_id
27
+
28
+ @handleNotSupported
29
+ def getSerial(self) -> str:
30
+ return str(self.getProperty("device.sensors.temperature")["deviceId"])
31
+
32
+ # --- Sensors (enriched from RoomControl) ---
33
+
34
+ @handleNotSupported
35
+ def getTemperature(self) -> float:
36
+ if self._room_control is not None and self._room_id is not None:
37
+ return self._room_control.getRoomTemperature(self._room_id)
38
+ return float(self.getProperty("device.sensors.temperature")["properties"]["value"]["value"])
39
+
40
+ @handleNotSupported
41
+ def getHumidity(self) -> float:
42
+ if self._room_control is not None and self._room_id is not None:
43
+ return self._room_control.getRoomHumidity(self._room_id)
44
+ return float(self.getProperty("device.sensors.humidity")["properties"]["value"]["value"])
45
+
46
+ @handleNotSupported
47
+ def getCO2(self) -> int:
48
+ rc, rid = self._getRoomContext()
49
+ return rc.getRoomCO2(rid)
50
+
51
+ @handleNotSupported
52
+ def getRoomName(self) -> str | None:
53
+ rc, rid = self._getRoomContext()
54
+ return rc.getRoomName(rid)
55
+
56
+ @handleNotSupported
57
+ def getRoomType(self) -> str | None:
58
+ rc, rid = self._getRoomContext()
59
+ return rc.getRoomType(rid)
60
+
61
+ @handleNotSupported
62
+ def getCondensationRisk(self) -> bool:
63
+ rc, rid = self._getRoomContext()
64
+ return rc.getRoomCondensationRisk(rid)
65
+
66
+ # --- Operating state ---
67
+
68
+ @handleNotSupported
69
+ def getOperatingStateLevel(self) -> str:
70
+ rc, rid = self._getRoomContext()
71
+ return rc.getRoomOperatingStateLevel(rid)
72
+
73
+ @handleNotSupported
74
+ def getOperatingStateDemand(self) -> str:
75
+ rc, rid = self._getRoomContext()
76
+ return rc.getRoomOperatingStateDemand(rid)
77
+
78
+ # --- Heating programs ---
79
+
80
+ @handleNotSupported
81
+ def getNormalHeatingTemperature(self) -> float:
82
+ rc, rid = self._getRoomContext()
83
+ return rc.getRoomNormalHeatingTemperature(rid)
84
+
85
+ @handleAPICommandErrors
86
+ def setNormalHeatingTemperature(self, temperature: float) -> None:
87
+ rc, rid = self._getRoomContext()
88
+ rc.setRoomNormalHeatingTemperature(rid, temperature)
89
+
90
+ @handleNotSupported
91
+ def getReducedHeatingTemperature(self) -> float:
92
+ rc, rid = self._getRoomContext()
93
+ return rc.getRoomReducedHeatingTemperature(rid)
94
+
95
+ @handleAPICommandErrors
96
+ def setReducedHeatingTemperature(self, temperature: float) -> None:
97
+ rc, rid = self._getRoomContext()
98
+ rc.setRoomReducedHeatingTemperature(rid, temperature)
99
+
100
+ @handleNotSupported
101
+ def getComfortHeatingTemperature(self) -> float:
102
+ rc, rid = self._getRoomContext()
103
+ return rc.getRoomComfortHeatingTemperature(rid)
104
+
105
+ @handleAPICommandErrors
106
+ def setComfortHeatingTemperature(self, temperature: float) -> None:
107
+ rc, rid = self._getRoomContext()
108
+ rc.setRoomComfortHeatingTemperature(rid, temperature)
109
+
110
+ # --- Quick modes ---
111
+
112
+ @handleNotSupported
113
+ def getManualTillNextScheduleActive(self) -> bool:
114
+ rc, rid = self._getRoomContext()
115
+ return rc.getRoomManualTillNextScheduleActive(rid)
116
+
117
+ @handleAPICommandErrors
118
+ def activateManualTillNextSchedule(self, temperature: float) -> None:
119
+ rc, rid = self._getRoomContext()
120
+ rc.activateRoomManualTillNextSchedule(rid, temperature)
121
+
122
+ @handleAPICommandErrors
123
+ def deactivateManualTillNextSchedule(self) -> None:
124
+ rc, rid = self._getRoomContext()
125
+ rc.deactivateRoomManualTillNextSchedule(rid)
126
+
127
+ # --- Schedule ---
128
+
129
+ @handleNotSupported
130
+ def getSchedule(self) -> dict[str, Any]:
131
+ rc, rid = self._getRoomContext()
132
+ return rc.getRoomSchedule(rid)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "PyViCare"
3
- version = "2.59.0"
3
+ version = "2.60.0"
4
4
  description = "Library to communicate with the Viessmann ViCare API."
5
5
  authors = [
6
6
  "Simon Gillet <mail+pyvicare@gillet.ninja>"
@@ -1,17 +0,0 @@
1
- from PyViCare.PyViCareDevice import ZigbeeBatteryDevice
2
- from PyViCare.PyViCareUtils import handleNotSupported
3
-
4
-
5
- class RoomSensor(ZigbeeBatteryDevice):
6
-
7
- @handleNotSupported
8
- def getSerial(self):
9
- return self.getProperty("device.sensors.temperature")["deviceId"]
10
-
11
- @handleNotSupported
12
- def getTemperature(self):
13
- return self.getProperty("device.sensors.temperature")["properties"]["value"]["value"]
14
-
15
- @handleNotSupported
16
- def getHumidity(self):
17
- return self.getProperty("device.sensors.humidity")["properties"]["value"]["value"]
File without changes
File without changes
File without changes