adafruit-stemma-detect 1.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 (158) hide show
  1. adafruit_stemma_detect-1.0.0/LICENSE +21 -0
  2. adafruit_stemma_detect-1.0.0/PKG-INFO +430 -0
  3. adafruit_stemma_detect-1.0.0/README.rst +398 -0
  4. adafruit_stemma_detect-1.0.0/adafruit_stemma_detect.egg-info/PKG-INFO +430 -0
  5. adafruit_stemma_detect-1.0.0/adafruit_stemma_detect.egg-info/SOURCES.txt +156 -0
  6. adafruit_stemma_detect-1.0.0/adafruit_stemma_detect.egg-info/dependency_links.txt +1 -0
  7. adafruit_stemma_detect-1.0.0/adafruit_stemma_detect.egg-info/entry_points.txt +2 -0
  8. adafruit_stemma_detect-1.0.0/adafruit_stemma_detect.egg-info/requires.txt +12 -0
  9. adafruit_stemma_detect-1.0.0/adafruit_stemma_detect.egg-info/top_level.txt +1 -0
  10. adafruit_stemma_detect-1.0.0/pyproject.toml +61 -0
  11. adafruit_stemma_detect-1.0.0/setup.cfg +4 -0
  12. adafruit_stemma_detect-1.0.0/stemma_detect/__init__.py +49 -0
  13. adafruit_stemma_detect-1.0.0/stemma_detect/__main__.py +3 -0
  14. adafruit_stemma_detect-1.0.0/stemma_detect/_version.py +3 -0
  15. adafruit_stemma_detect-1.0.0/stemma_detect/bus.py +107 -0
  16. adafruit_stemma_detect-1.0.0/stemma_detect/catalog.py +79 -0
  17. adafruit_stemma_detect-1.0.0/stemma_detect/chips/__init__.py +1 -0
  18. adafruit_stemma_detect-1.0.0/stemma_detect/chips/_possible.py +14 -0
  19. adafruit_stemma_detect-1.0.0/stemma_detect/chips/_sensirion.py +41 -0
  20. adafruit_stemma_detect-1.0.0/stemma_detect/chips/adt7410.py +12 -0
  21. adafruit_stemma_detect-1.0.0/stemma_detect/chips/adxl34x.py +16 -0
  22. adafruit_stemma_detect-1.0.0/stemma_detect/chips/adxl37x.py +16 -0
  23. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ags02ma.py +28 -0
  24. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ahtx0.py +15 -0
  25. adafruit_stemma_detect-1.0.0/stemma_detect/chips/am2320.py +32 -0
  26. adafruit_stemma_detect-1.0.0/stemma_detect/chips/apds9960.py +14 -0
  27. adafruit_stemma_detect-1.0.0/stemma_detect/chips/apds9999.py +10 -0
  28. adafruit_stemma_detect-1.0.0/stemma_detect/chips/as5600.py +21 -0
  29. adafruit_stemma_detect-1.0.0/stemma_detect/chips/as726x.py +39 -0
  30. adafruit_stemma_detect-1.0.0/stemma_detect/chips/as7331.py +13 -0
  31. adafruit_stemma_detect-1.0.0/stemma_detect/chips/as7341.py +12 -0
  32. adafruit_stemma_detect-1.0.0/stemma_detect/chips/as7343.py +24 -0
  33. adafruit_stemma_detect-1.0.0/stemma_detect/chips/aw9523.py +14 -0
  34. adafruit_stemma_detect-1.0.0/stemma_detect/chips/bh1750.py +8 -0
  35. adafruit_stemma_detect-1.0.0/stemma_detect/chips/bme280.py +27 -0
  36. adafruit_stemma_detect-1.0.0/stemma_detect/chips/bme680.py +27 -0
  37. adafruit_stemma_detect-1.0.0/stemma_detect/chips/bmp280.py +26 -0
  38. adafruit_stemma_detect-1.0.0/stemma_detect/chips/bmp3xx.py +45 -0
  39. adafruit_stemma_detect-1.0.0/stemma_detect/chips/bmp5xx.py +18 -0
  40. adafruit_stemma_detect-1.0.0/stemma_detect/chips/bno055.py +29 -0
  41. adafruit_stemma_detect-1.0.0/stemma_detect/chips/bno08x.py +8 -0
  42. adafruit_stemma_detect-1.0.0/stemma_detect/chips/cap1188.py +17 -0
  43. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ccs811.py +14 -0
  44. adafruit_stemma_detect-1.0.0/stemma_detect/chips/cst8xx.py +44 -0
  45. adafruit_stemma_detect-1.0.0/stemma_detect/chips/dps310.py +14 -0
  46. adafruit_stemma_detect-1.0.0/stemma_detect/chips/drv2605.py +26 -0
  47. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ds3502.py +20 -0
  48. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ens160.py +12 -0
  49. adafruit_stemma_detect-1.0.0/stemma_detect/chips/fxas21002c.py +9 -0
  50. adafruit_stemma_detect-1.0.0/stemma_detect/chips/fxos8700.py +9 -0
  51. adafruit_stemma_detect-1.0.0/stemma_detect/chips/guvx_i2c.py +9 -0
  52. adafruit_stemma_detect-1.0.0/stemma_detect/chips/hdc302x.py +28 -0
  53. adafruit_stemma_detect-1.0.0/stemma_detect/chips/hts221.py +10 -0
  54. adafruit_stemma_detect-1.0.0/stemma_detect/chips/htu21d.py +27 -0
  55. adafruit_stemma_detect-1.0.0/stemma_detect/chips/htu31d.py +15 -0
  56. adafruit_stemma_detect-1.0.0/stemma_detect/chips/icm20x.py +19 -0
  57. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ina219.py +24 -0
  58. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ina228.py +14 -0
  59. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ina23x.py +23 -0
  60. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ina260.py +14 -0
  61. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ina3221.py +26 -0
  62. adafruit_stemma_detect-1.0.0/stemma_detect/chips/l3gd20.py +17 -0
  63. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lc709203f.py +45 -0
  64. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lidarlite.py +28 -0
  65. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lis2mdl.py +10 -0
  66. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lis331.py +10 -0
  67. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lis3dh.py +12 -0
  68. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lis3mdl.py +10 -0
  69. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lps28.py +10 -0
  70. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lps2x.py +21 -0
  71. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lps35hw.py +18 -0
  72. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lsm303_accel.py +7 -0
  73. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lsm303dlh_mag.py +10 -0
  74. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lsm6ds.py +18 -0
  75. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lsm9ds0.py +14 -0
  76. adafruit_stemma_detect-1.0.0/stemma_detect/chips/lsm9ds1.py +11 -0
  77. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ltr329_ltr303.py +32 -0
  78. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ltr390.py +10 -0
  79. adafruit_stemma_detect-1.0.0/stemma_detect/chips/max1704x.py +12 -0
  80. adafruit_stemma_detect-1.0.0/stemma_detect/chips/max44009.py +16 -0
  81. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mcp3421.py +34 -0
  82. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mcp9600.py +14 -0
  83. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mcp9808.py +33 -0
  84. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mlx90393.py +22 -0
  85. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mlx90395.py +15 -0
  86. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mlx90614.py +30 -0
  87. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mlx90632.py +36 -0
  88. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mlx90640.py +16 -0
  89. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mma8451.py +10 -0
  90. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mmc56x3.py +14 -0
  91. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mpl115a2.py +16 -0
  92. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mpl3115a2.py +9 -0
  93. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mpr121.py +27 -0
  94. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mprls.py +14 -0
  95. adafruit_stemma_detect-1.0.0/stemma_detect/chips/mpu6050.py +13 -0
  96. adafruit_stemma_detect-1.0.0/stemma_detect/chips/ms8607.py +60 -0
  97. adafruit_stemma_detect-1.0.0/stemma_detect/chips/msa301.py +10 -0
  98. adafruit_stemma_detect-1.0.0/stemma_detect/chips/opt4048.py +9 -0
  99. adafruit_stemma_detect-1.0.0/stemma_detect/chips/pa1010d.py +41 -0
  100. adafruit_stemma_detect-1.0.0/stemma_detect/chips/pcf8591.py +17 -0
  101. adafruit_stemma_detect-1.0.0/stemma_detect/chips/pct2075.py +25 -0
  102. adafruit_stemma_detect-1.0.0/stemma_detect/chips/pmsa003i.py +37 -0
  103. adafruit_stemma_detect-1.0.0/stemma_detect/chips/qmc5883p.py +10 -0
  104. adafruit_stemma_detect-1.0.0/stemma_detect/chips/scd30.py +23 -0
  105. adafruit_stemma_detect-1.0.0/stemma_detect/chips/scd4x.py +39 -0
  106. adafruit_stemma_detect-1.0.0/stemma_detect/chips/seesaw.py +48 -0
  107. adafruit_stemma_detect-1.0.0/stemma_detect/chips/sen6x.py +30 -0
  108. adafruit_stemma_detect-1.0.0/stemma_detect/chips/sgp30.py +28 -0
  109. adafruit_stemma_detect-1.0.0/stemma_detect/chips/sgp40.py +40 -0
  110. adafruit_stemma_detect-1.0.0/stemma_detect/chips/sgp41.py +23 -0
  111. adafruit_stemma_detect-1.0.0/stemma_detect/chips/sht31d.py +24 -0
  112. adafruit_stemma_detect-1.0.0/stemma_detect/chips/sht4x.py +25 -0
  113. adafruit_stemma_detect-1.0.0/stemma_detect/chips/shtc3.py +28 -0
  114. adafruit_stemma_detect-1.0.0/stemma_detect/chips/si1145.py +12 -0
  115. adafruit_stemma_detect-1.0.0/stemma_detect/chips/si7021.py +27 -0
  116. adafruit_stemma_detect-1.0.0/stemma_detect/chips/spa06_003.py +10 -0
  117. adafruit_stemma_detect-1.0.0/stemma_detect/chips/stcc4.py +29 -0
  118. adafruit_stemma_detect-1.0.0/stemma_detect/chips/sths34pf80.py +9 -0
  119. adafruit_stemma_detect-1.0.0/stemma_detect/chips/tc74.py +15 -0
  120. adafruit_stemma_detect-1.0.0/stemma_detect/chips/tcs3430.py +9 -0
  121. adafruit_stemma_detect-1.0.0/stemma_detect/chips/tcs34725.py +12 -0
  122. adafruit_stemma_detect-1.0.0/stemma_detect/chips/tlv493d.py +7 -0
  123. adafruit_stemma_detect-1.0.0/stemma_detect/chips/tmag5273.py +25 -0
  124. adafruit_stemma_detect-1.0.0/stemma_detect/chips/tmp006.py +10 -0
  125. adafruit_stemma_detect-1.0.0/stemma_detect/chips/tmp007.py +10 -0
  126. adafruit_stemma_detect-1.0.0/stemma_detect/chips/tmp117.py +12 -0
  127. adafruit_stemma_detect-1.0.0/stemma_detect/chips/tsc2007.py +20 -0
  128. adafruit_stemma_detect-1.0.0/stemma_detect/chips/tsl2561.py +15 -0
  129. adafruit_stemma_detect-1.0.0/stemma_detect/chips/tsl2591.py +12 -0
  130. adafruit_stemma_detect-1.0.0/stemma_detect/chips/vcnl4010.py +14 -0
  131. adafruit_stemma_detect-1.0.0/stemma_detect/chips/vcnl4020.py +14 -0
  132. adafruit_stemma_detect-1.0.0/stemma_detect/chips/vcnl4030.py +20 -0
  133. adafruit_stemma_detect-1.0.0/stemma_detect/chips/vcnl4040.py +14 -0
  134. adafruit_stemma_detect-1.0.0/stemma_detect/chips/vcnl4200.py +9 -0
  135. adafruit_stemma_detect-1.0.0/stemma_detect/chips/veml6070.py +18 -0
  136. adafruit_stemma_detect-1.0.0/stemma_detect/chips/veml6075.py +14 -0
  137. adafruit_stemma_detect-1.0.0/stemma_detect/chips/veml7700.py +24 -0
  138. adafruit_stemma_detect-1.0.0/stemma_detect/chips/vl53l0x.py +14 -0
  139. adafruit_stemma_detect-1.0.0/stemma_detect/chips/vl53l1x.py +15 -0
  140. adafruit_stemma_detect-1.0.0/stemma_detect/chips/vl53l4cd.py +15 -0
  141. adafruit_stemma_detect-1.0.0/stemma_detect/chips/vl6180x.py +15 -0
  142. adafruit_stemma_detect-1.0.0/stemma_detect/cli.py +194 -0
  143. adafruit_stemma_detect-1.0.0/stemma_detect/installer.py +163 -0
  144. adafruit_stemma_detect-1.0.0/stemma_detect/mux.py +96 -0
  145. adafruit_stemma_detect-1.0.0/stemma_detect/result.py +83 -0
  146. adafruit_stemma_detect-1.0.0/stemma_detect/runtime.py +5 -0
  147. adafruit_stemma_detect-1.0.0/stemma_detect/scanner.py +335 -0
  148. adafruit_stemma_detect-1.0.0/stemma_detect/serialization.py +84 -0
  149. adafruit_stemma_detect-1.0.0/stemma_detect/signature.py +241 -0
  150. adafruit_stemma_detect-1.0.0/tests/test_bus.py +75 -0
  151. adafruit_stemma_detect-1.0.0/tests/test_catalog.py +178 -0
  152. adafruit_stemma_detect-1.0.0/tests/test_cli.py +338 -0
  153. adafruit_stemma_detect-1.0.0/tests/test_installer.py +145 -0
  154. adafruit_stemma_detect-1.0.0/tests/test_mux.py +197 -0
  155. adafruit_stemma_detect-1.0.0/tests/test_probes.py +968 -0
  156. adafruit_stemma_detect-1.0.0/tests/test_scanner.py +354 -0
  157. adafruit_stemma_detect-1.0.0/tests/test_serialization.py +109 -0
  158. adafruit_stemma_detect-1.0.0/tests/test_signature.py +122 -0
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Melissa LeBlanc-Williams for Adafruit Industries
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,430 @@
1
+ Metadata-Version: 2.4
2
+ Name: adafruit-stemma-detect
3
+ Version: 1.0.0
4
+ Summary: Detect supported Adafruit STEMMA QT sensors and install their CircuitPython drivers
5
+ Author-email: Adafruit Industries <circuitpython@adafruit.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/adafruit/Adafruit_Stemma_Detect
8
+ Project-URL: Documentation, https://docs.circuitpython.org/projects/stemma-detect/en/latest/
9
+ Project-URL: Issues, https://github.com/adafruit/Adafruit_Stemma_Detect/issues
10
+ Project-URL: Source, https://github.com/adafruit/Adafruit_Stemma_Detect
11
+ Keywords: adafruit,blinka,circuitpython,i2c,raspberry-pi,stemma-qt
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Topic :: System :: Hardware
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/x-rst
20
+ License-File: LICENSE
21
+ Requires-Dist: Adafruit-Python-Shell>=1.14.0
22
+ Requires-Dist: smbus2>=0.4
23
+ Provides-Extra: dev
24
+ Requires-Dist: build>=1.2; extra == "dev"
25
+ Requires-Dist: pre-commit>=4; extra == "dev"
26
+ Requires-Dist: ruff>=0.11; extra == "dev"
27
+ Requires-Dist: twine>=6; extra == "dev"
28
+ Provides-Extra: docs
29
+ Requires-Dist: Sphinx>=7; extra == "docs"
30
+ Requires-Dist: sphinx-rtd-theme>=3; extra == "docs"
31
+ Dynamic: license-file
32
+
33
+ Introduction
34
+ ============
35
+
36
+ .. image:: https://readthedocs.org/projects/adafruit-stemma-detect/badge/?version=latest
37
+ :target: https://docs.circuitpython.org/projects/stemma-detect/en/latest/
38
+ :alt: Documentation Status
39
+
40
+ .. image:: https://img.shields.io/discord/327254708534116352.svg
41
+ :target: https://adafru.it/discord
42
+ :alt: Discord
43
+
44
+ .. image:: https://github.com/adafruit/Adafruit_Stemma_Detect/workflows/Build%20CI/badge.svg
45
+ :target: https://github.com/adafruit/Adafruit_Stemma_Detect/actions
46
+ :alt: Build Status
47
+
48
+ .. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
49
+ :target: https://github.com/astral-sh/ruff
50
+ :alt: Code Style: Ruff
51
+
52
+ Detect selected Adafruit STEMMA QT sensors on a Raspberry Pi and optionally install their CircuitPython drivers. It recognizes only sensors with bundled probe modules; it is not a universal I²C device identifier.
53
+
54
+ Each sensor definition contains only:
55
+
56
+ - ``ADDRESSES``
57
+ - ``DEFAULT_ADDRESSES`` (optional for configurable-address sensors)
58
+ - ``PACKAGE``
59
+ - ``PROBE_CONFIDENCE``
60
+ - ``PROBE_RISK`` (optional for probes that send multi-byte addresses or commands)
61
+ - ``probe(bus, address)``
62
+
63
+ Definitions may optionally express a device signature with the helpers in
64
+ ``stemma_detect.signature``. A signature combines several safe, read-only characteristics, such as
65
+ an exact chip ID, reserved-bit patterns, revision values, and nonblank factory calibration data.
66
+ Checks carry weights so results can expose both a categorical confidence and an evidence score.
67
+
68
+ The scanner uses ``smbus2`` for I²C access and Adafruit Python Shell for prompts and streaming installation commands. Individual CircuitPython drivers are imported neither by the scanner nor by chip definitions.
69
+
70
+ Dependencies
71
+ =============
72
+ This driver depends on:
73
+
74
+ * `Adafruit Python Shell <https://github.com/adafruit/Adafruit_Python_Shell>`_
75
+
76
+
77
+ Installing from PyPI
78
+ =====================
79
+
80
+ On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from
81
+ PyPI <https://pypi.org/project/adafruit-stemma-detect/>`_. To install for current user:
82
+
83
+ .. code-block:: shell
84
+
85
+ pip3 install adafruit-stemma-detect
86
+
87
+ To install system-wide (this may be required in some cases):
88
+
89
+ .. code-block:: shell
90
+
91
+ sudo pip3 install adafruit-stemma-detect
92
+
93
+ To install in a virtual environment in your current project:
94
+
95
+ .. code-block:: shell
96
+
97
+ mkdir project-name && cd project-name
98
+ python3 -m venv .env
99
+ source .env/bin/activate
100
+ pip3 install adafruit-stemma-detect
101
+
102
+ Running from a checkout
103
+ =======================
104
+
105
+ .. code-block:: shell
106
+
107
+ python3 -m venv --system-site-packages .venv
108
+ .venv/bin/python -m pip install -e .
109
+ .venv/bin/stemma-scan --bus 1
110
+
111
+ Use ``--install`` to install drivers for definitive matches:
112
+
113
+ .. code-block:: shell
114
+
115
+ .venv/bin/stemma-scan --bus 1 --install
116
+
117
+ Address-only or otherwise ambiguous results are reported but not installed.
118
+
119
+ To be prompted before installing a driver for each possible match, use:
120
+
121
+ .. code-block:: shell
122
+
123
+ .venv/bin/stemma-scan --bus 1 --install --prompt-possible-matches
124
+
125
+ Possible matches default to “no.” This is important because several unrelated devices can share the same I²C address.
126
+
127
+ The complete scan finishes before any prompts are shown. Definitive-capable probes run before possible-only probes at each address. A definitive match claims its I²C address immediately, so possible-only candidates are neither probed nor presented. If no definitive probe matches, the possible candidates are collected. A declined candidate is removed from the results; once one is confirmed, it is retained and all remaining candidates at that address are removed without further prompts.
128
+
129
+ Possible candidates using their default address are prompted before candidates using an alternate
130
+ address. The prompt labels the address as default or alternate when that information is known.
131
+
132
+ Multiplexers
133
+ ============
134
+
135
+ The scanner automatically looks for PCA9546-compatible four-channel and PCA9548/TCA9548A-compatible
136
+ eight-channel multiplexers at ``0x70`` through ``0x77``. Each channel is scanned separately, and
137
+ the route is included in every result:
138
+
139
+ .. code-block:: text
140
+
141
+ MUX: PCA9546 at 0x70 (4 channels)
142
+ MUX: PCA9546 at 0x71 via mux 0x70 channel 1 (4 channels)
143
+ MATCH: VL53L4CD at 0x29 via mux 0x70 channel 2
144
+ MATCH: VL6180X at 0x29 via mux 0x70 channel 1 via mux 0x71 channel 3
145
+
146
+ No option or CircuitPython multiplexer driver is required. Detection and channel selection use the
147
+ project's small I²C bus interface, so this feature does not add Blinka as a dependency.
148
+
149
+ These multiplexers have no identity register. To reduce false identification, the scanner first
150
+ requires a plausible one-byte control value, then verifies that channel-selection writes read back
151
+ with the expected four- or eight-channel mask. The original control value is restored after probing
152
+ and again after the scan. This is still an active probe: an unrelated device at ``0x70`` through
153
+ ``0x77`` with mux-like behavior could be changed.
154
+
155
+ Muxes are discovered recursively to a maximum of eight channel hops. Each nested mux must have an
156
+ address different from every mux upstream of it. Muxes chained with the same address cannot be
157
+ controlled independently because a channel-selection write reaches both devices; change one mux's
158
+ address jumpers before scanning that topology.
159
+
160
+ Using as a library
161
+ ==================
162
+
163
+ The high-level ``detect`` function opens a Raspberry Pi I²C bus, scans it and any compatible
164
+ multiplexers, then closes the bus. It returns structured data without printing, prompting, or
165
+ installing drivers:
166
+
167
+ .. code-block:: python
168
+
169
+ from stemma_detect import detect
170
+
171
+ report = detect(bus_number=1)
172
+
173
+ for detection in report.matches:
174
+ print(detection.name, detection.address_hex, detection.driver_package)
175
+
176
+ for detection in report.possible_matches:
177
+ print("Needs confirmation:", detection.name)
178
+
179
+ Applications that already own an I²C connection can pass any object implementing
180
+ ``I2CBusProtocol``. The built-in catalog is used automatically:
181
+
182
+ .. code-block:: python
183
+
184
+ from stemma_detect import scan_all
185
+
186
+ report = scan_all(my_i2c_bus)
187
+
188
+ Pass ``chips=discover_chips()`` explicitly only when filtering or extending the catalog. Both
189
+ ``detect`` and ``scan_all`` accept ``diagnostic=callback`` for applications that need every probe
190
+ outcome.
191
+
192
+ Installing drivers from a library
193
+ =================================
194
+
195
+ ``create_install_plan`` automatically includes definitive matches. Possible matches are excluded
196
+ unless the application confirms them with a callback. A script that knows its expected hardware
197
+ can match the sensor name, address and mux path:
198
+
199
+ .. code-block:: python
200
+
201
+ from stemma_detect import create_install_plan, detect, install_drivers
202
+
203
+ report = detect(1)
204
+ expected = {
205
+ ((), 0x48): "pcf8591",
206
+ }
207
+
208
+ def confirm_possible(sensor):
209
+ return expected.get((sensor.path, sensor.address)) == sensor.name
210
+
211
+ plan = create_install_plan(report, confirm_possible=confirm_possible)
212
+
213
+ for item in plan:
214
+ state = "installed" if item.installed_version else "not installed"
215
+ print(item.package, state)
216
+
217
+ results = install_drivers(plan)
218
+
219
+ The callback is called only for possible matches. If it confirms two different candidates at the
220
+ same address and mux path, planning raises ``ValueError`` instead of silently selecting one.
221
+ Packages shared by multiple detected sensors are deduplicated. ``install_drivers`` does not print
222
+ or prompt; it returns an ``InstallResult`` for each package with an ``InstallOutcome`` of
223
+ ``INSTALLED``, ``ALREADY_INSTALLED`` or ``FAILED``.
224
+
225
+ JSON output
226
+ ===========
227
+
228
+ Use ``--json`` to write one machine-readable document to standard output. It contains the bus,
229
+ multiplexer topology, detections, confidence, signature evidence and scores, and CircuitPython
230
+ driver installation status. Both integer and hexadecimal forms of each I²C address are included:
231
+
232
+ .. code-block:: shell
233
+
234
+ .venv/bin/stemma-scan --bus 1 --json
235
+ .venv/bin/stemma-scan --bus 1 --json > stemma-scan.json
236
+
237
+ The top-level ``schema_version`` is incremented if a future release makes an incompatible output
238
+ change. JSON mode cannot be combined with ``--install`` or ``--diagnostics`` because prompts,
239
+ installation progress, and transaction traces would make standard output invalid JSON.
240
+
241
+ Library users can serialize an existing report without running another scan:
242
+
243
+ .. code-block:: python
244
+
245
+ data = report.to_dict(bus=1)
246
+ text = report.to_json(bus=1)
247
+
248
+ The existing ``report_to_dict`` and ``report_to_json`` functions remain available for functional
249
+ style code.
250
+
251
+ Diagnostics
252
+ ===========
253
+
254
+ Use ``--diagnostics`` to show every probe attempted, including its safety category, non-matches,
255
+ and I²C errors that are hidden during a normal scan:
256
+
257
+ .. code-block:: shell
258
+
259
+ .venv/bin/stemma-scan --bus 1 --diagnostics
260
+
261
+ An address that does not acknowledge an I²C transaction is reported as ``NOT DETECTED``. The
262
+ ``ERROR`` label is reserved for unexpected failures. Successful transactions include their raw
263
+ write and read bytes so new or mismatched identity probes can be investigated without changing the
264
+ chip module.
265
+
266
+ Definitive probes run before possible-only probes at each address. Within each category, probes are
267
+ ordered from lowest to highest risk: passive reads, ordinary one-byte register reads, then commands
268
+ or multi-byte register addressing. A definitive match prevents all remaining probes from touching
269
+ that address. When only possible matches remain, candidates with more weighted signature evidence
270
+ are reported and prompted first; a factory-default address adds a small score bonus.
271
+
272
+ Known limitations and planned work
273
+ ==================================
274
+
275
+ The CLI is a consumer of the same detection API available to other programs. Library imports do not
276
+ scan hardware, print, prompt, install packages, or exit the process as a side effect. Diagnostics
277
+ and driver installation remain explicit opt-in operations.
278
+
279
+ The scanner cannot resolve two devices responding at the same address. They may corrupt each
280
+ other's identity responses and produce only ambiguous possible matches. Conflicting devices must be
281
+ readdressed or placed on separate multiplexer channels. Automatic mux scanning resolves conflicts
282
+ between different channels, but it cannot resolve a conflict between a root-bus device and a device
283
+ behind a currently selected mux channel.
284
+
285
+ Adafruit's `Troublesome Chips guide
286
+ <https://learn.adafruit.com/i2c-addresses/troublesome-chips>`_ identifies devices with unusual I²C
287
+ behavior that can cause missed detections or communication failures:
288
+
289
+ - AGS02MA requires a 20--30 kHz bus.
290
+ - AM2320 automatically sleeps, making scans unreliable.
291
+ - ATECCx08 requires slow I²C communication when waking from sleep.
292
+ - BNO055 and BNO085 use clock stretching, can violate timing requirements, and may need resets.
293
+ - CCS811 uses clock stretching.
294
+ - LC709203F uses repeated starts, clock stretching, and sleep mode.
295
+ - Older MCP9600 devices can duplicate register data; MCP9600 and MCP9601 also use repeated starts
296
+ and clock stretching and may ignore zero-length scan writes.
297
+ - PN532 uses clock stretching.
298
+
299
+ The current catalog includes AGS02MA, AM2320, BNO055, BNO08x/BNO085, CCS811, LC709203F, and
300
+ MCP9600. A failed probe for one of these chips does not necessarily mean the device is absent.
301
+ Raspberry Pi users should also consult Adafruit's `I²C clock stretching guide
302
+ <https://learn.adafruit.com/circuitpython-on-raspberrypi-linux/i2c-clock-stretching>`_.
303
+
304
+ Supported sensors so far
305
+ ========================
306
+
307
+ The catalog currently contains 122 device definitions: 93 with definitive-capable probes and 29 that
308
+ produce possible matches. Possible matches are never installed without
309
+ ``--prompt-possible-matches`` and user confirmation.
310
+
311
+ Devices with definitive probes include ADT7410, AGS02MA, AM2320, APDS9960, APDS9999, AS726x,
312
+ AS7331, AS7341, AS7343, AW9523, BME280, BME680, BMP280, BMP3xx, BMP5xx, BNO055, CAP1188, CCS811,
313
+ CST8xx, DPS310, DRV2605/DRV2605L, ENS160,
314
+ FXAS21002C, FXOS8700, GUVX I2C, HDC302x, HTS221, HTU21D, ICM20x, INA228,
315
+ INA237/INA238, INA260, INA3221, LC709203F, LIS2MDL, LIS331, LIS3DH, LIS3MDL, LPS2x,
316
+ LPS28, L3GD20, LSM303DLH magnetometer, LSM6DS, LSM9DS0, LSM9DS1, LTR329/LTR303,
317
+ LTR390, MAX1704x, MCP9600, MCP9808, MLX90614, MLX90632, MMA8451, MMC5603,
318
+ MPL3115A2, MPU6050, MS8607, MSA301, OPT4048, PA1010D, PMSA003I, QMC5883P, SCD30, SCD4x, SEN6x,
319
+ Seesaw, SGP30, SGP40, SGP41, SHT31D, SHT4x, SHTC3, SI1145, Si7021, SPA06-003, STCC4,
320
+ STHS34PF80, TCS3430, TCS34725, TMAG5273, TMP006, TMP007, TMP117/TMP119, TSL2561,
321
+ TSL2591, VCNL4030, VCNL4040, VCNL4200, VEML6075, VL53L0X, VL53L1X, VL53L4CD,
322
+ and VL6180X.
323
+
324
+ Possible-match definitions include ADXL34x, ADXL37x, AHTx0, AS5600, BH1750, BNO08x,
325
+ DS3502, HTU31D, INA219, LIDAR-Lite, LPS35HW, LSM303 accelerometer,
326
+ MAX44009, MCP3421, MLX90393, MLX90395, MLX90640, MPL115A2, MPR121, MPRLS,
327
+ PCF8591, PCT2075, TC74, TLV493D, TSC2007, VCNL4010, VCNL4020, VEML6070,
328
+ and VEML7700.
329
+
330
+ Adding a sensor
331
+ ===============
332
+
333
+ Add one module under ``stemma_detect/chips/``. Modules are discovered automatically, so no registry edit is needed.
334
+ Use one module per installable driver family: chips that cannot be distinguished but use the same
335
+ CircuitPython package should share a family definition and produce one detection. The catalog
336
+ requires package names to be unique. Keep separate definitions when ambiguity changes which driver
337
+ would be installed.
338
+
339
+ .. code-block:: python
340
+
341
+ from stemma_detect.result import Confidence, ProbeResult, ProbeRisk
342
+
343
+ ADDRESSES = (0x44,)
344
+ DEFAULT_ADDRESSES = (0x44,) # Optional; single addresses are defaults automatically.
345
+ PACKAGE = "adafruit-circuitpython-example"
346
+ PROBE_CONFIDENCE = Confidence.MATCH
347
+ PROBE_RISK = ProbeRisk.COMMAND # Only for commands or multi-byte addresses.
348
+
349
+ def probe(bus, address):
350
+ value = bus.read_register(address, 0x00, 1)
351
+ if value == b"\x12":
352
+ return ProbeResult.match({"id": value.hex()})
353
+ return ProbeResult.no_match()
354
+
355
+ Keep probes short, non-destructive, and independent of the package they are intended to install.
356
+ Most identity-register probes should omit ``PROBE_RISK`` and use the default register category.
357
+ Address-only definitions automatically use the passive category. Set ``ProbeRisk.COMMAND`` when a
358
+ probe transmits a command or a multi-byte register address that another chip could interpret as a
359
+ write.
360
+ Family probes may pass ``name`` to ``ProbeResult.match()`` when an identity register distinguishes a
361
+ specific chip. Ambiguous IDs should remain at the family level.
362
+
363
+ When a sensor has several useful read-only registers or safe identity commands, prefer a device
364
+ signature over custom probe logic. ``command_response`` supports CRC-protected serial-number and
365
+ feature-set commands while using the same weights and result handling as register checks.
366
+
367
+ .. code-block:: python
368
+
369
+ from stemma_detect.result import Confidence
370
+ from stemma_detect.signature import DeviceSignature, exact, not_blank
371
+
372
+ ADDRESSES = (0x76, 0x77)
373
+ PACKAGE = "adafruit-circuitpython-example"
374
+ PROBE_CONFIDENCE = Confidence.MATCH
375
+
376
+ SIGNATURE = DeviceSignature(
377
+ (
378
+ exact("chip_id", 0xD0, b"\x60", show_value=True, weight=10),
379
+ exact(
380
+ "status_reserved",
381
+ 0xF3,
382
+ b"\x00",
383
+ mask=b"\xF6",
384
+ required=False,
385
+ weight=2,
386
+ ),
387
+ not_blank("calibration", 0x88, 24, required=False, weight=3),
388
+ ),
389
+ match_threshold=15,
390
+ )
391
+
392
+ def probe(bus, address):
393
+ return SIGNATURE.probe(bus, address)
394
+
395
+ Failure of a required check produces ``NO_MATCH``. Supporting checks contribute weight; missing
396
+ supporting evidence lowers the score and may reduce the result to ``POSSIBLE`` without rejecting it.
397
+ The scanner adds one weak point for a known default address but never lets that address bonus turn a
398
+ possible result into a definitive match. Only definitive ``MATCH`` results are installed
399
+ automatically.
400
+
401
+ Scores represent accumulated evidence, not a statistical probability. Weights should be kept
402
+ consistent across definitions: exact identity registers should dominate, while address responses
403
+ and default-address bonuses should remain weak evidence.
404
+
405
+ Only use documented, safe reads: avoid FIFO, read-to-clear, write-only, initialization, reset, and
406
+ measurement commands. Identity and serial-number commands are suitable when their datasheet says
407
+ they do not alter sensor state. Mark command-based probes as ``ProbeRisk.COMMAND``.
408
+ ``not_blank`` is intended for factory-programmed blocks where all-zero and all-``0xFF`` data are
409
+ invalid; it should not be used for ordinary configuration or measurement registers.
410
+
411
+ Development
412
+ ===========
413
+
414
+ .. code-block:: shell
415
+
416
+ python3 -m unittest discover -s tests -v
417
+ ruff check .
418
+ ruff format --check .
419
+
420
+ Contributing
421
+ ============
422
+
423
+ Contributions are welcome! Please read our `Code of Conduct
424
+ <https://github.com/adafruit/Adafruit_Stemma_Detect/blob/main/CODE_OF_CONDUCT.md>`_
425
+ before contributing to help this project stay welcoming
426
+
427
+ License
428
+ =======
429
+
430
+ MIT, see `LICENSE <LICENSE>`_.