Metadata-Version: 2.4
Name: signalk-cli
Version: 3.0.0
Summary: Query SignalK v2 APIs and export data as CSV, JSON or Apache Arrow Feather from command line or as a Python API
Keywords: signalk,sailing,marine,boating,nmea,dataframe,cli,pyarrow,nanoarrow
Author: jey burrows
Author-email: jey burrows <jrb@rhizomatics.org.uk>
License-Expression: Apache-2.0
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Natural Language :: English
Classifier: Intended Audience :: Developers
Classifier: Topic :: Scientific/Engineering
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: click>=8.3.3
Requires-Dist: nanoarrow>=0.9.0,<1.0.0
Requires-Dist: niquests[ws]>=2.28
Requires-Dist: niquests[ws]>=3.21.0 ; sys_platform == 'emscripten'
Requires-Dist: zeroconf>=0.149.16,<0.152.0 ; sys_platform != 'emscripten'
Requires-Dist: pyarrow>=17.0 ; extra == 'arrow'
Requires-Dist: pyarrow>=17.0 ; extra == 'feather'
Requires-Python: >=3.13
Project-URL: Homepage, https://signalk-cli.rhizomatics.org.uk
Project-URL: Repository, https://github.com/rhizomatics/signalk-cli
Project-URL: Issues, https://github.com/rhizomatics/signalk-cli/issues
Project-URL: Changelog, https://github.com/rhizomatics/signalk-cli/blob/main/CHANGELOG.md
Provides-Extra: arrow
Provides-Extra: feather
Description-Content-Type: text/markdown

# SignalK CLI

[![Rhizomatics Open Source](https://img.shields.io/badge/rhizomatics%20open%20source-lightseagreen)](https://github.com/rhizomatics)

[![PyPI - Version](https://img.shields.io/pypi/v/signalk-cli)](https://pypi.org/project/signalk-cli/)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/rhizomatics/signalk-cli)
[![Coverage](https://raw.githubusercontent.com/rhizomatics/signalk-cli/refs/heads/badges/badges/coverage.svg)](https://signalk-cli.rhizomatics.org.uk/developer/coverage/)
![Tests](https://raw.githubusercontent.com/rhizomatics/signalk-cli/refs/heads/badges/badges/tests.svg)
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/rhizomatics/signalk-cli/main.svg)](https://results.pre-commit.ci/latest/github/rhizomatics/signalk-cli/main)
[![Publish Python 🐍 distribution 📦 to PyPI and TestPyPI](https://github.com/rhizomatics/signalk-cli/actions/workflows/pypi-publish.yml/badge.svg)](https://github.com/rhizomatics/signalk-cli/actions/workflows/pypi-publish.yml)
[![Github Deploy](https://github.com/rhizomatics/signalk-cli/actions/workflows/python-package.yml/badge.svg?branch=main)](https://github.com/rhizomatics/signalk-cli/actions/workflows/python-package.yml)
[![CodeQL](https://github.com/rhizomatics/signalk-cli/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/rhizomatics/signalk-cli/actions/workflows/github-code-scanning/codeql)
[![Dependabot Updates](https://github.com/rhizomatics/signalk-cli/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/rhizomatics/signalk-cli/actions/workflows/dependabot/dependabot-updates)
[![boat tech directory](https://boat-tech-directory.rhizomatics.org.uk/images/badge.svg)](https://boat-tech-directory.rhizomatics.org.uk)

Query and explore NMEA and other boat data from SignalK APIs using the command line or the Python API.  Export data as CSV, Apache Arrow Feather, or JSON or use as dataframes in scripts or data notebooks.

APIs supported:

### [SignalK v2 History API](https://demo.signalk.org/documentation/Developing/REST_APIs/History_API.html). 

- Commands available:
    - `list-paths`
    - `list-providers`
    - `list-contexts`
    - `query`
    - `cardinality`

### [SignalK v1 Streaming API](https://signalk.org/specification/1.8.2/doc/streaming_api.html). 

- Commands available:
    - `deltas`

## Installation

`signalk-cli` is published to [PyPi](https://pypi.org/project/signalk-cli/)

Python is required to run this, version 3.13 or above. [uv](https://docs.astral.sh/uv/) is the recommended way to install the package ( and can install Python ) but is not required.

### PyPi

```pip install signalk-cli``` or ```uv pip install signalk-cli```

For Apache Arrow Feather export, use the optional dependency: ```pip install 'signalk-cli[feather]'```

### Pyodide / WebAssembly

The same wheel installs in Pyodide (e.g. marimo in the browser). Platform markers skip `zeroconf`, which can't run there, and require `niquests>=3.21.0`:

```python
import micropip
await micropip.install("signalk-cli")
```

Without `zeroconf` there's no mDNS discovery, so the host must be given with `--host` or `SIGNALK_HOST` (or come from the cache).

See [signalk-datalab-plugin](https://github.com/rhizomatics/signalk-datalab-plugin) for a working example of this.

### Local Copy

Requires Python 3.13+ and [uv](https://docs.astral.sh/uv/).

```bash
git clone https://github.com/rhizomatics/signalk-cli
cd signalk-cli
uv sync
```

## Temporary Installation

Use `uv` to run without installing the module permanently, for example:

```bash
uv run --with signalk-cli signalk_cli.history list-providers
```

```bash
uv run --with signalk-cli signalk_cli.stream deltas navigation.position --follow
```

## Python library

Everything the CLI does is also available from Python, returning tables that load straight into polars, pandas, pyarrow or DuckDB:

```python
from signalk_cli import HistoryClient, TimeRange
import polars as pl

with HistoryClient("http://boat.local:3000") as client:
    df = pl.DataFrame(client.query(["navigation.*"], TimeRange(duration="P1D")))
```

See the [Python library guide](https://signalk-cli.rhizomatics.org.uk/library/) and API reference.

## Running

Run via `python -m signalk_cli.history <command>` or `python -m signalk_cli.stream <command>` or without installing the module with `uv run --with signalk-cli signalk_cli.history <command>`.

## Determining SignalK host name

If no host name is set as an argument, the CLI will look for a `SIGNALK_HOST` environment variable, and failing that attempt to automatically discover the host using mDNS (aka Bonjour) and locally cached (see [Default Caching](#default-caching)).

```bash
export SIGNALK_HOST=192.168.6.99   # http:// is added automatically if omitted
```

## Built-in Help

Run with no arguments to list available commands:

```
$ python -m signalk_cli.history
Usage: signalk_cli.history [OPTIONS] COMMAND [ARGS]...

  SignalK v2 history CLI.

Commands:
  cardinality     Compute per-path value statistics for the given time range.
  list-contexts   List contexts that have historical data for the given time range.
  list-paths      List paths that have data for the given time range.
  list-providers  List registered history providers.
  query           Query history and write results as CSV, Feather, or JSON.
```

## Commands


### `query`

Fetch historical values for one or more paths and write to stdout (default) or a file. Aggregation can be controlled in the same way as the History API itself
or a default form where `min_value`,`avg_value` and `max_value` are returned for every period.

```
python -m signalk_cli.history query [OPTIONS] PATH...
```

**PATH** arguments may be:

- **Literal paths** — e.g. `navigation.speedOverGround`
- **Regex / glob patterns** — any argument containing a pattern character (`*`, `?`, `+`, `[`, `(`, `{`, `|`, `^`, `$` or `\`) is matched against the server's `/paths` endpoint. A `.` on its own doesn't count, so a plain dotted path is always literal. Bare `*` is treated as a glob wildcard.
- **Inline path specs** — `path:method` or `path:method:param`, e.g. `navigation.speedOverGround:sma:5`. These pass through to the server unchanged.

#### Options

| Option | Default | Description |
|---|---|---|
| `--host` | `$SIGNALK_HOST` | Server base URL. `http://` added if scheme omitted. |
| `--from DATETIME` | — | Start of range (ISO 8601, e.g. `2026-05-26T00:00:00Z`) |
| `--to DATETIME` | now | End of range (ISO 8601) |
| `--duration DURATION` | — | Duration as seconds (`3600`) or ISO 8601 duration (`PT1H`, `P1D`, `P1W`). Combined with `--from` or `--to`, or alone for a window ending now. |
| `--resolution RESOLUTION` | server default | Sample window size: seconds or time expression (`1s`, `1m`, `1h`, `1d`). |
| `-c, --context TEXT` | `vessels.self` | SignalK context |
| `--provider TEXT` | fetched & cached | History provider plugin name, for example `signalk-parquet` |
| `--no-cache` | — | Ignore the cached default provider |
| `--aggregation / --agg` | — | Aggregation method: `average`, `min`, `max`, `first`, `last`, `mid`, `middle_index`, `sma`, `ema`. Omit for wide mode (see below). |
| `--samples N` | server default | Sample count for `--agg sma` |
| `--alpha FLOAT` | server default | Alpha (0–1) for `--agg ema` |
| `--format [csv\|feather\|json\|raw]` | from extension, else csv | Output format. Auto-detected from `.feather`/`.arrow`/`.fea` and `.json` extensions. `feather` requires `pip install 'signalk-cli[feather]'`. |
| `--no-header` | — | Suppress the CSV header row |
| `-o / --output [FILE]` | stdout | Write to a file. Omit the filename (`--output` alone) to auto-name as `signalk-history-<server>-<timestamp>.<ext>`. Use `-` for stdout explicitly. Feather cannot be written to stdout. |
| `--pretty` | — | Pretty-print JSON output with indentation. **Warning:** disables streaming; the full response is buffered in memory before writing. |
| `--bare` | — | Print to stdout with **no informational messages** (server, provider, progress, row count). Ideal for piping to other tools. Not supported with feather. |

If no time range is given, the tool defaults to the hour ending now.

#### Duration normalisation

SignalK only accepts time-only ISO 8601 durations (`PT1H`, `PT30M`, etc.). Durations with date components (`P1D`, `P1W`, `P1Y`, `P1M`) are automatically expanded to explicit `--from`/`--to` timestamps:

- `--duration P1D` alone → `from = now − 1 day`, `to = now`
- `--from T --duration P1D` → `to = T + 1 day`
- `--to T --duration P1W` → `from = T − 1 week`

Integer seconds (`3600`) are passed through unchanged.

#### Output formats

**csv** (default): tabular output as comma-separated values.

**feather**: Apache Arrow Feather binary format, readable with pandas, Polars, R, pyarrow, etc. Requires `pip install 'signalk-cli[feather]'`. Cannot be written to stdout.

**json**: tabular records as JSON objects — same columns as CSV, formatted as a JSON array. Extension `.json` auto-selects this format.

**raw**: exact API response body as received from the server — no Python JSON parse/re-serialize. Streamed directly to stdout unless `--pretty` is used.

#### Output columns

**Wide mode** (default — no `--aggregation` and no inline specs): fetches `min`, `average`, and `max` for each scalar path and writes them as separate columns:

```
timestamp, path, min_value, avg_value, max_value
```

**Array-valued paths in wide mode**: paths whose values are arrays (non-scalar) cannot be meaningfully aggregated with min/max/average. They are requested with a single passthrough method instead and the array elements are expanded into named columns. The column names depend on the path:

| Path pattern | Array length | Columns |
|---|---|---|
| `navigation.*.position` or `navigation.position` | 2 | `longitude`, `latitude` |
| any other array path | N | `value_0`, `value_1`, …, `value_N-1` |

Example for `navigation.position`:

```
timestamp, path, latitude, longitude
```

**Narrow mode** (explicit `--aggregation` or inline path specs): single value column:

```
timestamp, path, value
```

Structured values (positions, arrays) are JSON-encoded in the value column.

#### Examples

```bash
# Last hour of speed — wide mode (min/max/avg columns), printed to stdout
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H \
    navigation.speedOverGround

# Write to auto-named file
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H --output \
    navigation.speedOverGround

# Write to named file
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H \
    --output may26.csv navigation.speedOverGround

# Last day (date-component duration, auto-expanded to from/to)
python -m signalk_cli.history query --host 10.36.10.21 --duration P1D \
    navigation.speedOverGround

# Simple moving average (5 samples), narrowed to one value column
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H \
    --agg sma --samples 5 \
    navigation.speedOverGround

# EMA with alpha 0.2
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H \
    --agg ema --alpha 0.2 \
    navigation.speedOverGround

# Inline spec — path:method, multiple paths with different methods
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H \
    'navigation.speedOverGround:max' 'navigation.courseOverGroundTrue:average'

# Multiple literal paths, 1-minute resolution
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H --resolution 1m \
    navigation.speedOverGround navigation.courseOverGroundTrue

# All navigation paths, specific date range, write to named file
python -m signalk_cli.history query --host 10.36.10.21 \
    --from 2026-05-26T00:00:00Z --to 2026-05-27T00:00:00Z \
    --output may26.csv \
    'navigation\..*'

# Glob wildcard — all paths for the last 30 minutes as Feather (auto-named)
python -m signalk_cli.history query --host 10.36.10.21 --duration PT30M \
    --format feather --output '*'

# Extension auto-selects feather format
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H \
    -o out.feather navigation.speedOverGround

# Exact API response body to stdout (streamed, no informational noise)
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H --format raw --bare \
    navigation.speedOverGround

# Pretty-printed raw JSON (buffered — avoid for large responses)
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H \
    --format raw --pretty navigation.speedOverGround

# Extension auto-selects JSON format
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H \
    -o out.json navigation.speedOverGround

# Suppress header, pipe to another tool
python -m signalk_cli.history query --host 10.36.10.21 --duration 3600 --no-header \
    navigation.speedOverGround | cut -d, -f1,3

# --bare: pure CSV output, no informational noise — pipe-friendly
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H --bare \
    navigation.speedOverGround | awk -F, 'NR>1 {print $2, $3}'

# Different context
python -m signalk_cli.history query --host 10.36.10.21 --duration PT1H \
    -c vessels.urn:mrn:imo:mmsi:123456789 navigation.speedOverGround
```

---

### `cardinality`

Fetch raw values for one or more paths and compute per-path statistics. Useful for exploring a dataset before querying it.

```
python -m signalk_cli.history cardinality [OPTIONS] PATH...
```

PATH arguments follow the same rules as `query` (literal paths, regex/glob patterns, inline specs).

#### Output columns

| Column | Description |
|---|---|
| `path` | SignalK path |
| `distinct_values` | Count of unique values (by string representation) |
| `min` | Minimum value (scalar numeric only; blank otherwise) |
| `max` | Maximum value (scalar numeric only; blank otherwise) |
| `average` | Mean value (scalar numeric only; blank otherwise) |
| `distinct_values_2_decimal_places` | Distinct count after rounding to 2 dp (scalar only; blank otherwise) |
| `nulls` | Count of null/missing values |

For non-scalar paths (e.g. `navigation.position` which returns a lat/lon pair), `min`, `max`, `average`, and `distinct_values_2_decimal_places` are left blank; `distinct_values` still counts unique array representations.

#### Options

Accepts the same `--host`, `--from`/`--to`/`--duration`, `--resolution`, `--context`, `--provider`/`--no-cache`, `--format [csv|json]`, `--no-header`, and `--bare` options as `query`.

```bash
# Statistics for speed over the last hour
python -m signalk_cli.history cardinality --host 10.36.10.21 --duration PT1H \
    navigation.speedOverGround

# All navigation paths, last 24 hours
python -m signalk_cli.history cardinality --host 10.36.10.21 --duration P1D \
    'navigation\..*'

# JSON output
python -m signalk_cli.history cardinality --host 10.36.10.21 --duration PT1H \
    --format json navigation.speedOverGround navigation.position
```

---

### `list-paths`

List all SignalK paths that have recorded data in a given time range.

```
python -m signalk_cli.history list-paths [OPTIONS]
```

Outputs one path per line to stdout by default. Defaults to the last hour if no time range is given. Accepts the same `--from`/`--to`/`--duration`, `--provider`/`--no-cache`, `-c/--context`, and `--bare` options as `query`.

#### Options

| Option | Default | Description |
|---|---|---|
| `--format [csv\|json\|raw]` | `csv` | `csv`: one item per line with `path` header. `json`: `[{"path": ...}]`. `raw`: exact API response body. |

```bash
# Paths recorded in the last hour
python -m signalk_cli.history list-paths --host 10.36.10.21

# Paths available on a specific day
python -m signalk_cli.history list-paths --host 10.36.10.21 \
    --from 2026-05-26T00:00:00Z --to 2026-05-27T00:00:00Z

# Pipe into grep
python -m signalk_cli.history list-paths --host 10.36.10.21 --duration PT24H | grep navigation

# Exact API response body, no informational noise
python -m signalk_cli.history list-paths --host 10.36.10.21 --format raw --bare
```

---

### `list-providers`

List all registered history provider plugins and identify the default. Supports `--bare` to suppress the "Server:" and provider-count lines.

```
python -m signalk_cli.history list-providers --host 10.36.10.21
```

Example output:

```
provider,isDefault
signalk-parquet,True
kip,False
2 provider(s)
```

The default provider is used automatically when `--provider` is not specified on other commands. It is fetched once and cached in `~/.cache/signalk-cli/`.

#### Options

| Option | Default | Description |
|---|---|---|
| `--format [csv\|json\|raw]` | `csv` | `csv`: `provider,isDefault` rows. `json`: `[{"provider": ..., "isDefault": ...}]`. `raw`: exact API response body. |

```bash
# Exact API response body, no informational noise
python -m signalk_cli.history list-providers --host 10.36.10.21 --format raw --bare
```

---

### `list-contexts`

List SignalK contexts (vessels, aircraft, etc.) that have recorded data in a given time range.

```bash
python -m signalk_cli.history list-contexts [OPTIONS]
```

Defaults to the last hour if no time range is given.

#### Options

| Option | Default | Description |
|---|---|---|
| `--format [csv\|json\|raw]` | `csv` | `csv`: one item per line with `context` header. `json`: `[{"context": ...}]`. `raw`: exact API response body. |

```bash
python -m signalk_cli.history list-contexts --host 10.36.10.21

python -m signalk_cli.history list-contexts --host 10.36.10.21 \
    --from 2026-05-26T00:00:00Z --to 2026-05-27T00:00:00Z

# Exact API response body, no informational noise
python -m signalk_cli.history list-contexts --host 10.36.10.21 --format raw --bare
```

---

## SignalK v1 Streaming API

The `signalk_cli.stream` module connects to a running SignalK server's live delta
WebSocket feed (as opposed to `signalk_cli.history`, which queries recorded history).

### `deltas`

Stream live delta updates from the SignalK v1 Streaming API.

```
python -m signalk_cli.stream deltas [OPTIONS] [PATH...]
```

By default, prints the next delta message and exits — useful for a quick check.
Use `--follow` to keep tailing until interrupted with Ctrl-C, optionally capped
with `--count`.

**PATH** arguments are sent verbatim to the server as an explicit subscription,
one per path. They may be literal SignalK paths (e.g. `navigation.speedOverGround`)
or use the SignalK subscription wildcard `*`, matched server-side per the
[Subscription Protocol](https://signalk.org/specification/1.8.2/doc/subscription_protocol.html)
— unlike `history`'s PATH patterns, which the client resolves by matching
against the server's enumerated `/paths` list:

- `*` at the end of a path matches any suffix, e.g. `navigation.*`
- `*` as a middle segment matches any single segment there, e.g. `propulsion.*.oilTemperature`
- a bare `*` subscribes to every path in the context

Quote wildcarded paths (e.g. `'navigation.*'`) so the shell doesn't expand them
against local filenames first.

`deltas` always sends its own explicit subscribe message for `--context`
(default `vessels.self`), covering PATH arguments if given, otherwise every
path (`*`). `--context` also accepts the SignalK wildcard: `'*'` or
`'vessels.*'` subscribes to every vessel, not just your own. `--policy`,
`--period`, and `--min-period` control that subscription per the
[Subscription Protocol](https://signalk.org/specification/1.8.2/doc/subscription_protocol.html)'s
`policy`/`period`/`minPeriod` fields:

- **`--policy`** (`instant` / `ideal` / `fixed`, default `ideal`): `instant` sends every change (throttled by `--min-period`); `ideal` behaves like `instant` but resends the last value if nothing changes within `--period`; `fixed` always sends the last known value every `--period`, regardless of changes.
- **`--period`** (seconds, default `60`): the resend interval for `ideal`/`fixed`. Converted to milliseconds on the wire.
- **`--min-period`** (seconds): fastest allowed transmission rate, only meaningful with `--policy instant`.

The protocol also defines a per-path `format` field (`delta`/`full`), but
this CLI doesn't expose it: signalk-server rejects `full` outright ("Only
delta format supported, using it") and always sends delta messages
regardless, so the choice would be misleading.

**`--subscribe` is a separate, easily-confused mechanism**: it's the
connection-level `subscribe` query parameter (`none`/`self`/`all`), which
controls whether the server *additionally* auto-subscribes the connection
at **its own** default policy/period — you don't get to choose the rate.
This CLI defaults it to `none`, diverging from the SignalK spec's own
default of `self`, because `deltas` always sends its own explicit `--context`
subscription anyway; leaving the connection-level default at `self` would
just double up on updates for your own vessel.

So there are two different ways to see every vessel, and they're not
interchangeable:

| Want | Use | Policy/period used |
|---|---|---|
| Just your own vessel | (defaults) | Yours (`--policy`/`--period`) |
| Every vessel, at your chosen rate | `--context '*'` | Yours (`--policy`/`--period`) |
| Every vessel, at whatever rate the server defaults to | `--subscribe all` | The server's own default |

#### Options

| Option | Default | Description |
|---|---|---|
| `--host` | `$SIGNALK_HOST` | Server base URL. `http://` added if scheme omitted; converted to `ws://`/`wss://` for the stream connection. |
| `--no-cache` | — | Ignore the cached host |
| `-c, --context TEXT` | `vessels.self` | SignalK context |
| `--subscribe [none\|self\|all]` | `none` | Connection-level subscribe policy (SignalK's own `subscribe` query parameter); see above |
| `--policy [instant\|ideal\|fixed]` | `ideal` | Per-path subscribe `policy` field; see above |
| `--period SECONDS` | `60` | Per-path subscribe `period` field, in seconds (converted to ms) |
| `--min-period SECONDS` | — | Per-path subscribe `minPeriod` field, in seconds (converted to ms); only meaningful with `--policy instant` |
| `--format [csv\|json\|raw\|values\|feather]` | from extension, else csv | Output format. `json` is JSON Lines (one row object per line, suitable for a live stream). `raw` is the exact delta message text, one per line. `values` is the bare value only, one per line — no other columns. `feather` requires `pip install 'signalk-cli[feather]'` and `--output` (cannot stream to stdout). |
| `--no-header` | — | Suppress the CSV header row |
| `--include-meta` | — | Also emit rows for `meta` entries (units, description, zones, etc.), not just `values`. Adds a `kind` column (`value`/`meta`) to csv/json/feather output. Ignored for `--format raw`, which always includes meta as-is. |
| `--source PATTERN` | — | Only include updates whose `$source` matches `PATTERN`. Repeatable, OR'd together. Substring match unless `PATTERN` contains a glob metacharacter (`*`/`?`/`[`), e.g. `--source Teltonika` or `--source '*.GP'`. Client-side, applied after receipt: `csv`/`json`/`values`/`feather` filter per-update; `raw` (whole message, verbatim) passes a message through if *any* of its updates match. |
| `-o, --output [FILE]` | stdout | Write to a file. Omit the filename (`--output` alone) to auto-name as `signalk-stream-<server>-<timestamp>.<ext>`. Required for `--format feather`. |
| `-f, --follow` | — | Keep streaming until interrupted (Ctrl-C) or `--count` is reached. Without this, print the next message then exit. |
| `-n, --count N` | 1 without `--follow`, unlimited with it | Number of delta messages to output |
| `--bare` | — | Print to stdout with **no informational messages**. Ideal for piping to other tools. |

#### Output formats

**csv** / **json**: `timestamp, context, source, path, value` — one row per `path`/`value` pair in each delta's updates, written incrementally as messages arrive. Structured values (e.g. `navigation.position`) are JSON-encoded in the `value` column. With `--include-meta`, a `kind` column (`value`/`meta`) is inserted before `value`, and `meta` entries (units, description, zones, etc. — also JSON-encoded) are included as rows too; without it, `meta` entries are silently dropped from csv/json/feather (they're still present in `raw`).

**raw**: the exact delta message JSON as received from the server, one message per line.

**values**: just the `value` column, one per line — no timestamp/context/source/path/kind. Best for piping a single path's readings straight into another tool or script. With `--include-meta`, meta values are interleaved in too, indistinguishable from data values (there's no `kind` column to tell them apart) — generally only useful combined with `--source`/PATH filtering down to one thing.

**feather**: Apache Arrow Feather binary format, same columns as csv/json. Requires `pip install 'signalk-cli[feather]'`. Unlike the other formats, rows are buffered in memory across all received messages and written once the session ends (`--count` reached, or Ctrl-C with `--follow`) — cannot be streamed to stdout.

#### Examples

```bash
# Next update for one path, then exit
python -m signalk_cli.stream deltas --host 10.36.10.21 navigation.speedOverGround

# Tail all navigation updates until Ctrl-C
python -m signalk_cli.stream deltas --host 10.36.10.21 --follow 'navigation.*'

# Tail oil temperature across every engine (mid-path wildcard)
python -m signalk_cli.stream deltas --host 10.36.10.21 --follow 'propulsion.*.oilTemperature'

# Next 20 messages across all subscribed paths, as JSON Lines
python -m signalk_cli.stream deltas --host 10.36.10.21 --format json --count 20

# Tail two specific paths, piping raw deltas to another tool
python -m signalk_cli.stream deltas --host 10.36.10.21 --follow --format raw --bare \
    navigation.speedOverGround navigation.courseOverGroundTrue

# Everything the server has (equivalent to ?subscribe=all), following
python -m signalk_cli.stream deltas --host 10.36.10.21 --subscribe all --follow

# Faster resend interval (5s instead of the 60s default)
python -m signalk_cli.stream deltas --host 10.36.10.21 --follow --period 5 'navigation.*'

# Send every change immediately, no more than 5 times/second
python -m signalk_cli.stream deltas --host 10.36.10.21 --follow \
    --policy instant --min-period 0.2 navigation.speedOverGround

# Capture 500 messages to an auto-named Feather file
python -m signalk_cli.stream deltas --host 10.36.10.21 --count 500 --format feather --output 'navigation.*'

# Capture until Ctrl-C to a named Feather file
python -m signalk_cli.stream deltas --host 10.36.10.21 --follow -o capture.feather 'navigation.*'

# Only updates from one sensor (substring match on $source)
python -m signalk_cli.stream deltas --host 10.36.10.21 --follow --source Teltonika 'navigation.*'

# Bare speed values from one sensor, piped straight into another tool
python -m signalk_cli.stream deltas --host 10.36.10.21 --follow --format values \
    --source Teltonika --bare navigation.speedOverGround
```

---

## Time range

All commands that query data accept the same three time parameters. At least one of `--from` or `--duration` must be provided (or the tool supplies a one-hour default).

| Parameter | Format | Examples |
|---|---|---|
| `--from` | ISO 8601 timestamp | `2026-05-26T00:00:00Z` |
| `--to` | ISO 8601 timestamp | `2026-05-27T00:00:00Z` |
| `--duration` | ISO 8601 duration or integer seconds | `PT1H`, `PT15M`, `P1D`, `P1W`, `3600` |

Typical combinations:

- `--duration PT1H` — last hour ending now
- `--from T --duration PT1H` — hour starting at T
- `--from T1 --to T2` — explicit range
- `--duration PT1H --to T` — hour ending at T
- `--duration P1D` — last 24 hours (expanded to from/to automatically)

## Default caching

The default history provider is fetched from the server once and cached per host in `~/.cache/signalk-history-cli/`. Pass `--no-cache` to force a fresh lookup, or `--provider <id>` to target a specific provider explicitly.
