ezo-driver 0.6.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 (86) hide show
  1. ezo_driver-0.6.0/LICENSE +22 -0
  2. ezo_driver-0.6.0/MANIFEST.in +7 -0
  3. ezo_driver-0.6.0/PKG-INFO +127 -0
  4. ezo_driver-0.6.0/README.md +257 -0
  5. ezo_driver-0.6.0/bindings/python/README.md +106 -0
  6. ezo_driver-0.6.0/bindings/python/build_ffi.py +54 -0
  7. ezo_driver-0.6.0/bindings/python/cdef/ezo_api.h +984 -0
  8. ezo_driver-0.6.0/bindings/python/cdef/ezo_fakes.h +46 -0
  9. ezo_driver-0.6.0/bindings/python/src/ezo_driver/__init__.py +47 -0
  10. ezo_driver-0.6.0/bindings/python/src/ezo_driver/_call.py +450 -0
  11. ezo_driver-0.6.0/bindings/python/src/ezo_driver/_ffi.py +5 -0
  12. ezo_driver-0.6.0/bindings/python/src/ezo_driver/_support.py +313 -0
  13. ezo_driver-0.6.0/bindings/python/src/ezo_driver/base.py +32 -0
  14. ezo_driver-0.6.0/bindings/python/src/ezo_driver/calibration_transfer.py +129 -0
  15. ezo_driver-0.6.0/bindings/python/src/ezo_driver/control.py +311 -0
  16. ezo_driver-0.6.0/bindings/python/src/ezo_driver/do.py +237 -0
  17. ezo_driver-0.6.0/bindings/python/src/ezo_driver/ec.py +237 -0
  18. ezo_driver-0.6.0/bindings/python/src/ezo_driver/enums.py +193 -0
  19. ezo_driver-0.6.0/bindings/python/src/ezo_driver/errors.py +37 -0
  20. ezo_driver-0.6.0/bindings/python/src/ezo_driver/hum.py +121 -0
  21. ezo_driver-0.6.0/bindings/python/src/ezo_driver/i2c.py +40 -0
  22. ezo_driver-0.6.0/bindings/python/src/ezo_driver/orp.py +121 -0
  23. ezo_driver-0.6.0/bindings/python/src/ezo_driver/parse.py +108 -0
  24. ezo_driver-0.6.0/bindings/python/src/ezo_driver/ph.py +193 -0
  25. ezo_driver-0.6.0/bindings/python/src/ezo_driver/product.py +54 -0
  26. ezo_driver-0.6.0/bindings/python/src/ezo_driver/rtd.py +244 -0
  27. ezo_driver-0.6.0/bindings/python/src/ezo_driver/schema.py +55 -0
  28. ezo_driver-0.6.0/bindings/python/src/ezo_driver/types.py +292 -0
  29. ezo_driver-0.6.0/bindings/python/src/ezo_driver/uart.py +52 -0
  30. ezo_driver-0.6.0/bindings/python/src/ezo_driver.egg-info/PKG-INFO +127 -0
  31. ezo_driver-0.6.0/bindings/python/src/ezo_driver.egg-info/SOURCES.txt +103 -0
  32. ezo_driver-0.6.0/bindings/python/src/ezo_driver.egg-info/dependency_links.txt +1 -0
  33. ezo_driver-0.6.0/bindings/python/src/ezo_driver.egg-info/requires.txt +5 -0
  34. ezo_driver-0.6.0/bindings/python/src/ezo_driver.egg-info/top_level.txt +1 -0
  35. ezo_driver-0.6.0/bindings/python/tests/__init__.py +2 -0
  36. ezo_driver-0.6.0/bindings/python/tests/_fakes.py +82 -0
  37. ezo_driver-0.6.0/bindings/python/tests/_support.py +253 -0
  38. ezo_driver-0.6.0/bindings/python/tests/test_control_and_calibration.py +103 -0
  39. ezo_driver-0.6.0/bindings/python/tests/test_products.py +180 -0
  40. ezo_driver-0.6.0/bindings/python/tests/test_public_api.py +36 -0
  41. ezo_driver-0.6.0/bindings/python/tests/test_shared_and_transport.py +143 -0
  42. ezo_driver-0.6.0/platform/linux/ezo_i2c_linux_i2c.c +81 -0
  43. ezo_driver-0.6.0/platform/linux/ezo_linux_device.c +216 -0
  44. ezo_driver-0.6.0/platform/linux/ezo_uart_posix_serial.c +267 -0
  45. ezo_driver-0.6.0/pyproject.toml +45 -0
  46. ezo_driver-0.6.0/setup.cfg +7 -0
  47. ezo_driver-0.6.0/setup.py +8 -0
  48. ezo_driver-0.6.0/src/ezo.c +52 -0
  49. ezo_driver-0.6.0/src/ezo.h +42 -0
  50. ezo_driver-0.6.0/src/ezo_calibration_transfer.c +321 -0
  51. ezo_driver-0.6.0/src/ezo_calibration_transfer.h +97 -0
  52. ezo_driver-0.6.0/src/ezo_common.c +243 -0
  53. ezo_driver-0.6.0/src/ezo_common.h +24 -0
  54. ezo_driver-0.6.0/src/ezo_control.c +862 -0
  55. ezo_driver-0.6.0/src/ezo_control.h +262 -0
  56. ezo_driver-0.6.0/src/ezo_do.c +947 -0
  57. ezo_driver-0.6.0/src/ezo_do.h +253 -0
  58. ezo_driver-0.6.0/src/ezo_ec.c +827 -0
  59. ezo_driver-0.6.0/src/ezo_ec.h +256 -0
  60. ezo_driver-0.6.0/src/ezo_hum.c +477 -0
  61. ezo_driver-0.6.0/src/ezo_hum.h +135 -0
  62. ezo_driver-0.6.0/src/ezo_i2c.c +344 -0
  63. ezo_driver-0.6.0/src/ezo_i2c.h +87 -0
  64. ezo_driver-0.6.0/src/ezo_i2c_arduino_wire.h +19 -0
  65. ezo_driver-0.6.0/src/ezo_i2c_linux_i2c.h +21 -0
  66. ezo_driver-0.6.0/src/ezo_linux_device.h +49 -0
  67. ezo_driver-0.6.0/src/ezo_orp.c +396 -0
  68. ezo_driver-0.6.0/src/ezo_orp.h +126 -0
  69. ezo_driver-0.6.0/src/ezo_parse.c +329 -0
  70. ezo_driver-0.6.0/src/ezo_parse.h +77 -0
  71. ezo_driver-0.6.0/src/ezo_ph.c +681 -0
  72. ezo_driver-0.6.0/src/ezo_ph.h +205 -0
  73. ezo_driver-0.6.0/src/ezo_product.c +526 -0
  74. ezo_driver-0.6.0/src/ezo_product.h +141 -0
  75. ezo_driver-0.6.0/src/ezo_rtd.c +803 -0
  76. ezo_driver-0.6.0/src/ezo_rtd.h +238 -0
  77. ezo_driver-0.6.0/src/ezo_schema.c +175 -0
  78. ezo_driver-0.6.0/src/ezo_schema.h +78 -0
  79. ezo_driver-0.6.0/src/ezo_uart.c +347 -0
  80. ezo_driver-0.6.0/src/ezo_uart.h +94 -0
  81. ezo_driver-0.6.0/src/ezo_uart_arduino_stream.h +22 -0
  82. ezo_driver-0.6.0/src/ezo_uart_posix_serial.h +45 -0
  83. ezo_driver-0.6.0/tests/fakes/ezo_fake_i2c_transport.c +105 -0
  84. ezo_driver-0.6.0/tests/fakes/ezo_fake_i2c_transport.h +38 -0
  85. ezo_driver-0.6.0/tests/fakes/ezo_fake_uart_transport.c +178 -0
  86. ezo_driver-0.6.0/tests/fakes/ezo_fake_uart_transport.h +44 -0
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Atlas Scientific
4
+ Copyright (c) 2026 Cameron K. Brooks
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ include bindings/python/README.md
2
+ include bindings/python/build_ffi.py
3
+ recursive-include bindings/python/cdef *.h
4
+ recursive-include bindings/python/tests *.py
5
+ recursive-include src *.c *.h
6
+ recursive-include platform/linux *.c *.h
7
+ recursive-include tests/fakes *.c *.h
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.4
2
+ Name: ezo-driver
3
+ Version: 0.6.0
4
+ Summary: Linux Python bindings for ezo-driver
5
+ Author: ezo-driver maintainers
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/feastorg/ezo-driver
8
+ Project-URL: Issues, https://github.com/feastorg/ezo-driver/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Programming Language :: C
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: cffi>=1.16
17
+ Provides-Extra: test
18
+ Requires-Dist: pytest>=7; extra == "test"
19
+ Requires-Dist: setuptools; extra == "test"
20
+ Dynamic: license-file
21
+
22
+ # `ezo-driver` Python bindings
23
+
24
+ Linux-only Python bindings for the canonical `ezo-driver` C library.
25
+
26
+ ## Support statement
27
+
28
+ - Supported host platform: Linux
29
+ - Supported transports: Linux I2C and POSIX UART
30
+ - Supported Python install mode: `pip install ezo-driver` from PyPI, with wheels for x86_64 and aarch64 Linux on Python 3.9+; other platforms build from the sdist or the repo checkout
31
+ - Public package name: `ezo-driver`
32
+ - Public import package: `ezo_driver`
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ python -m pip install ezo-driver
38
+ ```
39
+
40
+ PyPI carries `cp39-abi3` manylinux wheels for x86_64 and aarch64 Linux, so any CPython 3.9 or newer installs one without a compiler, a 64-bit Raspberry Pi included. On other platforms pip builds from the sdist, which needs a C compiler and the Python headers.
41
+
42
+ From the repo root:
43
+
44
+ ```bash
45
+ python -m pip install .
46
+ ```
47
+
48
+ For development, install it editable with the test dependencies:
49
+
50
+ ```bash
51
+ python -m pip install -e ".[test]"
52
+ python -m pytest bindings/python/tests
53
+ ```
54
+
55
+ The packaging lives at the repo root (`pyproject.toml`, `setup.py`, `setup.cfg`, `MANIFEST.in`) so the sdist carries the C sources from `src/` and `platform/linux/` and a wheel can be built from it. `setup.cfg` tags the wheel `cp39-abi3`: cffi builds `ezo_driver._native` against the limited C API, so one wheel per architecture covers every supported Python. The release workflow builds the wheels with cibuildwheel (`[tool.cibuildwheel]` in `pyproject.toml`) and publishes them to PyPI.
56
+
57
+ The shipped `ezo_driver._native` module holds the C core and the Linux transports only. The tests drive it through the fake transports from `tests/fakes/`, which `bindings/python/tests/_fakes.py` compiles into a separate test-only cffi module in a temporary directory each session; this needs a C compiler and the `test` extra. The C sources are found in the checkout that holds the tests; when the tests run from a copy elsewhere, point `EZO_DRIVER_SOURCE_DIR` at the checkout.
58
+
59
+ ## Public modules
60
+
61
+ - `ezo_driver.errors`
62
+ - `ezo_driver.enums`
63
+ - `ezo_driver.types`
64
+ - `ezo_driver.base`
65
+ - `ezo_driver.i2c`
66
+ - `ezo_driver.uart`
67
+ - `ezo_driver.product`
68
+ - `ezo_driver.parse`
69
+ - `ezo_driver.schema`
70
+ - `ezo_driver.control`
71
+ - `ezo_driver.calibration_transfer`
72
+ - `ezo_driver.ph`
73
+ - `ezo_driver.orp`
74
+ - `ezo_driver.rtd`
75
+ - `ezo_driver.ec`
76
+ - `ezo_driver.do`
77
+ - `ezo_driver.hum`
78
+
79
+ Top-level `ezo_driver` exports only the stable transport constructors, exception hierarchy, and shared enums/types.
80
+
81
+ ## I2C example
82
+
83
+ ```python
84
+ from ezo_driver import LinuxI2CDevice, ProductId
85
+ from ezo_driver import control, ph
86
+
87
+ with LinuxI2CDevice(bus=1, address=0x63) as dev:
88
+ wait_ms = control.send_info_query_i2c(dev, ProductId.PH)
89
+ info = control.read_info_i2c(dev)
90
+
91
+ wait_ms = ph.send_read_i2c(dev)
92
+ reading = ph.read_response_i2c(dev)
93
+
94
+ print(info)
95
+ print(reading)
96
+ ```
97
+
98
+ ## UART example
99
+
100
+ ```python
101
+ from ezo_driver import LinuxUARTDevice, ProductId
102
+ from ezo_driver import control, ph
103
+
104
+ with LinuxUARTDevice("/dev/ttyUSB0", baud=9600, read_timeout_ms=1000) as dev:
105
+ wait_ms = control.send_info_query_uart(dev, ProductId.PH)
106
+ info = control.read_info_uart(dev)
107
+
108
+ wait_ms = ph.send_read_uart(dev)
109
+ reading = ph.read_response_uart(dev)
110
+
111
+ print(info)
112
+ print(reading)
113
+ ```
114
+
115
+ ## Design constraints
116
+
117
+ - The Python layer stays transport-explicit. There is no unified device abstraction.
118
+ - The bindings mirror the C library concepts instead of exposing buffer management.
119
+ - Timing hints are returned as integer milliseconds.
120
+ - I2C device status codes and UART response kinds stay explicit values in the API.
121
+
122
+ ## Non-goals
123
+
124
+ - No implicit sleeps
125
+ - No implicit retries
126
+ - No implicit reconnect/resynchronization
127
+ - No workflow wrapper above the canonical transport and product helpers
@@ -0,0 +1,257 @@
1
+ # EZO Driver
2
+
3
+ Cross-platform Atlas Scientific EZO driver with:
4
+
5
+ - a platform-agnostic C99 core
6
+ - explicit I2C and UART driver families
7
+ - a thin C++11 wrapper for the I2C surface
8
+ - Arduino `TwoWire` integration for I2C
9
+ - Arduino `Stream` integration for UART
10
+ - Linux I2C support
11
+ - Linux host POSIX UART support
12
+ - Linux Python bindings
13
+ - host-side tests and Arduino compile CI
14
+
15
+ ## Status
16
+
17
+ Current implementation includes:
18
+
19
+ - shared `ezo.h` surface for results, timing hints, and numeric parsing
20
+ - product identity and metadata for the initial six documented EZO families
21
+ - shared query/CSV parsing, UART sequence-state helpers, and canonical output schemas
22
+ - shared control-plane helpers for identity, status, LED, UART response-code mode, sleep, factory reset, protocol lock, and mode switching
23
+ - shared calibration-transfer helpers for export/import workflows
24
+ - typed scalar product modules for pH, ORP, and RTD
25
+ - typed multi-output product modules for EC, DO, and HUM
26
+ - complete I2C core with text and raw response decoding
27
+ - complete UART core with line-based response handling
28
+ - Arduino integrations for both I2C and UART
29
+ - Linux I2C integration
30
+ - Linux host POSIX UART integration
31
+ - Linux Python bindings on PyPI as `ezo-driver`, with `cp39-abi3` wheels for x86_64 and aarch64
32
+ - focused Arduino and Linux examples
33
+ - host-side C and C++ tests plus fake transport coverage
34
+ - PlatformIO Arduino compile validation in CI
35
+
36
+ Current support matrix:
37
+
38
+ - I2C: C core, I2C C++ wrapper, Arduino `TwoWire`, Linux I2C adapter
39
+ - UART: C core, Arduino `Stream`, Linux host POSIX serial adapter
40
+ - Python: Linux-only bindings, `pip install ezo-driver` on Python 3.9+ with wheels for x86_64 and aarch64
41
+ - Product modules: full typed support for the initial six families, including shared control/admin coverage, calibration-transfer primitives, and advanced per-product helpers
42
+ - Product foundation: identity, metadata, timing fallback, and parse/schema utilities for the initial six documented families
43
+ - Shared: host-side tests and Arduino compile validation
44
+
45
+ ## Start Here
46
+
47
+ - Raw transport bring-up: [`src/ezo_i2c.h`](./src/ezo_i2c.h) or [`src/ezo_uart.h`](./src/ezo_uart.h)
48
+ - Device identity and support metadata: [`src/ezo_control.h`](./src/ezo_control.h) plus [`src/ezo_product.h`](./src/ezo_product.h)
49
+ - Shared admin/protocol workflows: [`src/ezo_control.h`](./src/ezo_control.h)
50
+ - Calibration export/import primitives: [`src/ezo_calibration_transfer.h`](./src/ezo_calibration_transfer.h)
51
+ - Known-product application code: the matching typed product header in `src/`
52
+
53
+ Public guidance docs:
54
+
55
+ - [`docs/public-api-layers.md`](./docs/public-api-layers.md)
56
+ - [`docs/support-matrix.md`](./docs/support-matrix.md)
57
+ - [`docs/product-onboarding.md`](./docs/product-onboarding.md)
58
+ - [`docs/examples.md`](./docs/examples.md)
59
+
60
+ ## Layout
61
+
62
+ - `src/`: canonical library root for public headers and Arduino-safe implementation
63
+ - `platform/`: host-only platform implementation code not intended for Arduino library builds
64
+ - `examples/linux/`: full Linux reference example tree with `raw`, `commissioning`, `typed`, and `advanced` flows for I2C and UART
65
+ - `examples/arduino/`: Arduino smoke, commissioning, full typed reads, broad I2C advanced workflows, and focused UART admin/routing sketches
66
+ - `bindings/python/`: Linux-only Python bindings package and tests; its packaging (`pyproject.toml`, `setup.py`, `setup.cfg`, `MANIFEST.in`) is at the repo root
67
+ - `tests/`: host-side tests and fakes
68
+ - `docs/`: tracked handoff docs and curated EZO product/protocol notes
69
+
70
+ ## Build
71
+
72
+ Canonical host-side CMake usage is via presets:
73
+
74
+ ```sh
75
+ cmake --preset host-linux-debug
76
+ cmake --build --preset host-linux-debug --parallel
77
+ ctest --preset host-linux-debug
78
+ ```
79
+
80
+ That Linux preset is the main maintained path. It enables:
81
+
82
+ - host tests
83
+ - Linux I2C adapter
84
+ - Linux POSIX UART adapter
85
+ - Linux examples
86
+
87
+ For Windows development on the shared/core surface, use:
88
+
89
+ ```sh
90
+ cmake --preset host-windows-debug
91
+ cmake --build --preset host-windows-debug --parallel
92
+ ctest --preset host-windows-debug
93
+ ```
94
+
95
+ The Windows preset is intentionally limited to the shared/core host test surface. Linux-only adapters and examples are not part of the Windows development contract. The tracked Windows presets target the Visual Studio 2022 generator.
96
+
97
+ To install the public headers and static libraries for host-side consumption:
98
+
99
+ ```sh
100
+ cmake --preset host-linux-release
101
+ cmake --build --preset host-linux-release --parallel
102
+ cmake --install build/host-linux-release --prefix <install-prefix>
103
+ ```
104
+
105
+ ## Validation
106
+
107
+ - host CI builds and runs C and C++ tests for the shared, parse/schema, I2C, UART, and product-foundation paths
108
+ - host CI also runs typed product-module tests for pH, ORP, RTD, EC, DO, and HUM
109
+ - Linux I2C and Linux host POSIX UART adapter behavior are covered by host-side tests
110
+ - PlatformIO CI compile-checks the full Arduino sketch surface on `uno` and the UART/helper path on `esp32dev`
111
+ - Python CI builds the sdist, builds a wheel from it, checks both with `twine`, installs the wheel and runs the Linux bindings pytest suite against it from outside the source tree
112
+ - the release workflow builds the `cp39-abi3` wheels for x86_64 and aarch64 Linux with cibuildwheel, runs the bindings tests against each inside its manylinux container, and publishes them to PyPI through Trusted Publishing
113
+ - Arduino IDE validation is manual by design
114
+
115
+ ## Packaging
116
+
117
+ - `library.properties`: Arduino tooling metadata for the combined library
118
+ - `library.json`: PlatformIO metadata for the combined library
119
+
120
+ ## Examples
121
+
122
+ Start with the example chooser in [`docs/examples.md`](./docs/examples.md). That doc is the canonical matrix and first-use guide.
123
+
124
+ Short version:
125
+
126
+ - If transport is unknown, start with [`examples/linux/uart/commissioning/inspect_device.c`](./examples/linux/uart/commissioning/inspect_device.c). UART is the shipping default across the currently documented products.
127
+ - If transport is known but setup state is not, start with the matching readiness example:
128
+ [`examples/linux/i2c/commissioning/readiness_check.c`](./examples/linux/i2c/commissioning/readiness_check.c)
129
+ or [`examples/linux/uart/commissioning/readiness_check.c`](./examples/linux/uart/commissioning/readiness_check.c)
130
+ - If product and transport are already known, jump straight to the matching typed example under `examples/linux/<transport>/typed/`.
131
+ - If you need the bare transport contract first, start with:
132
+ [`examples/linux/i2c/raw/raw_command.c`](./examples/linux/i2c/raw/raw_command.c)
133
+ or [`examples/linux/uart/raw/raw_command.c`](./examples/linux/uart/raw/raw_command.c)
134
+ - If you need Arduino, use the maintained sketches under `examples/arduino/`:
135
+ raw smoke and commissioning inspect for both transports, full typed reads for all six products,
136
+ a broad I2C advanced bench layer, and focused UART admin/routing sketches.
137
+
138
+ Representative entry points:
139
+
140
+ - Linux I2C raw: [`examples/linux/i2c/raw/raw_command.c`](./examples/linux/i2c/raw/raw_command.c)
141
+ - Linux UART raw: [`examples/linux/uart/raw/raw_command.c`](./examples/linux/uart/raw/raw_command.c)
142
+ - Linux I2C commissioning: [`examples/linux/i2c/commissioning/inspect_device.c`](./examples/linux/i2c/commissioning/inspect_device.c)
143
+ - Linux UART commissioning: [`examples/linux/uart/commissioning/inspect_device.c`](./examples/linux/uart/commissioning/inspect_device.c)
144
+ - Linux I2C typed pH: [`examples/linux/i2c/typed/read_ph.c`](./examples/linux/i2c/typed/read_ph.c)
145
+ - Linux UART typed pH: [`examples/linux/uart/typed/read_ph.c`](./examples/linux/uart/typed/read_ph.c)
146
+ - Linux advanced staged pH calibration:
147
+ [`examples/linux/i2c/advanced/ph_calibration.c`](./examples/linux/i2c/advanced/ph_calibration.c)
148
+ and [`examples/linux/uart/advanced/ph_calibration.c`](./examples/linux/uart/advanced/ph_calibration.c)
149
+ - Linux advanced EC workflow:
150
+ [`examples/linux/i2c/advanced/ec_workflow.c`](./examples/linux/i2c/advanced/ec_workflow.c)
151
+ and [`examples/linux/uart/advanced/ec_workflow.c`](./examples/linux/uart/advanced/ec_workflow.c)
152
+ - Linux advanced calibration transfer:
153
+ [`examples/linux/i2c/advanced/calibration_transfer.c`](./examples/linux/i2c/advanced/calibration_transfer.c)
154
+ and [`examples/linux/uart/advanced/calibration_transfer.c`](./examples/linux/uart/advanced/calibration_transfer.c)
155
+ - Linux advanced shared control workflow:
156
+ [`examples/linux/i2c/advanced/control_workflow.c`](./examples/linux/i2c/advanced/control_workflow.c)
157
+ and [`examples/linux/uart/advanced/control_workflow.c`](./examples/linux/uart/advanced/control_workflow.c)
158
+ - Linux advanced transport switch:
159
+ [`examples/linux/i2c/advanced/transport_switch.c`](./examples/linux/i2c/advanced/transport_switch.c)
160
+ and [`examples/linux/uart/advanced/transport_switch.c`](./examples/linux/uart/advanced/transport_switch.c)
161
+ - Linux advanced RTD-driven EC temperature compensation:
162
+ [`examples/linux/i2c/advanced/ec_temp_comp_from_rtd.c`](./examples/linux/i2c/advanced/ec_temp_comp_from_rtd.c)
163
+ and [`examples/linux/uart/advanced/ec_temp_comp_from_rtd.c`](./examples/linux/uart/advanced/ec_temp_comp_from_rtd.c)
164
+ - Linux advanced EC-driven D.O. salinity compensation:
165
+ [`examples/linux/i2c/advanced/do_salinity_comp_from_ec.c`](./examples/linux/i2c/advanced/do_salinity_comp_from_ec.c)
166
+ and [`examples/linux/uart/advanced/do_salinity_comp_from_ec.c`](./examples/linux/uart/advanced/do_salinity_comp_from_ec.c)
167
+ - Linux advanced full D.O. compensation chain:
168
+ [`examples/linux/i2c/advanced/do_full_compensation_chain.c`](./examples/linux/i2c/advanced/do_full_compensation_chain.c)
169
+ and [`examples/linux/uart/advanced/do_full_compensation_chain.c`](./examples/linux/uart/advanced/do_full_compensation_chain.c)
170
+ - Arduino I2C inspect: [`examples/arduino/i2c/commissioning/inspect_device/inspect_device.ino`](./examples/arduino/i2c/commissioning/inspect_device/inspect_device.ino)
171
+ - Arduino UART inspect: [`examples/arduino/uart/commissioning/inspect_device/inspect_device.ino`](./examples/arduino/uart/commissioning/inspect_device/inspect_device.ino)
172
+ - Arduino I2C typed EC: [`examples/arduino/i2c/typed/read_ec/read_ec.ino`](./examples/arduino/i2c/typed/read_ec/read_ec.ino)
173
+ - Arduino UART typed RTD: [`examples/arduino/uart/typed/read_rtd/read_rtd.ino`](./examples/arduino/uart/typed/read_rtd/read_rtd.ino)
174
+ - Arduino I2C advanced staged pH calibration:
175
+ [`examples/arduino/i2c/advanced/ph_calibration/ph_calibration.ino`](./examples/arduino/i2c/advanced/ph_calibration/ph_calibration.ino)
176
+ - Arduino I2C advanced EC workflow:
177
+ [`examples/arduino/i2c/advanced/ec_workflow/ec_workflow.ino`](./examples/arduino/i2c/advanced/ec_workflow/ec_workflow.ino)
178
+ - Arduino I2C advanced shared control workflow:
179
+ [`examples/arduino/i2c/advanced/control_workflow/control_workflow.ino`](./examples/arduino/i2c/advanced/control_workflow/control_workflow.ino)
180
+ - Arduino UART advanced shared control workflow:
181
+ [`examples/arduino/uart/advanced/control_workflow/control_workflow.ino`](./examples/arduino/uart/advanced/control_workflow/control_workflow.ino)
182
+ - Arduino I2C advanced calibration transfer:
183
+ [`examples/arduino/i2c/advanced/calibration_transfer/calibration_transfer.ino`](./examples/arduino/i2c/advanced/calibration_transfer/calibration_transfer.ino)
184
+ - Arduino I2C advanced RTD-driven EC temperature compensation:
185
+ [`examples/arduino/i2c/advanced/ec_temp_comp_from_rtd/ec_temp_comp_from_rtd.ino`](./examples/arduino/i2c/advanced/ec_temp_comp_from_rtd/ec_temp_comp_from_rtd.ino)
186
+ - Arduino I2C advanced full D.O. compensation chain:
187
+ [`examples/arduino/i2c/advanced/do_full_compensation_chain/do_full_compensation_chain.ino`](./examples/arduino/i2c/advanced/do_full_compensation_chain/do_full_compensation_chain.ino)
188
+ - Arduino UART advanced multi-device routing:
189
+ [`examples/arduino/uart/advanced/multi_device_router/multi_device_router.ino`](./examples/arduino/uart/advanced/multi_device_router/multi_device_router.ino)
190
+
191
+ ## Entry Points
192
+
193
+ Primary public headers:
194
+
195
+ - [`src/ezo.h`](./src/ezo.h)
196
+ - [`src/ezo_do.h`](./src/ezo_do.h)
197
+ - [`src/ezo_ec.h`](./src/ezo_ec.h)
198
+ - [`src/ezo_hum.h`](./src/ezo_hum.h)
199
+ - [`src/ezo_calibration_transfer.h`](./src/ezo_calibration_transfer.h)
200
+ - [`src/ezo_control.h`](./src/ezo_control.h)
201
+ - [`src/ezo_parse.h`](./src/ezo_parse.h)
202
+ - [`src/ezo_i2c.h`](./src/ezo_i2c.h)
203
+ - [`src/ezo_i2c.hpp`](./src/ezo_i2c.hpp)
204
+ - [`src/ezo_i2c_arduino_wire.h`](./src/ezo_i2c_arduino_wire.h)
205
+ - [`src/ezo_i2c_linux_i2c.h`](./src/ezo_i2c_linux_i2c.h)
206
+ - [`src/ezo_orp.h`](./src/ezo_orp.h)
207
+ - [`src/ezo_ph.h`](./src/ezo_ph.h)
208
+ - [`src/ezo_product.h`](./src/ezo_product.h)
209
+ - [`src/ezo_rtd.h`](./src/ezo_rtd.h)
210
+ - [`src/ezo_schema.h`](./src/ezo_schema.h)
211
+ - [`src/ezo_uart.h`](./src/ezo_uart.h)
212
+ - [`src/ezo_uart_posix_serial.h`](./src/ezo_uart_posix_serial.h)
213
+ - [`src/ezo_uart_arduino_stream.h`](./src/ezo_uart_arduino_stream.h)
214
+
215
+ Primary implementation files:
216
+
217
+ - [`src/ezo.c`](./src/ezo.c)
218
+ - [`src/ezo_calibration_transfer.c`](./src/ezo_calibration_transfer.c)
219
+ - [`src/ezo_common.c`](./src/ezo_common.c)
220
+ - [`src/ezo_control.c`](./src/ezo_control.c)
221
+ - [`src/ezo_do.c`](./src/ezo_do.c)
222
+ - [`src/ezo_ec.c`](./src/ezo_ec.c)
223
+ - [`src/ezo_hum.c`](./src/ezo_hum.c)
224
+ - [`src/ezo_i2c.c`](./src/ezo_i2c.c)
225
+ - [`src/ezo_i2c_arduino_wire.cpp`](./src/ezo_i2c_arduino_wire.cpp)
226
+ - [`platform/linux/ezo_i2c_linux_i2c.c`](./platform/linux/ezo_i2c_linux_i2c.c)
227
+ - [`src/ezo_orp.c`](./src/ezo_orp.c)
228
+ - [`src/ezo_ph.c`](./src/ezo_ph.c)
229
+ - [`src/ezo_parse.c`](./src/ezo_parse.c)
230
+ - [`src/ezo_product.c`](./src/ezo_product.c)
231
+ - [`src/ezo_rtd.c`](./src/ezo_rtd.c)
232
+ - [`src/ezo_schema.c`](./src/ezo_schema.c)
233
+ - [`src/ezo_uart.c`](./src/ezo_uart.c)
234
+ - [`platform/linux/ezo_uart_posix_serial.c`](./platform/linux/ezo_uart_posix_serial.c)
235
+ - [`src/ezo_uart_arduino_stream.cpp`](./src/ezo_uart_arduino_stream.cpp)
236
+
237
+ ## Docs
238
+
239
+ - [`docs/ezo/README.md`](./docs/ezo/README.md): curated EZO product and protocol context for this repo
240
+ - [`docs/public-api-layers.md`](./docs/public-api-layers.md): where to start by API layer and use case
241
+ - [`docs/support-matrix.md`](./docs/support-matrix.md): tracked public support statement and tier policy
242
+ - [`docs/product-onboarding.md`](./docs/product-onboarding.md): maintainer checklist for onboarding another product family
243
+ - [`docs/examples.md`](./docs/examples.md): staged example chooser and full matrix
244
+ - [`bindings/python/README.md`](./bindings/python/README.md): canonical Linux Python bindings doc
245
+ - [`docs/architecture.md`](./docs/architecture.md): structure, boundaries, packaging, validation
246
+ - [`docs/api-contract.md`](./docs/api-contract.md): behavioral contract for the public API
247
+ - [`docs/canonical-library-direction.md`](./docs/canonical-library-direction.md): stable long-term direction for the canonical product-aware library
248
+ - [`CHANGELOG.md`](./CHANGELOG.md): tracked change history
249
+
250
+ ## Scope Notes
251
+
252
+ Intentionally out of scope for the current baseline:
253
+
254
+ - async/state-machine behavior
255
+ - hidden reconnect or resynchronization workflows around rebooting, sleep, or mode changes
256
+ - hidden retries or hidden delays
257
+ - UART C++ wrapper
@@ -0,0 +1,106 @@
1
+ # `ezo-driver` Python bindings
2
+
3
+ Linux-only Python bindings for the canonical `ezo-driver` C library.
4
+
5
+ ## Support statement
6
+
7
+ - Supported host platform: Linux
8
+ - Supported transports: Linux I2C and POSIX UART
9
+ - Supported Python install mode: `pip install ezo-driver` from PyPI, with wheels for x86_64 and aarch64 Linux on Python 3.9+; other platforms build from the sdist or the repo checkout
10
+ - Public package name: `ezo-driver`
11
+ - Public import package: `ezo_driver`
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ python -m pip install ezo-driver
17
+ ```
18
+
19
+ PyPI carries `cp39-abi3` manylinux wheels for x86_64 and aarch64 Linux, so any CPython 3.9 or newer installs one without a compiler, a 64-bit Raspberry Pi included. On other platforms pip builds from the sdist, which needs a C compiler and the Python headers.
20
+
21
+ From the repo root:
22
+
23
+ ```bash
24
+ python -m pip install .
25
+ ```
26
+
27
+ For development, install it editable with the test dependencies:
28
+
29
+ ```bash
30
+ python -m pip install -e ".[test]"
31
+ python -m pytest bindings/python/tests
32
+ ```
33
+
34
+ The packaging lives at the repo root (`pyproject.toml`, `setup.py`, `setup.cfg`, `MANIFEST.in`) so the sdist carries the C sources from `src/` and `platform/linux/` and a wheel can be built from it. `setup.cfg` tags the wheel `cp39-abi3`: cffi builds `ezo_driver._native` against the limited C API, so one wheel per architecture covers every supported Python. The release workflow builds the wheels with cibuildwheel (`[tool.cibuildwheel]` in `pyproject.toml`) and publishes them to PyPI.
35
+
36
+ The shipped `ezo_driver._native` module holds the C core and the Linux transports only. The tests drive it through the fake transports from `tests/fakes/`, which `bindings/python/tests/_fakes.py` compiles into a separate test-only cffi module in a temporary directory each session; this needs a C compiler and the `test` extra. The C sources are found in the checkout that holds the tests; when the tests run from a copy elsewhere, point `EZO_DRIVER_SOURCE_DIR` at the checkout.
37
+
38
+ ## Public modules
39
+
40
+ - `ezo_driver.errors`
41
+ - `ezo_driver.enums`
42
+ - `ezo_driver.types`
43
+ - `ezo_driver.base`
44
+ - `ezo_driver.i2c`
45
+ - `ezo_driver.uart`
46
+ - `ezo_driver.product`
47
+ - `ezo_driver.parse`
48
+ - `ezo_driver.schema`
49
+ - `ezo_driver.control`
50
+ - `ezo_driver.calibration_transfer`
51
+ - `ezo_driver.ph`
52
+ - `ezo_driver.orp`
53
+ - `ezo_driver.rtd`
54
+ - `ezo_driver.ec`
55
+ - `ezo_driver.do`
56
+ - `ezo_driver.hum`
57
+
58
+ Top-level `ezo_driver` exports only the stable transport constructors, exception hierarchy, and shared enums/types.
59
+
60
+ ## I2C example
61
+
62
+ ```python
63
+ from ezo_driver import LinuxI2CDevice, ProductId
64
+ from ezo_driver import control, ph
65
+
66
+ with LinuxI2CDevice(bus=1, address=0x63) as dev:
67
+ wait_ms = control.send_info_query_i2c(dev, ProductId.PH)
68
+ info = control.read_info_i2c(dev)
69
+
70
+ wait_ms = ph.send_read_i2c(dev)
71
+ reading = ph.read_response_i2c(dev)
72
+
73
+ print(info)
74
+ print(reading)
75
+ ```
76
+
77
+ ## UART example
78
+
79
+ ```python
80
+ from ezo_driver import LinuxUARTDevice, ProductId
81
+ from ezo_driver import control, ph
82
+
83
+ with LinuxUARTDevice("/dev/ttyUSB0", baud=9600, read_timeout_ms=1000) as dev:
84
+ wait_ms = control.send_info_query_uart(dev, ProductId.PH)
85
+ info = control.read_info_uart(dev)
86
+
87
+ wait_ms = ph.send_read_uart(dev)
88
+ reading = ph.read_response_uart(dev)
89
+
90
+ print(info)
91
+ print(reading)
92
+ ```
93
+
94
+ ## Design constraints
95
+
96
+ - The Python layer stays transport-explicit. There is no unified device abstraction.
97
+ - The bindings mirror the C library concepts instead of exposing buffer management.
98
+ - Timing hints are returned as integer milliseconds.
99
+ - I2C device status codes and UART response kinds stay explicit values in the API.
100
+
101
+ ## Non-goals
102
+
103
+ - No implicit sleeps
104
+ - No implicit retries
105
+ - No implicit reconnect/resynchronization
106
+ - No workflow wrapper above the canonical transport and product helpers
@@ -0,0 +1,54 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from cffi import FFI
6
+
7
+
8
+ THIS_DIR = Path(__file__).resolve().parent
9
+ REPO_ROOT = THIS_DIR.parent.parent
10
+ SRC_DIR = REPO_ROOT / "src"
11
+ LINUX_DIR = REPO_ROOT / "platform" / "linux"
12
+ CDEF_DIR = THIS_DIR / "cdef"
13
+
14
+ # The headers behind the cdef. The test-only fakes module (bindings/python/
15
+ # tests/_fakes.py) includes this ffibuilder and compiles against the same list.
16
+ HEADERS = [
17
+ "ezo.h",
18
+ "ezo_common.h",
19
+ "ezo_i2c.h",
20
+ "ezo_uart.h",
21
+ "ezo_product.h",
22
+ "ezo_parse.h",
23
+ "ezo_schema.h",
24
+ "ezo_control.h",
25
+ "ezo_calibration_transfer.h",
26
+ "ezo_ph.h",
27
+ "ezo_orp.h",
28
+ "ezo_rtd.h",
29
+ "ezo_ec.h",
30
+ "ezo_do.h",
31
+ "ezo_hum.h",
32
+ "ezo_linux_device.h",
33
+ ]
34
+ INCLUDE_DIRS = [str(REPO_ROOT), str(SRC_DIR), str(LINUX_DIR)]
35
+
36
+ ffibuilder = FFI()
37
+ ffibuilder.cdef((CDEF_DIR / "ezo_api.h").read_text(encoding="ascii"))
38
+
39
+ ffibuilder.set_source(
40
+ "ezo_driver._native",
41
+ '\n'.join(f'#include "{header}"' for header in HEADERS),
42
+ sources=[
43
+ *sorted(str(path) for path in SRC_DIR.glob("*.c")),
44
+ str(LINUX_DIR / "ezo_i2c_linux_i2c.c"),
45
+ str(LINUX_DIR / "ezo_uart_posix_serial.c"),
46
+ str(LINUX_DIR / "ezo_linux_device.c"),
47
+ ],
48
+ include_dirs=INCLUDE_DIRS,
49
+ define_macros=[("_GNU_SOURCE", "1")],
50
+ )
51
+
52
+
53
+ if __name__ == "__main__":
54
+ ffibuilder.compile(verbose=True)