Apache-2.0 does not require per-file headers, but a file lifted out of the tree carried no marking at all. One comment line, first (or right after a shebang), on every .py under meshterm/, tests/, packaging/ and scripts/, plus the extensionless SPI bridge; tests/test_spdx.py keeps a new file from landing without one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
73 lines
2.5 KiB
Python
73 lines
2.5 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""The ``advert`` tool: announce this node to the mesh, straight from the main menu.
|
|
|
|
Opens the Send advert flow (see :func:`meshterm.ui.config_editor.send_advert`) — a
|
|
zero-hop or flood advertisement, or this node's shareable contact card as a QR code.
|
|
A ``popup`` tool: the flow's prompts and result float over the still-pushed main menu
|
|
as modal dialogs (the quit-dialog pattern), so the everyday action stays a quick
|
|
in-place popup rather than a screen change.
|
|
|
|
Menu-only: the scripted equivalents are ``config advert`` and ``config share``, so no
|
|
duplicate subcommand is registered here.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
import typer
|
|
|
|
from ..context import AppContext
|
|
from .base import Tool, ToolResult, register
|
|
|
|
|
|
@register
|
|
class SendAdvertTool(Tool):
|
|
"""Send a zero-hop or flood advert, or share this node's contact card."""
|
|
|
|
name = "advert"
|
|
title = "Send advert"
|
|
icon = "📡"
|
|
help = "Announce this node — zero-hop, flood, or QR"
|
|
category = "This node"
|
|
order = 40 # your own node announcing itself: a device action, not a view
|
|
popup = True # dialog-sized: float over the menu instead of replacing it
|
|
|
|
async def prompt_params(self, ctx: AppContext) -> dict[str, Any] | None:
|
|
"""Run the interactive advert flow; there are never parameters to collect.
|
|
|
|
The flow presents its own result (the sent-advert note or the contact-card
|
|
view) before returning, so by the time the user backs out there is nothing
|
|
left for :meth:`run` to do — returning ``None`` tells the menu the invocation
|
|
is complete (the same pattern as the Device config page).
|
|
|
|
Args:
|
|
ctx: Shared application context.
|
|
|
|
Returns:
|
|
Always ``None``.
|
|
"""
|
|
from ..ui.config_editor import send_advert
|
|
|
|
await send_advert(ctx)
|
|
return None
|
|
|
|
async def run(self, ctx: AppContext, params: dict[str, Any]) -> ToolResult:
|
|
"""Nothing runs here: the advert is sent inside :meth:`prompt_params`.
|
|
|
|
Args:
|
|
ctx: Shared application context.
|
|
params: Unused.
|
|
|
|
Returns:
|
|
An empty :class:`ToolResult` (only reachable from a scripted call).
|
|
"""
|
|
return ToolResult(summary={})
|
|
|
|
def register_cli(self, app: typer.Typer) -> None:
|
|
"""Register no CLI command — ``config advert`` / ``config share`` cover scripting.
|
|
|
|
Args:
|
|
app: The Typer application (untouched).
|
|
"""
|