husdata-h66-modbus 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jonny Bergdahl
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.4
2
+ Name: husdata-h66-modbus
3
+ Version: 0.1.0
4
+ Summary: Async Modbus TCP library for the Husdata H66 heat pump gateway
5
+ Author-email: Jonny Bergdahl <bergdahl@users.noreply.github.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jonnybergdahl/Python-husdata-h66-modbus
8
+ Project-URL: Repository, https://github.com/jonnybergdahl/Python-husdata-h66-modbus
9
+ Project-URL: Documentation, https://jonnybergdahl.github.io/husdata-h66-modbus/
10
+ Project-URL: Issues, https://github.com/jonnybergdahl/Python-husdata-h66-modbus/issues
11
+ Keywords: husdata,h66,modbus,heat pump,ivt,rego,home assistant
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: AsyncIO
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Home Automation
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.13
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: modbus-connection>=4.12
26
+ Provides-Extra: cli
27
+ Requires-Dist: modbus-connection[pymodbus]>=4.12; extra == "cli"
28
+ Dynamic: license-file
29
+
30
+ # husdata-h66-modbus
31
+
32
+ Async Python library for the Modbus TCP interface of the [Husdata H66](https://husdata.se/produkt/h66-wifi-eth-gateway-styr-din-varmepump/) heat pump gateway.
33
+
34
+ It is built on [modbus-connection](https://github.com/home-assistant-libs/modbus-connection), the Modbus layer Home Assistant uses from 2026.9. The library never opens a connection itself: you give it a `ModbusUnit`, either from `modbus_connection` or from Home Assistant's `async_get_unit`. It has no Home Assistant dependency.
35
+
36
+ Tested with an H66 on firmware 360801 connected to an IVT Greenline (Rego 600 controller).
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ pip install husdata-h66-modbus
42
+ ```
43
+
44
+ Add the `cli` extra to get the pymodbus backend and the `husdata-h66` command line tool:
45
+
46
+ ```bash
47
+ pip install "husdata-h66-modbus[cli]"
48
+ ```
49
+
50
+ ## Documentation
51
+
52
+ The full library documentation can be [found here](https://jonnybergdahl.github.io/husdata-h66-modbus/).
53
+
54
+ ## Enable Modbus on the H66
55
+
56
+ Modbus TCP is off by default. On the H66 web page, open **Config** > **Modbus**, set `MODBUS_PORT` to `502` and restart the H66. `MODBUS_IP` can limit which client may connect. The H66 accepts only one Modbus client at a time.
57
+
58
+ ## Usage
59
+
60
+ ```python
61
+ import asyncio
62
+
63
+ from modbus_connection import ModbusTcpParams
64
+ from modbus_connection.pymodbus import ModbusConnection
65
+
66
+ from husdata_h66 import HusdataH66
67
+
68
+
69
+ async def main() -> None:
70
+ connection = ModbusConnection(ModbusTcpParams(host="h66.local"))
71
+ try:
72
+ device = HusdataH66(connection.for_unit(1))
73
+ await device.async_update()
74
+ print(device.get("outdoor")) # 3.5 (°C)
75
+ print(device.get("compressor")) # True
76
+ print(device.values) # everything, keyed by register key
77
+
78
+ await device.async_set("room_setpoint", 21.5)
79
+ finally:
80
+ await connection.close()
81
+
82
+
83
+ asyncio.run(main())
84
+ ```
85
+
86
+ In Home Assistant, pass the unit from `async_get_unit` instead:
87
+
88
+ ```python
89
+ unit = async_get_unit(
90
+ hass, entry, ModbusTcpParams(host=entry.data[CONF_HOST]), entry.data[CONF_UNIT_ID]
91
+ )
92
+ entry.runtime_data = HusdataH66(unit)
93
+ ```
94
+
95
+ `async_update()` reads all registers in three requests and returns an `UpdateReport`. It raises `ModbusConnectionError` or `ModbusTimeoutError` when the gateway cannot be reached.
96
+
97
+ `async_set()` takes the value in the register's own unit and checks it against the register's range and step. It raises `ValueError` for an invalid value and `AttributeError` for a read-only register. `async_reset_alarm()` writes 1 to **Alarm reset**.
98
+
99
+ From the command line (read-only):
100
+
101
+ ```bash
102
+ husdata-h66 h66.local
103
+ ```
104
+
105
+ ## Register map
106
+
107
+ The H66 numbers its Modbus registers one per parameter, in the order its home page lists them. Read-only parameters are INPUT registers, settings are HOLDING registers, and every register is a signed or unsigned 16-bit value. The addresses depend on the heat pump controller and can move after a firmware update or when you add custom registers in the H66.
108
+
109
+ `REGO600_REGISTERS` is the default map, for a Rego 600 controller on firmware 360801. To follow the H66 instead, fetch its home page and parse it:
110
+
111
+ ```python
112
+ from husdata_h66 import HusdataH66, parse_firmware_version, parse_registers
113
+
114
+ page = await (await session.get("http://h66.local/")).text()
115
+ device = HusdataH66(unit, parse_registers(page))
116
+ ```
117
+
118
+ `husdata-h66 h66.local --from-web` does the same from the command line.
119
+
120
+ Each parameter has a Husdata variable ID. Its first hex digit gives the type and scaling:
121
+
122
+ | First digit | Type | Scale | Unit |
123
+ |---|---|---|---|
124
+ | 0 | Temperature | 0.1 | °C |
125
+ | 1 | Status | 1 | on/off |
126
+ | 2 | Number | 0.1 | |
127
+ | 3 | Percent | 0.1 | % |
128
+ | 5 | Energy | 0.1 | kWh |
129
+ | 6 | Hours | 1 | h |
130
+ | 7 | Minutes | 1 | min |
131
+ | B | Integer | 1 | |
132
+ | C | Power | 1 | W |
133
+
134
+ Temperatures and numbers are signed, the others unsigned. A temperature sensor that isn't connected reads `None` (the H66 shows −48.3 °C).
135
+
136
+ ### Rego 600 (default map)
137
+
138
+ | Address | Type | ID | Key | Range |
139
+ |---|---|---|---|---|
140
+ | 0 | Input | 0001 | `radiator_return` | |
141
+ | 1 | Input | 0002 | `radiator_forward` | |
142
+ | 2 | Input | 0003 | `heat_carrier_return` | |
143
+ | 3 | Input | 0004 | `heat_carrier_forward` | |
144
+ | 4 | Input | 0005 | `brine_in` | |
145
+ | 5 | Input | 0006 | `brine_out` | |
146
+ | 6 | Input | 0007 | `outdoor` | |
147
+ | 7 | Input | 0008 | `indoor` | |
148
+ | 8 | Input | 0009 | `hot_water_top` | |
149
+ | 9 | Input | 000A | `hot_water_mid` | |
150
+ | 10 | Input | 000B | `hot_gas` | |
151
+ | 11 | Input | 3104 | `add_heat_status` | |
152
+ | 12 | Input | 0107 | `heating_setpoint` | |
153
+ | 13 | Input | 0111 | `hot_water_setpoint` | |
154
+ | 14 | Holding | 0203 | `room_setpoint` | 5–29 °C |
155
+ | 15 | Holding | 2204 | `room_sensor_influence` | |
156
+ | 16 | Holding | 2205 | `heat_curve` | 0–10 |
157
+ | 17 | Holding | 0207 | `heat_curve_parallel` | −10–10 °C |
158
+ | 18 | Holding | 0208 | `hot_water_stop_temperature` | 20–60 °C |
159
+ | 19 | Holding | 020B | `hot_water_difference` | 2–15 °C |
160
+ | 20 | Holding | 7209 | `extra_hot_water` | 0–1440 min |
161
+ | 21 | Holding | 1215 | `electric_heater_allowed` | on/off |
162
+ | 22 | Holding | B21B | `electric_heater_max_power` | 1–4 |
163
+ | 23 | Holding | 1233 | `external_control` | on/off |
164
+ | 24 | Holding | 020A | `summer_mode` | 0–30 °C |
165
+ | 25 | Holding | 2210 | `holiday_mode` | 0–30 |
166
+ | 26 | Holding | 12F2 | `alarm_reset` | write 1 |
167
+ | 27 | Input | 1A01 | `compressor` | |
168
+ | 28 | Input | 1A02 | `add_heat_step_1` | |
169
+ | 29 | Input | 1A03 | `add_heat_step_2` | |
170
+ | 30 | Input | 1A04 | `pump_cold_circuit` | |
171
+ | 31 | Input | 1A05 | `pump_heat_circuit` | |
172
+ | 32 | Input | 1A06 | `pump_radiator` | |
173
+ | 33 | Input | 1A07 | `switch_valve_1` | |
174
+ | 34 | Input | 1A20 | `alarm` | |
175
+ | 35 | Input | BA91 | `alarm_code` | |
176
+ | 36 | Input | 6C55 | `compressor_hours_heating` | |
177
+ | 37 | Input | 6C56 | `compressor_hours_hot_water` | |
178
+ | 38 | Input | 6C58 | `aux_heater_hours_heating` | |
179
+ | 39 | Input | 6C59 | `aux_heater_hours_hot_water` | |
180
+ | 40 | Input | CFAA | `power_consumption` | |
181
+ | 41 | Input | 5FAB | `accumulated_consumption` | |
182
+
183
+ `power_consumption` and `accumulated_consumption` are calculated by the H66 from the wattages set for each status variable on its web page. `accumulated_consumption` restarts at 0 when the H66 boots.
184
+
185
+ Not yet verified: `room_sensor_influence` is read unscaled because the H66 web page shows it that way, and the unit and scale of `holiday_mode` are unknown.
186
+
187
+ ## Development
188
+
189
+ ```bash
190
+ uv sync
191
+ uv run pytest
192
+ uv run ruff check src tests
193
+ ```
194
+
195
+ `build.sh` publishes a release: it converts this README to `README.rst`, builds the documentation, copies it to the `jonnybergdahl.github.io` repository and pushes it, then builds the package and uploads it to PyPI.
196
+
197
+ ## License
198
+
199
+ MIT
@@ -0,0 +1,170 @@
1
+ # husdata-h66-modbus
2
+
3
+ Async Python library for the Modbus TCP interface of the [Husdata H66](https://husdata.se/produkt/h66-wifi-eth-gateway-styr-din-varmepump/) heat pump gateway.
4
+
5
+ It is built on [modbus-connection](https://github.com/home-assistant-libs/modbus-connection), the Modbus layer Home Assistant uses from 2026.9. The library never opens a connection itself: you give it a `ModbusUnit`, either from `modbus_connection` or from Home Assistant's `async_get_unit`. It has no Home Assistant dependency.
6
+
7
+ Tested with an H66 on firmware 360801 connected to an IVT Greenline (Rego 600 controller).
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install husdata-h66-modbus
13
+ ```
14
+
15
+ Add the `cli` extra to get the pymodbus backend and the `husdata-h66` command line tool:
16
+
17
+ ```bash
18
+ pip install "husdata-h66-modbus[cli]"
19
+ ```
20
+
21
+ ## Documentation
22
+
23
+ The full library documentation can be [found here](https://jonnybergdahl.github.io/husdata-h66-modbus/).
24
+
25
+ ## Enable Modbus on the H66
26
+
27
+ Modbus TCP is off by default. On the H66 web page, open **Config** > **Modbus**, set `MODBUS_PORT` to `502` and restart the H66. `MODBUS_IP` can limit which client may connect. The H66 accepts only one Modbus client at a time.
28
+
29
+ ## Usage
30
+
31
+ ```python
32
+ import asyncio
33
+
34
+ from modbus_connection import ModbusTcpParams
35
+ from modbus_connection.pymodbus import ModbusConnection
36
+
37
+ from husdata_h66 import HusdataH66
38
+
39
+
40
+ async def main() -> None:
41
+ connection = ModbusConnection(ModbusTcpParams(host="h66.local"))
42
+ try:
43
+ device = HusdataH66(connection.for_unit(1))
44
+ await device.async_update()
45
+ print(device.get("outdoor")) # 3.5 (°C)
46
+ print(device.get("compressor")) # True
47
+ print(device.values) # everything, keyed by register key
48
+
49
+ await device.async_set("room_setpoint", 21.5)
50
+ finally:
51
+ await connection.close()
52
+
53
+
54
+ asyncio.run(main())
55
+ ```
56
+
57
+ In Home Assistant, pass the unit from `async_get_unit` instead:
58
+
59
+ ```python
60
+ unit = async_get_unit(
61
+ hass, entry, ModbusTcpParams(host=entry.data[CONF_HOST]), entry.data[CONF_UNIT_ID]
62
+ )
63
+ entry.runtime_data = HusdataH66(unit)
64
+ ```
65
+
66
+ `async_update()` reads all registers in three requests and returns an `UpdateReport`. It raises `ModbusConnectionError` or `ModbusTimeoutError` when the gateway cannot be reached.
67
+
68
+ `async_set()` takes the value in the register's own unit and checks it against the register's range and step. It raises `ValueError` for an invalid value and `AttributeError` for a read-only register. `async_reset_alarm()` writes 1 to **Alarm reset**.
69
+
70
+ From the command line (read-only):
71
+
72
+ ```bash
73
+ husdata-h66 h66.local
74
+ ```
75
+
76
+ ## Register map
77
+
78
+ The H66 numbers its Modbus registers one per parameter, in the order its home page lists them. Read-only parameters are INPUT registers, settings are HOLDING registers, and every register is a signed or unsigned 16-bit value. The addresses depend on the heat pump controller and can move after a firmware update or when you add custom registers in the H66.
79
+
80
+ `REGO600_REGISTERS` is the default map, for a Rego 600 controller on firmware 360801. To follow the H66 instead, fetch its home page and parse it:
81
+
82
+ ```python
83
+ from husdata_h66 import HusdataH66, parse_firmware_version, parse_registers
84
+
85
+ page = await (await session.get("http://h66.local/")).text()
86
+ device = HusdataH66(unit, parse_registers(page))
87
+ ```
88
+
89
+ `husdata-h66 h66.local --from-web` does the same from the command line.
90
+
91
+ Each parameter has a Husdata variable ID. Its first hex digit gives the type and scaling:
92
+
93
+ | First digit | Type | Scale | Unit |
94
+ |---|---|---|---|
95
+ | 0 | Temperature | 0.1 | °C |
96
+ | 1 | Status | 1 | on/off |
97
+ | 2 | Number | 0.1 | |
98
+ | 3 | Percent | 0.1 | % |
99
+ | 5 | Energy | 0.1 | kWh |
100
+ | 6 | Hours | 1 | h |
101
+ | 7 | Minutes | 1 | min |
102
+ | B | Integer | 1 | |
103
+ | C | Power | 1 | W |
104
+
105
+ Temperatures and numbers are signed, the others unsigned. A temperature sensor that isn't connected reads `None` (the H66 shows −48.3 °C).
106
+
107
+ ### Rego 600 (default map)
108
+
109
+ | Address | Type | ID | Key | Range |
110
+ |---|---|---|---|---|
111
+ | 0 | Input | 0001 | `radiator_return` | |
112
+ | 1 | Input | 0002 | `radiator_forward` | |
113
+ | 2 | Input | 0003 | `heat_carrier_return` | |
114
+ | 3 | Input | 0004 | `heat_carrier_forward` | |
115
+ | 4 | Input | 0005 | `brine_in` | |
116
+ | 5 | Input | 0006 | `brine_out` | |
117
+ | 6 | Input | 0007 | `outdoor` | |
118
+ | 7 | Input | 0008 | `indoor` | |
119
+ | 8 | Input | 0009 | `hot_water_top` | |
120
+ | 9 | Input | 000A | `hot_water_mid` | |
121
+ | 10 | Input | 000B | `hot_gas` | |
122
+ | 11 | Input | 3104 | `add_heat_status` | |
123
+ | 12 | Input | 0107 | `heating_setpoint` | |
124
+ | 13 | Input | 0111 | `hot_water_setpoint` | |
125
+ | 14 | Holding | 0203 | `room_setpoint` | 5–29 °C |
126
+ | 15 | Holding | 2204 | `room_sensor_influence` | |
127
+ | 16 | Holding | 2205 | `heat_curve` | 0–10 |
128
+ | 17 | Holding | 0207 | `heat_curve_parallel` | −10–10 °C |
129
+ | 18 | Holding | 0208 | `hot_water_stop_temperature` | 20–60 °C |
130
+ | 19 | Holding | 020B | `hot_water_difference` | 2–15 °C |
131
+ | 20 | Holding | 7209 | `extra_hot_water` | 0–1440 min |
132
+ | 21 | Holding | 1215 | `electric_heater_allowed` | on/off |
133
+ | 22 | Holding | B21B | `electric_heater_max_power` | 1–4 |
134
+ | 23 | Holding | 1233 | `external_control` | on/off |
135
+ | 24 | Holding | 020A | `summer_mode` | 0–30 °C |
136
+ | 25 | Holding | 2210 | `holiday_mode` | 0–30 |
137
+ | 26 | Holding | 12F2 | `alarm_reset` | write 1 |
138
+ | 27 | Input | 1A01 | `compressor` | |
139
+ | 28 | Input | 1A02 | `add_heat_step_1` | |
140
+ | 29 | Input | 1A03 | `add_heat_step_2` | |
141
+ | 30 | Input | 1A04 | `pump_cold_circuit` | |
142
+ | 31 | Input | 1A05 | `pump_heat_circuit` | |
143
+ | 32 | Input | 1A06 | `pump_radiator` | |
144
+ | 33 | Input | 1A07 | `switch_valve_1` | |
145
+ | 34 | Input | 1A20 | `alarm` | |
146
+ | 35 | Input | BA91 | `alarm_code` | |
147
+ | 36 | Input | 6C55 | `compressor_hours_heating` | |
148
+ | 37 | Input | 6C56 | `compressor_hours_hot_water` | |
149
+ | 38 | Input | 6C58 | `aux_heater_hours_heating` | |
150
+ | 39 | Input | 6C59 | `aux_heater_hours_hot_water` | |
151
+ | 40 | Input | CFAA | `power_consumption` | |
152
+ | 41 | Input | 5FAB | `accumulated_consumption` | |
153
+
154
+ `power_consumption` and `accumulated_consumption` are calculated by the H66 from the wattages set for each status variable on its web page. `accumulated_consumption` restarts at 0 when the H66 boots.
155
+
156
+ Not yet verified: `room_sensor_influence` is read unscaled because the H66 web page shows it that way, and the unit and scale of `holiday_mode` are unknown.
157
+
158
+ ## Development
159
+
160
+ ```bash
161
+ uv sync
162
+ uv run pytest
163
+ uv run ruff check src tests
164
+ ```
165
+
166
+ `build.sh` publishes a release: it converts this README to `README.rst`, builds the documentation, copies it to the `jonnybergdahl.github.io` repository and pushes it, then builds the package and uploads it to PyPI.
167
+
168
+ ## License
169
+
170
+ MIT
@@ -0,0 +1,217 @@
1
+ husdata-h66-modbus
2
+ ==================
3
+
4
+ Async Python library for the Modbus TCP interface of the `Husdata
5
+ H66 <https://husdata.se/produkt/h66-wifi-eth-gateway-styr-din-varmepump/>`__
6
+ heat pump gateway.
7
+
8
+ It is built on
9
+ `modbus-connection <https://github.com/home-assistant-libs/modbus-connection>`__,
10
+ the Modbus layer Home Assistant uses from 2026.9. The library never
11
+ opens a connection itself: you give it a ``ModbusUnit``, either from
12
+ ``modbus_connection`` or from Home Assistant’s ``async_get_unit``. It
13
+ has no Home Assistant dependency.
14
+
15
+ Tested with an H66 on firmware 360801 connected to an IVT Greenline
16
+ (Rego 600 controller).
17
+
18
+ Installation
19
+ ------------
20
+
21
+ .. code:: bash
22
+
23
+ pip install husdata-h66-modbus
24
+
25
+ Add the ``cli`` extra to get the pymodbus backend and the
26
+ ``husdata-h66`` command line tool:
27
+
28
+ .. code:: bash
29
+
30
+ pip install "husdata-h66-modbus[cli]"
31
+
32
+ Documentation
33
+ -------------
34
+
35
+ The full library documentation can be `found
36
+ here <https://jonnybergdahl.github.io/husdata-h66-modbus/>`__.
37
+
38
+ Enable Modbus on the H66
39
+ ------------------------
40
+
41
+ Modbus TCP is off by default. On the H66 web page, open **Config** >
42
+ **Modbus**, set ``MODBUS_PORT`` to ``502`` and restart the H66.
43
+ ``MODBUS_IP`` can limit which client may connect. The H66 accepts only
44
+ one Modbus client at a time.
45
+
46
+ Usage
47
+ -----
48
+
49
+ .. code:: python
50
+
51
+ import asyncio
52
+
53
+ from modbus_connection import ModbusTcpParams
54
+ from modbus_connection.pymodbus import ModbusConnection
55
+
56
+ from husdata_h66 import HusdataH66
57
+
58
+
59
+ async def main() -> None:
60
+ connection = ModbusConnection(ModbusTcpParams(host="h66.local"))
61
+ try:
62
+ device = HusdataH66(connection.for_unit(1))
63
+ await device.async_update()
64
+ print(device.get("outdoor")) # 3.5 (°C)
65
+ print(device.get("compressor")) # True
66
+ print(device.values) # everything, keyed by register key
67
+
68
+ await device.async_set("room_setpoint", 21.5)
69
+ finally:
70
+ await connection.close()
71
+
72
+
73
+ asyncio.run(main())
74
+
75
+ In Home Assistant, pass the unit from ``async_get_unit`` instead:
76
+
77
+ .. code:: python
78
+
79
+ unit = async_get_unit(
80
+ hass, entry, ModbusTcpParams(host=entry.data[CONF_HOST]), entry.data[CONF_UNIT_ID]
81
+ )
82
+ entry.runtime_data = HusdataH66(unit)
83
+
84
+ ``async_update()`` reads all registers in three requests and returns an
85
+ ``UpdateReport``. It raises ``ModbusConnectionError`` or
86
+ ``ModbusTimeoutError`` when the gateway cannot be reached.
87
+
88
+ ``async_set()`` takes the value in the register’s own unit and checks it
89
+ against the register’s range and step. It raises ``ValueError`` for an
90
+ invalid value and ``AttributeError`` for a read-only register.
91
+ ``async_reset_alarm()`` writes 1 to **Alarm reset**.
92
+
93
+ From the command line (read-only):
94
+
95
+ .. code:: bash
96
+
97
+ husdata-h66 h66.local
98
+
99
+ Register map
100
+ ------------
101
+
102
+ The H66 numbers its Modbus registers one per parameter, in the order its
103
+ home page lists them. Read-only parameters are INPUT registers, settings
104
+ are HOLDING registers, and every register is a signed or unsigned 16-bit
105
+ value. The addresses depend on the heat pump controller and can move
106
+ after a firmware update or when you add custom registers in the H66.
107
+
108
+ ``REGO600_REGISTERS`` is the default map, for a Rego 600 controller on
109
+ firmware 360801. To follow the H66 instead, fetch its home page and
110
+ parse it:
111
+
112
+ .. code:: python
113
+
114
+ from husdata_h66 import HusdataH66, parse_firmware_version, parse_registers
115
+
116
+ page = await (await session.get("http://h66.local/")).text()
117
+ device = HusdataH66(unit, parse_registers(page))
118
+
119
+ ``husdata-h66 h66.local --from-web`` does the same from the command
120
+ line.
121
+
122
+ Each parameter has a Husdata variable ID. Its first hex digit gives the
123
+ type and scaling:
124
+
125
+ =========== =========== ===== ======
126
+ First digit Type Scale Unit
127
+ =========== =========== ===== ======
128
+ 0 Temperature 0.1 °C
129
+ 1 Status 1 on/off
130
+ 2 Number 0.1
131
+ 3 Percent 0.1 %
132
+ 5 Energy 0.1 kWh
133
+ 6 Hours 1 h
134
+ 7 Minutes 1 min
135
+ B Integer 1
136
+ C Power 1 W
137
+ =========== =========== ===== ======
138
+
139
+ Temperatures and numbers are signed, the others unsigned. A temperature
140
+ sensor that isn’t connected reads ``None`` (the H66 shows −48.3 °C).
141
+
142
+ Rego 600 (default map)
143
+ ~~~~~~~~~~~~~~~~~~~~~~
144
+
145
+ ======= ======= ==== ============================== ==========
146
+ Address Type ID Key Range
147
+ ======= ======= ==== ============================== ==========
148
+ 0 Input 0001 ``radiator_return``
149
+ 1 Input 0002 ``radiator_forward``
150
+ 2 Input 0003 ``heat_carrier_return``
151
+ 3 Input 0004 ``heat_carrier_forward``
152
+ 4 Input 0005 ``brine_in``
153
+ 5 Input 0006 ``brine_out``
154
+ 6 Input 0007 ``outdoor``
155
+ 7 Input 0008 ``indoor``
156
+ 8 Input 0009 ``hot_water_top``
157
+ 9 Input 000A ``hot_water_mid``
158
+ 10 Input 000B ``hot_gas``
159
+ 11 Input 3104 ``add_heat_status``
160
+ 12 Input 0107 ``heating_setpoint``
161
+ 13 Input 0111 ``hot_water_setpoint``
162
+ 14 Holding 0203 ``room_setpoint`` 5–29 °C
163
+ 15 Holding 2204 ``room_sensor_influence``
164
+ 16 Holding 2205 ``heat_curve`` 0–10
165
+ 17 Holding 0207 ``heat_curve_parallel`` −10–10 °C
166
+ 18 Holding 0208 ``hot_water_stop_temperature`` 20–60 °C
167
+ 19 Holding 020B ``hot_water_difference`` 2–15 °C
168
+ 20 Holding 7209 ``extra_hot_water`` 0–1440 min
169
+ 21 Holding 1215 ``electric_heater_allowed`` on/off
170
+ 22 Holding B21B ``electric_heater_max_power`` 1–4
171
+ 23 Holding 1233 ``external_control`` on/off
172
+ 24 Holding 020A ``summer_mode`` 0–30 °C
173
+ 25 Holding 2210 ``holiday_mode`` 0–30
174
+ 26 Holding 12F2 ``alarm_reset`` write 1
175
+ 27 Input 1A01 ``compressor``
176
+ 28 Input 1A02 ``add_heat_step_1``
177
+ 29 Input 1A03 ``add_heat_step_2``
178
+ 30 Input 1A04 ``pump_cold_circuit``
179
+ 31 Input 1A05 ``pump_heat_circuit``
180
+ 32 Input 1A06 ``pump_radiator``
181
+ 33 Input 1A07 ``switch_valve_1``
182
+ 34 Input 1A20 ``alarm``
183
+ 35 Input BA91 ``alarm_code``
184
+ 36 Input 6C55 ``compressor_hours_heating``
185
+ 37 Input 6C56 ``compressor_hours_hot_water``
186
+ 38 Input 6C58 ``aux_heater_hours_heating``
187
+ 39 Input 6C59 ``aux_heater_hours_hot_water``
188
+ 40 Input CFAA ``power_consumption``
189
+ 41 Input 5FAB ``accumulated_consumption``
190
+ ======= ======= ==== ============================== ==========
191
+
192
+ ``power_consumption`` and ``accumulated_consumption`` are calculated by
193
+ the H66 from the wattages set for each status variable on its web page.
194
+ ``accumulated_consumption`` restarts at 0 when the H66 boots.
195
+
196
+ Not yet verified: ``room_sensor_influence`` is read unscaled because the
197
+ H66 web page shows it that way, and the unit and scale of
198
+ ``holiday_mode`` are unknown.
199
+
200
+ Development
201
+ -----------
202
+
203
+ .. code:: bash
204
+
205
+ uv sync
206
+ uv run pytest
207
+ uv run ruff check src tests
208
+
209
+ ``build.sh`` publishes a release: it converts this README to
210
+ ``README.rst``, builds the documentation, copies it to the
211
+ ``jonnybergdahl.github.io`` repository and pushes it, then builds the
212
+ package and uploads it to PyPI.
213
+
214
+ License
215
+ -------
216
+
217
+ MIT
@@ -0,0 +1,78 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "husdata-h66-modbus"
7
+ version = "0.1.0"
8
+ description = "Async Modbus TCP library for the Husdata H66 heat pump gateway"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.13"
13
+ authors = [
14
+ {name = "Jonny Bergdahl", email = "bergdahl@users.noreply.github.com"},
15
+ ]
16
+ keywords = ["husdata", "h66", "modbus", "heat pump", "ivt", "rego", "home assistant"]
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Framework :: AsyncIO",
20
+ "Intended Audience :: Developers",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
+ "Topic :: Home Automation",
26
+ "Topic :: Software Development :: Libraries :: Python Modules",
27
+ "Typing :: Typed",
28
+ ]
29
+ dependencies = [
30
+ "modbus-connection>=4.12",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ cli = ["modbus-connection[pymodbus]>=4.12"]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/jonnybergdahl/Python-husdata-h66-modbus"
38
+ Repository = "https://github.com/jonnybergdahl/Python-husdata-h66-modbus"
39
+ Documentation = "https://jonnybergdahl.github.io/husdata-h66-modbus/"
40
+ Issues = "https://github.com/jonnybergdahl/Python-husdata-h66-modbus/issues"
41
+
42
+ [project.scripts]
43
+ husdata-h66 = "husdata_h66.__main__:main"
44
+
45
+ [dependency-groups]
46
+ dev = [
47
+ "modbus-connection[pymodbus]>=4.12",
48
+ "pytest>=8",
49
+ "pytest-asyncio>=0.24",
50
+ "ruff>=0.8",
51
+ ]
52
+ docs = [
53
+ "sphinx>=8",
54
+ "sphinx-autodoc-typehints>=2",
55
+ "sphinx-rtd-theme>=3",
56
+ ]
57
+ release = [
58
+ "build>=1.2",
59
+ "twine>=6",
60
+ ]
61
+
62
+ [tool.setuptools.packages.find]
63
+ where = ["src"]
64
+
65
+ [tool.setuptools.package-data]
66
+ husdata_h66 = ["py.typed"]
67
+
68
+ [tool.pytest.ini_options]
69
+ asyncio_mode = "auto"
70
+ asyncio_default_fixture_loop_scope = "function"
71
+ testpaths = ["tests"]
72
+
73
+ [tool.ruff]
74
+ line-length = 88
75
+ target-version = "py313"
76
+
77
+ [tool.ruff.lint]
78
+ select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+