weheat 2024.11.1__py3-none-any.whl → 2025.11.24rc1__py3-none-any.whl
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.
- weheat/__init__.py +6 -2
- weheat/abstractions/__init__.py +1 -1
- weheat/abstractions/discovery.py +11 -7
- weheat/abstractions/heat_pump.py +223 -81
- weheat/abstractions/user.py +16 -11
- weheat/api/__init__.py +1 -0
- weheat/api/energy_log_api.py +615 -127
- weheat/api/heat_pump_api.py +580 -374
- weheat/api/heat_pump_log_api.py +884 -360
- weheat/api/user_api.py +260 -115
- weheat/api_client.py +238 -265
- weheat/api_response.py +11 -15
- weheat/configuration.py +14 -9
- weheat/exceptions.py +59 -25
- weheat/models/__init__.py +8 -0
- weheat/models/boiler_type.py +8 -3
- weheat/models/device_state.py +9 -5
- weheat/models/dhw_type.py +8 -3
- weheat/models/energy_view_dto.py +87 -65
- weheat/models/heat_pump_log_view_dto.py +1394 -559
- weheat/models/heat_pump_model.py +8 -3
- weheat/models/heat_pump_status_enum.py +9 -4
- weheat/models/pagination_metadata.py +95 -0
- weheat/models/raw_heat_pump_log_dto.py +438 -375
- weheat/models/raw_heatpump_log_and_is_online_dto.py +578 -0
- weheat/models/read_all_heat_pump_dto.py +69 -53
- weheat/models/read_all_heat_pump_dto_paged_response.py +107 -0
- weheat/models/read_heat_pump_dto.py +64 -48
- weheat/models/read_user_dto.py +55 -37
- weheat/models/read_user_me_dto.py +124 -0
- weheat/models/role.py +10 -4
- weheat/models/total_energy_aggregate.py +111 -0
- weheat/rest.py +152 -259
- weheat-2025.11.24rc1.dist-info/METADATA +104 -0
- weheat-2025.11.24rc1.dist-info/RECORD +39 -0
- {weheat-2024.11.1.dist-info → weheat-2025.11.24rc1.dist-info}/WHEEL +1 -1
- weheat/abstractions/auth.py +0 -34
- weheat/models/heat_pump_type.py +0 -42
- weheat-2024.11.1.dist-info/METADATA +0 -107
- weheat-2024.11.1.dist-info/RECORD +0 -36
- {weheat-2024.11.1.dist-info → weheat-2025.11.24rc1.dist-info/licenses}/LICENSE +0 -0
- {weheat-2024.11.1.dist-info → weheat-2025.11.24rc1.dist-info}/top_level.txt +0 -0
weheat/__init__.py
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
""" # noqa: E501
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
__version__ = "
|
|
17
|
+
__version__ = "2025.11.24"
|
|
18
18
|
|
|
19
19
|
# import apis into sdk package
|
|
20
20
|
from weheat.api.energy_log_api import EnergyLogApi
|
|
@@ -34,12 +34,16 @@ from weheat.exceptions import ApiAttributeError
|
|
|
34
34
|
from weheat.exceptions import ApiException
|
|
35
35
|
|
|
36
36
|
# import models into sdk package
|
|
37
|
+
from weheat.models.boiler_type import BoilerType
|
|
37
38
|
from weheat.models.device_state import DeviceState
|
|
39
|
+
from weheat.models.dhw_type import DhwType
|
|
38
40
|
from weheat.models.energy_view_dto import EnergyViewDto
|
|
39
41
|
from weheat.models.heat_pump_log_view_dto import HeatPumpLogViewDto
|
|
42
|
+
from weheat.models.heat_pump_model import HeatPumpModel
|
|
43
|
+
from weheat.models.heat_pump_status_enum import HeatPumpStatusEnum
|
|
40
44
|
from weheat.models.raw_heat_pump_log_dto import RawHeatPumpLogDto
|
|
41
45
|
from weheat.models.read_all_heat_pump_dto import ReadAllHeatPumpDto
|
|
42
46
|
from weheat.models.read_heat_pump_dto import ReadHeatPumpDto
|
|
43
47
|
from weheat.models.read_user_dto import ReadUserDto
|
|
48
|
+
from weheat.models.read_user_me_dto import ReadUserMeDto
|
|
44
49
|
from weheat.models.role import Role
|
|
45
|
-
|
weheat/abstractions/__init__.py
CHANGED
weheat/abstractions/discovery.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
from dataclasses import dataclass
|
|
2
3
|
|
|
4
|
+
import aiohttp
|
|
5
|
+
|
|
3
6
|
from weheat import DeviceState
|
|
4
7
|
from weheat.configuration import Configuration
|
|
5
8
|
from weheat.api_client import ApiClient
|
|
@@ -10,22 +13,23 @@ class HeatPumpDiscovery:
|
|
|
10
13
|
@dataclass
|
|
11
14
|
class HeatPumpInfo:
|
|
12
15
|
uuid: str
|
|
13
|
-
|
|
16
|
+
device_name: str
|
|
14
17
|
model: str
|
|
15
18
|
sn : str
|
|
16
19
|
has_dhw: bool = False
|
|
17
20
|
|
|
18
21
|
@staticmethod
|
|
19
|
-
async def
|
|
22
|
+
async def async_discover_active(api_url: str, access_token: str, client_session:aiohttp.ClientSession|None = None) -> list[HeatPumpInfo]:
|
|
20
23
|
discovered_pumps = []
|
|
21
24
|
|
|
22
|
-
config = Configuration(host=api_url, access_token=access_token)
|
|
25
|
+
config = Configuration(host=api_url, access_token=access_token, client_session=client_session)
|
|
26
|
+
|
|
27
|
+
async with ApiClient(configuration=config) as client:
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
response = await HeatPumpApi(client).api_v1_heat_pumps_get_with_http_info(1, 1000, state=DeviceState.NUMBER_3)
|
|
25
30
|
|
|
26
|
-
response = HeatPumpApi(client).api_v1_heat_pumps_get_with_http_info('', 0, 1000, DeviceState.NUMBER_3 ,async_req=True).get()
|
|
27
31
|
if response.status_code == 200:
|
|
28
|
-
for pump in response.data:
|
|
32
|
+
for pump in response.data.data:
|
|
29
33
|
# Model of the heat pump
|
|
30
34
|
# - BlackBirdP80: BlackBird P80 heat pump (0)
|
|
31
35
|
# - BlackBirdP60: BlackBird P60 heat pump (1)
|
|
@@ -49,7 +53,7 @@ class HeatPumpDiscovery:
|
|
|
49
53
|
discovered_pumps.append(
|
|
50
54
|
HeatPumpDiscovery.HeatPumpInfo(
|
|
51
55
|
uuid=pump.id,
|
|
52
|
-
|
|
56
|
+
device_name=pump.name,
|
|
53
57
|
model=model_string,
|
|
54
58
|
sn=pump.serial_number,
|
|
55
59
|
has_dhw=dhw,
|
weheat/abstractions/heat_pump.py
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
"""Weheat heat pump abstraction from the API."""
|
|
2
|
+
import asyncio
|
|
2
3
|
from enum import Enum, auto
|
|
3
4
|
|
|
5
|
+
import aiohttp
|
|
6
|
+
|
|
4
7
|
from weheat import HeatPumpApi
|
|
5
8
|
from weheat.configuration import Configuration
|
|
6
9
|
from weheat.api_client import ApiClient
|
|
7
10
|
from weheat.api.heat_pump_log_api import HeatPumpLogApi
|
|
8
11
|
from weheat.api.energy_log_api import EnergyLogApi
|
|
9
12
|
from datetime import datetime, timedelta
|
|
13
|
+
from typing import TypeVar, Union, Optional
|
|
14
|
+
|
|
15
|
+
from weheat.models import TotalEnergyAggregate
|
|
10
16
|
|
|
11
17
|
# before this date no energy logs are available, so start from this point onwards
|
|
12
18
|
START_DATE = datetime(2023, 1, 1, 0, 0, 0)
|
|
13
19
|
|
|
20
|
+
T = TypeVar("T", bool, int, float)
|
|
21
|
+
|
|
14
22
|
|
|
15
23
|
class HeatPump:
|
|
16
24
|
"""Heat pump class representing a heat pump."""
|
|
25
|
+
|
|
17
26
|
class State(Enum):
|
|
18
27
|
STANDBY = auto()
|
|
19
28
|
WATER_CHECK = auto()
|
|
@@ -25,162 +34,172 @@ class HeatPump:
|
|
|
25
34
|
SELF_TEST = auto()
|
|
26
35
|
MANUAL_CONTROL = auto()
|
|
27
36
|
|
|
28
|
-
def __init__(self, api_url: str, uuid: str) -> None:
|
|
37
|
+
def __init__(self, api_url: str, uuid: str, client_session: aiohttp.ClientSession | None = None) -> None:
|
|
29
38
|
self._api_url = api_url
|
|
30
39
|
self._uuid = uuid
|
|
31
40
|
self._last_log = None
|
|
32
|
-
self.
|
|
33
|
-
self.
|
|
34
|
-
self.
|
|
41
|
+
self._energy_total: Union[TotalEnergyAggregate, None] = None
|
|
42
|
+
self._nominal_max_power: Union[float, None] = None
|
|
43
|
+
self._client = client_session
|
|
35
44
|
|
|
36
|
-
def
|
|
45
|
+
async def async_get_status(self, access_token: str) -> None:
|
|
46
|
+
"""Updates the heat pump instance with data from the API."""
|
|
47
|
+
await self.async_get_logs(access_token)
|
|
48
|
+
await self.async_get_energy(access_token)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
async def async_get_logs(self, access_token: str) -> None:
|
|
37
52
|
"""Updates the heat pump instance with data from the API."""
|
|
38
53
|
try:
|
|
39
|
-
config = Configuration(host=self._api_url, access_token=access_token)
|
|
54
|
+
config = Configuration(host=self._api_url, access_token=access_token, client_session=self._client)
|
|
40
55
|
|
|
41
|
-
with ApiClient(configuration=config) as client:
|
|
56
|
+
async with ApiClient(configuration=config) as client:
|
|
42
57
|
# Set the max power once
|
|
43
58
|
if self._nominal_max_power is None:
|
|
44
|
-
|
|
59
|
+
response = await HeatPumpApi(client).api_v1_heat_pumps_heat_pump_id_get_with_http_info(
|
|
60
|
+
heat_pump_id=self._uuid)
|
|
45
61
|
|
|
46
|
-
if
|
|
47
|
-
self._set_nominal_max_power_for_model(
|
|
62
|
+
if response.status_code == 200:
|
|
63
|
+
self._set_nominal_max_power_for_model(response.data.model)
|
|
48
64
|
|
|
49
|
-
|
|
50
|
-
response = HeatPumpLogApi(
|
|
65
|
+
response = await HeatPumpLogApi(
|
|
51
66
|
client
|
|
52
67
|
).api_v1_heat_pumps_heat_pump_id_logs_latest_get_with_http_info(
|
|
53
|
-
heat_pump_id=self._uuid
|
|
54
|
-
|
|
68
|
+
heat_pump_id=self._uuid)
|
|
69
|
+
|
|
55
70
|
if response.status_code == 200:
|
|
56
71
|
self._last_log = response.data
|
|
57
72
|
|
|
58
|
-
# Also get all energy totals form past years and add them together
|
|
59
|
-
# As end time pick today + 1 day to avoid issues with timezones
|
|
60
|
-
response = EnergyLogApi(client).api_v1_energy_logs_heat_pump_id_get_with_http_info(heat_pump_id=self._uuid,
|
|
61
|
-
start_time=START_DATE,
|
|
62
|
-
end_time=datetime.now() + timedelta(days=1),
|
|
63
|
-
interval='Year')
|
|
64
73
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
for year in response.data:
|
|
70
|
-
self._energy_consumption += year.total_ein_cooling
|
|
71
|
-
self._energy_consumption += year.total_ein_heating
|
|
72
|
-
self._energy_consumption += year.total_ein_heating_defrost
|
|
73
|
-
self._energy_consumption += year.total_ein_dhw
|
|
74
|
-
self._energy_consumption += year.total_ein_dhw_defrost
|
|
75
|
-
self._energy_output += year.total_e_out_cooling
|
|
76
|
-
self._energy_output += year.total_e_out_heating
|
|
77
|
-
self._energy_output += year.total_e_out_heating_defrost
|
|
78
|
-
self._energy_output += year.total_e_out_dhw
|
|
79
|
-
self._energy_output += year.total_e_out_dhw_defrost
|
|
74
|
+
except Exception as e:
|
|
75
|
+
self._last_log = None
|
|
76
|
+
raise e
|
|
77
|
+
|
|
80
78
|
|
|
79
|
+
async def async_get_energy(self, access_token: str) -> None:
|
|
80
|
+
"""Updates the heat pump instance with data from the API."""
|
|
81
|
+
try:
|
|
82
|
+
config = Configuration(host=self._api_url, access_token=access_token, client_session=self._client)
|
|
81
83
|
|
|
84
|
+
async with ApiClient(configuration=config) as client:
|
|
85
|
+
response = await EnergyLogApi(client).total_heat_pump_id_get_with_http_info(heat_pump_id=self._uuid)
|
|
86
|
+
|
|
87
|
+
if response.status_code == 200:
|
|
88
|
+
# aggregate the energy consumption
|
|
89
|
+
self._energy_total = response.data
|
|
82
90
|
|
|
83
91
|
except Exception as e:
|
|
84
|
-
self.
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
self._energy_total = None
|
|
93
|
+
|
|
94
|
+
def _if_available_and_valid(self, key:str) -> Optional[T]:
|
|
95
|
+
"""Return the value from the last logged value if available and not -1. None otherwise."""
|
|
96
|
+
value = self._if_available(key)
|
|
97
|
+
if value is not None and value != -1:
|
|
98
|
+
return value
|
|
99
|
+
return None
|
|
87
100
|
|
|
88
|
-
def _if_available(self, key):
|
|
101
|
+
def _if_available(self, key: str) -> Optional[T]:
|
|
89
102
|
"""Returns the value from the last logged value if available. None otherwise."""
|
|
90
103
|
if self._last_log is not None and hasattr(self._last_log, key):
|
|
91
104
|
return getattr(self._last_log, key)
|
|
92
105
|
return None
|
|
93
106
|
|
|
94
|
-
def _set_nominal_max_power_for_model(self, model_id:int):
|
|
107
|
+
def _set_nominal_max_power_for_model(self, model_id: int) -> None:
|
|
95
108
|
# These numbers are the rpm at 100% power in the portal
|
|
96
109
|
# RPM can go above 100% if the limit is increased in the portal.
|
|
97
110
|
# except for the Flint, that cannot go above 100%.
|
|
98
111
|
if model_id == 1:
|
|
99
|
-
#BB60
|
|
100
|
-
self._nominal_max_power =
|
|
112
|
+
# BB60
|
|
113
|
+
self._nominal_max_power = 5520
|
|
101
114
|
elif 2 <= model_id <= 4:
|
|
102
|
-
#SP60
|
|
103
|
-
self._nominal_max_power =
|
|
115
|
+
# SP60
|
|
116
|
+
self._nominal_max_power = 5520
|
|
104
117
|
elif model_id == 5:
|
|
105
118
|
# Flint
|
|
106
119
|
self._nominal_max_power = 5400
|
|
107
120
|
else:
|
|
108
|
-
#BB80
|
|
121
|
+
# BB80
|
|
109
122
|
self._nominal_max_power = 4500
|
|
110
123
|
|
|
111
|
-
def __str__(self):
|
|
124
|
+
def __str__(self) -> str:
|
|
112
125
|
return f"WeheatHeatPump(uuid={self._uuid}, last update={self._if_available('timestamp')})"
|
|
113
126
|
|
|
114
|
-
def __repr__(self):
|
|
127
|
+
def __repr__(self) -> str:
|
|
115
128
|
return self.__str__()
|
|
116
129
|
|
|
117
130
|
@property
|
|
118
|
-
def
|
|
131
|
+
def raw_content(self) -> Optional[dict]:
|
|
132
|
+
if self._last_log is not None:
|
|
133
|
+
return vars(self._last_log) # type: ignore[unreachable]
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def water_inlet_temperature(self) -> Union[float, None]:
|
|
119
138
|
"""The heat pump water inlet temperature."""
|
|
120
139
|
return self._if_available("t_water_in")
|
|
121
140
|
|
|
122
141
|
@property
|
|
123
|
-
def water_outlet_temperature(self):
|
|
142
|
+
def water_outlet_temperature(self) -> Union[float, None]:
|
|
124
143
|
"""The heat pump water outlet temperature."""
|
|
125
144
|
return self._if_available("t_water_out")
|
|
126
145
|
|
|
127
146
|
@property
|
|
128
|
-
def water_house_in_temperature(self):
|
|
147
|
+
def water_house_in_temperature(self) -> Union[float, None]:
|
|
129
148
|
"""The water house in temperature."""
|
|
130
149
|
return self._if_available("t_water_house_in")
|
|
131
150
|
|
|
132
151
|
@property
|
|
133
|
-
def air_inlet_temperature(self):
|
|
152
|
+
def air_inlet_temperature(self) -> Union[float, None]:
|
|
134
153
|
"""The heat pump air inlet temperature."""
|
|
135
154
|
return self._if_available("t_air_in")
|
|
136
155
|
|
|
137
156
|
@property
|
|
138
|
-
def air_outlet_temperature(self):
|
|
157
|
+
def air_outlet_temperature(self) -> Union[float, None]:
|
|
139
158
|
"""The heat pump air outlet temperature."""
|
|
140
159
|
return self._if_available("t_air_out")
|
|
141
160
|
|
|
142
161
|
@property
|
|
143
|
-
def thermostat_water_setpoint(self):
|
|
162
|
+
def thermostat_water_setpoint(self) -> Union[float, None]:
|
|
144
163
|
"""The thermostat water setpoint."""
|
|
145
164
|
return self._if_available("t_thermostat_setpoint")
|
|
146
165
|
|
|
147
166
|
@property
|
|
148
|
-
def thermostat_room_temperature(self):
|
|
167
|
+
def thermostat_room_temperature(self) -> Union[float, None]:
|
|
149
168
|
"""The thermostat current room temperature."""
|
|
150
|
-
return self.
|
|
169
|
+
return self._if_available_and_valid("t_room")
|
|
151
170
|
|
|
152
171
|
@property
|
|
153
|
-
def thermostat_room_temperature_setpoint(self):
|
|
172
|
+
def thermostat_room_temperature_setpoint(self) -> Union[float, None]:
|
|
154
173
|
"""The thermostat room temperature setpoint."""
|
|
155
|
-
return self.
|
|
174
|
+
return self._if_available_and_valid("t_room_target")
|
|
156
175
|
|
|
157
176
|
@property
|
|
158
|
-
def thermostat_on_off_state(self):
|
|
177
|
+
def thermostat_on_off_state(self) -> Union[bool, None]:
|
|
159
178
|
"""The thermostat on/off state."""
|
|
160
179
|
return self._if_available("on_off_thermostat_state")
|
|
161
180
|
|
|
162
181
|
@property
|
|
163
|
-
def power_input(self):
|
|
182
|
+
def power_input(self) -> Union[float, None]:
|
|
164
183
|
"""The heat pumps power input."""
|
|
165
184
|
return self._if_available("cm_mass_power_in")
|
|
166
185
|
|
|
167
186
|
@property
|
|
168
|
-
def power_output(self):
|
|
187
|
+
def power_output(self) -> Union[float, None]:
|
|
169
188
|
"""The heat pumps hydraulic output power."""
|
|
170
189
|
return self._if_available("cm_mass_power_out")
|
|
171
190
|
|
|
172
191
|
@property
|
|
173
|
-
def dhw_top_temperature(self):
|
|
192
|
+
def dhw_top_temperature(self) -> Union[float, None]:
|
|
174
193
|
"""The DHW vessel top temperature."""
|
|
175
194
|
return self._if_available("t1")
|
|
176
195
|
|
|
177
196
|
@property
|
|
178
|
-
def dhw_bottom_temperature(self):
|
|
197
|
+
def dhw_bottom_temperature(self) -> Union[float, None]:
|
|
179
198
|
"""The DHW vessel bottom temperature."""
|
|
180
199
|
return self._if_available("t2")
|
|
181
200
|
|
|
182
201
|
@property
|
|
183
|
-
def cop(self):
|
|
202
|
+
def cop(self) -> Union[float, None]:
|
|
184
203
|
"""
|
|
185
204
|
Returns the coefficient of performance of the heat pump.
|
|
186
205
|
Note that this is calculated from a singular log entry and might not be accurate when the
|
|
@@ -188,36 +207,42 @@ class HeatPump:
|
|
|
188
207
|
"""
|
|
189
208
|
input = self.power_input
|
|
190
209
|
output = self.power_output
|
|
191
|
-
|
|
210
|
+
# When either is not available, the calculation cannot be made.
|
|
211
|
+
if input is None or output is None:
|
|
212
|
+
return None
|
|
213
|
+
|
|
214
|
+
if input > 0:
|
|
192
215
|
return output / input
|
|
193
216
|
|
|
217
|
+
return 0
|
|
218
|
+
|
|
194
219
|
@property
|
|
195
|
-
def
|
|
220
|
+
def indoor_unit_water_pump_state(self) -> Union[bool, None]:
|
|
196
221
|
"""Decoded water pump state."""
|
|
197
222
|
return self._if_available("control_bridge_status_decoded_water_pump")
|
|
198
223
|
|
|
199
224
|
@property
|
|
200
|
-
def
|
|
201
|
-
"""Decoded
|
|
225
|
+
def indoor_unit_auxiliary_pump_state(self) -> Union[bool, None]:
|
|
226
|
+
"""Decoded auxiliary pump state."""
|
|
202
227
|
return self._if_available("control_bridge_status_decoded_water_pump2")
|
|
203
228
|
|
|
204
229
|
@property
|
|
205
|
-
def
|
|
230
|
+
def indoor_unit_dhw_valve_or_pump_state(self) -> Union[bool, None]:
|
|
206
231
|
"""Decoded DHW valve or pump state."""
|
|
207
232
|
return self._if_available("control_bridge_status_decoded_dhw_valve")
|
|
208
233
|
|
|
209
234
|
@property
|
|
210
|
-
def
|
|
235
|
+
def indoor_unit_gas_boiler_state(self) -> Union[bool, None]:
|
|
211
236
|
"""Decoded gas boiler state."""
|
|
212
237
|
return self._if_available("control_bridge_status_decoded_gas_boiler")
|
|
213
238
|
|
|
214
239
|
@property
|
|
215
|
-
def
|
|
240
|
+
def indoor_unit_electric_heater_state(self) -> Union[bool, None]:
|
|
216
241
|
"""Decoded electric heater state."""
|
|
217
242
|
return self._if_available("control_bridge_status_decoded_electric_heater")
|
|
218
243
|
|
|
219
244
|
@property
|
|
220
|
-
def compressor_percentage(self):
|
|
245
|
+
def compressor_percentage(self) -> Union[int, None]:
|
|
221
246
|
current_rpm = self._if_available("rpm")
|
|
222
247
|
if self._nominal_max_power is not None and current_rpm is not None:
|
|
223
248
|
# calculate percentage of rpm
|
|
@@ -225,12 +250,12 @@ class HeatPump:
|
|
|
225
250
|
return None
|
|
226
251
|
|
|
227
252
|
@property
|
|
228
|
-
def compressor_rpm(self):
|
|
253
|
+
def compressor_rpm(self) -> Union[float, None]:
|
|
229
254
|
"""Compressor RPM."""
|
|
230
255
|
return self._if_available("rpm")
|
|
231
256
|
|
|
232
257
|
@property
|
|
233
|
-
def heat_pump_state(self) -> State
|
|
258
|
+
def heat_pump_state(self) -> Union[State, None]:
|
|
234
259
|
"""The heat pump state."""
|
|
235
260
|
numeric_state = self._if_available("state")
|
|
236
261
|
if numeric_state is None:
|
|
@@ -240,7 +265,7 @@ class HeatPump:
|
|
|
240
265
|
return self.State.STANDBY
|
|
241
266
|
elif numeric_state == 70:
|
|
242
267
|
return self.State.HEATING
|
|
243
|
-
elif numeric_state
|
|
268
|
+
elif numeric_state >= 130 and numeric_state < 140:
|
|
244
269
|
return self.State.COOLING
|
|
245
270
|
elif numeric_state == 150:
|
|
246
271
|
return self.State.DHW
|
|
@@ -250,16 +275,133 @@ class HeatPump:
|
|
|
250
275
|
return self.State.SELF_TEST
|
|
251
276
|
elif numeric_state == 180:
|
|
252
277
|
return self.State.MANUAL_CONTROL
|
|
253
|
-
elif numeric_state
|
|
278
|
+
elif numeric_state >= 200 and numeric_state <= 240:
|
|
254
279
|
return self.State.DEFROSTING
|
|
255
280
|
return None
|
|
256
281
|
|
|
282
|
+
@staticmethod
|
|
283
|
+
def _pwm_to_volume(pwm: float, max: float) -> Union[float, None]:
|
|
284
|
+
"""Calculate PWM to Volume in m3/h based on the max available volume"""
|
|
285
|
+
|
|
286
|
+
# 0 or > 75 are abnormal states. 255 = Off
|
|
287
|
+
if pwm < 1 or pwm > 75:
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
# 2 = standby
|
|
291
|
+
if pwm <= 5:
|
|
292
|
+
return 0
|
|
293
|
+
|
|
294
|
+
# 5-75 is linear from 0 to max
|
|
295
|
+
return ((pwm - 5) / 70) * max
|
|
296
|
+
|
|
257
297
|
@property
|
|
258
|
-
def
|
|
259
|
-
"""The
|
|
260
|
-
|
|
298
|
+
def dhw_flow_volume(self) -> Union[float, None]:
|
|
299
|
+
"""The DHW Flow in m3/h."""
|
|
300
|
+
pwm = pwm=self._if_available("dhw_flow")
|
|
301
|
+
if pwm is None:
|
|
302
|
+
return None
|
|
303
|
+
|
|
304
|
+
return self._pwm_to_volume(pwm, max=2.1)
|
|
305
|
+
|
|
306
|
+
@property
|
|
307
|
+
def central_heating_flow_volume(self) -> Union[float, None]:
|
|
308
|
+
"""The Central Heating Flow in m3/h."""
|
|
309
|
+
pwm = pwm=self._if_available("central_heating_flow")
|
|
310
|
+
if pwm is None:
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
return self._pwm_to_volume(pwm, max=2.1)
|
|
314
|
+
|
|
315
|
+
@property
|
|
316
|
+
def energy_in_heating(self) -> Union[float, None]:
|
|
317
|
+
"""The total used energy in heating mode."""
|
|
318
|
+
if self._energy_total is None:
|
|
319
|
+
return None
|
|
320
|
+
return float(self._energy_total.total_ein_heating)
|
|
261
321
|
|
|
262
322
|
@property
|
|
263
|
-
def
|
|
264
|
-
"""The total energy
|
|
265
|
-
|
|
323
|
+
def energy_in_dhw(self) -> Union[float, None]:
|
|
324
|
+
"""The total used energy in DHW mode."""
|
|
325
|
+
if self._energy_total is None:
|
|
326
|
+
return None
|
|
327
|
+
return float(self._energy_total.total_ein_dhw)
|
|
328
|
+
|
|
329
|
+
@property
|
|
330
|
+
def energy_in_defrost(self) -> Union[float, None]:
|
|
331
|
+
"""The total used energy in defrost modes."""
|
|
332
|
+
if self._energy_total is None:
|
|
333
|
+
return None
|
|
334
|
+
return float(self._energy_total.total_ein_heating_defrost + self._energy_total.total_ein_dhw_defrost)
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def energy_in_defrost_dhw(self) -> Union[float, None]:
|
|
338
|
+
"""The total used energy in defrost from DHW mode."""
|
|
339
|
+
if self._energy_total is None:
|
|
340
|
+
return None
|
|
341
|
+
return float(self._energy_total.total_ein_dhw_defrost)
|
|
342
|
+
|
|
343
|
+
@property
|
|
344
|
+
def energy_in_defrost_ch(self) -> Union[float, None]:
|
|
345
|
+
"""The total used energy in defrost from CH mode."""
|
|
346
|
+
if self._energy_total is None:
|
|
347
|
+
return None
|
|
348
|
+
return float(self._energy_total.total_ein_heating_defrost)
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def energy_in_cooling(self) -> Union[float, None]:
|
|
352
|
+
"""The total used energy in cooling mode."""
|
|
353
|
+
if self._energy_total is None:
|
|
354
|
+
return None
|
|
355
|
+
return float(self._energy_total.total_ein_cooling)
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
def energy_out_heating(self) -> Union[float, None]:
|
|
359
|
+
"""The total supplied energy in heating mode."""
|
|
360
|
+
if self._energy_total is None:
|
|
361
|
+
return None
|
|
362
|
+
return float(self._energy_total.total_e_out_heating)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
@property
|
|
366
|
+
def energy_out_dhw(self) -> Union[float, None]:
|
|
367
|
+
"""The total supplied energy in DHW mode."""
|
|
368
|
+
if self._energy_total is None:
|
|
369
|
+
return None
|
|
370
|
+
return float(self._energy_total.total_e_out_dhw)
|
|
371
|
+
|
|
372
|
+
@property
|
|
373
|
+
def energy_out_defrost(self) -> Union[float, None]:
|
|
374
|
+
"""The total supplied energy in defrost modes.
|
|
375
|
+
Note that this energy value is negative as energy is removed from the water.
|
|
376
|
+
"""
|
|
377
|
+
if self._energy_total is None:
|
|
378
|
+
return None
|
|
379
|
+
return float(self._energy_total.total_e_out_dhw_defrost + self._energy_total.total_e_out_heating_defrost)
|
|
380
|
+
|
|
381
|
+
@property
|
|
382
|
+
def energy_out_defrost_dhw(self) -> Union[float, None]:
|
|
383
|
+
"""The total supplied energy in defrost DHW mode.
|
|
384
|
+
Note that this energy value is negative as energy is removed from the water.
|
|
385
|
+
"""
|
|
386
|
+
if self._energy_total is None:
|
|
387
|
+
return None
|
|
388
|
+
return float(self._energy_total.total_e_out_dhw_defrost)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
@property
|
|
392
|
+
def energy_out_defrost_ch(self) -> Union[float, None]:
|
|
393
|
+
"""The total supplied energy in defrost CH mode.
|
|
394
|
+
Note that this energy value is negative as energy is removed from the water.
|
|
395
|
+
"""
|
|
396
|
+
if self._energy_total is None:
|
|
397
|
+
return None
|
|
398
|
+
return float(self._energy_total.total_e_out_heating_defrost)
|
|
399
|
+
|
|
400
|
+
@property
|
|
401
|
+
def energy_out_cooling(self) -> Union[float, None]:
|
|
402
|
+
"""The total supplied energy in cooling mode.
|
|
403
|
+
Note that this energy value is negative as energy is removed from the water.
|
|
404
|
+
"""
|
|
405
|
+
if self._energy_total is None:
|
|
406
|
+
return None
|
|
407
|
+
return float(self._energy_total.total_e_out_cooling)
|
weheat/abstractions/user.py
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
import aiohttp
|
|
5
|
+
|
|
1
6
|
from weheat.configuration import Configuration
|
|
2
7
|
from weheat.api_client import ApiClient
|
|
3
8
|
from weheat.api.user_api import UserApi
|
|
4
9
|
|
|
5
10
|
|
|
6
|
-
async def
|
|
11
|
+
async def async_get_user_id_from_token(api_url: str, access_token: str, client_session:aiohttp.ClientSession|None = None) -> Union[str,None]:
|
|
7
12
|
""" Get the user id from the current logged-in user. """
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
config = Configuration(host=api_url, access_token=access_token, client_session=client_session)
|
|
14
|
+
|
|
15
|
+
async with ApiClient(configuration=config) as client:
|
|
16
|
+
response = await UserApi(
|
|
17
|
+
client
|
|
18
|
+
).api_v1_users_me_get_with_http_info()
|
|
19
|
+
|
|
20
|
+
if response.status_code == 200:
|
|
21
|
+
return response.data.id # type: ignore[no-any-return]
|
|
22
|
+
return None
|
|
10
23
|
|
|
11
|
-
with ApiClient(configuration=config) as client:
|
|
12
|
-
response = UserApi(
|
|
13
|
-
client
|
|
14
|
-
).api_v1_users_me_get_with_http_info(async_req=True).get()
|
|
15
|
-
if response.status_code == 200:
|
|
16
|
-
return response.data.id
|
|
17
|
-
except Exception as e:
|
|
18
|
-
raise e
|
weheat/api/__init__.py
CHANGED