scietex.hal.vfd 2.0.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 (43) hide show
  1. scietex_hal_vfd-2.0.0/LICENSE +21 -0
  2. scietex_hal_vfd-2.0.0/PKG-INFO +222 -0
  3. scietex_hal_vfd-2.0.0/README.md +174 -0
  4. scietex_hal_vfd-2.0.0/pyproject.toml +57 -0
  5. scietex_hal_vfd-2.0.0/setup.cfg +4 -0
  6. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/__init__.py +3 -0
  7. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/base/__init__.py +47 -0
  8. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/base/capabilities.py +49 -0
  9. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/base/data.py +122 -0
  10. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/base/protocol.py +129 -0
  11. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/base/rs485.py +153 -0
  12. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/__init__.py +5 -0
  13. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/spe_b/__init__.py +26 -0
  14. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/spe_b/client.py +384 -0
  15. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/spe_b/errors.py +67 -0
  16. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/spe_b/registers.py +238 -0
  17. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/intek/spe_b/rs485.py +166 -0
  18. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/modbus_drivers.py +60 -0
  19. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/py.typed +0 -0
  20. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/version.py +8 -0
  21. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/__init__.py +11 -0
  22. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e4_8400/__init__.py +35 -0
  23. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e4_8400/client.py +402 -0
  24. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e4_8400/errors.py +99 -0
  25. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e4_8400/registers.py +316 -0
  26. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e4_8400/rs485.py +289 -0
  27. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e5_8200/__init__.py +28 -0
  28. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e5_8200/client.py +338 -0
  29. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e5_8200/errors.py +77 -0
  30. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e5_8200/registers.py +235 -0
  31. scietex_hal_vfd-2.0.0/src/scietex/hal/vfd/vesper/e5_8200/rs485.py +216 -0
  32. scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/PKG-INFO +222 -0
  33. scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/SOURCES.txt +41 -0
  34. scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/dependency_links.txt +1 -0
  35. scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/entry_points.txt +9 -0
  36. scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/requires.txt +15 -0
  37. scietex_hal_vfd-2.0.0/src/scietex.hal.vfd.egg-info/top_level.txt +1 -0
  38. scietex_hal_vfd-2.0.0/tests/test_modbus_client.py +283 -0
  39. scietex_hal_vfd-2.0.0/tests/test_modbus_drivers.py +69 -0
  40. scietex_hal_vfd-2.0.0/tests/test_registers.py +132 -0
  41. scietex_hal_vfd-2.0.0/tests/test_version.py +47 -0
  42. scietex_hal_vfd-2.0.0/tests/test_vesper_e4_8400_registers.py +149 -0
  43. scietex_hal_vfd-2.0.0/tests/test_vesper_e5_8200_registers.py +107 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Anton Bondarenko
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,222 @@
1
+ Metadata-Version: 2.4
2
+ Name: scietex.hal.vfd
3
+ Version: 2.0.0
4
+ Summary: Scietex VFD HAL package
5
+ Author-email: Anton Bondarenko <bond.anton@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Anton Bondarenko
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://github.com/bond-anton/scietex.hal.vfd
28
+ Project-URL: Bug Tracker, https://github.com/bond-anton/scietex.hal.vfd/issues
29
+ Classifier: Programming Language :: Python :: 3
30
+ Classifier: License :: OSI Approved :: MIT License
31
+ Classifier: Operating System :: OS Independent
32
+ Requires-Python: >=3.10
33
+ Description-Content-Type: text/markdown
34
+ License-File: LICENSE
35
+ Requires-Dist: msgspec[yaml]~=0.20.0
36
+ Requires-Dist: scietex.hal.serial<3.0.0,>=2.4.0
37
+ Requires-Dist: pymodbus~=3.15
38
+ Provides-Extra: dev
39
+ Requires-Dist: tox>=4.46.0; extra == "dev"
40
+ Provides-Extra: test
41
+ Requires-Dist: pytest; extra == "test"
42
+ Requires-Dist: pytest-asyncio; extra == "test"
43
+ Requires-Dist: pytest-xdist; extra == "test"
44
+ Provides-Extra: lint
45
+ Requires-Dist: ruff; extra == "lint"
46
+ Requires-Dist: ty; extra == "lint"
47
+ Dynamic: license-file
48
+
49
+ # scietex.hal.vfd
50
+
51
+ The `scietex.hal.vfd` package is a Python library for interfacing with variable
52
+ frequency drives (VFD). It defines a transport-agnostic driver contract
53
+ (`VFDProtocol`) plus the data structures and capability model shared by every
54
+ driver, and ships concrete implementations for three drive families.
55
+
56
+ A driver reaches a drive over one of two transports:
57
+
58
+ - **Direct RS485** — the driver owns the serial port and speaks Modbus RTU to
59
+ the drive itself (`scietex.hal.vfd.base.rs485.VFD`).
60
+ - **Scietex Modbus gateway** — the gateway owns the RS485 bus, and the driver
61
+ speaks Modbus/TCP to a gateway device entry that routes to the drive by unit
62
+ id (`scietex.hal.vfd.intek.spe_b.client.IntekSPEBModbusVFD`).
63
+
64
+ ## Features
65
+
66
+ - `VFDProtocol`, a runtime-checkable contract: connect, disconnect, read
67
+ parameters, read state, read and reset faults, start, stop and set the
68
+ frequency setpoint.
69
+ - `VFDCapabilities`, which advertises optional control surface (direction, jog,
70
+ freewheel stop, fault reset, frequency setpoint) so a caller can reject an
71
+ unsupported command before dispatching it.
72
+ - `VFDParameters`, which reports frequency in both Hz and percent of the
73
+ configured maximum, plus output current, voltage and power, and the optional
74
+ DC bus voltage and temperatures a drive exposes. An optional field is `None`
75
+ when the drive does not report it, which is distinct from a reported `0.0`.
76
+ - Implemented drive models, each with both transports:
77
+ - `Intek SPE-B` — direct RS485 (`SPEvB`) and gateway (`IntekSPEBModbusVFD`).
78
+ - `Vesper E5-8200` — direct RS485 (`VesperE58200`) and gateway
79
+ (`VesperE58200ModbusVFD`).
80
+ - `Vesper E4-8400` / `E4-P8402` — direct RS485 (`VesperE48400`) and gateway
81
+ (`VesperE48400ModbusVFD`).
82
+ - Driver descriptors for the gateway (`scietex.modbus.drivers` entry-point
83
+ group) and driver classes for a service (`scietex.hal.vfd.drivers` group).
84
+
85
+ ### What each drive reports
86
+
87
+ | Field | Intek SPE-B | Vesper E5-8200 | Vesper E4-8400 |
88
+ | --- | --- | --- | --- |
89
+ | `frequency_hz` | yes | yes | yes |
90
+ | `frequency_percent` | yes | yes | yes |
91
+ | `output_current` | yes | yes | yes |
92
+ | `output_voltage` | yes | yes | yes |
93
+ | `output_power` | yes | no (`0.0`) | no (`0.0`) |
94
+ | `dc_bus_voltage` | yes | yes | yes |
95
+ | `heatsink_temperature_c` | no (`None`) | no (`None`) | yes |
96
+ | `motor_temperature_c` | no (`None`) | no (`None`) | no (`None`) |
97
+
98
+ Only the E4-8400 exposes a live temperature reading (`004Dh`, U1-46). The other
99
+ two report overheat only as a fault code, and a fault is not a temperature.
100
+
101
+ ## Installation
102
+
103
+ Install the package via pip (assuming it's published to PyPI):
104
+
105
+ ```bash
106
+ pip install scietex.hal.vfd
107
+ ```
108
+
109
+ Alternatively, clone the repository and install locally:
110
+
111
+ ```bash
112
+ git clone https://github.com/bond-anton/scietex.hal.vfd.git
113
+ cd scietex.hal.vfd
114
+ pip install .
115
+ ```
116
+
117
+ ## Requirements
118
+
119
+ - Python 3.10 or higher.
120
+ - `scietex.hal.serial` 2.4 or newer (RS485 transport and Modbus gateway
121
+ descriptors).
122
+ - `pymodbus` 3.15 or newer (Modbus/TCP client for the gateway transport).
123
+
124
+ ## Usage
125
+
126
+ Every driver implements `VFDProtocol`: `connect`, `close`, `read_data`,
127
+ `read_parameters`, `read_state`, `read_error_code`, `clear_error`, `start`,
128
+ `stop` and `set_frequency`. Check `driver.capabilities` before issuing an
129
+ optional command.
130
+
131
+ Direct RS485 with the Intek SPE-B VFD:
132
+
133
+ ```python
134
+ import asyncio
135
+
136
+ from scietex.hal.serial import ModbusSerialConnectionConfig
137
+ from scietex.hal.vfd.intek import SPEvB
138
+
139
+
140
+ async def main(serial_conf: ModbusSerialConnectionConfig):
141
+ """Main function."""
142
+ vfd = SPEvB(serial_conf, address=1, label="Intek VFD")
143
+ data = await vfd.read_data()
144
+ print(data)
145
+
146
+
147
+ if __name__ == "__main__":
148
+ serial_configuration = ModbusSerialConnectionConfig(
149
+ port="/dev/serial0", baudrate=19200
150
+ )
151
+ asyncio.run(main(serial_configuration))
152
+ ```
153
+
154
+ The same drive behind the Scietex Modbus gateway:
155
+
156
+ ```python
157
+ import asyncio
158
+
159
+ from scietex.hal.vfd.intek import IntekSPEBModbusVFD
160
+
161
+
162
+ async def main():
163
+ """Main function."""
164
+ vfd = IntekSPEBModbusVFD("gateway.local", port=502, unit_id=1)
165
+ await vfd.connect()
166
+ try:
167
+ print(await vfd.read_data())
168
+ print(await vfd.read_error_code())
169
+ finally:
170
+ await vfd.close()
171
+
172
+
173
+ if __name__ == "__main__":
174
+ asyncio.run(main())
175
+ ```
176
+
177
+ A Vesper drive behind the gateway works the same way:
178
+
179
+ ```python
180
+ from scietex.hal.vfd.vesper import VesperE48400ModbusVFD
181
+
182
+ vfd = VesperE48400ModbusVFD("gateway.local", port=502, unit_id=1)
183
+ ```
184
+
185
+ `set_frequency` takes Hz, not percent: the caller works in engineering units and
186
+ each driver converts with its own maximum-frequency parameter. The E5-8200's
187
+ communication manual names no maximum-frequency parameter, so its driver takes
188
+ `max_frequency_hz` from the constructor (default `50.0`, the factory base
189
+ frequency); a caller whose drive runs a different maximum must pass it, or
190
+ `frequency_percent` will be wrong.
191
+
192
+ A drive that has never been configured for the gateway answers neither
193
+ transport. See the per-family references for the register maps, the fault tables
194
+ and the settings the gateway requires:
195
+
196
+ - [`intek/docs/protocol.md`](src/scietex/hal/vfd/intek/docs/protocol.md) —
197
+ Intek SPE-B (`Pd.01`/`Pd.05`/`Pd.02`).
198
+ - [`vesper/docs/README.md`](src/scietex/hal/vfd/vesper/docs/README.md) — Vesper
199
+ E5-8200 and E4-8400, plus the transport facts both manuals share.
200
+
201
+ ## Contributing
202
+
203
+ Contributions are welcome! To contribute:
204
+
205
+ 1. Fork the repository.
206
+ 2. Create a feature branch (`git checkout -b feature/YourFeature`).
207
+ 3. Commit your changes (`git commit -m "Add your message"`).
208
+ 4. Push to the branch (`git push origin feature/YourFeature`).
209
+ 5. Open a Pull Request.
210
+
211
+ Run the checks before opening a pull request:
212
+
213
+ ```bash
214
+ tox -e format,lint,type,test
215
+ ```
216
+
217
+ `format` and `lint` run `ruff`, `type` runs `ty`, and `test` runs `pytest` across
218
+ Python 3.10, 3.12 and 3.14.
219
+
220
+ ## License
221
+
222
+ This project is licensed under the **MIT License** - see the LICENSE file for details.
@@ -0,0 +1,174 @@
1
+ # scietex.hal.vfd
2
+
3
+ The `scietex.hal.vfd` package is a Python library for interfacing with variable
4
+ frequency drives (VFD). It defines a transport-agnostic driver contract
5
+ (`VFDProtocol`) plus the data structures and capability model shared by every
6
+ driver, and ships concrete implementations for three drive families.
7
+
8
+ A driver reaches a drive over one of two transports:
9
+
10
+ - **Direct RS485** — the driver owns the serial port and speaks Modbus RTU to
11
+ the drive itself (`scietex.hal.vfd.base.rs485.VFD`).
12
+ - **Scietex Modbus gateway** — the gateway owns the RS485 bus, and the driver
13
+ speaks Modbus/TCP to a gateway device entry that routes to the drive by unit
14
+ id (`scietex.hal.vfd.intek.spe_b.client.IntekSPEBModbusVFD`).
15
+
16
+ ## Features
17
+
18
+ - `VFDProtocol`, a runtime-checkable contract: connect, disconnect, read
19
+ parameters, read state, read and reset faults, start, stop and set the
20
+ frequency setpoint.
21
+ - `VFDCapabilities`, which advertises optional control surface (direction, jog,
22
+ freewheel stop, fault reset, frequency setpoint) so a caller can reject an
23
+ unsupported command before dispatching it.
24
+ - `VFDParameters`, which reports frequency in both Hz and percent of the
25
+ configured maximum, plus output current, voltage and power, and the optional
26
+ DC bus voltage and temperatures a drive exposes. An optional field is `None`
27
+ when the drive does not report it, which is distinct from a reported `0.0`.
28
+ - Implemented drive models, each with both transports:
29
+ - `Intek SPE-B` — direct RS485 (`SPEvB`) and gateway (`IntekSPEBModbusVFD`).
30
+ - `Vesper E5-8200` — direct RS485 (`VesperE58200`) and gateway
31
+ (`VesperE58200ModbusVFD`).
32
+ - `Vesper E4-8400` / `E4-P8402` — direct RS485 (`VesperE48400`) and gateway
33
+ (`VesperE48400ModbusVFD`).
34
+ - Driver descriptors for the gateway (`scietex.modbus.drivers` entry-point
35
+ group) and driver classes for a service (`scietex.hal.vfd.drivers` group).
36
+
37
+ ### What each drive reports
38
+
39
+ | Field | Intek SPE-B | Vesper E5-8200 | Vesper E4-8400 |
40
+ | --- | --- | --- | --- |
41
+ | `frequency_hz` | yes | yes | yes |
42
+ | `frequency_percent` | yes | yes | yes |
43
+ | `output_current` | yes | yes | yes |
44
+ | `output_voltage` | yes | yes | yes |
45
+ | `output_power` | yes | no (`0.0`) | no (`0.0`) |
46
+ | `dc_bus_voltage` | yes | yes | yes |
47
+ | `heatsink_temperature_c` | no (`None`) | no (`None`) | yes |
48
+ | `motor_temperature_c` | no (`None`) | no (`None`) | no (`None`) |
49
+
50
+ Only the E4-8400 exposes a live temperature reading (`004Dh`, U1-46). The other
51
+ two report overheat only as a fault code, and a fault is not a temperature.
52
+
53
+ ## Installation
54
+
55
+ Install the package via pip (assuming it's published to PyPI):
56
+
57
+ ```bash
58
+ pip install scietex.hal.vfd
59
+ ```
60
+
61
+ Alternatively, clone the repository and install locally:
62
+
63
+ ```bash
64
+ git clone https://github.com/bond-anton/scietex.hal.vfd.git
65
+ cd scietex.hal.vfd
66
+ pip install .
67
+ ```
68
+
69
+ ## Requirements
70
+
71
+ - Python 3.10 or higher.
72
+ - `scietex.hal.serial` 2.4 or newer (RS485 transport and Modbus gateway
73
+ descriptors).
74
+ - `pymodbus` 3.15 or newer (Modbus/TCP client for the gateway transport).
75
+
76
+ ## Usage
77
+
78
+ Every driver implements `VFDProtocol`: `connect`, `close`, `read_data`,
79
+ `read_parameters`, `read_state`, `read_error_code`, `clear_error`, `start`,
80
+ `stop` and `set_frequency`. Check `driver.capabilities` before issuing an
81
+ optional command.
82
+
83
+ Direct RS485 with the Intek SPE-B VFD:
84
+
85
+ ```python
86
+ import asyncio
87
+
88
+ from scietex.hal.serial import ModbusSerialConnectionConfig
89
+ from scietex.hal.vfd.intek import SPEvB
90
+
91
+
92
+ async def main(serial_conf: ModbusSerialConnectionConfig):
93
+ """Main function."""
94
+ vfd = SPEvB(serial_conf, address=1, label="Intek VFD")
95
+ data = await vfd.read_data()
96
+ print(data)
97
+
98
+
99
+ if __name__ == "__main__":
100
+ serial_configuration = ModbusSerialConnectionConfig(
101
+ port="/dev/serial0", baudrate=19200
102
+ )
103
+ asyncio.run(main(serial_configuration))
104
+ ```
105
+
106
+ The same drive behind the Scietex Modbus gateway:
107
+
108
+ ```python
109
+ import asyncio
110
+
111
+ from scietex.hal.vfd.intek import IntekSPEBModbusVFD
112
+
113
+
114
+ async def main():
115
+ """Main function."""
116
+ vfd = IntekSPEBModbusVFD("gateway.local", port=502, unit_id=1)
117
+ await vfd.connect()
118
+ try:
119
+ print(await vfd.read_data())
120
+ print(await vfd.read_error_code())
121
+ finally:
122
+ await vfd.close()
123
+
124
+
125
+ if __name__ == "__main__":
126
+ asyncio.run(main())
127
+ ```
128
+
129
+ A Vesper drive behind the gateway works the same way:
130
+
131
+ ```python
132
+ from scietex.hal.vfd.vesper import VesperE48400ModbusVFD
133
+
134
+ vfd = VesperE48400ModbusVFD("gateway.local", port=502, unit_id=1)
135
+ ```
136
+
137
+ `set_frequency` takes Hz, not percent: the caller works in engineering units and
138
+ each driver converts with its own maximum-frequency parameter. The E5-8200's
139
+ communication manual names no maximum-frequency parameter, so its driver takes
140
+ `max_frequency_hz` from the constructor (default `50.0`, the factory base
141
+ frequency); a caller whose drive runs a different maximum must pass it, or
142
+ `frequency_percent` will be wrong.
143
+
144
+ A drive that has never been configured for the gateway answers neither
145
+ transport. See the per-family references for the register maps, the fault tables
146
+ and the settings the gateway requires:
147
+
148
+ - [`intek/docs/protocol.md`](src/scietex/hal/vfd/intek/docs/protocol.md) —
149
+ Intek SPE-B (`Pd.01`/`Pd.05`/`Pd.02`).
150
+ - [`vesper/docs/README.md`](src/scietex/hal/vfd/vesper/docs/README.md) — Vesper
151
+ E5-8200 and E4-8400, plus the transport facts both manuals share.
152
+
153
+ ## Contributing
154
+
155
+ Contributions are welcome! To contribute:
156
+
157
+ 1. Fork the repository.
158
+ 2. Create a feature branch (`git checkout -b feature/YourFeature`).
159
+ 3. Commit your changes (`git commit -m "Add your message"`).
160
+ 4. Push to the branch (`git push origin feature/YourFeature`).
161
+ 5. Open a Pull Request.
162
+
163
+ Run the checks before opening a pull request:
164
+
165
+ ```bash
166
+ tox -e format,lint,type,test
167
+ ```
168
+
169
+ `format` and `lint` run `ruff`, `type` runs `ty`, and `test` runs `pytest` across
170
+ Python 3.10, 3.12 and 3.14.
171
+
172
+ ## License
173
+
174
+ This project is licensed under the **MIT License** - see the LICENSE file for details.
@@ -0,0 +1,57 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "scietex.hal.vfd"
7
+ dynamic = ["version"]
8
+ description = "Scietex VFD HAL package"
9
+ authors = [
10
+ { name = "Anton Bondarenko", email = "bond.anton@gmail.com" },
11
+ ]
12
+ license = { file = "LICENSE" }
13
+ readme = "README.md"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+ requires-python = ">=3.10"
20
+ dependencies = [
21
+ "msgspec[yaml] ~= 0.20.0",
22
+ "scietex.hal.serial >= 2.4.0, < 3.0.0",
23
+ "pymodbus ~= 3.15",
24
+ ]
25
+
26
+ [project.entry-points."scietex.hal.vfd.drivers"]
27
+ intek_spe_b_modbus = "scietex.hal.vfd.intek.spe_b:IntekSPEBModbusVFD"
28
+ vesper_e5_8200_modbus = "scietex.hal.vfd.vesper.e5_8200:VesperE58200ModbusVFD"
29
+ vesper_e4_8400_modbus = "scietex.hal.vfd.vesper.e4_8400:VesperE48400ModbusVFD"
30
+
31
+ [project.entry-points."scietex.modbus.drivers"]
32
+ intek_spe_b = "scietex.hal.vfd.modbus_drivers:intek_spe_b"
33
+ vesper_e5_8200 = "scietex.hal.vfd.modbus_drivers:vesper_e5_8200"
34
+ vesper_e4_8400 = "scietex.hal.vfd.modbus_drivers:vesper_e4_8400"
35
+
36
+ [project.urls]
37
+ "Homepage" = "https://github.com/bond-anton/scietex.hal.vfd"
38
+ "Bug Tracker" = "https://github.com/bond-anton/scietex.hal.vfd/issues"
39
+
40
+ [project.optional-dependencies]
41
+ dev = ["tox>=4.46.0"]
42
+ test = ["pytest", "pytest-asyncio", "pytest-xdist"]
43
+ lint = ["ruff", "ty"]
44
+
45
+ [tool.setuptools.dynamic]
46
+ version = {attr = "scietex.hal.vfd.version.__version__"}
47
+
48
+ [tool.setuptools.package-data]
49
+ "scietex.hal.vfd" = ["py.typed"]
50
+
51
+ [tool.setuptools.packages.find]
52
+ where = ["src"]
53
+
54
+ [tool.pytest.ini_options]
55
+ pythonpath = [
56
+ "src"
57
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Scietex VFD HAL package."""
2
+
3
+ from .version import __version__ as __version__
@@ -0,0 +1,47 @@
1
+ """
2
+ Base VFD subpackage.
3
+
4
+ Provides the transport-agnostic driver contract, the capability model, the
5
+ shared data structures, and the RS485 base class the direct-serial drivers
6
+ derive from.
7
+
8
+ Classes:
9
+ VFD: Base class for drivers that own a serial connection.
10
+ VFDCapabilities: The control surface of one drive.
11
+ VFDError: A fault code paired with its message.
12
+ VFDParameters: A snapshot of the drive's operating parameters.
13
+ VFDProtocol: The contract every driver implements.
14
+ VFDState: The drive run state.
15
+
16
+ Modules:
17
+ protocol: Defines the `VFDProtocol` driver contract.
18
+ capabilities: Defines the `VFDCapabilities` model.
19
+ data: Defines the data structures shared by every driver.
20
+ rs485: Defines the `VFD` serial base class.
21
+ """
22
+
23
+ from .capabilities import VFDCapabilities
24
+ from .data import (
25
+ VFDClearErrorCMD,
26
+ VFDError,
27
+ VFDParameters,
28
+ VFDSetFrequencyCMD,
29
+ VFDStartCMD,
30
+ VFDState,
31
+ VFDStopCMD,
32
+ )
33
+ from .protocol import VFDProtocol
34
+ from .rs485 import VFD
35
+
36
+ __all__ = [
37
+ "VFD",
38
+ "VFDCapabilities",
39
+ "VFDClearErrorCMD",
40
+ "VFDError",
41
+ "VFDParameters",
42
+ "VFDProtocol",
43
+ "VFDSetFrequencyCMD",
44
+ "VFDStartCMD",
45
+ "VFDState",
46
+ "VFDStopCMD",
47
+ ]
@@ -0,0 +1,49 @@
1
+ """
2
+ VFD capability model.
3
+
4
+ A drive advertises what it can do through a single :class:`VFDCapabilities`
5
+ value. The fields are the operations a caller can dispatch without inspecting
6
+ the driver: whether the drive accepts a direction choice, a jog command, a
7
+ coasting stop, a fault reset, and a frequency setpoint.
8
+
9
+ The set is a frozen dataclass so a driver declares it once as a class attribute
10
+ and callers cannot mutate it.
11
+
12
+ Capabilities describe the *control* surface only. Whether a drive reports a
13
+ particular *reading* — a heatsink temperature, a DC bus voltage — is answered by
14
+ the corresponding :class:`scietex.hal.vfd.base.data.VFDParameters` field being
15
+ ``None``, not by a flag here. One source of truth cannot drift from the driver.
16
+
17
+ Classes:
18
+ VFDCapabilities: The control surface of one drive.
19
+ """
20
+
21
+ from dataclasses import dataclass
22
+
23
+
24
+ @dataclass(frozen=True, slots=True)
25
+ class VFDCapabilities:
26
+ """
27
+ The control surface of one drive.
28
+
29
+ Every field defaults to "unsupported", so a driver declares only what its
30
+ hardware exposes. Read-side operations (parameters, state, error code) are
31
+ mandatory for every driver and are therefore not advertised here.
32
+
33
+ Attributes:
34
+ direction (bool): Whether the drive can be started in either rotation
35
+ direction. ``False`` means forward only.
36
+ jog (bool): Whether the drive accepts a jog command.
37
+ freewheel_stop (bool): Whether the drive can stop by coasting instead of
38
+ ramping down.
39
+ fault_reset (bool): Whether the drive accepts an explicit fault reset.
40
+ frequency_setpoint (bool): Whether the drive accepts a frequency
41
+ setpoint over the communication protocol, independent of the
42
+ keypad or an analog input.
43
+ """
44
+
45
+ direction: bool = True
46
+ jog: bool = False
47
+ freewheel_stop: bool = False
48
+ fault_reset: bool = False
49
+ frequency_setpoint: bool = False