python-qube-heatpump 1.12.0__tar.gz → 1.13.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.
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/PKG-INFO +1 -1
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/pyproject.toml +1 -1
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/client.py +131 -40
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/tests/test_client.py +22 -11
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/tests/test_client_batching.py +75 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/.github/workflows/ci.yml +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/.github/workflows/python-publish.yml +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/.gitignore +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/AGENTS.md +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/CLAUDE.md +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/LICENSE +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/README.md +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/docs/modbus-lijst-qube-totaal.pdf +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/pytest.ini +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/__init__.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/const.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/entities/__init__.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/entities/base.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/entities/binary_sensors.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/entities/sensors.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/entities/switches.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/models.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/network.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/py.typed +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/tests/conftest.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/tests/test_const.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/tests/test_entities.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/tests/test_models.py +0 -0
- {python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/tests/test_network.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-qube-heatpump
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.13.0
|
|
4
4
|
Summary: Async Modbus client for Qube Heat Pumps
|
|
5
5
|
Project-URL: Homepage, https://github.com/MattieGit/python-qube-heatpump
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/MattieGit/python-qube-heatpump/issues
|
{python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/client.py
RENAMED
|
@@ -18,6 +18,112 @@ from .models import QubeState
|
|
|
18
18
|
|
|
19
19
|
_LOGGER = logging.getLogger(__name__)
|
|
20
20
|
|
|
21
|
+
# Entities read by get_all_data() (used by the official HA core integration).
|
|
22
|
+
# These mirror the register definitions in `const` exactly (address, scale,
|
|
23
|
+
# offset) so batching cannot change any value get_all_data() returns.
|
|
24
|
+
# Note: some of these intentionally differ from the corresponding entries in
|
|
25
|
+
# entities/sensors.py (e.g. compressor_speed has a x60 RPM scale here but not
|
|
26
|
+
# there; cop_calc is unrounded here but rounded to 1 decimal there) — reusing
|
|
27
|
+
# SENSORS would silently change get_all_data()'s output, so a dedicated table
|
|
28
|
+
# is kept instead.
|
|
29
|
+
# Format: (key, address, input_type, data_type, scale, offset)
|
|
30
|
+
_CORE_STATE_REGISTERS: tuple[
|
|
31
|
+
tuple[str, int, InputType, DataType, float | None, float | None], ...
|
|
32
|
+
] = (
|
|
33
|
+
("temp_supply", 20, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
34
|
+
("temp_return", 22, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
35
|
+
("temp_source_in", 24, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
36
|
+
("temp_source_out", 26, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
37
|
+
("temp_room", 28, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
38
|
+
("temp_dhw", 30, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
39
|
+
("temp_outside", 32, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
40
|
+
("power_thermic", 36, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
41
|
+
("power_electric", 61, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
42
|
+
(
|
|
43
|
+
"energy_total_electric",
|
|
44
|
+
69,
|
|
45
|
+
InputType.INPUT_REGISTER,
|
|
46
|
+
DataType.FLOAT32,
|
|
47
|
+
None,
|
|
48
|
+
None,
|
|
49
|
+
),
|
|
50
|
+
(
|
|
51
|
+
"energy_total_thermic",
|
|
52
|
+
71,
|
|
53
|
+
InputType.INPUT_REGISTER,
|
|
54
|
+
DataType.FLOAT32,
|
|
55
|
+
None,
|
|
56
|
+
None,
|
|
57
|
+
),
|
|
58
|
+
("cop_calc", 34, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
59
|
+
("status_code", 38, InputType.INPUT_REGISTER, DataType.UINT16, None, None),
|
|
60
|
+
("compressor_speed", 45, InputType.INPUT_REGISTER, DataType.FLOAT32, 60, None),
|
|
61
|
+
("flow_rate", 18, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
62
|
+
(
|
|
63
|
+
"setpoint_room_heat_day",
|
|
64
|
+
27,
|
|
65
|
+
InputType.HOLDING_REGISTER,
|
|
66
|
+
DataType.FLOAT32,
|
|
67
|
+
None,
|
|
68
|
+
None,
|
|
69
|
+
),
|
|
70
|
+
(
|
|
71
|
+
"setpoint_room_heat_night",
|
|
72
|
+
29,
|
|
73
|
+
InputType.HOLDING_REGISTER,
|
|
74
|
+
DataType.FLOAT32,
|
|
75
|
+
None,
|
|
76
|
+
None,
|
|
77
|
+
),
|
|
78
|
+
(
|
|
79
|
+
"setpoint_room_cool_day",
|
|
80
|
+
31,
|
|
81
|
+
InputType.HOLDING_REGISTER,
|
|
82
|
+
DataType.FLOAT32,
|
|
83
|
+
None,
|
|
84
|
+
None,
|
|
85
|
+
),
|
|
86
|
+
(
|
|
87
|
+
"setpoint_room_cool_night",
|
|
88
|
+
33,
|
|
89
|
+
InputType.HOLDING_REGISTER,
|
|
90
|
+
DataType.FLOAT32,
|
|
91
|
+
None,
|
|
92
|
+
None,
|
|
93
|
+
),
|
|
94
|
+
("setpoint_dhw", 173, InputType.HOLDING_REGISTER, DataType.FLOAT32, None, None),
|
|
95
|
+
(
|
|
96
|
+
"usr_pid_heatsetp",
|
|
97
|
+
101,
|
|
98
|
+
InputType.HOLDING_REGISTER,
|
|
99
|
+
DataType.FLOAT32,
|
|
100
|
+
None,
|
|
101
|
+
None,
|
|
102
|
+
),
|
|
103
|
+
(
|
|
104
|
+
"usr_pid_coolsetp",
|
|
105
|
+
103,
|
|
106
|
+
InputType.HOLDING_REGISTER,
|
|
107
|
+
DataType.FLOAT32,
|
|
108
|
+
None,
|
|
109
|
+
None,
|
|
110
|
+
),
|
|
111
|
+
("modbus_roomtemp", 75, InputType.INPUT_REGISTER, DataType.FLOAT32, None, None),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
_CORE_STATE_ENTITIES: tuple[EntityDef, ...] = tuple(
|
|
115
|
+
EntityDef(
|
|
116
|
+
key=key,
|
|
117
|
+
name=key,
|
|
118
|
+
address=address,
|
|
119
|
+
input_type=input_type,
|
|
120
|
+
data_type=data_type,
|
|
121
|
+
scale=scale,
|
|
122
|
+
offset=offset,
|
|
123
|
+
)
|
|
124
|
+
for key, address, input_type, data_type, scale, offset in _CORE_STATE_REGISTERS
|
|
125
|
+
)
|
|
126
|
+
|
|
21
127
|
|
|
22
128
|
class QubeClient:
|
|
23
129
|
"""Qube Modbus Client."""
|
|
@@ -109,53 +215,27 @@ class QubeClient:
|
|
|
109
215
|
|
|
110
216
|
state = QubeState()
|
|
111
217
|
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
#
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# Fetch power and energy sensors
|
|
126
|
-
state.power_thermic = await _read(const.POWER_THERMIC)
|
|
127
|
-
state.power_electric = await _read(const.POWER_ELECTRIC_CALC)
|
|
128
|
-
state.energy_total_electric = await _read(const.ENERGY_ELECTRIC_TOTAL)
|
|
129
|
-
state.energy_total_thermic = await _read(const.ENERGY_THERMIC_TOTAL)
|
|
130
|
-
state.cop_calc = await _read(const.COP_CALC)
|
|
131
|
-
|
|
132
|
-
# Fetch operation sensors
|
|
133
|
-
state.status_code = await _read(const.STATUS_CODE)
|
|
134
|
-
state.compressor_speed = await _read(const.COMPRESSOR_SPEED)
|
|
135
|
-
flow_rate = await _read(const.FLOW_RATE)
|
|
218
|
+
# Batch-read the core sensor fields plus all binary sensors together.
|
|
219
|
+
# _plan_blocks groups by input type (input/holding/discrete_input/coil)
|
|
220
|
+
# regardless of which list an entity came from, so combining both
|
|
221
|
+
# tables into a single read_entities_batched() call still yields one
|
|
222
|
+
# handful of block transactions instead of ~59 per-field reads.
|
|
223
|
+
entities = [*_CORE_STATE_ENTITIES, *BINARY_SENSORS.values()]
|
|
224
|
+
results = await self.read_entities_batched(entities)
|
|
225
|
+
|
|
226
|
+
for ent in _CORE_STATE_ENTITIES:
|
|
227
|
+
setattr(state, ent.key, results.get(ent.key))
|
|
228
|
+
|
|
229
|
+
flow_rate = state.flow_rate
|
|
136
230
|
if flow_rate is not None and flow_rate < 0:
|
|
137
231
|
flow_rate = 0.0
|
|
138
232
|
state.flow_rate = flow_rate
|
|
139
233
|
|
|
140
|
-
# Fetch setpoints (holding registers)
|
|
141
|
-
state.setpoint_room_heat_day = await _read(const.SETPOINT_HEAT_DAY)
|
|
142
|
-
state.setpoint_room_heat_night = await _read(const.SETPOINT_HEAT_NIGHT)
|
|
143
|
-
state.setpoint_room_cool_day = await _read(const.SETPOINT_COOL_DAY)
|
|
144
|
-
state.setpoint_room_cool_night = await _read(const.SETPOINT_COOL_NIGHT)
|
|
145
|
-
state.setpoint_dhw = await _read(const.USER_DHW_SETPOINT)
|
|
146
|
-
state.usr_pid_heatsetp = await _read(const.USER_HEAT_SETPOINT)
|
|
147
|
-
state.usr_pid_coolsetp = await _read(const.USER_COOL_SETPOINT)
|
|
148
|
-
|
|
149
|
-
# LinQ thermostat room temperature (optional)
|
|
150
|
-
state.modbus_roomtemp = await _read(const.TEMP_ROOM_MODBUS)
|
|
151
|
-
|
|
152
234
|
self._apply_monotonic_clamping(state)
|
|
153
235
|
|
|
154
|
-
|
|
155
|
-
binary_data = await self.read_all_binary_sensors()
|
|
156
|
-
for key, value in binary_data.items():
|
|
236
|
+
for key in BINARY_SENSORS:
|
|
157
237
|
if hasattr(state, key):
|
|
158
|
-
setattr(state, key,
|
|
238
|
+
setattr(state, key, results.get(key))
|
|
159
239
|
|
|
160
240
|
# Compute unified status (status_code + anti-legionella override)
|
|
161
241
|
state.status = const.resolve_status(state.status_code, state.req_antileg_1)
|
|
@@ -427,6 +507,17 @@ class QubeClient:
|
|
|
427
507
|
)
|
|
428
508
|
if result.isError():
|
|
429
509
|
raise OSError(f"Modbus error response for block @{start}")
|
|
510
|
+
if input_type_str in ("coil", "discrete_input"):
|
|
511
|
+
if len(result.bits) < count:
|
|
512
|
+
raise OSError(
|
|
513
|
+
f"Short bit response for block {input_type_str}@{start} "
|
|
514
|
+
f"(got {len(result.bits)}, expected {count})"
|
|
515
|
+
)
|
|
516
|
+
elif len(result.registers) < count:
|
|
517
|
+
raise OSError(
|
|
518
|
+
f"Short register response for block {input_type_str}@{start} "
|
|
519
|
+
f"(got {len(result.registers)}, expected {count})"
|
|
520
|
+
)
|
|
430
521
|
except Exception as exc:
|
|
431
522
|
self._log_read_failure(
|
|
432
523
|
f"block {input_type_str}@{start} (count {count})", exc
|
|
@@ -331,12 +331,18 @@ async def test_get_all_data_auto_reconnects(mock_modbus_client):
|
|
|
331
331
|
client._client = mock_instance
|
|
332
332
|
client._connected = False
|
|
333
333
|
|
|
334
|
-
# Mock successful register reads
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
334
|
+
# Mock successful register reads. Batched block reads request a
|
|
335
|
+
# variable number of registers per block, so the mock must honor the
|
|
336
|
+
# requested `count` (a real Modbus server would too), unlike a fixed
|
|
337
|
+
# 2-register response.
|
|
338
|
+
def _resp(address, count=1, **kwargs):
|
|
339
|
+
resp = MagicMock()
|
|
340
|
+
resp.isError.return_value = False
|
|
341
|
+
resp.registers = [0] * count
|
|
342
|
+
return resp
|
|
343
|
+
|
|
344
|
+
mock_instance.read_input_registers = AsyncMock(side_effect=_resp)
|
|
345
|
+
mock_instance.read_holding_registers = AsyncMock(side_effect=_resp)
|
|
340
346
|
|
|
341
347
|
state = await client.get_all_data()
|
|
342
348
|
assert state is not None
|
|
@@ -424,11 +430,16 @@ async def test_get_all_data_applies_clamping(mock_modbus_client):
|
|
|
424
430
|
client._client = mock_instance
|
|
425
431
|
client._connected = True
|
|
426
432
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
433
|
+
# Batched block reads request a variable `count` of registers per
|
|
434
|
+
# block, so the mock must honor it (a real Modbus server would too).
|
|
435
|
+
def _resp(address, count=1, **kwargs):
|
|
436
|
+
resp = MagicMock()
|
|
437
|
+
resp.isError.return_value = False
|
|
438
|
+
resp.registers = [0] * count
|
|
439
|
+
return resp
|
|
440
|
+
|
|
441
|
+
mock_instance.read_input_registers = AsyncMock(side_effect=_resp)
|
|
442
|
+
mock_instance.read_holding_registers = AsyncMock(side_effect=_resp)
|
|
432
443
|
|
|
433
444
|
state = await client.get_all_data()
|
|
434
445
|
assert state is not None
|
|
@@ -143,6 +143,41 @@ async def test_batched_block_failure_falls_back_to_individual_reads(
|
|
|
143
143
|
assert round(results["temp_return"], 1) == 25.0
|
|
144
144
|
|
|
145
145
|
|
|
146
|
+
@pytest.mark.asyncio
|
|
147
|
+
async def test_batched_short_successful_response_falls_back_to_individual_reads(
|
|
148
|
+
mock_modbus_client,
|
|
149
|
+
):
|
|
150
|
+
"""A well-formed but too-short block response degrades gracefully.
|
|
151
|
+
|
|
152
|
+
A device can return isError()==False with fewer registers/bits than the
|
|
153
|
+
requested count (a malformed-but-"successful" response). Previously this
|
|
154
|
+
slipped past the isError() check and crashed with an uncaught IndexError
|
|
155
|
+
while decoding, instead of falling back to individual reads like a failed
|
|
156
|
+
block does. Since get_all_data() is now polled by HA core every 15s via
|
|
157
|
+
this same batching path, such a response must degrade the same way a
|
|
158
|
+
failed block does, not raise.
|
|
159
|
+
"""
|
|
160
|
+
client = QubeClient("1.2.3.4", 502)
|
|
161
|
+
mock_instance = mock_modbus_client.return_value
|
|
162
|
+
mock_instance.read_input_registers = AsyncMock(
|
|
163
|
+
side_effect=[
|
|
164
|
+
# Block read: isError() is False, but only 2 registers came back
|
|
165
|
+
# for a block that needs 4 (two float32 entities).
|
|
166
|
+
_register_response(_float32_regs(24.5)),
|
|
167
|
+
_register_response(_float32_regs(24.5)), # individual read
|
|
168
|
+
_register_response(_float32_regs(25.0)), # individual read
|
|
169
|
+
]
|
|
170
|
+
)
|
|
171
|
+
client._client = mock_instance
|
|
172
|
+
|
|
173
|
+
entities = [SENSORS["temp_supply"], SENSORS["temp_return"]]
|
|
174
|
+
results = await client.read_entities_batched(entities)
|
|
175
|
+
|
|
176
|
+
assert mock_instance.read_input_registers.call_count == 3
|
|
177
|
+
assert round(results["temp_supply"], 1) == 24.5
|
|
178
|
+
assert round(results["temp_return"], 1) == 25.0
|
|
179
|
+
|
|
180
|
+
|
|
146
181
|
@pytest.mark.asyncio
|
|
147
182
|
async def test_get_all_entities_uses_batched_reads(mock_modbus_client):
|
|
148
183
|
"""get_all_entities reads all entities in a handful of transactions."""
|
|
@@ -172,3 +207,43 @@ async def test_get_all_entities_uses_batched_reads(mock_modbus_client):
|
|
|
172
207
|
+ mock_instance.read_holding_registers.call_count
|
|
173
208
|
)
|
|
174
209
|
assert total_calls <= 15
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@pytest.mark.asyncio
|
|
213
|
+
async def test_get_all_data_uses_batched_reads(mock_modbus_client):
|
|
214
|
+
"""get_all_data (used by HA core, polled every 15s) reads via block reads.
|
|
215
|
+
|
|
216
|
+
Previously get_all_data() performed one Modbus transaction per field
|
|
217
|
+
(~59 transactions: 23 core sensors + 37 binary sensors). It should now
|
|
218
|
+
use the same batched block-read infrastructure as get_all_entities(),
|
|
219
|
+
landing in a handful of transactions instead.
|
|
220
|
+
"""
|
|
221
|
+
client = QubeClient("1.2.3.4", 502)
|
|
222
|
+
mock_instance = mock_modbus_client.return_value
|
|
223
|
+
|
|
224
|
+
def _bits(address, count=1, **kwargs):
|
|
225
|
+
return _bit_response([False] * count)
|
|
226
|
+
|
|
227
|
+
def _regs(address, count=1, **kwargs):
|
|
228
|
+
return _register_response([0] * count)
|
|
229
|
+
|
|
230
|
+
mock_instance.read_coils = AsyncMock(side_effect=_bits)
|
|
231
|
+
mock_instance.read_discrete_inputs = AsyncMock(side_effect=_bits)
|
|
232
|
+
mock_instance.read_input_registers = AsyncMock(side_effect=_regs)
|
|
233
|
+
mock_instance.read_holding_registers = AsyncMock(side_effect=_regs)
|
|
234
|
+
client._client = mock_instance
|
|
235
|
+
client._connected = True
|
|
236
|
+
|
|
237
|
+
state = await client.get_all_data()
|
|
238
|
+
|
|
239
|
+
assert state is not None
|
|
240
|
+
total_calls = (
|
|
241
|
+
mock_instance.read_coils.call_count
|
|
242
|
+
+ mock_instance.read_discrete_inputs.call_count
|
|
243
|
+
+ mock_instance.read_input_registers.call_count
|
|
244
|
+
+ mock_instance.read_holding_registers.call_count
|
|
245
|
+
)
|
|
246
|
+
# Old per-register/per-entity path made ~59 transactions (23 core
|
|
247
|
+
# fields + 37 binary sensors). Batched block reads should collapse
|
|
248
|
+
# this into a handful of transactions.
|
|
249
|
+
assert total_calls <= 15
|
{python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/.github/ISSUE_TEMPLATE/bug_report.yml
RENAMED
|
File without changes
|
{python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/.github/ISSUE_TEMPLATE/config.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/.github/workflows/python-publish.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/docs/modbus-lijst-qube-totaal.pdf
RENAMED
|
File without changes
|
|
File without changes
|
{python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/__init__.py
RENAMED
|
File without changes
|
{python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/const.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/models.py
RENAMED
|
File without changes
|
{python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/network.py
RENAMED
|
File without changes
|
{python_qube_heatpump-1.12.0 → python_qube_heatpump-1.13.0}/src/python_qube_heatpump/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|