Quick Start¶
Connect to a running Zelos Agent and run your first query. By the end you'll have browsed the live catalog, pulled a frame, charted it, checked an invariant, watched values stream in, and saved a snapshot to disk.
Prerequisites¶
- Zelos App or Agent running (default:
localhost:2300) - Python 3.10+
pip install "zelos-sdk[notebook]"— thenotebookextra adds pandas and numpy, which the frame's pandas hand-off anddescribe()need
No live data yet?
Run the demo data generator in another terminal so the agent has signals to serve:
The examples below use paths from this generator. Substitute your own if your catalog differs.
1. Connect¶
connect() checks that something answers before it returns, and the handle renders a connection card: target, health, and how many signals are live. It is also a context manager, and agent.close() is the explicit form.
Connection options
- Bare host —
connect("myhost")→http://myhost:2300 - Host + port —
connect("myhost:2400")→http://myhost:2400 - Full URL —
connect("http://myhost:2300") - Default —
connect()readsZELOS_AGENT_URL, falling back tohttp://localhost:2300
Catch zelos_sdk.errors.AgentError (or a subclass like AgentUnavailable) for connection failures.
For a richer snapshot, agent.info() returns health, log and config directories, memory, and settings. Anything that can't be loaded reads None and is named in info.failures.
2. Browse Signals¶
Every signal has a path of the form source/message.name, a data type, and usually a unit. The catalog renders as a listing:
Narrow it with a glob, or look one up directly:
catalog.match("bus0/BMS_message/cells.*") # a smaller SignalCatalog
catalog.search("cell") # loose, scored substring search
catalog["bus0/BMS_message/status.pack_current"]
"bus0/BMS_message/cells.cell_0" in catalog
lookback= bounds how recently a signal must have produced a sample to count as live; it defaults to 60 seconds.
3. Query Time-Series¶
Pull a window of samples into a SignalFrame. Pass one path, a list, or a glob — they all expand against the live catalog.
The frame tells you what you got: rows, time span, and one line per column with its unit, type, and non-null count. start is required; end defaults to now. Both read the time grammar — a datetime, "now", ISO 8601, or a signed offset like "-3m".
Every column is a SignalSeries that carries its unit through arithmetic:
series = frame["bus0/BMS_message/cells.cell_0"]
series.min() # NamedScalar: prints "3.628 V"
frame.describe() # count, mean, std and quartiles per column, with units
Two messages, one frame
A frame is one row per timestamp, with nulls where a signal had no sample. Combining columns from different messages? Call frame.ffill().dropna() first — see how a frame joins signals.
Downsampling
downsample=N returns about N points per signal, shaped for charting a long window. Omit it for full resolution; max_rows=N caps the row count instead.
4. Chart It¶
plot() emits Vega-Lite: the Zelos App renders it inline, an HTML export renders it offline, and nothing has to be added to your dependencies.
5. Latest Values¶
A single literal path returns a LatestValue carrying the value, its unit, its enum label if it has one, and its timestamp:
A pattern or a sequence returns a Snapshot — a read-only mapping that renders as a table:
Snapshot @ 2026-09-04 17:07:51.920 · 8 signals
bus0/BMS_message/cells.cell_0 3.643 V
bus0/BMS_message/cells.cell_1 3.644 V
bus0/BMS_message/cells.cell_2 3.646 V
bus0/BMS_message/cells.cell_3 2.914 V
bus0/BMS_message/cells.cell_4 3.656 V
bus0/BMS_message/cells.cell_5 3.665 V
bus0/BMS_message/cells.cell_6 3.673 V
bus0/BMS_message/cells.cell_7 3.679 V
To read at a specific instant, use agent.at(paths, cursor), which reads each signal at or before that time.
6. Check an Invariant¶
A check turns the series you just queried into a rule, and reports its result with the evidence behind it:
agent.check.that(series, ">", 3.0)
# 2026-09-04 17:06:58.855 cells.cell_0 (3.628 V) > 3 V always PASSED
A series operand carries its own window, so the check covers exactly what you queried. Use a signal handle and last= when you want a rule with its own window:
See Checks for every operator, and Notebooks as tests for what a failing check does to a run.
7. Watch Live Changes¶
agent.watch() polls and yields a Snapshot per tick. Bound it with until=, which reads the duration grammar:
for tick in agent.watch(["bus1/inverter_status.mode"], interval=1.0, until="5s"):
value = tick["bus1/inverter_status.mode"]
print(f"{value.time:%H:%M:%S} {value.value}")
Polling can miss a transition between ticks. agent.window(path, start="-1m", duration="1m") cannot: it returns the opening state plus every change in the interval.
Keeping a watch alive
on_error="skip" keeps the loop running through transient errors, up to max_consecutive_errors consecutive failures.
8. Save and Re-Open¶
agent.export() writes a .trz file from live data. The file lives on the agent's host.
Re-open it and run the same calls against the frozen copy:
On a file, time is measured from the file: "-60s" is the last minute of the recording and "start" / "+30s" its first thirty seconds. agent.traces({...}) opens several recordings as labeled runs on a shared clock — see Open Trace Files.
9. Inspect Actions¶
Actions are typed operations the agent (or an extension) exposes. Each one has a JSON Schema for its parameters.
for action in agent.actions.list():
print(action.name)
schema = agent.actions.schema("action/name")
print(schema.action_schema) # JSON Schema dict
print(schema.ui_schema) # rendering hints
print(schema.default_timeout_ms)
Execute by name with a params dict:
result = agent.actions.execute("action/name", {"param": "value"}, timeout=5.0)
print("status:", result.status, "ok:", result.ok)
result.ok is True for "pass" and "done". See Run Actions for concrete extension examples.
What's Next¶
- Query Live Data — the full surface of
query(),latest(),at(),window(),watch(), and series math - Open Trace Files —
TraceandTraceSetfor offline analysis - Checks — every operator, temporal word, and tolerance
- Notebooks — put all of this in a markdown file the agent runs
- Run Actions — schemas, validation, dynamic choices, error handling
- Streaming SDK — produce data the agent can serve