Skip to content

NOYITO USB Relay Power Driver#268

Open
kirkbrauer wants to merge 1 commit intomainfrom
driver-noyito-power
Open

NOYITO USB Relay Power Driver#268
kirkbrauer wants to merge 1 commit intomainfrom
driver-noyito-power

Conversation

@kirkbrauer
Copy link
Member

@kirkbrauer kirkbrauer commented Feb 26, 2026

Add jumpstarter-driver-noyito-relay — NOYITO USB Relay Power Driver

Summary

Adds a new driver package supporting the full NOYITO USB relay board family
across two distinct hardware series:

  • NoyitoPowerSerial — 1/2-channel boards using a CH340 USB-to-serial chip
    (9600 baud). Supports the 0xFF status query to read channel state.
    Controlled via pyserial.
  • NoyitoPowerHID — 4/8-channel "HID Drive-free" boards that enumerate as
    USB HID devices (no serial port). Status query is not available on this
    hardware. Controlled via the hid Python package.

Both classes share the same 4-byte command protocol
(0xA0 + channel + state + checksum) via a common _build_command helper.

Key configuration parameters

Class Parameters Notes
NoyitoPowerSerial port, channel (1–2), dual dual=True fires both contacts simultaneously for high-current wiring
NoyitoPowerHID num_channels (4 or 8), channel, all_channels, vendor_id, product_id all_channels=True fires every channel simultaneously

macOS / Apple Silicon note

The hid package requires the native hidapi shared library
(brew install hidapi). On Apple Silicon, Homebrew installs to
/opt/homebrew/lib which is not in dlopen's default search path.
NoyitoPowerHID._send_command automatically prepends the Homebrew prefix to
DYLD_FALLBACK_LIBRARY_PATH before the first import hid, so no manual
environment setup is needed on Intel or Apple Silicon Macs.

Files added

Path Description
jumpstarter_driver_noyito_relay/driver.py NoyitoPowerSerial + NoyitoPowerHID driver classes
jumpstarter_driver_noyito_relay/client.py NoyitoPowerClient (thin PowerClient subclass)
jumpstarter_driver_noyito_relay/driver_test.py 28 unit/integration tests (serial + HID, no hardware required)
jumpstarter_driver_noyito_relay/conftest.py Stubs hid in sys.modules so tests run without native hidapi
examples/exporter.yaml Serial (ch1, ch2, dual), 4-ch HID (single, all), 8-ch HID (single, all)
README.md Board detection guide, per-class config tables, CLI usage, Amazon purchase links
pyproject.toml Declares pyserial>=3.5, hid>=1.0.4 deps; registers both entry points
python/uv.lock Adds hid 1.0.9 from pypi.org

Test plan

  • All 28 pytest tests pass with hid.Device mocked (no hardware needed):
    make pkg-test-jumpstarter-driver-noyito-relay
  • ruff check passes clean
  • Smoke test with physical 2-ch serial board: on / off / status / cycle
  • Smoke test with physical 4-ch or 8-ch HID board: on / off / cycle,
    confirm status is not available

Summary by CodeRabbit

Release Notes

  • New Features

    • Added NOYITO relay driver supporting both serial and HID-based relay devices with multi-channel configurations.
    • Enables control and status monitoring of 1, 2, 4, and 8-channel relay variants.
  • Documentation

    • Added comprehensive documentation with installation instructions, configuration examples, and usage guidance.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2026

📝 Walkthrough

Walkthrough

This PR introduces the jumpstarter-driver-noyito-relay package, a new driver supporting NOYITO relay boards via serial (NoyitoPowerSerial) and HID (NoyitoPowerHID) interfaces. Includes implementation, client, comprehensive documentation, examples, and test suite.

Changes

Cohort / File(s) Summary
Documentation & Reference
python/docs/source/reference/package-apis/drivers/noyito-relay.md, python/packages/jumpstarter-driver-noyito-relay/README.md
Added comprehensive documentation covering supported hardware (serial/HID variants), installation, board detection, configuration, API reference, and CLI usage examples.
Project Configuration
python/packages/jumpstarter-driver-noyito-relay/pyproject.toml, python/packages/jumpstarter-driver-noyito-relay/.gitignore
Configured new Python package with dependencies (pyserial, hid, jumpstarter), build system (Hatch), test settings, and VCS integration. Added standard ignore patterns for Python artifacts.
Driver Implementation
python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/driver.py
Implemented two driver classes: NoyitoPowerSerial (serial-based relay control with status parsing and dual-channel support) and NoyitoPowerHID (HID-based relay with multi-channel configuration). Includes protocol-level command building, serial communication, and channel orchestration.
Client & CLI
python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/client.py
Added NoyitoPowerClient subclass extending PowerClient with status() method and CLI command for querying relay state.
Test Setup & Configuration
python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/conftest.py, python/packages/jumpstarter-driver-noyito-relay/examples/exporter.yaml
Set up test fixtures with HID mocking and serial exception handling. Provided example exporter configuration with multiple relay definitions (serial and HID variants).
Test Suite
python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/driver_test.py
Comprehensive test coverage for command construction, channel validation, serial/HID integration, dual-channel modes, status parsing, and cycle operations using mocked I/O devices.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Driver as NoyitoPowerSerial<br/>or<br/>NoyitoPowerHID
    participant HW as Serial Port<br/>or HID Device
    
    Client->>Driver: on() / off()
    activate Driver
    Driver->>Driver: _build_command(channel, state)
    Driver->>Driver: _channels() - determine targets
    Driver->>HW: _send_command(bytes)
    HW-->>Driver: ✓ sent
    deactivate Driver
    Driver-->>Client: return
    
    Client->>Driver: status() [Serial only]
    activate Driver
    Driver->>HW: _query_status()
    HW-->>Driver: response text
    Driver->>Driver: parse status dict
    deactivate Driver
    Driver-->>Client: "on"/"off"/"partial"
    
    Client->>Driver: read()
    activate Driver
    Driver-->>Client: Generator[PowerReading]
    deactivate Driver
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

backport release-0.6

Suggested reviewers

  • NickCao
  • mangelajo
  • bennyz

Poem

🐰 A driver so grand, for relays so keen,
Serial and HID, both working pristine,
With channels and commands in byte-coded dance,
NOYITO's power bends to our command's advance! ⚡
Tests mock the hardware with magical care. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and concisely describes the main change: adding a NOYITO USB Relay Power Driver. It accurately reflects the primary objective of introducing the jumpstarter-driver-noyito-relay package.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch driver-noyito-power

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
python/packages/jumpstarter-driver-noyito-relay/.gitignore (1)

1-3: LGTM! Appropriate ignore patterns for Python package.

The patterns correctly exclude Python bytecode caches and test coverage artifacts, which aligns with the test suite described in the PR.

Optionally, you may consider adding additional common Python patterns if they're not already covered by a root .gitignore:

  • .pytest_cache/
  • *.egg-info/
  • dist/
  • build/
  • htmlcov/

However, this is perfectly adequate if those are handled at the repository level.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/packages/jumpstarter-driver-noyito-relay/.gitignore` around lines 1 -
3, Current .gitignore correctly excludes __pycache__, .coverage and
coverage.xml; optionally add common Python ignore patterns to the same
.gitignore (or confirm they're in the root) by including entries for
.pytest_cache/, *.egg-info/, dist/, build/, and htmlcov/ so build artifacts and
pytest caches are also ignored; update the file named .gitignore in the package
(python/packages/jumpstarter-driver-noyito-relay/.gitignore) to add those lines
if the repo root does not already cover them.
python/packages/jumpstarter-driver-noyito-relay/pyproject.toml (1)

4-4: Package description undersells current functionality.

The description mentions only the 2-channel serial board, but this package also ships HID 4/8-channel support. Updating this helps avoid confusion in package indexes.

💡 Proposed tweak
-description = "Jumpstarter driver for the NOYITO 5V 2-Channel USB Relay Module"
+description = "Jumpstarter driver for NOYITO USB relay boards (serial 1/2-channel and HID 4/8-channel)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/packages/jumpstarter-driver-noyito-relay/pyproject.toml` at line 4,
Update the package description value in pyproject.toml (the description field)
to reflect all supported hardware: change the current "Jumpstarter driver for
the NOYITO 5V 2-Channel USB Relay Module" to a broader string that mentions both
the 2-channel serial board and HID 4/8-channel support (e.g., include "2-channel
serial and HID 4/8-channel" or similar wording) so package indexes correctly
describe the package capabilities.
python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/driver_test.py (1)

217-312: Add a regression test for HID/status capability mismatch.

Please add one integration-style test that verifies status is unavailable (or fails with a clear error) for NoyitoPowerHID. It would lock in expected behavior and guard against CLI/client drift.

Based on learnings Add comprehensive tests and update documentation in the driver package.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/driver_test.py`
around lines 217 - 312, Add an integration test that asserts the status
capability is not present for NoyitoPowerHID: create a new test function (e.g.,
test_hid_status_unavailable) in driver_test.py that patches hid.Device (reuse
_make_hid_mock and the `@patch`("hid.Device") pattern), opens the device with
serve(NoyitoPowerHID(...)) as client, and then assert that either
getattr(client, "status", None) is None or that calling client.status() raises a
clear exception (AttributeError or NotImplementedError) to lock in expected
behavior; reference NoyitoPowerHID, serve, and _make_hid_mock when locating
where to add the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@python/docs/source/reference/package-apis/drivers/noyito-relay.md`:
- Line 1: The docs page "noyito-relay.md" is not included in any Sphinx toctree;
open the drivers toctree index (the drivers reference index used for package
driver docs) and add an entry for "noyito-relay.md" to the toctree so Sphinx
will include it in the generated reference docs. Ensure the toctree entry
matches the document name "noyito-relay.md" (or the same relative doc path used
by other driver entries) and update the drivers index file so the page is
discoverable in navigation.

In
`@python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/conftest.py`:
- Around line 16-20: Replace the direct call to item.runtest() with a pytest
hookwrapper: add `@pytest.hookimpl`(hookwrapper=True) to pytest_runtest_call,
yield to let pytest run the test, capture the outcome via outcome = yield, then
call outcome.get_result() inside a try/except that catches
serial.SerialException and calls pytest.skip("Serial device not available");
ensure serial is imported and remove the manual item.runtest() invocation so
pytest's runner and plugins handle execution and reporting correctly.

In
`@python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/driver.py`:
- Around line 118-120: The client() classmethod on NoyitoPowerHID incorrectly
returns the status-capable
"jumpstarter_driver_noyito_relay.client.NoyitoPowerClient" causing CLI to expose
a status command for HID; update NoyitoPowerHID.client() to return the proper
HID-only client (or the module/class that does not export status) — e.g. point
it at the HID-specific client class name (the one that does not implement
status) instead of NoyitoPowerClient, or alternatively implement/export a safe
status() on NoyitoPowerHID if status behavior is required.

---

Nitpick comments:
In `@python/packages/jumpstarter-driver-noyito-relay/.gitignore`:
- Around line 1-3: Current .gitignore correctly excludes __pycache__, .coverage
and coverage.xml; optionally add common Python ignore patterns to the same
.gitignore (or confirm they're in the root) by including entries for
.pytest_cache/, *.egg-info/, dist/, build/, and htmlcov/ so build artifacts and
pytest caches are also ignored; update the file named .gitignore in the package
(python/packages/jumpstarter-driver-noyito-relay/.gitignore) to add those lines
if the repo root does not already cover them.

In
`@python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/driver_test.py`:
- Around line 217-312: Add an integration test that asserts the status
capability is not present for NoyitoPowerHID: create a new test function (e.g.,
test_hid_status_unavailable) in driver_test.py that patches hid.Device (reuse
_make_hid_mock and the `@patch`("hid.Device") pattern), opens the device with
serve(NoyitoPowerHID(...)) as client, and then assert that either
getattr(client, "status", None) is None or that calling client.status() raises a
clear exception (AttributeError or NotImplementedError) to lock in expected
behavior; reference NoyitoPowerHID, serve, and _make_hid_mock when locating
where to add the test.

In `@python/packages/jumpstarter-driver-noyito-relay/pyproject.toml`:
- Line 4: Update the package description value in pyproject.toml (the
description field) to reflect all supported hardware: change the current
"Jumpstarter driver for the NOYITO 5V 2-Channel USB Relay Module" to a broader
string that mentions both the 2-channel serial board and HID 4/8-channel support
(e.g., include "2-channel serial and HID 4/8-channel" or similar wording) so
package indexes correctly describe the package capabilities.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2eecf2 and 10ce9f9.

⛔ Files ignored due to path filters (1)
  • python/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • python/docs/source/reference/package-apis/drivers/noyito-relay.md
  • python/packages/jumpstarter-driver-noyito-relay/.gitignore
  • python/packages/jumpstarter-driver-noyito-relay/README.md
  • python/packages/jumpstarter-driver-noyito-relay/examples/exporter.yaml
  • python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/__init__.py
  • python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/client.py
  • python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/conftest.py
  • python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/driver.py
  • python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/driver_test.py
  • python/packages/jumpstarter-driver-noyito-relay/pyproject.toml

@@ -0,0 +1 @@
../../../../../packages/jumpstarter-driver-noyito-relay/README.md No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Docs page is currently orphaned from Sphinx navigation.

The docs pipeline warning shows this document is not included in any toctree, so it won’t be discoverable in generated reference docs. Please add it to the appropriate drivers toctree index.

🧰 Tools
🪛 GitHub Actions: Documentation

[warning] 1-1: Sphinx warning: document isn't included in any toctree

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/docs/source/reference/package-apis/drivers/noyito-relay.md` at line 1,
The docs page "noyito-relay.md" is not included in any Sphinx toctree; open the
drivers toctree index (the drivers reference index used for package driver docs)
and add an entry for "noyito-relay.md" to the toctree so Sphinx will include it
in the generated reference docs. Ensure the toctree entry matches the document
name "noyito-relay.md" (or the same relative doc path used by other driver
entries) and update the drivers index file so the page is discoverable in
navigation.

Comment on lines +16 to +20
def pytest_runtest_call(item):
try:
item.runtest()
except serial.SerialException:
pytest.skip("Serial device not available") # ty: ignore[call-non-callable]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the actual conftest.py file
cat -n python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/conftest.py

Repository: jumpstarter-dev/jumpstarter

Length of output: 871


🏁 Script executed:

# Run the verification script from the review comment
#!/bin/bash
set -euo pipefail

echo "Custom pytest_runtest_call hooks:"
rg -n --type=py 'def pytest_runtest_call\('

echo
echo "Direct calls to item.runtest() (should be absent in custom hooks):"
rg -n --type=py 'item\.runtest\s*\('

echo
echo "All pytest hook declarations:"
rg -n --type=py 'def pytest_' python/packages/jumpstarter-driver-noyito-relay/

Repository: jumpstarter-dev/jumpstarter

Length of output: 1013


🌐 Web query:

pytest_runtest_call hook semantics documentation

💡 Result:

pytest_runtest_call(item) is a runtest-phase hook that pytest calls to execute the “call” phase of a single test item (i.e., the actual test body, after setup and before teardown). Its default implementation is essentially to run the test via item.runtest(). [1]

Where it fits in the per-item runtest protocol:

  • pytest_runtest_logstart(...) happens before setup/call/teardown. [2]
  • pytest_runtest_setup(item) runs before pytest_runtest_call(item). [2]
  • pytest_runtest_call(item) runs the test body (call phase). [1]
  • pytest_runtest_teardown(item, nextitem) runs teardown after the call phase. [1]
  • pytest_runtest_logfinish(...) happens after setup/call/teardown. [2]

Implementation/usage notes:

  • You can implement this hook in conftest.py (subject to the usual conftest visibility rules). [1]
  • Unlike most non-pytest_runtest_* hooks, runtest hooks are allowed to raise exceptions as part of reporting test failures/errors. [3]

Docs: see the pytest “API Reference” entry for pytest_runtest_call. [1]

Sources:
[1] pytest docs – API Reference (pytest_runtest_call) (docs.pytest.org)
[2] pytest docs – runtest hook order (logstart, setup, call, teardown, logfinish) (docs.pytest.org)
[3] pytest docs – hook-writing notes (exception rules; runtest hooks special) (docs.pytest.org)

Citations:


Implement pytest_runtest_call using the hookwrapper pattern to properly integrate with pytest's test execution protocol.

The current implementation directly calls item.runtest() without using @pytest.hookimpl(hookwrapper=True) and yield. This violates pytest's runtest hook semantics, which expects hooks to yield control back to pytest's runner rather than manually executing the test. Improper exception handling also prevents integration with pytest's error reporting and other plugins.

💡 Proposed fix
+@pytest.hookimpl(hookwrapper=True)
 def pytest_runtest_call(item):
-    try:
-        item.runtest()
-    except serial.SerialException:
-        pytest.skip("Serial device not available")  # ty: ignore[call-non-callable]
+    outcome = yield
+    if outcome.excinfo and issubclass(outcome.excinfo[0], serial.SerialException):
+        pytest.skip("Serial device not available")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/conftest.py`
around lines 16 - 20, Replace the direct call to item.runtest() with a pytest
hookwrapper: add `@pytest.hookimpl`(hookwrapper=True) to pytest_runtest_call,
yield to let pytest run the test, capture the outcome via outcome = yield, then
call outcome.get_result() inside a try/except that catches
serial.SerialException and calls pytest.skip("Serial device not available");
ensure serial is imported and remove the manual item.runtest() invocation so
pytest's runner and plugins handle execution and reporting correctly.

Comment on lines +118 to +120
@classmethod
def client(cls) -> str:
return "jumpstarter_driver_noyito_relay.client.NoyitoPowerClient"
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

NoyitoPowerHID points to a status-capable client, but HID has no status export.

NoyitoPowerHID.client() currently returns NoyitoPowerClient, which adds status()/status CLI command. That command can be invoked for HID and then fail at runtime because NoyitoPowerHID does not implement/export status.

💡 Proposed fix
 `@classmethod`
 def client(cls) -> str:
-    return "jumpstarter_driver_noyito_relay.client.NoyitoPowerClient"
+    return "jumpstarter_driver_power.client.PowerClient"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@python/packages/jumpstarter-driver-noyito-relay/jumpstarter_driver_noyito_relay/driver.py`
around lines 118 - 120, The client() classmethod on NoyitoPowerHID incorrectly
returns the status-capable
"jumpstarter_driver_noyito_relay.client.NoyitoPowerClient" causing CLI to expose
a status command for HID; update NoyitoPowerHID.client() to return the proper
HID-only client (or the module/class that does not export status) — e.g. point
it at the HID-specific client class name (the one that does not implement
status) instead of NoyitoPowerClient, or alternatively implement/export a safe
status() on NoyitoPowerHID if status behavior is required.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, the status part is missing in this variant of the driver, can it be implemented?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🌐 Web query:

NOYITO USB HID relay board status query support

💡 Result:

Many “NOYITO USB HID relay” boards are re-branded LCUS/CH-style HID relays that use the simple 4‑byte 0xA0 command protocol (often sent to the device’s HID endpoint / hidraw). A Linux user report for LCUS_HID_X1 shows the NOYITO/LCUS HID variant accepts the same A0 … on/off frames over HID. [2]

Does it support relay status queries?

Yes—if your NOYITO board uses this 0xA0 protocol, it supports a query operation where the 3rd byte is 0x02 (“query status”), and the device returns text like CH1:ON / CH1:OFF. [1]

Per-channel status query

Send:

  • A0 <CH> 02 <CHK>

Examples:

  • CH1 status: A0 01 02 A3 → response like CH1:ON (ASCII) [1]
  • CH2 status: A0 02 02 A4 → response like CH2:ON (ASCII) [1]

All-channel status query

Send:

  • A0 0F 02 B1 → returns multiple lines (CH1:..., CH2:..., etc.) [1]

Checksum

CHK = (0xA0 + address + operation) & 0xFF (matches the examples above). [1]

Quick way to confirm on your system

If your board is supported by common HID-relay tooling, the usbrelay utility can query and print relay state (with no action it prints all relays). [3]

Sources: Diustou relay protocol + query/response examples [1], LCUS_HID_X1 controlled via A0 ... frames over HID (hidraw) [2], usbrelay tool supports querying HID relay state [3].


@mangelajo Yes, status can be implemented for the HID variant!

The web search confirms that NOYITO HID relay boards using the 0xA0 protocol support status queries:

Protocol for status query:

  • Send: 0xA0 <channel> 0x02 <checksum> where checksum = (0xA0 + channel + 0x02) & 0xFF
  • For single channel: 0xA0 0x01 0x02 0xA3 → returns ASCII "CH1:ON" or "CH1:OFF"
  • For all channels: 0xA0 0x0F 0x02 0xB1 → returns multiple lines (CH1:..., CH2:..., etc.)

Implementation approach:

The existing code already has most pieces needed:

  1. The _build_command(channel, state) helper can be used with state=0x02 for queries
  2. The response format is identical to the Serial variant (ASCII text like "CH1:ON")
  3. You can adapt the _query_status() and status() methods from NoyitoPowerSerial (lines 51-96)

The main difference is sending/receiving via HID instead of serial. The _send_command method in NoyitoPowerHID would need a companion _query_status() that:

  • Opens the HID device
  • Sends the status query command
  • Reads the HID response (ASCII text)
  • Parses it the same way as the Serial variant

This would also resolve the original issue—once status() is exported on NoyitoPowerHID, the NoyitoPowerClient client becomes appropriate for both driver variants.

Would you like me to plan the implementation of the status query for the HID driver?

@mangelajo
Copy link
Member

just a couple of warnings from the bot, and from docs compilation.

from jumpstarter_driver_power.client import PowerClient


class NoyitoPowerClient(PowerClient):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would go ahead and add status and cli to all PowerClients, defaulting to "not implemented", then we can add it to others as well. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saying because we have been talking about that sometimes. ... anyway, we can do that later TBH, not really this PR, please ignore :D


@export
def read(self) -> Generator[PowerReading, None, None]:
yield PowerReading(voltage=0.0, current=0.0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I think we should throw a non implemented exception

Copy link
Member

@mangelajo mangelajo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants