Add CLI Console: run companion commands with visible output from the dashboard#295
Conversation
The existing command card runs execute_command_ui but discards the response, so output is only visible in Developer Tools or the logs (issues meshcore-dev#240, meshcore-dev#111). This adds a CLI Console: an interactive command surface that records each command and its response into a sensor transcript visible in the UI. It records only command/response pairs, never the device's continuous diagnostic/noise-floor log stream. - const: SERVICE_CLI_COMMAND / SERVICE_CLI_COMMAND_UI, CONF_CLI_CONSOLE_ENABLED, CLI_CONSOLE_MAX_LINES, EVENT_CLI_RESPONSE - coordinator: bounded cli_console_history deque + record_cli_console() - sensor: MeshCoreCLIConsoleSensor (created when CONF_CLI_CONSOLE_ENABLED); state = last command, attributes carry structured history + a pre-rendered markdown transcript for a markdown card - services: cli_command wraps execute_command, records the result, and fires meshcore_cli_response; cli_command_ui drives it from text.meshcore_command - config_flow: "Enable CLI Console" toggle in Global Settings - services.yaml + en.json translations + docs (dashboard, services, events) - tests: tests/test_cli_command.py Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- New docs/docs/cli-commands.md: explains these are meshcore-py SDK method names (not the iOS app / meshcli / repeater-CLI vocabularies), with a grouped reference (query, config, channels, remote-node commands), argument syntax, and pointers to the authoritative source. - Cross-link it from services.md and the dashboard overview. - Fix the CLI Console card example: use a literal block (|) for the markdown content so the transcript keeps its newlines instead of collapsing onto one line, and modern perform_action syntax. Addresses the discoverability gap from upstream issue meshcore-dev#111. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses console testing feedback: - No way to clear the transcript -> add coordinator.clear_cli_console(), a meshcore.cli_console_clear service, and a "CLI Clear Console" button. - The Run button as a `type: button` card renders huge -> ship Run/Clear as compact button *entities* (button platform), attached to the device so they also appear on the device page automatically. - Docs: recommend an entities-card layout (compact rows) instead of the oversized button card, and explain that the auto device page can't host the transcript card / control placement (use a dashboard sections view). New button platform (gated on CONF_CLI_CONSOLE_ENABLED): button.*_cli_run -> calls cli_command_ui button.*_cli_clear -> clears the console - services.yaml + en.json for cli_console_clear - test: cli_console_clear clears the resolved coordinator Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Device page feedback: the CLI buttons + console sensor showed under the device's Controls/Sensors, implying a console that can't actually work there (a device page can't render the transcript). Detach all three CLI entities from the device and hide them by default; they're used only by entity_id in the dashboard CLI Console card. Error feedback: contact-typed commands that failed to resolve returned a silent None, surfacing as a vague "(no response)". Add _contact_error() so the response explains why — pubkey_prefix_too_short (e.g. "ae6d" is under 6 hex chars), contact_not_found, or not_connected — with the offending argument. Also return a structured unknown_keyword error. - sensor/button: no device_info, entity_registry_visible_default = False - docs: correct the device-page claims; note entities are dashboard-only - tests: short-prefix and unknown-contact return structured errors Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@awolden When will this feature be approved? I'm waiting for it for my Meshcore-card project https://github.com/dida886/meshcore-card/tree/main. |
awolden
left a comment
There was a problem hiding this comment.
Nice work, this is well put together and the structured contact errors are a win on their own. Two changes before merge:
-
Fold the wrapper services into a flag. Instead of
cli_command/cli_command_ui, add an optionalrecord_to_console: trueboolean toexecute_commandandexecute_command_ui. When set, record the command/response pair to the transcript and firemeshcore_cli_response, exactly what cli_command does today. That keeps one command API instead of two parallel ones (the docs currently have to explain the difference), and the Run button just callsexecute_command_uiwith the flag set.cli_console_clearstays as-is. The sensor, docs, tests, and error handling all carry over, this mostly deletes code from your diff. -
Keep the transcript out of the recorder DB. Add
_unrecorded_attributes = frozenset({"history", "transcript", "last_response", "last_command"})to the console sensor (select.py:268 has the pattern), and use a neutral state like command count or last-run timestamp instead of the raw command string. Right now every command rewrites the full 50-entry transcript into the DB (a single get_contacts response can be tens of KB), and the state can land secrets likesend_login <contact> <password>in recorder history.
Non-blocking: consider attaching the sensor and buttons to the root device so the entity ids are findable without Dev Tools. The buttons work fine from a device page even if the transcript card doesn't render there.
…t out of recorder Address PR meshcore-dev#295 review: - Replace cli_command/cli_command_ui with a record_to_console flag on execute_command/execute_command_ui; the Run button sets the flag. Removes the parallel command API, its schema, yaml, translations, and unload entries. - Exclude history/transcript/last_command/last_response from the recorder DB and make the console sensor state a neutral command count instead of the raw command string (which could leak secrets like send_login <contact> <password>). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@awolden Thanks for the review! Both blocking items are addressed in 59ac044. 1. Folded the wrapper services into a flag. Removed 2. Kept the transcript out of the recorder DB. Added All 222 unit tests pass, and I deployed to a live HA instance and confirmed the flag, the Run button, the fired event, and the neutral sensor state all behave as expected. Non-blocking (attach the sensor/buttons to the root device): left as a potential follow-up for now, happy to fold it in if you'd like it in this PR. |
awolden
left a comment
There was a problem hiding this comment.
Fast turnaround, all addressed cleanly. Thanks for the extra negative test and the dead code cleanup.
|
@drewmccal hi. How I can read the response of CLI ?
I thought the answer would be in the logbook |
|
Hi @dida886 — good news, it's actually working. The number that keeps incrementing is the sensor's state, and that's intentional: it's just a command counter, not the output. The command and its response are deliberately kept out of the state (and out of the recorder database). The state gets written to history, and a command like The actual CLI output lives in the sensor's attributes, not the state and not the logbook:
To see it on your dashboard, add a markdown card that renders the type: markdown
content: |{{ state_attr('sensor.meshcore_XXXXXX_cli_console', 'transcript') }} (Replace The logbook won't show responses — for automations, use the For your meshcore-card project: read |
…transcript attribute Address confusion from PR meshcore-dev#295 / issue meshcore-dev#313: users expect the CLI response in the sensor state or the logbook. Add a note that the state is a neutral counter (to keep secrets out of the recorder) and the output lives in the transcript / history / last_response attributes, rendered via the markdown card. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@awolden I pushed documentation changes to this feature branch |




What
Adds a CLI Console for the local companion radio: run SDK commands and see their responses in a persistent on-dashboard transcript, with a clear button and an event for automations.
execute_commandalready returns structured responses in the Actions dev tool, but there was no way to see command output in the UI or for non-admins. This adds that visibility layer without changing the existing service.Changes
cli_command(wrapsexecute_command, records to a transcript, firesmeshcore_cli_response, returns the response),cli_command_ui(reads thetext.meshcore_commandhelper, runs it, clears the input),cli_console_clear.MeshCoreCLIConsoleSensor(new): rolling transcript (last 50 lines) exposed via state +transcript/historyattributes for a markdown card. Off the device page by default.button.py(new): "CLI Run Command" and "CLI Clear Console" button entities (compact entity rows, not full-width cards). Off the device page by default.{error: ..., command, argument, detail}instead of silentNone, so the console shows why a command failed (pubkey_prefix_too_short,contact_not_found,not_connected).cli-commands.mdreference (explains the SDK-method vocabulary vs iOS-app/meshcli/repeater-CLI naming), plus dashboard/services/events docs.cli_console_enabledtoggle in global settings.🤖 Generated with Claude Code