Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9e650c07d | ||
|
|
b09d97f8a3 | ||
|
|
0cd40bbd6c | ||
|
|
1949d5b1a3 | ||
|
|
700cbb57b7 | ||
|
|
f02b794861 |
No files matched your search
@@ -48,8 +48,27 @@ A release does not go out on a red suite. If something fails, stop and say so.
|
||||
|
||||
## 2. Decide the version
|
||||
|
||||
Patch unless JP says otherwise. Say the number you arrived at before you write it anywhere,
|
||||
so a wrong assumption costs a sentence instead of a tag.
|
||||
**Always ask, as a multiple choice**, whenever JP asks for a release — even when his
|
||||
request names a bump, because words like "minor" have meant "a small one" before (he
|
||||
asked to "bump minor" and meant the patch). One AskUserQuestion, three choices, each
|
||||
showing the actual number it produces:
|
||||
|
||||
- **Patch — X.Y.(Z+1)** (Recommended)
|
||||
- **Minor — X.(Y+1).0**
|
||||
- **Major — (X+1).0.0**
|
||||
|
||||
**Major asks twice.** If he picks it, ask a second, separate question before writing the
|
||||
number anywhere — "Release (X+1).0.0? A major version is a promise about compatibility
|
||||
that can't be taken back." — with *Yes, (X+1).0.0* and *No, go back to patch* as its
|
||||
choices. Only a second yes makes it major.
|
||||
|
||||
Then say the number you arrived at before you write it anywhere, so a wrong assumption
|
||||
costs a sentence instead of a tag.
|
||||
|
||||
**Don't recommend a bigger bump**, however many features or changed defaults the release
|
||||
carries — not even on the strength of the changelog's SemVer note. 0.10.0 went out that way
|
||||
on a recommendation, and JP would have preferred 0.9.1. A minor or major bump happens only
|
||||
when he names it.
|
||||
|
||||
## 3. Write the entry
|
||||
|
||||
@@ -116,10 +135,16 @@ field, which is the live `{version}` placeholder and must show the new one.
|
||||
## 5. Commit, tag, push
|
||||
|
||||
```
|
||||
git commit -am "MeshTerm X.Y.Z"
|
||||
git commit -a -m "MeshTerm X.Y.Z" -m "Co-Authored-By: <the model running this skill> <noreply@anthropic.com>"
|
||||
git tag -a vX.Y.Z -m "MeshTerm X.Y.Z"
|
||||
```
|
||||
|
||||
The trailer is not optional: the changelog entry in this commit is prose you wrote, and
|
||||
CONTRIBUTING.md asks every contributor to say when an AI tool wrote part of a change —
|
||||
the project's own commits say it first. Name the model actually running, not a
|
||||
hardcoded one. Seven release commits (0.3.1 through 0.10.0) went out without it because
|
||||
this line used to omit it.
|
||||
|
||||
Commit on `main` — never a topic branch. Then **confirm with JP before pushing**, because
|
||||
the push is the act that builds and publishes the release:
|
||||
|
||||
|
||||
@@ -2,15 +2,27 @@
|
||||
|
||||
<!-- The why matters more than the what — the diff already says the what. -->
|
||||
|
||||
## Related issue
|
||||
## Discord
|
||||
|
||||
<!-- Small fixes can come straight to a PR. For anything bigger, CONTRIBUTING asks for an
|
||||
issue first and a yes on it, so a large PR with no issue behind it will get a
|
||||
conversation before it gets a review. Link it here. -->
|
||||
<!-- Your Discord username on the MeshTerm server, and a link to the message in
|
||||
#contributing where you got a yes. A pull request without them is closed without
|
||||
a review. -->
|
||||
|
||||
Username:
|
||||
Link to the yes:
|
||||
|
||||
## AI use
|
||||
|
||||
<!-- Required. Did an AI tool write any part of this change? If yes: which tool, what it
|
||||
did, and what you checked yourself. Each commit it helped with also needs a line at
|
||||
the end naming it, like `Co-Authored-By:` or `Assisted-by:`. If you used AI and don't
|
||||
say so, this pull request is treated as a bot's and closed. -->
|
||||
|
||||
## Before you mark it ready
|
||||
|
||||
- [ ] I've read [CONTRIBUTING.md](../blob/main/CONTRIBUTING.md) and agree its licensing terms apply to this contribution
|
||||
- [ ] I've put my Discord username and a link to the yes above
|
||||
- [ ] If I used AI, I said so above and in the commits it helped with
|
||||
- [ ] `python -m pytest -q` passes, including the dual-platform gallery gate
|
||||
- [ ] `ruff check .` and `ruff format --check .` are clean
|
||||
- [ ] New screens, rows, and hints go through the helpers in `ui/menus.py`, and follow the
|
||||
|
||||
+20
-1
@@ -9,6 +9,24 @@ one caveat SemVer makes for a leading zero: while the major version is still `0`
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.10.1] — 2026-09-27
|
||||
|
||||
**Three small fixes to the command line.** A mistyped `records --width` now says so instead
|
||||
of looking like an empty result, the simulator's row in `devices` loses a stray `()`, and
|
||||
two help pages read like the rest.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`meshterm records --width` refuses a width that doesn't exist.** Hash widths are 1, 2,
|
||||
or 4. Any other number used to look like an empty result, so a typo and an empty
|
||||
database gave the same answer, and `--width 0` was ignored and listed every width. Now
|
||||
it's a usage error that names the three choices, like `--category` already was.
|
||||
- **The simulator's row in `meshterm --mock devices` no longer ends in an empty `()`.**
|
||||
The brackets hold a serial port, and the simulator doesn't have one.
|
||||
- **`meshterm platform --help` and `meshterm specimen --help` read like the others.** They
|
||||
used to print notes meant for someone reading the source code, markup and all, and cut
|
||||
their summaries short in `meshterm --help`. Now each has one plain line.
|
||||
|
||||
## [0.10.0] — 2026-09-25
|
||||
|
||||
**Regions, a uConsole with no bridge, and Bluetooth that works on Linux.** MeshTerm now
|
||||
@@ -188,6 +206,7 @@ exact commands.
|
||||
MeshTerm is free and open source under the Apache 2.0 licence. The name and the logo are
|
||||
not covered by the licence (see `NOTICE`), so a fork is welcome under its own name.
|
||||
|
||||
[Unreleased]: https://github.com/jpmartineau/MeshTerm/compare/v0.10.0...HEAD
|
||||
[Unreleased]: https://github.com/jpmartineau/MeshTerm/compare/v0.10.1...HEAD
|
||||
[0.10.1]: https://github.com/jpmartineau/MeshTerm/releases/tag/v0.10.1
|
||||
[0.10.0]: https://github.com/jpmartineau/MeshTerm/releases/tag/v0.10.0
|
||||
[0.9.0]: https://github.com/jpmartineau/MeshTerm/releases/tag/v0.9.0
|
||||
+60
-2
@@ -25,8 +25,66 @@ this repository.
|
||||
seen, etc.), UX standards for screens/dialogs/menus, and where the reusable building
|
||||
blocks live in code (`ui/menus.py`, `ui/markdown.py`, `ui/widgets.py`, `ui/theme.py`).
|
||||
New code should read like it always belonged here.
|
||||
- Small fixes can go straight to a pull request. For anything bigger, open an issue
|
||||
first and wait for a yes before writing a lot of code — saves both of us a rewrite.
|
||||
- Tell us before you start working on something (see below). For anything bigger than a
|
||||
small fix, expect to talk it through first. That saves everyone a rewrite.
|
||||
|
||||
## Who can contribute
|
||||
|
||||
If you use MeshTerm, you're welcome here. Maybe something broke for you, or you wished
|
||||
it could do one more thing, or a screen looked wrong on your radio. Those are exactly
|
||||
the changes we want.
|
||||
|
||||
### How MeshTerm was made
|
||||
|
||||
Some people in the MeshCore community don't trust code written by AI. After some of
|
||||
what has happened lately, that's easy to understand, and it deserves a straight answer.
|
||||
|
||||
Jean-Pierre Martineau, who started MeshTerm, sees it differently. AI was a big help on
|
||||
this project. Honestly, MeshTerm couldn't have been built without it. But the choices
|
||||
were his. He decided what MeshTerm should be, what it should do, and how it should
|
||||
look. He uses it every day. It's handmade, with AI's help.
|
||||
|
||||
The project wants to stay that way. That's what the rules below are for.
|
||||
|
||||
### Come say hello first
|
||||
|
||||
Using AI tools to help you work is fine. What matters is that a real person who cares
|
||||
about MeshTerm is behind the change.
|
||||
|
||||
The MeshTerm community lives on [Discord](https://discord.gg/AZwe5Uvb3S), and everyone
|
||||
is welcome to join. But please join before you change things. Before you start writing
|
||||
code, say hello in the **#contributing** channel and tell us what you'd like to work on.
|
||||
Then wait until we say yes. This goes for small fixes too.
|
||||
|
||||
When you open your pull request, include two things:
|
||||
|
||||
- **Your Discord username**, the one you use on the MeshTerm server.
|
||||
- **A link to the message where we said yes.** In Discord, right-click the message and
|
||||
choose "Copy Message Link."
|
||||
|
||||
If you haven't joined the Discord, or nobody said yes to your idea, your pull request
|
||||
will be closed without a review.
|
||||
|
||||
### No bots
|
||||
|
||||
Some accounts use AI to find open issues, write a fix, and open a pull request, all on
|
||||
their own. There's no person behind it who read the change or can answer questions
|
||||
about it. People call this "AI slop." We close those pull requests without a review,
|
||||
even if the code looks fine. The same goes for issues and bug reports written that way.
|
||||
|
||||
### Tell us if you used AI
|
||||
|
||||
This part is required. If an AI tool wrote any part of your change, say so in two
|
||||
places:
|
||||
|
||||
- **In each commit it helped with.** Add a line at the end of the commit message naming
|
||||
the tool, like `Co-Authored-By: Claude <noreply@anthropic.com>` or
|
||||
`Assisted-by: GitHub Copilot`.
|
||||
- **In your pull request.** Write a sentence about what the tool did and what you
|
||||
checked yourself.
|
||||
|
||||
If you used AI and didn't say so, we'll treat your pull request as a bot's. Saying so
|
||||
costs you nothing. Either way, you're the author, so be ready to explain your change.
|
||||
|
||||
## Making a change
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ your mesh's history is worth.
|
||||
<a href="https://discord.gg/AZwe5Uvb3S"><img alt="Discord" src="https://img.shields.io/badge/chat-Discord-5865F2"></a>
|
||||
</p>
|
||||
|
||||
> MeshTerm is a side project, run by one person. Bug reports are very welcome. Small fixes
|
||||
> can go straight to a pull request; for anything bigger, please open an issue first.
|
||||
> MeshTerm is a side project, run by one person. Bug reports are very welcome. Want to
|
||||
> change something? Say hello on [Discord](https://discord.gg/AZwe5Uvb3S) first.
|
||||
> [More on how it's run](#how-this-project-is-run).
|
||||
|
||||
https://github.com/user-attachments/assets/f91a6695-14d5-4ae0-8fdc-e65492d0955d
|
||||
@@ -297,11 +297,13 @@ fair bit, and that's on purpose: how well a problem is described really does dec
|
||||
I can do anything with it. If I can reproduce it, I'll usually chase it. If I can't, I'm
|
||||
mostly guessing.
|
||||
|
||||
**Ask before you write a big pull request.** Small fixes can go straight to a pull
|
||||
request. For anything bigger, open an issue first and wait for a yes. I'm not being
|
||||
precious — MeshTerm has firm house rules about how screens get built (they're in
|
||||
[CLAUDE.md](CLAUDE.md)), and I'd hate for you to spend a weekend on something I then ask
|
||||
you to rewrite. A quick conversation first saves us both.
|
||||
**Ask before you write a pull request.** Join the [Discord](https://discord.gg/AZwe5Uvb3S)
|
||||
and say hello in `#contributing`. Tell us what you'd like to change. Then wait for a yes.
|
||||
That goes for small fixes too. I'm not being precious. MeshTerm has firm house rules
|
||||
about how screens get built (they're in [CLAUDE.md](CLAUDE.md)), and I'd hate for you to
|
||||
spend a weekend on something I then ask you to rewrite. A quick conversation first saves
|
||||
us both. AI tools are fine, as long as you say you used them.
|
||||
[CONTRIBUTING.md](CONTRIBUTING.md) has the details.
|
||||
|
||||
**I can't promise timelines.** Some things get fixed the same night. Some sit for a month
|
||||
because life happened. If your issue goes quiet, please give it a nudge. That helps me.
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ USB, Bluetooth, or Wi-Fi needs none of this.
|
||||
| --- | --- |
|
||||
| [How the code is laid out](architecture.md) | The layering, and where a new feature goes. |
|
||||
| [CLAUDE.md](../CLAUDE.md) | The actual style guide — terminology, UX standards, and the reusable building blocks. Written for AI assistants, and the house rules for everyone. |
|
||||
| [CONTRIBUTING.md](../CONTRIBUTING.md) | The dev install, the three gates a change has to pass, and what happens to your contribution's licensing. |
|
||||
| [CONTRIBUTING.md](../CONTRIBUTING.md) | How to propose a change, the rules on AI use, the dev install, the three checks a change must pass, and what happens to your contribution's licensing. |
|
||||
|
||||
## Historical record
|
||||
|
||||
|
||||
+1
-1
@@ -1556,7 +1556,7 @@ each link; the scripted face prints the link alone.
|
||||
**`--json`:**
|
||||
|
||||
```json
|
||||
{"page":"discord","title":"Join Discord","version":"0.10.0","text":"Join the Discord\n Questions, ideas, bug reports, and mesh talk.\n\n • https://discord.gg/AZwe5Uvb3S","links":["https://discord.gg/AZwe5Uvb3S"]}
|
||||
{"page":"discord","title":"Join Discord","version":"0.10.1","text":"Join the Discord\n Questions, ideas, bug reports, and mesh talk.\n\n • https://discord.gg/AZwe5Uvb3S","links":["https://discord.gg/AZwe5Uvb3S"]}
|
||||
```
|
||||
|
||||
| Field | Type | Meaning |
|
||||
|
||||
+10
-5
@@ -33,7 +33,7 @@ That's [further down](#always-on-the-bridge) — most people want the section ab
|
||||
- [Step 2 — Install the radio library](#step-2--install-the-radio-library)
|
||||
- [Step 3 — Install MeshTerm](#step-3--install-meshterm)
|
||||
- [Step 4 — Connect](#step-4--connect)
|
||||
- [Moving over from the bridge](#moving-over-from-the-bridge)
|
||||
- [Moving over from the bridge (0.9.0 and earlier)](#moving-over-from-the-bridge-090-and-earlier)
|
||||
- [Board wiring](#board-wiring)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Always on: the bridge](#always-on-the-bridge)
|
||||
@@ -215,11 +215,16 @@ The AIO's wiring is the default, so this profile needs nothing beyond `transport
|
||||
|
||||
---
|
||||
|
||||
## Moving over from the bridge
|
||||
## Moving over from the bridge (0.9.0 and earlier)
|
||||
|
||||
Already running the bridge? The first time you connect with `--spi` (or a profile, or the
|
||||
picker row), MeshTerm carries the bridge's node over — **copying**, never moving, so the
|
||||
bridge still works afterwards if you go back to it:
|
||||
> **Only if you set up the bridge with MeshTerm 0.9.0 or earlier.** Before 0.10.0 the bridge
|
||||
> was the only way to use the uConsole's radio, so an older setup is almost certainly
|
||||
> running one. On a fresh install of 0.10.0 or newer there is nothing to move over — skip
|
||||
> ahead to [Board wiring](#board-wiring).
|
||||
|
||||
Upgraded from 0.9.0 or earlier with the bridge still installed? The first time you connect
|
||||
with `--spi` (or a profile, or the picker row), MeshTerm carries the bridge's node over —
|
||||
**copying**, never moving, so the bridge still works afterwards if you go back to it:
|
||||
|
||||
- the identity key — from the `meshcore-console` GUI's copy first
|
||||
(`~/.local/share/meshcore-uconsole/identity.key`), the way the bridge itself looks for
|
||||
|
||||
@@ -6,7 +6,7 @@ Connects to a companion over USB, Bluetooth, or TCP. For Windows, macOS, and Lin
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
__version__ = "0.10.0"
|
||||
__version__ = "0.10.1"
|
||||
__author__ = "Jean-Pierre Martineau"
|
||||
|
||||
#: The year MeshTerm was first published; the copyright span starts here.
|
||||
|
||||
+8
-2
@@ -603,7 +603,10 @@ def _report_no_windows_console(console: Console) -> None:
|
||||
console.print("try [accent]meshterm --help[/accent].")
|
||||
|
||||
|
||||
@app.command(name="platform")
|
||||
@app.command(
|
||||
name="platform",
|
||||
help="Show which platform MeshTerm runs as, and why",
|
||||
)
|
||||
def platform_command() -> None:
|
||||
"""Print the resolved platform and every input the resolution looked at.
|
||||
|
||||
@@ -650,7 +653,10 @@ def platform_command() -> None:
|
||||
)
|
||||
|
||||
|
||||
@app.command(name="specimen")
|
||||
@app.command(
|
||||
name="specimen",
|
||||
help="Print every mark, icon, and colour MeshTerm draws",
|
||||
)
|
||||
def specimen_command() -> None:
|
||||
"""Print the visual-language specimen for the active platform.
|
||||
|
||||
|
||||
@@ -273,6 +273,8 @@ class DiscoveredDevice:
|
||||
if self.is_spi:
|
||||
return f"{self.name or 'SPI radio'} ({self.port})"
|
||||
name = self.product or self.description or self.vendor_label or "Serial device"
|
||||
if not self.port: # the simulator has nothing to point at, so no "()"
|
||||
return name
|
||||
suffix = f"({self.port})"
|
||||
if name.endswith(suffix): # avoid "… (COM11) (COM11)"
|
||||
name = name[: -len(suffix)].rstrip()
|
||||
|
||||
@@ -26,6 +26,9 @@ from ..services.records import CATEGORIES, CATEGORY_BY_ID, Category
|
||||
from ..ui.script import NONE
|
||||
from .base import Tool, ToolResult, register
|
||||
|
||||
#: The hash widths a record can be set at: MeshCore's path hashes are 1, 2 or 4 bytes.
|
||||
WIDTHS = (1, 2, 4)
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover - typing only
|
||||
from ..persistence.repository import DiscoveredPath
|
||||
from ..ui.fields import NodeRef
|
||||
@@ -183,7 +186,13 @@ class TrophyCaseTool(Tool):
|
||||
f"--category must be one of: {choices} (got {category!r})"
|
||||
)
|
||||
tool_params["category"] = category
|
||||
if width:
|
||||
if width is not None:
|
||||
# The same closed set as --category, for the same reason: `--width 3`
|
||||
# found nothing and exited 5, indistinguishable from an empty database,
|
||||
# and `--width 0` was falsy and so quietly listed every width.
|
||||
if width not in WIDTHS:
|
||||
choices = ", ".join(map(str, WIDTHS))
|
||||
raise typer.BadParameter(f"--width must be one of: {choices} (got {width})")
|
||||
tool_params["width"] = width
|
||||
run_tool_command(self, tool_params)
|
||||
|
||||
|
||||
@@ -566,8 +566,19 @@ def test_a_destructive_command_without_yes_is_a_silent_usage_error(run, args) ->
|
||||
[
|
||||
(("contacts", "--sort", "bogus"), "--sort"),
|
||||
(("records", "--category", "long_hual"), "--category"),
|
||||
(("records", "--width", "3"), "--width"),
|
||||
(("records", "--width", "0"), "--width"),
|
||||
(("records", "--width", "-1"), "--width"),
|
||||
(("records", "--json", "--width", "3"), "--width"),
|
||||
],
|
||||
ids=[
|
||||
"contacts-sort",
|
||||
"records-category",
|
||||
"records-width-3",
|
||||
"records-width-0",
|
||||
"records-width-negative",
|
||||
"records-width-3-json",
|
||||
],
|
||||
ids=["contacts-sort", "records-category"],
|
||||
)
|
||||
def test_a_value_outside_a_closed_set_is_refused_rather_than_ignored(run, args, flag) -> None: # noqa: ANN001
|
||||
"""A typo in a closed-set option used to be answered with a different question.
|
||||
@@ -754,6 +765,49 @@ def test_a_closing_message_goes_to_stderr_too(run) -> None: # noqa: ANN001
|
||||
assert "records stored" in result.stderr
|
||||
|
||||
|
||||
@pytest.mark.parametrize("width", ["1", "2", "4"])
|
||||
def test_every_real_hash_width_is_accepted(run, width) -> None: # noqa: ANN001
|
||||
"""Refusing the widths that don't exist must not refuse the three that do."""
|
||||
result = run("records", "--width", width)
|
||||
assert result.exit_code == exitcodes.NO_RESULT, result.stderr
|
||||
assert "records stored" in result.stderr
|
||||
|
||||
|
||||
def test_the_simulator_is_listed_without_empty_brackets(run) -> None: # noqa: ANN001
|
||||
"""``--mock devices`` named the simulator ``… (nothing transmits) ()``: a port it lacks."""
|
||||
result = run("devices")
|
||||
assert result.exit_code == exitcodes.OK, result.stderr
|
||||
assert "the built-in simulator (nothing transmits)" in result.stdout
|
||||
assert "()" not in result.stdout
|
||||
|
||||
|
||||
#: What a docstring written for the source leaks into ``--help`` when Typer is left to
|
||||
#: use it: a Sphinx role, reST's double backticks, or a path into the package.
|
||||
_SOURCE_NOTES = re.compile(r":(func|mod|class|meth|attr|data):`|``|meshterm/[\w/]+\.py")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("leaf", _leaf_commands(), ids=lambda leaf: "-".join(leaf))
|
||||
def test_every_command_help_is_written_for_the_terminal(run, leaf) -> None: # noqa: ANN001
|
||||
"""A help page is for someone at a prompt asking what a command does.
|
||||
|
||||
``platform`` and ``specimen`` printed their whole docstrings — notes for whoever reads
|
||||
``cli.py``, Sphinx roles and source paths included — and the same markup cut their
|
||||
one-line summaries short in ``meshterm --help``.
|
||||
"""
|
||||
result = run(*leaf, "--help")
|
||||
assert result.exit_code == exitcodes.OK, result.output
|
||||
assert not _SOURCE_NOTES.search(result.output), result.output
|
||||
|
||||
|
||||
def test_the_command_list_has_no_source_notes(run) -> None: # noqa: ANN001
|
||||
"""The one-line summaries in ``meshterm --help`` come from the same help text."""
|
||||
result = run("--help")
|
||||
assert not _SOURCE_NOTES.search(result.output), result.output
|
||||
for line in result.output.splitlines():
|
||||
if line.split()[:1] in (["platform"], ["specimen"]):
|
||||
assert not line.rstrip().endswith("..."), f"summary cut short: {line!r}"
|
||||
|
||||
|
||||
# -- the simulator's own history ------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
+16
-1
@@ -14,7 +14,7 @@ import pytest
|
||||
|
||||
from meshterm.core.config import DeviceProfile
|
||||
from meshterm.core.device_store import DeviceStore
|
||||
from meshterm.core.discovery import DiscoveredDevice, discover_devices
|
||||
from meshterm.core.discovery import TRANSPORT_MOCK, DiscoveredDevice, discover_devices
|
||||
from meshterm.core.selection import DeviceSelectionError, resolve_device
|
||||
|
||||
|
||||
@@ -112,6 +112,21 @@ def test_label_does_not_repeat_the_port() -> None:
|
||||
assert DiscoveredDevice("COM5", product="Wio SX1262").label == "Wio SX1262 (COM5)"
|
||||
|
||||
|
||||
def test_a_device_with_no_port_gets_no_empty_brackets() -> None:
|
||||
"""The simulator has nothing to point at, so its label ends at its name, not in "()".
|
||||
|
||||
The serial branch is also where every transport without one of its own lands, and it
|
||||
appended ``({port})`` whether or not there was a port.
|
||||
"""
|
||||
sim = DiscoveredDevice(
|
||||
transport=TRANSPORT_MOCK,
|
||||
description="the built-in simulator (nothing transmits)",
|
||||
name="simulator",
|
||||
)
|
||||
assert sim.label == "the built-in simulator (nothing transmits)"
|
||||
assert DiscoveredDevice(product="Wio SX1262").label == "Wio SX1262"
|
||||
|
||||
|
||||
def test_stable_id_precedence() -> None:
|
||||
"""stable_id prefers serial number, then vid:pid, then the port name."""
|
||||
assert DiscoveredDevice("COM5", serial_number="SN1", vid=1, pid=2).stable_id == "sn:SN1"
|
||||
|
||||
Reference in new issue
Block a user