sds200 0.8.2__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 (79) hide show
  1. sds200-0.8.2/.github/ISSUE_TEMPLATE/bug_report.yml +91 -0
  2. sds200-0.8.2/.github/ISSUE_TEMPLATE/config.yml +8 -0
  3. sds200-0.8.2/.github/ISSUE_TEMPLATE/feature_request.yml +54 -0
  4. sds200-0.8.2/.github/PULL_REQUEST_TEMPLATE.md +25 -0
  5. sds200-0.8.2/.github/dependabot.yml +15 -0
  6. sds200-0.8.2/.github/workflows/ci.yml +69 -0
  7. sds200-0.8.2/.gitignore +12 -0
  8. sds200-0.8.2/CHANGELOG.md +195 -0
  9. sds200-0.8.2/CODE_OF_CONDUCT.md +27 -0
  10. sds200-0.8.2/CONTRIBUTING.md +108 -0
  11. sds200-0.8.2/LICENSE +17 -0
  12. sds200-0.8.2/PKG-INFO +397 -0
  13. sds200-0.8.2/README.md +360 -0
  14. sds200-0.8.2/SECURITY.md +55 -0
  15. sds200-0.8.2/SUPPORT.md +62 -0
  16. sds200-0.8.2/contrib/udev/70-uniden-sds.rules +7 -0
  17. sds200-0.8.2/docs/audio.md +21 -0
  18. sds200-0.8.2/docs/discovery-and-profiles.md +107 -0
  19. sds200-0.8.2/docs/fallback-profiles.md +60 -0
  20. sds200-0.8.2/docs/releasing.md +117 -0
  21. sds200-0.8.2/docs/reliability.md +76 -0
  22. sds200-0.8.2/docs/supported-models.md +87 -0
  23. sds200-0.8.2/docs/transports.md +130 -0
  24. sds200-0.8.2/docs/udev.md +37 -0
  25. sds200-0.8.2/examples/basic_info.py +8 -0
  26. sds200-0.8.2/examples/handheld_battery.py +10 -0
  27. sds200-0.8.2/examples/health_check.py +5 -0
  28. sds200-0.8.2/examples/live_monitor.py +9 -0
  29. sds200-0.8.2/examples/network_discovery.py +4 -0
  30. sds200-0.8.2/examples/network_monitor.py +8 -0
  31. sds200-0.8.2/examples/raw_monitor.py +5 -0
  32. sds200-0.8.2/pyproject.toml +83 -0
  33. sds200-0.8.2/scanner.trace +291 -0
  34. sds200-0.8.2/scripts/check_docs.py +98 -0
  35. sds200-0.8.2/sdstest.py +27 -0
  36. sds200-0.8.2/src/sds200/__init__.py +145 -0
  37. sds200-0.8.2/src/sds200/audio.py +77 -0
  38. sds200-0.8.2/src/sds200/cli.py +946 -0
  39. sds200-0.8.2/src/sds200/commands.py +222 -0
  40. sds200-0.8.2/src/sds200/completion.py +96 -0
  41. sds200-0.8.2/src/sds200/device.py +83 -0
  42. sds200-0.8.2/src/sds200/discovery.py +264 -0
  43. sds200-0.8.2/src/sds200/events.py +37 -0
  44. sds200-0.8.2/src/sds200/exceptions.py +42 -0
  45. sds200-0.8.2/src/sds200/fallback.py +438 -0
  46. sds200-0.8.2/src/sds200/models.py +442 -0
  47. sds200-0.8.2/src/sds200/monitor.py +69 -0
  48. sds200-0.8.2/src/sds200/network.py +660 -0
  49. sds200-0.8.2/src/sds200/parser.py +141 -0
  50. sds200-0.8.2/src/sds200/profiles.py +479 -0
  51. sds200-0.8.2/src/sds200/py.typed +0 -0
  52. sds200-0.8.2/src/sds200/radio.py +810 -0
  53. sds200-0.8.2/src/sds200/reliability.py +190 -0
  54. sds200-0.8.2/src/sds200/scanner.py +88 -0
  55. sds200-0.8.2/src/sds200/state.py +83 -0
  56. sds200-0.8.2/src/sds200/trace.py +31 -0
  57. sds200-0.8.2/src/sds200/transport.py +453 -0
  58. sds200-0.8.2/src/sds200/xml_protocol.py +78 -0
  59. sds200-0.8.2/tests/__init__.py +0 -0
  60. sds200-0.8.2/tests/fakes.py +217 -0
  61. sds200-0.8.2/tests/test_audio.py +18 -0
  62. sds200-0.8.2/tests/test_cli.py +81 -0
  63. sds200-0.8.2/tests/test_commands.py +45 -0
  64. sds200-0.8.2/tests/test_completion.py +317 -0
  65. sds200-0.8.2/tests/test_device.py +42 -0
  66. sds200-0.8.2/tests/test_discovery.py +172 -0
  67. sds200-0.8.2/tests/test_fallback.py +159 -0
  68. sds200-0.8.2/tests/test_monitor.py +38 -0
  69. sds200-0.8.2/tests/test_network.py +330 -0
  70. sds200-0.8.2/tests/test_parser.py +79 -0
  71. sds200-0.8.2/tests/test_profiles.py +219 -0
  72. sds200-0.8.2/tests/test_psi.py +101 -0
  73. sds200-0.8.2/tests/test_radio.py +310 -0
  74. sds200-0.8.2/tests/test_reliability.py +106 -0
  75. sds200-0.8.2/tests/test_scanner.py +47 -0
  76. sds200-0.8.2/tests/test_state.py +36 -0
  77. sds200-0.8.2/tests/test_trace.py +16 -0
  78. sds200-0.8.2/tests/test_transport.py +100 -0
  79. sds200-0.8.2/tests/test_xml_protocol.py +47 -0
@@ -0,0 +1,91 @@
1
+ name: Bug report
2
+ description: Report reproducible incorrect behavior
3
+ title: "[Bug]: "
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thanks for reporting a problem. Remove private scanner and network
9
+ information from logs before submitting.
10
+ - type: textarea
11
+ id: summary
12
+ attributes:
13
+ label: What happened?
14
+ description: Describe the observed behavior and what you expected.
15
+ validations:
16
+ required: true
17
+ - type: input
18
+ id: version
19
+ attributes:
20
+ label: sds200-python version or commit
21
+ placeholder: "0.5.3 or commit SHA"
22
+ validations:
23
+ required: true
24
+ - type: input
25
+ id: python
26
+ attributes:
27
+ label: Python version
28
+ placeholder: "3.14.4"
29
+ validations:
30
+ required: true
31
+ - type: input
32
+ id: platform
33
+ attributes:
34
+ label: Operating system
35
+ placeholder: "Ubuntu 26.04"
36
+ validations:
37
+ required: true
38
+ - type: dropdown
39
+ id: model
40
+ attributes:
41
+ label: Scanner model
42
+ options:
43
+ - SDS100
44
+ - SDS150
45
+ - SDS200
46
+ - Other or unknown
47
+ validations:
48
+ required: true
49
+ - type: input
50
+ id: firmware
51
+ attributes:
52
+ label: Scanner firmware
53
+ placeholder: "1.26.01"
54
+ validations:
55
+ required: true
56
+ - type: dropdown
57
+ id: transport
58
+ attributes:
59
+ label: Connection
60
+ options:
61
+ - USB serial
62
+ - SDS200 Ethernet UDP
63
+ - USB and SDS200 Ethernet
64
+ - Not applicable
65
+ validations:
66
+ required: true
67
+ - type: textarea
68
+ id: reproduce
69
+ attributes:
70
+ label: Reproduction steps
71
+ description: Include the exact command or a minimal Python example.
72
+ render: shell
73
+ validations:
74
+ required: true
75
+ - type: textarea
76
+ id: output
77
+ attributes:
78
+ label: Error output or sanitized trace
79
+ description: Redact private IPs, system names, channel names, and unit identifiers.
80
+ render: text
81
+ - type: checkboxes
82
+ id: checks
83
+ attributes:
84
+ label: Checklist
85
+ options:
86
+ - label: I searched existing issues.
87
+ required: true
88
+ - label: I tested the latest default branch or release.
89
+ required: true
90
+ - label: I removed sensitive scanner and network information.
91
+ required: true
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Support and troubleshooting
4
+ url: https://github.com/stevenboyd78/sds200-python/blob/main/SUPPORT.md
5
+ about: Review the support guide before opening a bug.
6
+ - name: Security vulnerability
7
+ url: https://github.com/stevenboyd78/sds200-python/blob/main/SECURITY.md
8
+ about: Do not disclose vulnerabilities in a public issue.
@@ -0,0 +1,54 @@
1
+ name: Feature request
2
+ description: Propose a focused addition or improvement
3
+ title: "[Feature]: "
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: Problem or use case
9
+ description: What limitation are you trying to solve?
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: proposal
14
+ attributes:
15
+ label: Proposed behavior
16
+ description: Describe the API, CLI, protocol, or documentation change.
17
+ validations:
18
+ required: true
19
+ - type: dropdown
20
+ id: area
21
+ attributes:
22
+ label: Area
23
+ options:
24
+ - Python API
25
+ - CLI
26
+ - USB transport
27
+ - Ethernet transport
28
+ - Protocol parsing
29
+ - Discovery and profiles
30
+ - Monitoring and events
31
+ - Documentation
32
+ - Testing and tooling
33
+ - Other
34
+ validations:
35
+ required: true
36
+ - type: textarea
37
+ id: alternatives
38
+ attributes:
39
+ label: Alternatives considered
40
+ description: Describe current workarounds or other designs.
41
+ - type: textarea
42
+ id: hardware
43
+ attributes:
44
+ label: Hardware or firmware considerations
45
+ description: Include relevant scanner or firmware behavior without private data.
46
+ - type: checkboxes
47
+ id: checks
48
+ attributes:
49
+ label: Checklist
50
+ options:
51
+ - label: I searched existing issues and the roadmap.
52
+ required: true
53
+ - label: This request does not expose private scanner or network information.
54
+ required: true
@@ -0,0 +1,25 @@
1
+ ## Summary
2
+
3
+ <!-- What changed and why? -->
4
+
5
+ ## Validation
6
+
7
+ - [ ] `ruff check .`
8
+ - [ ] `mypy src/sds200`
9
+ - [ ] `pytest`
10
+ - [ ] `python scripts/check_docs.py`
11
+ - [ ] Documentation and changelog updated when needed
12
+
13
+ ## Hardware validation
14
+
15
+ <!-- Optional. Include scanner firmware and transport; redact private data. -->
16
+
17
+ - Scanner firmware:
18
+ - Python version:
19
+ - Operating system:
20
+ - Transport:
21
+ - Commands or workflows tested:
22
+
23
+ ## Compatibility and security
24
+
25
+ <!-- Note public API, protocol, transport, privacy, or security implications. -->
@@ -0,0 +1,15 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: pip
4
+ directory: "/"
5
+ schedule:
6
+ interval: monthly
7
+ labels:
8
+ - dependencies
9
+
10
+ - package-ecosystem: github-actions
11
+ directory: "/"
12
+ schedule:
13
+ interval: monthly
14
+ labels:
15
+ - dependencies
@@ -0,0 +1,69 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: ci-${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ jobs:
15
+ test:
16
+ name: Python ${{ matrix.python-version }}
17
+ runs-on: ubuntu-latest
18
+ strategy:
19
+ fail-fast: false
20
+ matrix:
21
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
22
+
23
+ steps:
24
+ - uses: actions/checkout@v7
25
+
26
+ - uses: actions/setup-python@v7
27
+ with:
28
+ python-version: ${{ matrix.python-version }}
29
+ cache: pip
30
+
31
+ - name: Install project
32
+ run: |
33
+ python -m pip install --upgrade pip
34
+ python -m pip install -e ".[dev]"
35
+
36
+ - name: Ruff
37
+ run: ruff check .
38
+
39
+ - name: MyPy
40
+ run: mypy src/sds200
41
+
42
+ - name: Pytest
43
+ run: pytest --cov=sds200 --cov-report=term-missing
44
+
45
+ package:
46
+ name: Documentation and package
47
+ runs-on: ubuntu-latest
48
+
49
+ steps:
50
+ - uses: actions/checkout@v7
51
+
52
+ - uses: actions/setup-python@v7
53
+ with:
54
+ python-version: "3.14"
55
+ cache: pip
56
+
57
+ - name: Install build tools
58
+ run: |
59
+ python -m pip install --upgrade pip
60
+ python -m pip install -e ".[dev]"
61
+
62
+ - name: Check documentation links
63
+ run: python scripts/check_docs.py
64
+
65
+ - name: Build distributions
66
+ run: python -m build
67
+
68
+ - name: Check distributions
69
+ run: python -m twine check dist/*
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ .coverage
9
+ htmlcov/
10
+ build/
11
+ dist/
12
+ *.log
@@ -0,0 +1,195 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims
5
+ to follow [Semantic Versioning](https://semver.org/) as the public API matures.
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Planned
10
+
11
+ - Validated SDS200 network audio transport after control reliability work
12
+ - Optional preferred-transport recovery without connection flapping
13
+
14
+ ## [0.8.2] - 2026-07-24
15
+
16
+ ### Added
17
+
18
+ - Optional SDS100 battery telemetry through the documented `GSI`/`PSI` `Property.Battery` attribute
19
+ - Immediate `CommandRejectedError` handling for generic scanner `ERR` and `NG` replies
20
+ - Extended `scanner-info` output for RSSI, optional battery, recording, and mute state
21
+ - Opt-in Uniden SDS-series udev rule for desktop ACLs and ModemManager exclusion
22
+
23
+ ### Changed
24
+
25
+ - Corrected SDS100 capabilities after firmware 1.26.01 hardware testing showed
26
+ that `GCS` returns `ERR`
27
+ - Kept SDS150 detailed `GCS` charge status as specification-based and hardware unverified
28
+ - Package version advanced to 0.8.2
29
+
30
+ ## [0.8.1] - 2026-07-24
31
+
32
+ ### Changed
33
+
34
+ - Replaced the model-specific `sds200` executable with the model-neutral `sdsctl` command
35
+ - Updated CLI help, shell completion, documentation, support guidance, and tests for `sdsctl`
36
+ - Kept the distribution, Python import package, configuration directory, and repository named `sds200`
37
+ - Package version advanced to 0.8.1
38
+
39
+ ## [0.8.0] - 2026-07-24
40
+
41
+ ### Added
42
+
43
+ - USB serial control support for the Uniden SDS100 and SDS150
44
+ - Model-neutral `SDSScanner` API while retaining the historical `SDS200` alias
45
+ - Scanner capability metadata and model-specific volume and squelch limits
46
+ - SDS100/SDS150 `GCS` battery and charge-status parsing and CLI output
47
+ - Model-aware USB discovery, selection, profiles, repair, and completions
48
+ - SDS150 `SDS150GBT` and Uniden internal model-name normalization
49
+ - LF, CR, and CRLF serial response framing for shared SDS-series commands
50
+ - Multi-model protocol, profile, discovery, and command regression tests
51
+
52
+ ### Changed
53
+
54
+ - Network discovery and UDP profiles are explicitly restricted to the SDS200
55
+ - Profile documents advance to version 3 and can retain the scanner model
56
+ - Documentation distinguishes protocol support from physical-hardware validation
57
+ - Package version advanced to 0.8.0
58
+
59
+ ## [0.7.0] - 2026-07-23
60
+
61
+ ### Added
62
+
63
+ - Configurable exponential reconnect backoff with finite or unlimited attempts
64
+ - Structured `RadioEvent` notifications and `events --json` JSON Lines output
65
+ - Bounded health history with latency, error-rate, reconnect, and failover summaries
66
+ - Health thresholds for healthy, degraded, unhealthy, and disconnected states
67
+ - Discovery-based profile repair for stale USB paths and changed network addresses
68
+ - Detailed failover telemetry including previous and active endpoints
69
+ - Reliability regression tests for backoff, history, events, and profile repair
70
+
71
+ ### Changed
72
+
73
+ - Serial, UDP, and fallback reconnect loops now share one recovery policy
74
+ - `health --history` can include historical metrics in human or JSON output
75
+ - Network audio remains deferred and documented as future work
76
+ - Package version advanced to 0.7.0
77
+
78
+ ## [0.6.0] - 2026-07-23
79
+
80
+ ### Added
81
+
82
+ - Discovery-driven serial, network, or fallback profile creation
83
+ - Configurable serial/network preference with runtime transport failover
84
+ - One-time command retry after a successful failover
85
+ - Continuous `health --watch` output and JSON health reports
86
+ - Connection, response, state, serial, network, and failover diagnostics
87
+ - Independent `AudioTransport`, `AudioStream`, and `AudioChunk` API groundwork
88
+
89
+ ### Changed
90
+
91
+ - Profile files now use version 2 and can store both control endpoints
92
+ - Package version advanced to 0.6.0
93
+
94
+ ## [0.5.3] - 2026-07-23
95
+
96
+ First planned GitHub prerelease.
97
+
98
+ ### Added
99
+
100
+ - Reliable active LAN discovery with isolated per-host UDP sockets
101
+ - Bounded discovery parallelism and configurable worker count
102
+ - USB and network connection profiles stored as TOML
103
+ - Network health checks, statistics, diagnostics, and XML retry limits
104
+ - User-focused README and project documentation
105
+ - Contribution, support, security, and conduct guidance
106
+ - GitHub issue forms, pull-request template, and Dependabot configuration
107
+ - Package metadata, typed-package marker, build verification, and release checklist
108
+
109
+ ### Changed
110
+
111
+ - CI now uses Node 24-based GitHub Actions majors
112
+ - CI verifies documentation links and built distribution metadata
113
+ - LAN discovery uses per-host timeouts and bounded concurrency
114
+
115
+ ### Fixed
116
+
117
+ - `/24` discovery could miss a scanner because unrelated UDP errors, ARP delays,
118
+ and shared-socket behavior interfered with valid replies
119
+ - Network XML handling now supports bare and fragmented `GSI`/`PSI` responses
120
+ - Strict MyPy narrowing in network XML decoding
121
+
122
+ ## [0.5.2]
123
+
124
+ - Continued discovery after transient UDP refusal, reset, host-unreachable, and
125
+ network-unreachable errors.
126
+
127
+ ## [0.5.1]
128
+
129
+ - Improved discovery timeout placement, batching, and response draining.
130
+
131
+ ## [0.5.0]
132
+
133
+ - Added LAN discovery, profiles, health checks, UDP counters, diagnostics, and
134
+ bounded XML retries.
135
+
136
+ ## [0.4.2]
137
+
138
+ - Completed strict typing for bare network XML response handling.
139
+
140
+ ## [0.4.1]
141
+
142
+ - Added command-aware handling for bare `ScannerInfo` XML over UDP.
143
+
144
+ ## [0.4.0]
145
+
146
+ - Added native SDS200 UDP control, multi-datagram XML reassembly, and network
147
+ support across the existing command, state, trace, and monitor APIs.
148
+
149
+ ## [0.3.1]
150
+
151
+ - Correctly handled the SDS200 `PSI` acknowledgment followed by streamed XML.
152
+
153
+ ## [0.3.0]
154
+
155
+ - Added continuous `PSI` monitoring, state-difference events, live terminal
156
+ display, traffic timestamps, and the public transport abstraction.
157
+
158
+ ## [0.2.4]
159
+
160
+ - Added Ruff- and MyPy-clean shell completion integration.
161
+
162
+ ## [0.2.3]
163
+
164
+ - Added Bash and Zsh completion for commands, flags, ports, profiles, and common
165
+ scanner protocol commands.
166
+
167
+ ## [0.2.2]
168
+
169
+ - Completed strict PySerial factory and write-return typing.
170
+
171
+ ## [0.2.1]
172
+
173
+ - Fixed a serial-reader shutdown race and added regression coverage.
174
+
175
+ ## [0.2.0]
176
+
177
+ - Added typed command objects, structured scanner XML, synchronized radio state,
178
+ state events, traffic tracing, and the `scanner-info` command.
179
+
180
+ ## [0.1.2]
181
+
182
+ - Established a Ruff-, MyPy-, and Pytest-clean transport baseline.
183
+
184
+ ## [0.1.0]
185
+
186
+ - Added serial discovery, transport, packet framing, core responses, CLI tools,
187
+ examples, tests, and CI.
188
+
189
+ [Unreleased]: https://github.com/stevenboyd78/sds200-python/compare/v0.8.2...HEAD
190
+ [0.8.2]: https://github.com/stevenboyd78/sds200-python/compare/v0.8.1...v0.8.2
191
+ [0.8.1]: https://github.com/stevenboyd78/sds200-python/compare/v0.8.0...v0.8.1
192
+ [0.8.0]: https://github.com/stevenboyd78/sds200-python/compare/v0.7.0...v0.8.0
193
+ [0.7.0]: https://github.com/stevenboyd78/sds200-python/compare/v0.6.0...v0.7.0
194
+ [0.6.0]: https://github.com/stevenboyd78/sds200-python/compare/v0.5.3...v0.6.0
195
+ [0.5.3]: https://github.com/stevenboyd78/sds200-python/releases/tag/v0.5.3
@@ -0,0 +1,27 @@
1
+ # Code of Conduct
2
+
3
+ ## Our standard
4
+
5
+ Project participation should be respectful, constructive, and focused on making
6
+ the software and documentation better.
7
+
8
+ Examples of welcome behavior include:
9
+
10
+ - Giving specific, actionable technical feedback
11
+ - Assuming good faith while asking for clarification
12
+ - Respecting differing experience levels and communication styles
13
+ - Protecting private information found in scanner traces or diagnostics
14
+ - Accepting maintainer decisions about project scope and safety
15
+
16
+ Unacceptable behavior includes harassment, personal attacks, discriminatory
17
+ language, deliberate disruption, publishing another person's private
18
+ information, or pressuring contributors to disclose sensitive radio or network
19
+ details.
20
+
21
+ ## Enforcement
22
+
23
+ Report conduct concerns privately to the maintainer through the GitHub profile.
24
+ Do not use a public issue when doing so would expose the affected person.
25
+
26
+ The maintainer may edit or remove contributions, lock discussions, or restrict
27
+ participation when necessary to protect the project community.
@@ -0,0 +1,108 @@
1
+ # Contributing
2
+
3
+ Thank you for helping improve `sds200-python`.
4
+
5
+ The project is hardware-facing alpha software, so contributions should preserve
6
+ a clear separation between transport behavior, protocol parsing, scanner state,
7
+ and user interfaces.
8
+
9
+ ## Before opening an issue
10
+
11
+ Search existing issues and review [SUPPORT.md](SUPPORT.md). Include the project
12
+ version, Python version, operating system, scanner firmware, connection type,
13
+ and a minimal reproduction.
14
+
15
+ Remove private or location-sensitive information from traces before posting.
16
+ Scanner output can contain system names, channel names, IP addresses, and unit
17
+ identifiers.
18
+
19
+ Security vulnerabilities should follow [SECURITY.md](SECURITY.md), not a public
20
+ issue.
21
+
22
+ ## Development setup
23
+
24
+ ```bash
25
+ git clone https://github.com/stevenboyd78/sds200-python.git
26
+ cd sds200-python
27
+
28
+ python3 -m venv .venv
29
+ source .venv/bin/activate
30
+
31
+ python -m pip install --upgrade pip
32
+ python -m pip install -e ".[dev]"
33
+ ```
34
+
35
+ ## Required checks
36
+
37
+ Run these before submitting a pull request:
38
+
39
+ ```bash
40
+ ruff check .
41
+ mypy src/sds200
42
+ pytest
43
+ python scripts/check_docs.py
44
+ python -m build
45
+ python -m twine check dist/*
46
+ ```
47
+
48
+ The automated test suite must run without scanner hardware.
49
+
50
+ ## Project structure
51
+
52
+ - `src/sds200/transport.py`: transport contract and USB serial transport
53
+ - `src/sds200/network.py`: UDP transport and network XML datagram handling
54
+ - `src/sds200/fallback.py`: ordered control-transport activation and failover
55
+ - `src/sds200/audio.py`: control-independent audio lifecycle contracts
56
+ - `src/sds200/scanner.py`: model names, aliases, capabilities, and limits
57
+ - `src/sds200/commands.py`: typed command objects
58
+ - `src/sds200/parser.py`: CR-delimited response parsing
59
+ - `src/sds200/xml_protocol.py`: scanner-information XML parsing
60
+ - `src/sds200/state.py`: synchronized state and change detection
61
+ - `src/sds200/cli.py`: command-line interface
62
+ - `tests/`: hardware-independent regression tests
63
+ - `examples/`: focused usage examples
64
+ - `docs/`: architecture and operational guidance
65
+
66
+ ## Adding protocol support
67
+
68
+ When adding or changing a scanner command:
69
+
70
+ 1. Preserve the raw protocol response in a trace or sanitized fixture.
71
+ 2. Add a typed command or response model when practical.
72
+ 3. Keep transport-specific framing out of high-level command code.
73
+ 4. Add positive, malformed-response, timeout, and validation tests.
74
+ 5. Document hardware validation separately from simulated tests.
75
+ 6. Avoid assuming that USB and UDP return identical framing.
76
+
77
+ Do not add tests that require a live scanner or a specific LAN.
78
+
79
+ ## Hardware validation
80
+
81
+ A pull request may include optional manual validation notes:
82
+
83
+ ```text
84
+ Scanner: SDS100 / SDS150 / SDS200
85
+ Firmware: ...
86
+ Python: 3.14.x
87
+ Transport: USB / UDP (SDS200 only)
88
+ Commands tested: ...
89
+ Observed result: ...
90
+ ```
91
+
92
+ Sanitize system, department, channel, unit, and network information before
93
+ sharing logs publicly.
94
+
95
+ ## Pull requests
96
+
97
+ Keep pull requests focused. Describe:
98
+
99
+ - What changed
100
+ - Why it changed
101
+ - Tests added or updated
102
+ - Local check results
103
+ - Hardware validation, when applicable
104
+ - Compatibility or security implications
105
+
106
+ Update documentation and `CHANGELOG.md` when behavior or public APIs change.
107
+
108
+ By participating, you agree to follow [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
sds200-0.8.2/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Steven Boyd
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.