flipper

package
v0.9.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 27, 2026 License: AGPL-3.0 Imports: 14 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrConnectTimeout = errors.New("timeout waiting for Flipper CLI prompt")

ErrConnectTimeout is returned when the Flipper does not produce a CLI prompt within the connect timeout. The Flipper is likely inside an app or on a dialog that has taken over the CLI — press Back on the device and retry.

View Source
var ErrInRPCMode = errors.New("flipper is in RPC (mirror) mode; release the mirror to use CLI commands")

ErrInRPCMode is returned by CLI methods when the Flipper is held by a concurrent RPC session. Release the mirror (call the closure returned by EnterRPC) before issuing CLI commands.

View Source
var ErrResponseTruncated = errors.New("flipper response truncated: exceeded max accumulator size")

ErrResponseTruncated is returned by read paths when accumulated output exceeds the configured cap. Partial output is still returned alongside the error so callers can inspect what arrived before the overflow.

Functions

func SanitizeArg

func SanitizeArg(s string) string

SanitizeArg is the exported wrapper for callers outside this package that build Flipper CLI commands directly (e.g. the agent's inline bruteforce dispatch). Prefer the typed wrapper functions when one exists. Delegates to clisafe.SanitizeArg.

Types

type Capabilities

type Capabilities struct {
	// ===== Identity (existing, preserved) =====
	FirmwareFork    string // "" (stock/OFW), "Unleashed", "Momentum", "Xtreme", "RogueMaster", …
	FirmwareVersion string // fork-specific version string from firmware_version
	FirmwareCommit  string // short git SHA from firmware_commit
	FirmwareDate    string // build date string from firmware_build_date (DD-MM-YYYY)
	HardwareUID     string // STM32 unique ID (16 hex chars) from hardware_uid
	HardwareName    string // user-settable dolphin name from hardware_name

	// ===== Identity (new) =====
	// FirmwareBand is the resolved fork+version band, e.g. "momentum/mntm-dev",
	// "unleashed/unlshd-086", "stock/1.0.x". See resolveBand() for the full
	// value set (firmware-matrix.md §3.6).
	FirmwareBand        string
	FirmwareAPIMajor    int    // from firmware_api_major (numeric); 80-83=OFW 0.10x, 85+=OFW 1.x, 77-79=Momentum, 70-72=Xtreme, 86-87=Unleashed/RM
	FirmwareAPIMinor    int    // from firmware_api_minor
	FirmwareCommitDirty bool   // from firmware_commit_dirty ("1" → true); nightly-build signal
	FirmwareOriginGit   string // from firmware_origin_git (upstream repo URL)
	HardwareRegion      string // from hardware_region — string form (EU/US/JP/WW or numeric)
	HardwareVer         int    // from hardware_ver (board revision; F7 production = 13)
	// DeviceInfoKeyStyle indicates which key separator the parser saw.
	// Always "underscore" when populated via device_info (the standard path);
	// set to "dotted" if a future caller uses `info device` output instead.
	DeviceInfoKeyStyle string

	// ===== CLI surface (existing, preserved) =====
	// PowerInfoCmd is the CLI verb that returns power/battery information.
	// All modern forks use "info power"; empty means unavailable.
	PowerInfoCmd           string // "info power" on all modern forks
	HasNFCSubshell         bool   // `nfc` subshell present; false only on Xtreme
	SubGHzNeedsDev         bool   // `subghz tx/rx` requires a trailing `<device>` arg (0=INT, 1=EXT)
	NFCFlaggedArgs         bool   // NFC subshell uses flag-based args (-p, -d, -b) rather than positional
	SubGHzRxRawHasFilePath bool   // `subghz rx_raw` accepts a file-path arg (false on all modern forks — streams to stdout)

	// ===== CLI surface (new — architect additions §C.1) =====
	// JSEngineKind is the CLI verb prefix for the JS engine ("mjs" on all
	// four active forks including archived Xtreme; firmware-matrix.md §4.1
	// found no divergence despite the runbook's earlier claim).
	JSEngineKind           string // "mjs" universally; "" if JS is absent
	HasBLESpam             bool   // BLE Spam FAP present (Momentum/Xtreme in-tree; RM optimistic default)
	HasSubGHzBruteforcer   bool   // Sub-GHz Bruteforcer FAP available on this fork
	HasMouseJackerFAP      bool   // NRF24 Mousejacker FAP (NRF24 add-on required)
	HasSeaderFAP           bool   // HID iCLASS Seader FAP (Momentum/Unleashed/RM)
	HasPicopassFAP         bool   // PicoPass FAP (all custom forks)
	HasNFCMagicFAP         bool   // NFC Magic card-writer FAP (all custom forks)
	HasMFKeyFAP            bool   // MFKey32 FAP (all custom forks; Unleashed ships it in-tree)
	HasMifareNestedFAP     bool   // Mifare Nested attack FAP (all custom forks)
	UniversalIRLibraryName string // SD path for the universal IR library; "assets/infrared/assets" (stock/Unleashed/RM) vs "infrared/assets" (Momentum/Xtreme)

	// ===== CLI surface (new — research additions §4.2) =====
	HasStorageFormatExt    bool // `storage format_ext` verb present (all custom forks; absent on stock OFW)
	HasSubGHzEncryptKeeloq bool // `subghz encrypt_keeloq` verb present (all custom forks)
	HasSubGHzChat          bool // `subghz chat` verb present (universal — all five forks)
	HasPsCmd               bool // `ps` alias for `top` present (Momentum + Xtreme)
	HasClearCmd            bool // `clear` terminal-clear command present (Momentum only)

	// ===== Storage quirks (new) =====
	// StorageExtFatLabel is the FAT volume label of the SD card.
	// Defaults to "Flipper SD"; Momentum freshly formats with "MOMENTUM".
	StorageExtFatLabel string
	// SnapshotPrefix is the root path for pre-write SD snapshots used by /rewind.
	SnapshotPrefix string

	// ===== Marauder-side (probed separately; not set by device_info) =====
	// MarauderDetected and MarauderCompatBand are set by the Marauder
	// connect path, not by DetectCapabilities. Left as zero-value TODOs
	// here for a follow-up v0.5.1 research task (firmware-matrix.md §6 Q6).
	MarauderDetected   bool
	MarauderCompatBand string
}

Capabilities captures the firmware-specific CLI surface of the connected Flipper, detected from `device_info` at connect time. Different custom firmwares (stock, Unleashed, RogueMaster, Xtreme, Momentum, ...) expose different CLI commands and behaviours; wrappers branch on these flags to stay portable across all five active forks.

Field ordering: identity → CLI surface → RF/NFC quirks → apps/FAPs → storage → marauder. Existing fields are preserved byte-for-byte; new fields are appended in their sections so callers that embed Capabilities by value are not broken.

func (Capabilities) FriendlyFork

func (c Capabilities) FriendlyFork() string

FriendlyFork returns a display-ready fork name, falling back to "stock" when the fork field is empty (OFW omits firmware_origin_fork entirely).

type Flipper

type Flipper struct {
	// contains filtered or unexported fields
}

Flipper is the command-layer handle for a connected device. It wraps a transport.Transport and layers prompt framing, capability detection, and hot-plug reconnect on top. All command wrappers (commands.go) go through Exec / ExecLong / StreamCtx / WriteFile — none touch the transport directly.

func Connect

func Connect(ctx context.Context, portName string, baudRate int, timeout time.Duration) (*Flipper, error)

Connect opens a serial port and performs the CLI handshake.

Preserves the pre-Phase-6 signature — the tree has ~5 callers (cmd main, tests in flipper_mock_test.go / primitives_mock_test.go) that pass a path + baud + timeout directly. Internally, Connect builds a serial:// URL and hands it to ConnectURL; new callers that want to pick a non-serial transport should use ConnectURL directly.

func ConnectURL

func ConnectURL(ctx context.Context, rawURL string, timeout time.Duration) (*Flipper, error)

ConnectURL opens the transport identified by rawURL and performs the CLI handshake. rawURL is any scheme registered with the transport package; a bare device path (e.g. "/dev/ttyACM0") is accepted as shorthand for serial://.

The handshake is cancelable: if ctx is cancelled or timeout elapses before the prompt arrives, Connect closes the transport and returns ctx.Err() or ErrConnectTimeout respectively.

func NewForTest added in v0.3.0

func NewForTest(caps Capabilities) *Flipper

NewForTest returns a Flipper with preloaded capabilities and no transport. Intended for unit tests in other packages that need to exercise State-consuming code paths without wiring up a mock serial port. The returned Flipper will never successfully Exec — only capability-derived and transport-less-safe methods should be called.

func (*Flipper) BTHCIInfo

func (f *Flipper) BTHCIInfo() (string, error)

BTHCIInfo returns local Bluetooth controller info (chip, firmware version, MAC). Read-only; does not bring up a BLE stack — native BLE operations still require an external devboard. CLI: bt hci_info

func (*Flipper) BadUSBRun

func (f *Flipper) BadUSBRun(scriptPath string) (string, error)

BadUSBRun launches a BadUSB script via the app loader. CLI: loader open "Bad USB" <script_path>

func (*Flipper) Capabilities

func (f *Flipper) Capabilities() Capabilities

Capabilities returns the detected firmware capability map. If DetectCapabilities has not yet been called (or failed), returns a zero-valued struct with the conservative defaults (power_info, nfc subshell, no subghz device arg).

func (*Flipper) Close

func (f *Flipper) Close() error

func (*Flipper) CryptoStoreKey

func (f *Flipper) CryptoStoreKey(slot int, keyType string, keySize int, keyHex string) (string, error)

CryptoStoreKey stores a key in one of the Flipper's secure-storage slots. Overwrites whatever was in that slot. keyType: "master", "simple", or "encrypted" keySize: 128 or 256 (bits); keyHex must be exactly keySize/8 bytes as hex. CLI: crypto store_key <slot> <keyType> <keySize> <keyHex>

func (*Flipper) DetectApps added in v0.5.0

func (f *Flipper) DetectApps()

DetectApps queries the connected Flipper's FAP (Flipper App Package) list via `loader list` and updates the capability flags for app-presence fields (HasBLESpam, HasSubGHzBruteforcer, HasMouseJackerFAP, etc.).

This is a best-effort probe: if `loader list` fails or times out, the FAP flags retain their fork-typical static defaults from detectCapabilities (set conservatively for Unleashed and optimistically for RogueMaster per firmware-matrix.md §6 Q8). A failed probe never propagates an error — the device_info parse is the authoritative signal for fork/version detection.

Integration note: DetectApps should be called from DetectCapabilities (serial.go) after the device_info parse and initial caps.Store. Since serial.go is managed by the transport layer (not in this engineer's write scope for v0.5), callers may invoke DetectApps() independently after DetectCapabilities() when FAP-presence accuracy is required. The firmware_introspect Handler (internal/tools/firmware.go) calls it when refresh=true.

Name-to-flag mapping (firmware-matrix.md §4.3):

"BLE Spam"           → HasBLESpam
"SubGHz Bruteforcer" → HasSubGHzBruteforcer
"Sub-GHz BF"         → HasSubGHzBruteforcer (alternate name on some forks)
"NRF24 Mousejacker"  → HasMouseJackerFAP
"Seader"             → HasSeaderFAP
"PicoPass"           → HasPicopassFAP
"NFC Magic"          → HasNFCMagicFAP
"MFKey"              → HasMFKeyFAP   (Unleashed in-tree path)
"MFKey32"            → HasMFKeyFAP   (RM/Momentum external FAP)
"Mifare Nested"      → HasMifareNestedFAP

func (*Flipper) DetectCapabilities

func (f *Flipper) DetectCapabilities() (Capabilities, error)

DetectCapabilities queries device_info and caches the parsed capability map. Best-effort: on error, leaves prior caps (or defaults) in place.

func (*Flipper) DeviceInfo

func (f *Flipper) DeviceInfo() (string, error)

DeviceInfo returns device information. CLI: device_info

func (*Flipper) DeviceInfoMap added in v0.2.0

func (f *Flipper) DeviceInfoMap() (map[string]string, error)

DeviceInfoMap runs device_info and parses the output into a flat key→value map. Blank lines and lines missing a colon are skipped. The full surface is preserved — callers wanting one field (e.g. the dolphin name) should look up the key directly. Numeric-looking values stay as strings so consumers decide the typing (some fields like storage_sdcard_totalSpace are huge int64s that don't survive JavaScript number coercion without care).

func (*Flipper) EnterRPC added in v0.9.2

func (f *Flipper) EnterRPC(ctx context.Context) (*rpc.Client, func(), error)

EnterRPC transitions the Flipper into RPC mode and returns a typed client along with a release closure.

Semantics:

  1. Acquires f.mu for the entire RPC session.
  2. Attempts reconnect if the transport is marked disconnected.
  3. Drains any pending CLI output from the buffer.
  4. Constructs rpc.NewClient, calls Open. On error: unlocks and returns.
  5. Sets rpcMode to true.
  6. Returns the client and a release closure that: clears rpcMode, closes the client, re-handshakes the CLI prompt, and unlocks the mutex.

The release closure is safe to call exactly once; subsequent calls are no-ops.

func (*Flipper) Exec

func (f *Flipper) Exec(command string) (string, error)

Exec sends a CLI command and returns the full response. Preserved for backward compatibility; new callers should use ExecCtx so cancellation propagates through to the reconnect path.

func (*Flipper) ExecCtx

func (f *Flipper) ExecCtx(ctx context.Context, command string) (string, error)

ExecCtx is the context-aware variant of Exec. The ctx is honoured during reconnect polling and during the 10 s per-command read deadline.

func (*Flipper) ExecLong

func (f *Flipper) ExecLong(command string, timeout time.Duration) (string, error)

ExecLong sends a command that may take a while (captures, brute force, etc). Preserved for backward compatibility; new callers should use ExecLongCtx so cancellation propagates through to the reconnect path.

func (*Flipper) ExecLongCtx

func (f *Flipper) ExecLongCtx(ctx context.Context, command string, timeout time.Duration) (string, error)

ExecLongCtx is the context-aware variant of ExecLong. A non-positive timeout is floored to 60 s so a caller passing a zero duration still gets a sane per-command deadline.

func (*Flipper) GPIORead

func (f *Flipper) GPIORead(pin string) (string, error)

GPIORead reads the current value of a GPIO pin. CLI: gpio read <pin>

func (*Flipper) GPIOSet

func (f *Flipper) GPIOSet(pin string, value int) (string, error)

GPIOSet sets a GPIO pin to a value. CLI: gpio set <pin> <value>

func (*Flipper) I2CScan

func (f *Flipper) I2CScan() (string, error)

I2CScan scans the I²C bus for connected devices. Tries the built-in `i2c scan` CLI first (available on Xtreme and forks that ship it); if the firmware rejects the command, falls back to launching the "I2C Scanner" FAP via loader_open. Buzzes on success. CLI: i2c scan → loader open "I2C Scanner"

func (*Flipper) IButtonEmulate

func (f *Flipper) IButtonEmulate(protocol string, hexData string, duration time.Duration) (string, error)

IButtonEmulate emulates an iButton key for the given duration. The firmware command is streaming (prints "Emulating key ..." then waits for Ctrl+C), so the wrapper uses ExecLong with streaming semantics. Supported protocols: Dallas, Cyfral, Metakom. A reader must be in contact with the iButton contacts during the emulation window. CLI: ikey emulate <protocol> <hex_data>

func (*Flipper) IButtonRead

func (f *Flipper) IButtonRead(timeout time.Duration) (string, error)

IButtonRead reads an iButton key. CLI: ikey read

func (*Flipper) IButtonWrite

func (f *Flipper) IButtonWrite(hexData string) (string, error)

IButtonWrite writes an iButton key (Dallas only). CLI: ikey write Dallas <hex_data>

func (*Flipper) IRDecodeFile

func (f *Flipper) IRDecodeFile(path string) (string, error)

IRDecodeFile parses a saved .ir file and returns the decoded entries. Read-only and local to the SD card — no transmit. CLI: ir decode <path>

func (*Flipper) IRRx

func (f *Flipper) IRRx(timeout time.Duration) (string, error)

IRRx listens for an incoming infrared signal. CLI: ir rx

func (*Flipper) IRRxRaw

func (f *Flipper) IRRxRaw(timeout time.Duration) (string, error)

IRRxRaw listens for a raw infrared signal. CLI: ir rx raw

func (*Flipper) IRTxParsed

func (f *Flipper) IRTxParsed(protocol string, address string, command string) (string, error)

IRTxParsed transmits a decoded infrared signal. CLI: ir tx <protocol> <address_hex> <command_hex>

func (*Flipper) IRTxRaw

func (f *Flipper) IRTxRaw(frequency uint32, dutyCycle float64, data string) (string, error)

IRTxRaw transmits a raw infrared signal. CLI: ir tx RAW F:<freq> DC:<duty_cycle> <data>

func (*Flipper) IRUniversal

func (f *Flipper) IRUniversal(remoteName string, signalName string) (string, error)

IRUniversal brute-forces every variant of the named signal category across all manufacturer codes in the specified universal remote library. It is NOT a single-shot transmission — the firmware sweeps all frames of that signal type (e.g. "Power" fires every known power-off frame). CLI: ir universal <remote_name> <signal_name>

func (*Flipper) IRUniversalList

func (f *Flipper) IRUniversalList(library string) (string, error)

IRUniversalList lists entries in a universal remote library file so the agent can see which buttons are available before calling IRUniversal. CLI: ir universal list <library>

func (*Flipper) InputSend

func (f *Flipper) InputSend(button string, eventType string) (string, error)

InputSend sends a synthetic button input event. CLI: input send <button> <type> button: up, down, left, right, ok, back eventType: press, release, short, long

func (*Flipper) InvalidateState added in v0.3.0

func (f *Flipper) InvalidateState()

InvalidateState drops the cached snapshot so the next State() call forces a re-query. Intended for use after operations that might change the observable state materially (firmware updates, power reboots, storage format), not for every write — the 2 s TTL already covers ordinary drift.

func (*Flipper) JSRun

func (f *Flipper) JSRun(path string, duration time.Duration) (string, error)

JSRun executes a saved JavaScript file on the Flipper's JS runtime. Fork-gated: only the Xtreme, Momentum, and RogueMaster forks ship a JS engine. On stock the call returns a friendly-fork error rather than issuing a no-op CLI command that hangs. CLI: js <path>

func (*Flipper) LED

func (f *Flipper) LED(channel string, value int) (string, error)

LED sets a single LED channel to a brightness value (0-255). CLI: led <r|g|b|bl> <0-255> channel: "r" (red), "g" (green), "b" (blue), "bl" (backlight)

func (*Flipper) LoaderClose

func (f *Flipper) LoaderClose() (string, error)

LoaderClose closes the currently running application. CLI: loader close

func (*Flipper) LoaderInfo

func (f *Flipper) LoaderInfo() (string, error)

LoaderInfo returns metadata about the currently running app (name, flags). CLI: loader info

func (*Flipper) LoaderList

func (f *Flipper) LoaderList() (string, error)

LoaderList lists all available applications. CLI: loader list

func (*Flipper) LoaderListParsed

func (f *Flipper) LoaderListParsed() (LoaderApps, error)

LoaderListParsed returns the app/settings lists as structured data so the agent can decide whether a target app is installed before calling loader_open. Returned fields are empty slices (not nil) when a section is missing from the output.

func (*Flipper) LoaderMFKey

func (f *Flipper) LoaderMFKey() (string, error)

LoaderMFKey launches the "MFKey32" FAP for MIFARE Classic key recovery.

func (*Flipper) LoaderMifareNested

func (f *Flipper) LoaderMifareNested() (string, error)

LoaderMifareNested launches the "Mifare Nested" FAP (nested attack recovery).

func (*Flipper) LoaderNFCMagic

func (f *Flipper) LoaderNFCMagic() (string, error)

LoaderNFCMagic launches the "NFC Magic" FAP used to write MIFARE magic tags.

func (*Flipper) LoaderNRF24Mousejacker

func (f *Flipper) LoaderNRF24Mousejacker() (string, error)

LoaderNRF24Mousejacker launches the "NRF24 Mousejacker" FAP. Requires an external NRF24L01+ devboard wired to the Flipper's GPIO header. The FAP takes over the screen and reads target addresses from /ext/apps_data/nrfsniff/addresses.txt plus DuckyScript payloads from /ext/mousejacker/*.txt. Momentum firmware exposes no nrf24 CLI, so all run-time interaction happens through the FAP UI (navigate via input_send; back-button to exit).

func (*Flipper) LoaderNRF24Sniffer added in v0.3.1

func (f *Flipper) LoaderNRF24Sniffer() (string, error)

LoaderNRF24Sniffer launches the companion "NRF24 Sniffer" FAP. The FAP scans 2.4 GHz bands for active wireless-peripheral addresses and writes hits to /ext/apps_data/nrfsniff/addresses.txt (comma-separated address,rate lines). Prerequisite for any Mousejack flow — the FAP UI is operator-driven; there is no CLI equivalent.

func (*Flipper) LoaderOpen

func (f *Flipper) LoaderOpen(appName string, args string) (string, error)

LoaderOpen opens a Flipper application by name with optional arguments. The app name is always double-quoted so multi-word names (e.g. "Bad USB", "Sub-GHz BF") are parsed as a single token by the firmware's args_read_probably_quoted_string_and_trim. CLI: loader open "<app_name>" [args]

func (*Flipper) LoaderPicopass

func (f *Flipper) LoaderPicopass() (string, error)

LoaderPicopass launches the "PicoPass" FAP (HID iClass/Picopass tooling).

func (*Flipper) LoaderProtoView

func (f *Flipper) LoaderProtoView() (string, error)

LoaderProtoView launches the "ProtoView" FAP for raw Sub-GHz signal visualisation.

func (*Flipper) LoaderSPIMemManager

func (f *Flipper) LoaderSPIMemManager() (string, error)

LoaderSPIMemManager launches the "SPI Mem Manager" FAP for reading and writing SPI flash chips via the GPIO header.

func (*Flipper) LoaderSeader

func (f *Flipper) LoaderSeader() (string, error)

LoaderSeader launches the "SEADER" FAP (HID iClass SE advanced tooling).

func (*Flipper) LoaderSignal

func (f *Flipper) LoaderSignal(signal int, argHex string) (string, error)

LoaderSignal sends a numeric signal to the currently running app with an optional hex argument (many apps document custom opcodes that consume argHex). Pass "" to omit the argument. CLI: loader signal <n> [<hex>]

func (*Flipper) LoaderSignalGenerator

func (f *Flipper) LoaderSignalGenerator() (string, error)

LoaderSignalGenerator launches the "Signal Generator" FAP.

func (*Flipper) LoaderSpectrumAnalyzer

func (f *Flipper) LoaderSpectrumAnalyzer() (string, error)

LoaderSpectrumAnalyzer launches the "Spectrum Analyzer" FAP.

func (*Flipper) LoaderSubGHzBruteforcer

func (f *Flipper) LoaderSubGHzBruteforcer() (string, error)

LoaderSubGHzBruteforcer launches the "Sub-GHz BF" brute-force FAP. Destructive by design — runs enormous code sweeps.

func (*Flipper) LoaderSubGHzPlaylist

func (f *Flipper) LoaderSubGHzPlaylist() (string, error)

LoaderSubGHzPlaylist launches the "Playlist" FAP that replays a sequence of .sub captures.

func (*Flipper) LoaderT5577MultiWriter

func (f *Flipper) LoaderT5577MultiWriter() (string, error)

LoaderT5577MultiWriter launches the "T5577 Multiwriter" FAP for batch writing of 125 kHz T5577 tags.

func (*Flipper) LoaderUARTTerminal

func (f *Flipper) LoaderUARTTerminal() (string, error)

LoaderUARTTerminal launches the "UART Terminal" FAP for serial comms on the Flipper's GPIO header.

func (*Flipper) LoaderUnitemp

func (f *Flipper) LoaderUnitemp() (string, error)

LoaderUnitemp launches the "Unitemp" FAP for reading external temperature sensors over the GPIO header.

func (*Flipper) LogStream

func (f *Flipper) LogStream(duration time.Duration, level string) (string, error)

LogStream opens a live log stream from the Flipper for the supplied duration, returning the captured text. Read-only; the Flipper keeps running after the stream ends. level filters the minimum severity — empty string means the firmware's default; recognised values are "default", "error", "warn", "info", "debug", "trace". CLI: log [<level>]

func (*Flipper) NFCAPDU

func (f *Flipper) NFCAPDU(apduHex string, timeout time.Duration) (string, error)

NFCAPDU sends an APDU command to a contactless smart card (ISO7816) via the nfc subshell. Fork-gated. Subshell verb (stock/Unleashed): apdu <hex> Subshell verb (Momentum): apdu -d <hex>

func (*Flipper) NFCDetect

func (f *Flipper) NFCDetect(timeout time.Duration) (string, error)

NFCDetect enters the NFC subshell, runs the scanner subcommand, and exits. The subshell prompt is "[nfc]>: ". Not all firmware forks expose an NFC CLI — Xtreme XFW ships an empty `nfc` subsystem — so we surface a clear error rather than hanging on a non-responsive subcommand.

func (*Flipper) NFCDumpProtocol

func (f *Flipper) NFCDumpProtocol(protocol string, timeout time.Duration) (string, error)

NFCDumpProtocol dumps tag contents for a specific NFC protocol via the nfc subshell. Callers pass the canonical friendly name ("Mifare_Classic", "Mifare_Ultralight", "Mifare_Plus", "FeliCa"); the wrapper translates to the firmware's accepted token.

Subshell verb (stock/Unleashed): dump <protocol> Subshell verb (Momentum): dump -p <token> (token is mfc/mfu/mfp/felica)

Pass an empty string to skip the protocol arg entirely — Momentum's `dump` (no -p) auto-detects the protocol and writes a .nfc file in /ext/nfc/dump-YYYYMMDD-HHMMSS.nfc. That auto-save shape is the real "scan and save" workflow on Momentum and is preferred when the caller doesn't already know the protocol.

func (*Flipper) NFCEmulate

func (f *Flipper) NFCEmulate(filePath string) (string, error)

NFCEmulate launches the NFC emulation app via the loader, waits for the app to exit, then verifies the loader is free before returning. This ensures subsequent Exec calls are not blocked by "application is open" errors. Returns an error if the loader does not free within ~1 second. CLI: loader open NFC <file_path> → loader close → poll loader info

func (*Flipper) NFCMFURead

func (f *Flipper) NFCMFURead(page int, timeout time.Duration) (string, error)

NFCMFURead reads a single MIFARE Ultralight page/block. Fork-gated. Subshell verb (stock/Unleashed): mfu rdbl <page> Subshell verb (Momentum): mfu rdbl -b <page>

func (*Flipper) NFCMFUWrite

func (f *Flipper) NFCMFUWrite(page int, hexData string, timeout time.Duration) (string, error)

NFCMFUWrite writes 4 bytes of hex data to a MIFARE Ultralight page/block. Destructive — overwrites whatever the tag currently holds. Fork-gated. Subshell verb (stock): mfu wrbl <page> <hex> Subshell verb (Momentum/Unleashed): mfu wrbl -b <page> -d <hex>

func (*Flipper) NFCRawFrame

func (f *Flipper) NFCRawFrame(hexData string, timeout time.Duration) (string, error)

NFCRawFrame sends a raw ISO14443 frame to a tag via the nfc subshell and returns the tag's response. Fork-gated: not available on Xtreme (no NFC CLI subshell). Subshell verb (stock/Unleashed): raw <hex> Subshell verb (Momentum): raw -p iso14a -d <hex> Momentum's NFC CLI uses a flag-based parser; positional args are rejected. Protocol defaults to iso14a (ISO 14443-3A), the most common NFC protocol.

func (*Flipper) NFCSubcommand

func (f *Flipper) NFCSubcommand(subcommand string, timeout time.Duration) (string, error)

NFCSubcommand enters the NFC subshell, sends an arbitrary subcommand, and exits. Valid subcommands include: scanner, emulate, dump, field, raw, apdu, mfu. Not available on firmware forks without an NFC CLI subshell (e.g., Xtreme).

func (*Flipper) OneWireSearch

func (f *Flipper) OneWireSearch(duration time.Duration) (string, error)

OneWireSearch enumerates devices on the 1-Wire bus. Read-only; buzzes on success so the user knows something was found. CLI: onewire search

func (*Flipper) PowerInfo

func (f *Flipper) PowerInfo() (string, error)

PowerInfo returns power/battery information. The CLI spelling differs by fork: Xtreme uses `info power`; stock/Unleashed/RogueMaster use `power_info`. The capability map stores the right verb at connect time.

func (*Flipper) PowerInfoMap added in v0.2.0

func (f *Flipper) PowerInfoMap() (map[string]string, error)

PowerInfoMap runs the fork-appropriate power_info command and returns the parsed key→value map (charge_level, battery_voltage, capacity_*, etc.).

Separate from DeviceInfoMap because none of the forks expose power fields via device_info: Xtreme and Momentum serve them via `info power` (dot-separated keys — normalised to underscore here), stock/Unleashed/RogueMaster via the legacy `power_info` (already underscore-separated).

func (*Flipper) PowerRebootDFU

func (f *Flipper) PowerRebootDFU() (string, error)

PowerRebootDFU reboots the Flipper into the STM32 DFU bootloader. Leaves the device without a running firmware until a host reflashes or the user power-cycles — recovery is physical. Guarded as Critical at the risk layer. CLI: power reboot2dfu

func (*Flipper) RFIDEmulate

func (f *Flipper) RFIDEmulate(protocol string, data string, duration time.Duration) (string, error)

RFIDEmulate emulates an RFID tag for the given duration. The firmware command is streaming (prints "Emulating RFID..." then waits for Ctrl+C), so the wrapper uses ExecLong with streaming semantics: on deadline the lower layer sends \x03 to abort and returns the accumulated output with nil error. Pass a reasonable duration (typical: 2–10 s) — a reader needs to be pointed at the Flipper during the window. CLI: rfid emulate <protocol> <hex_data>

func (*Flipper) RFIDRawAnalyze

func (f *Flipper) RFIDRawAnalyze(filePath string) (string, error)

RFIDRawAnalyze post-processes a raw LF capture, attempting to decode the contained protocol. Pure local analysis — no RF activity. CLI: rfid raw_analyze <file_path>

func (*Flipper) RFIDRawEmulate

func (f *Flipper) RFIDRawEmulate(filePath string, duration time.Duration) (string, error)

RFIDRawEmulate replays a raw 125 kHz capture against a reader. Active transmission — use with authorisation. CLI: rfid raw_emulate <file_path>

func (*Flipper) RFIDRawRead

func (f *Flipper) RFIDRawRead(mode, filePath string, duration time.Duration) (string, error)

RFIDRawRead performs a raw 125 kHz capture to a file for later analysis. Mode is "ask" or "psk" (pass "" for auto); filePath is where the raw capture is written. Read-only from the RF perspective — no transmit. CLI: rfid raw_read [<mode>] <file_path>

Momentum firmware (confirmed via Next-Flip/Momentum-Firmware lfrfid_cli.c) uses the same `rfid raw_read` verb with the same arg shape as stock. Firmware-side arg errors are reported as a usage banner (no error code), which callers would otherwise see as a silent success. Output-scanning converts the banner to an explicit error.

func (*Flipper) RFIDRead

func (f *Flipper) RFIDRead(ctx context.Context, mode string, timeout time.Duration) (string, error)

RFIDRead reads a 125kHz RFID tag. mode is optional (normal, indala, ask, psk); pass "" for auto. Unlike a plain ExecLong, this streams the Flipper's output and returns as soon as a tag is decoded — so a successful scan takes ~1-2 seconds instead of waiting out the full timeout. If nothing appears within timeout, a helpful "no tag detected" error is returned. CLI: rfid read [mode]

func (*Flipper) RFIDWrite

func (f *Flipper) RFIDWrite(protocol string, data string) (string, error)

RFIDWrite writes data to an RFID tag. CLI: rfid write <protocol> <hex_data>

func (*Flipper) RawCLI

func (f *Flipper) RawCLI(command string) (string, error)

RawCLI sends an arbitrary CLI command string to the Flipper and returns its output. Escape hatch for firmware features we haven't wrapped, or for debugging. Callers MUST risk-gate this — it can reboot the device, write arbitrary files, jam frequencies, etc. The 30s timeout is a safety cap; for long-running commands use ExecLong directly from a wrapper.

func (*Flipper) Reboot

func (f *Flipper) Reboot() (string, error)

Reboot reboots the Flipper Zero. CLI: power reboot

func (*Flipper) Reconnect

func (f *Flipper) Reconnect(ctx context.Context) error

Reconnect forces a fresh reconnect cycle: closes the current transport, asks the transport to rescan + reopen, re-handshakes, and re-detects capabilities. Useful from a /reconnect slash command when the user has replugged and auto-detect didn't fire (e.g., the agent was idle and no IO error surfaced the drop).

func (*Flipper) SetExecTimeout added in v0.2.5

func (f *Flipper) SetExecTimeout(d time.Duration)

SetExecTimeout overrides the per-command read deadline for ExecCtx. A zero or negative value restores the 10 s default.

func (*Flipper) SetLED

func (f *Flipper) SetLED(color string, brightness int) error

SetLED sets the RGB LED to the given color + brightness (0-255). Best-effort — errors are returned but most callers ignore them. The REPL drives this at turn scope so the LED stays steady for the whole prompt, rather than flickering on/off per scan. Color is one of "r", "g", "b" (or "bl" for backlight).

func (*Flipper) SetMaxAccumBytes

func (f *Flipper) SetMaxAccumBytes(n int)

SetMaxAccumBytes overrides the per-operation read-buffer cap. Values <= 0 reset to the default (8 MiB). The cap applies to the next Exec/Stream call.

func (*Flipper) SetReconnectCallback

func (f *Flipper) SetReconnectCallback(cb func(phase, message string))

SetReconnectCallback registers a function invoked at each reconnect phase ("start", "success", "fail"). Called while f.mu is held, so keep the handler quick — typically a single stderr write via the output mutex.

func (*Flipper) SetWriteFileTimeout added in v0.2.5

func (f *Flipper) SetWriteFileTimeout(d time.Duration)

SetWriteFileTimeout overrides the post-payload read deadline for WriteFileCtx. A zero or negative value restores the 10 s default.

func (*Flipper) State added in v0.3.0

func (f *Flipper) State(ctx context.Context) (State, error)

State returns the freshest State snapshot that satisfies the cache TTL. On a cache miss it re-queries capabilities + power_info with the caller's context; partial results (capabilities only, no power data) are still cached and returned because they carry useful framing for the agent. A genuinely empty cache is returned with Connected=false and an error — the caller treats that as "skip injection this turn".

func (*Flipper) StorageCopy

func (f *Flipper) StorageCopy(src, dst string) (string, error)

StorageCopy copies a file or directory on the Flipper SD card. CLI: storage copy <src> <dst>

func (*Flipper) StorageFSInfo added in v0.2.0

func (f *Flipper) StorageFSInfo(path string) (string, error)

StorageFSInfo returns filesystem info for a storage root. CLI: storage info <path>

A real Flipper emits a multi-line block like:

Label: Flipper SD
Type: FAT32
60194KiB total
42088KiB free

Or, when the filesystem isn't ready (no SD card inserted):

Storage error: not ready

func (*Flipper) StorageFSInfoMap added in v0.2.0

func (f *Flipper) StorageFSInfoMap(path string) (map[string]string, error)

StorageFSInfoMap runs `storage info <path>` and parses it into a flat key→value map. Known keys:

present     — "true" / "false"
error       — error text when present=false
label       — filesystem label (ext) or device name (int)
type        — filesystem type ("FAT32", "exFAT", "Virtual", ...)
totalSpace  — total bytes (decimal)
freeSpace   — free bytes (decimal)

device_info does NOT carry storage fields on any fork; this CLI is the canonical source both for the mobile app and /status.

func (*Flipper) StorageList

func (f *Flipper) StorageList(path string) (string, error)

StorageList lists files and directories at the given path. CLI: storage list <path>

func (*Flipper) StorageMD5

func (f *Flipper) StorageMD5(path string) (string, error)

StorageMD5 returns the MD5 hash of a file on the SD card. CLI: storage md5 <path>

func (*Flipper) StorageMkdir

func (f *Flipper) StorageMkdir(path string) (string, error)

StorageMkdir creates a directory. CLI: storage mkdir <path>

func (*Flipper) StorageRead

func (f *Flipper) StorageRead(path string) (string, error)

StorageRead reads the contents of a file. CLI: storage read <path>

func (*Flipper) StorageRemove

func (f *Flipper) StorageRemove(path string) (string, error)

StorageRemove removes a file or directory. CLI: storage remove <path>

func (*Flipper) StorageRename

func (f *Flipper) StorageRename(src, dst string) (string, error)

StorageRename renames/moves a file or directory on the SD card. CLI: storage rename <src> <dst>

func (*Flipper) StorageStat

func (f *Flipper) StorageStat(path string) (string, error)

StorageStat returns metadata about a file or directory. CLI: storage stat <path>

func (*Flipper) StorageTree

func (f *Flipper) StorageTree(path string) (string, error)

StorageTree walks a directory recursively and returns its tree listing. CLI: storage tree <path>

func (*Flipper) StorageWrite

func (f *Flipper) StorageWrite(path string, data string) error

StorageWrite writes data to a file using the write_chunk protocol.

func (*Flipper) StorageWriteCtx added in v0.2.5

func (f *Flipper) StorageWriteCtx(ctx context.Context, path string, data string) error

StorageWriteCtx is the context-aware variant of StorageWrite.

func (*Flipper) StreamCtx

func (f *Flipper) StreamCtx(ctx context.Context, command string, onLine func(line string) (stop bool)) error

StreamCtx runs command and invokes onLine for each output line as it arrives. It returns when ctx is done, when onLine returns true (caller asks to stop), or when the Flipper emits its terminating ">: " prompt. A Ctrl+C is always sent to the Flipper on exit so in-flight commands (like `rfid read` or `subghz rx`) are halted.

func (*Flipper) SubGHzChat

func (f *Flipper) SubGHzChat(frequency uint32, duration time.Duration) (string, error)

SubGHzChat joins an interactive Sub-GHz text chat on the given frequency. Long-running and actively transmits — the caller bounds it with a duration. Xtreme firmware requires the trailing `<device>` arg. CLI: subghz chat <frequency> [<device>]

func (*Flipper) SubGHzDecode

func (f *Flipper) SubGHzDecode(filePath string) (string, error)

SubGHzDecode decodes a previously captured raw Sub-GHz file. CLI: subghz decode_raw <file_path>

func (*Flipper) SubGHzRx

func (f *Flipper) SubGHzRx(frequency uint32, duration time.Duration) (string, error)

SubGHzRx receives Sub-GHz signals on the given frequency (Hz). Xtreme firmware's `subghz rx` requires a trailing <device> arg (0=internal CC1101, 1=external); we append "0" when capabilities report that quirk.

Note: withSuccessBuzz is intentionally omitted here. ExecLong returns nil on timeout (streaming semantics), so the buzz would always fire. On firmware that does not honour Ctrl+C (e.g. Momentum's subghz rx), the device may still be executing the command when the vibro Exec is sent, causing the buzz Exec calls to hang for their full safety-net deadline.

func (*Flipper) SubGHzRxRaw

func (f *Flipper) SubGHzRxRaw(frequency uint32, duration time.Duration) (string, error)

SubGHzRxRaw streams raw Sub-GHz pulse data to the caller's return value. Available on Momentum firmware (subghz_cli.c:subghz_cli_command_rx_raw streams pulses to stdout with no file-path argument). On stock/Unleashed/ Xtreme firmware, the rx_raw verb requires a file-path argument that this API no longer accepts — those callers should use SubGHzRx for time-bounded capture, or construct a .sub capture manually via StorageWrite. CLI (Momentum): subghz rx_raw [<frequency>]

func (*Flipper) SubGHzTx

func (f *Flipper) SubGHzTx(filePath string) (string, error)

SubGHzTx transmits a Sub-GHz signal from a saved file. CLI: subghz tx_from_file <file_path>

func (*Flipper) SubGHzTxKey

func (f *Flipper) SubGHzTxKey(keyHex string, freq uint32, te uint32, repeat int) (string, error)

SubGHzTxKey transmits a raw Sub-GHz key. Xtreme firmware requires a trailing <device> arg (0=internal CC1101, 1=external); appended when the detected capability flag is set. CLI: subghz tx <key_hex> <frequency> <te> <repeat> [device]

func (*Flipper) Transport

func (f *Flipper) Transport() transport.Transport

Transport returns the underlying byte channel. Exposed read-only so /status output and telemetry can read Identity/Kind without the command-layer mutex.

func (*Flipper) UpdateInstall

func (f *Flipper) UpdateInstall(manifestPath string) (string, error)

UpdateInstall applies a firmware update from an already-staged manifest on the SD card. Long-running — uses a 5-minute deadline. Critical. CLI: update install <manifest_path>

func (*Flipper) Vibro

func (f *Flipper) Vibro(on bool) (string, error)

Vibro turns the vibration motor on (true) or off (false). On Momentum, vibro 1 is silently suppressed in two cases:

  • stealth mode (FuriHalRtcFlagStealthMode): "Flipper is in stealth mode…"
  • vibro disabled in settings: "Vibro is disabled in settings…"

Both return success at the firmware layer, so we detect the banner and return an error so callers know the motor was never activated. CLI: vibro <0|1>

func (*Flipper) WriteFile

func (f *Flipper) WriteFile(path string, data []byte) error

WriteFile writes data to a file on the Flipper using the storage write_chunk interactive protocol: send the command with the byte count, then send the raw bytes immediately after. Preserved for backward compatibility; prefer WriteFileCtx so cancellation and reconnect propagate a ctx.

func (*Flipper) WriteFileCtx

func (f *Flipper) WriteFileCtx(ctx context.Context, path string, data []byte) error

WriteFileCtx is the context-aware variant of WriteFile. It reconnects if needed, uses a cancellable sleep between command and payload, and tags any disconnect-class error via markDisconnectedIfRelevant so the next op can recover.

Firmware append behaviour: some firmware builds — notably Momentum dev branch as of mntm-dev 430a3d50 (2026-03-09) — do NOT truncate an existing file when storage write_chunk is called; they append to the existing content instead. Callers that need truncate semantics must issue "storage remove <path>" before writing, or the re-written file will contain concatenated data.

type LoaderApps

type LoaderApps struct {
	Apps     []string `json:"apps"`
	Settings []string `json:"settings"`
}

LoaderApps is the parsed shape of `loader list`: user-facing apps plus the settings menu entries (which are also launchable via `loader open`).

type NFCDetectResult added in v0.3.0

type NFCDetectResult struct {
	Detected   bool   `json:"detected"`
	Type       string `json:"type,omitempty"`       // "NTAG215", "MIFARE Classic 1K", ...
	Technology string `json:"technology,omitempty"` // "ISO14443-3a (NFC-A)" etc.
	UID        string `json:"uid,omitempty"`
	ATQA       string `json:"atqa,omitempty"`
	SAK        string `json:"sak,omitempty"`
	Raw        string `json:"raw,omitempty"`
}

NFCDetectResult captures the structured shape of an NFC subshell scanner response. At least one of UID/Type is set when a card was detected.

func ParseNFCDetect added in v0.3.0

func ParseNFCDetect(raw string) NFCDetectResult

ParseNFCDetect parses the output of the nfc subshell scanner subcommand into a structured result. A successfully parsed card sets Detected=true plus whatever fields the firmware emitted; empty / timeout output sets Detected=false.

type State added in v0.3.0

type State struct {
	Connected       bool   `json:"connected"`
	Fork            string `json:"fork,omitempty"`             // stock/Momentum/Unleashed/RogueMaster/Xtreme
	FirmwareVersion string `json:"firmware_version,omitempty"` // version string from device_info
	HardwareName    string `json:"hardware_name,omitempty"`    // user-settable dolphin name
	HardwareUID     string `json:"hardware_uid,omitempty"`
	BatteryPct      int    `json:"battery_pct,omitempty"`  // 0-100, omitted when unknown
	ChargeState     string `json:"charge_state,omitempty"` // "charging" / "discharging" / ""

	// Transport identifies how PromptZero is talking to the Flipper
	// ("serial" / "ble" / "mock"). The agent uses it to warn before
	// high-throughput operations on the slower BLE path.
	Transport string `json:"transport,omitempty"`

	// SDPresent reports whether the /ext volume exists at all. When
	// false, the SD-space fields are omitted and any storage_* tool
	// call will fail — surfacing this early saves the model a turn.
	SDPresent bool `json:"sd_present"`
	// SDTotalBytes and SDFreeBytes track SD capacity in bytes. Zero
	// values are omitted so a failed storage-info probe doesn't
	// masquerade as "0 free".
	SDTotalBytes int64 `json:"sd_total_bytes,omitempty"`
	SDFreeBytes  int64 `json:"sd_free_bytes,omitempty"`

	CollectedAt time.Time `json:"collected_at"`
}

State is a point-in-time snapshot of the connected Flipper, cheap to render into the model's turn context. Carries only fields that help the agent avoid redundant "what's connected?" round-trips; heavyweight probes (SD walk, loader state, log dump) are deliberately excluded.

Fields honour `omitempty` wherever missing data is better expressed as absence than as a zero sentinel (notably BatteryPct — a partial fetch that couldn't reach power_info must not surface as "battery: 0%").

type StorageStatResult added in v0.3.0

type StorageStatResult struct {
	Exists    bool   `json:"exists"`
	IsDir     bool   `json:"is_dir"`
	SizeBytes int64  `json:"size_bytes,omitempty"`
	Error     string `json:"error,omitempty"`
	Raw       string `json:"raw,omitempty"`
}

StorageStatResult captures the structured shape of `storage stat <path>` output. The Flipper firmware emits a short line per attribute: "File, size: 1234" for regular files, "Directory" for directories, "Storage error: <msg>" on failure.

func ParseStorageStat added in v0.3.0

func ParseStorageStat(raw string) StorageStatResult

ParseStorageStat parses `storage stat <path>` output. Order matters: the "Storage error:" check runs FIRST so interleaved output like "File\nStorage error: not found" (seen on some firmware forks) doesn't produce a false-positive Exists=true. When both markers appear, the error takes precedence — the file-regex matches anywhere on any line (case-insensitive), so a naked File/Directory check without the error gate would misclassify the error path.

type SubGHzCandidate added in v0.3.0

type SubGHzCandidate struct {
	Protocol  string `json:"protocol,omitempty"`
	Frequency uint32 `json:"frequency,omitempty"`
	Key       string `json:"key,omitempty"`
	Bit       int    `json:"bit,omitempty"`
	TE        int    `json:"te,omitempty"`
	RSSI      int    `json:"rssi,omitempty"`
}

SubGHzCandidate is one detected protocol / key block.

type SubGHzReceiveResult added in v0.3.0

type SubGHzReceiveResult struct {
	Candidates []SubGHzCandidate `json:"candidates,omitempty"`
	Count      int               `json:"count"`
	RawLines   []string          `json:"raw_lines,omitempty"`
}

SubGHzReceiveResult summarises the output of `subghz rx` / the subghz_receive tool. The Flipper emits detected protocol candidates as blocks like:

[Protocol: Princeton]
  Frequency: 433920000
  Key: 00 00 00 1A 2B 3C 4D 00
  Bit: 24

The parser collects one Candidate per block; unstructured noise lines go into RawLines.

func ParseSubGHzReceive added in v0.3.0

func ParseSubGHzReceive(raw string) SubGHzReceiveResult

ParseSubGHzReceive parses the output of subghz_receive. Detects one or more "Protocol:" blocks and extracts the common fields of each. Lines that don't belong to a block are preserved in RawLines.

Directories

Path Synopsis
Package mock provides a pty-backed fake Flipper CLI so serial.go and the command wrappers can be exercised without real hardware.
Package mock provides a pty-backed fake Flipper CLI so serial.go and the command wrappers can be exercised without real hardware.
rpc
Package rpc implements a typed Flipper Zero RPC client over a transport.Transport.
Package rpc implements a typed Flipper Zero RPC client over a transport.Transport.
pb
Package transport defines the byte-channel substrate the Flipper CLI layer operates over.
Package transport defines the byte-channel substrate the Flipper CLI layer operates over.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL