Introduction
equser is the open-source Python interface to EQ Wave power quality data and sensors. It provides tools for loading, analyzing, and visualizing high-resolution electrical measurements.
What equser does
equser can work with EQ Wave data in several ways:
- Load and analyze Parquet files stored by an EQ gateway, including continuous waveform (CPOW) and power monitoring (PMon) data
- Connect directly to an EQ Wave sensor to collect PMon data in real time
- Capture waveform snapshots on demand via an EQ gateway
- Query gateway APIs for historical data, events, and live streams
- Visualize power quality data with publication-ready matplotlib plots
Data formats
| Format | Description | Sample Rate | File Pattern |
|---|---|---|---|
| CPOW | Continuous Point-on-Wave waveforms (7 channels: VA, VB, VC, IA, IB, IC, IN) | 32 kHz | YYYYMMDD_HHMMSS.parquet |
| PMon | 10/12-cycle RMS power quality summaries (voltage, current, power, frequency) | ~5 Hz | YYYYMMDD_HHMM.parquet |
Quick install
pip install equser # Data loading and analysis
pip install equser[analysis] # Add plotting and API client
pip install equser[jupyter] # Full notebook environment
Get started
See the Installation guide, then follow the Quick Start tutorial.
The broader platform
equser is one component of the Energy Quotient platform for continuous waveform intelligence. For the full capabilities, including real-time CPOW visualization through EQ Sight and power system intelligence through EQ Syntropy, see the platform overview.
Installation
Requirements
- Python 3.10 or later
- Linux (for hardware integration features)
Install from source
equser uses a tiered dependency model. Install only what you need.
Base (data loading and analysis)
pip install equser
Includes: numpy, pyarrow, pyyaml, argcomplete, colorlog. Enough to load CPOW/PMon files, run waveform analysis, and use the CLI with tab completion and colored output.
Plotting and API client
pip install equser[analysis]
Adds: matplotlib, requests, websocket-client. Enables equser.plotting,
equser.api, and WebSocket streaming.
Jupyter notebook environment
pip install equser[jupyter]
Superset of [analysis]. Adds: jupyterlab, duckdb, ipywidgets, ipykernel,
nbconvert. Full interactive notebook environment for data exploration.
Live sensor acquisition
pip install equser[daq]
Adds: avro, fastavro. Required for equser pmon acquire to connect to EQ Wave
sensor hardware.
Full installation
pip install equser[full]
All optional extras.
Dependency tiers
| Extra | Description | Key Packages |
|---|---|---|
| (base) | Data loading, analysis, CLI | numpy, pyarrow, pyyaml, argcomplete, colorlog |
[daq] | Live sensor acquisition | avro, fastavro |
[analysis] | Plotting + API client | matplotlib, requests, websocket-client |
[jupyter] | Full notebook environment | [analysis] + jupyterlab, duckdb, ipywidgets |
[full] | All of the above | - |
Quick Start
Load CPOW waveform data
CPOW (Continuous Point-on-Wave) files contain high-resolution waveform captures at 32 kHz across 7 channels.
from equser.data import load_cpow_scaled
result = load_cpow_scaled('20250623_075056.parquet')
# Scaled voltage and current arrays (numpy float64)
print(f"Voltage A peak: {result['VA'].max():.1f} V")
print(f"Current A peak: {result['IA'].max():.3f} A")
print(f"Start time: {result['start_time']}")
print(f"Sample rate: {result['sample_rate']} Hz")
The function handles both data formats automatically:
- int32 (current format): raw ADC counts scaled by
vscale/iscalefrom Parquet metadata. - float (legacy format): values already in volts/amps.
Load PMon summary data
PMon (Power Monitor) files contain 10/12-cycle RMS measurements (~200 ms intervals).
from equser.data import load_pmon
table = load_pmon('20250623_0750.parquet')
print(table.column_names)
# ['time_us', 'FREQ', 'AVRMS', 'BVRMS', 'CVRMS', 'AIRMS', ...]
Analyze zero crossings
Zero-crossing detection is useful for frequency estimation, cycle extraction, and identifying waveform distortions.
import numpy as np
from equser.data import load_cpow_scaled, SAMPLE_RATE_HZ
from equser.analysis import find_zero_crossings
result = load_cpow_scaled('cpow_data.parquet')
time = np.arange(len(result['VA'])) / SAMPLE_RATE_HZ
crossings, indices = find_zero_crossings(result['VA'], time)
print(f"Found {len(crossings)} zero crossings")
# Estimate frequency from crossing intervals
periods = np.diff(crossings)
freq = 1.0 / np.mean(periods)
print(f"Estimated frequency: {freq:.2f} Hz")
Extract complete cycles
from equser.analysis import extract_complete_cycles
cycles = extract_complete_cycles(result['VA'], time, [0.0, 0.05], num_cycles=1)
for cycle_time, cycle_signal, start, end in cycles:
duration_ms = (end - start) * 1000
print(f"Cycle at {start:.6f}s, duration: {duration_ms:.2f} ms")
Plot data (requires [analysis])
from equser.plotting import PowerMonitorPlotter, WaveformPlotter
# PMon data
plotter = PowerMonitorPlotter()
plotter.plot_file('pmon_data.parquet')
# CPOW waveforms
wf_plotter = WaveformPlotter()
wf_plotter.plot_file('cpow_data.parquet')
Query a gateway (requires [analysis])
from equser.api import SynapseClient
client = SynapseClient('http://gateway:8080')
devices = client.list_devices()
table = client.get_pmon_data(devices[0]['id'])
# SQL queries
rows = client.query_sql("SELECT * FROM pmon ORDER BY time_us DESC", limit=10)
Stream real-time data (requires [analysis])
from equser.api import connect_cpow_stream
for batch_or_gap in connect_cpow_stream('http://gateway:8080'):
if isinstance(batch_or_gap, dict):
print(f"Gap: {batch_or_gap['skipped_samples']} samples")
else:
print(f"Batch: {batch_or_gap.num_rows} rows")
CLI tools
# Start power monitoring (requires [daq] + EQ Wave hardware)
equser pmon acquire -c config.yaml
# Convert Avro files to Parquet (requires [daq])
equser pmon convert data/*.avro --remove
# Plot data file (requires [analysis])
equser plot data.parquet
Configuration
equser uses YAML configuration files for sensor and acquisition settings. The configuration system follows XDG conventions and supports multiple lookup locations.
Configuration lookup order
equser searches for configuration files in this order:
EQUSER_CONFIGenvironment variable (if set)./equser.yaml(current directory)~/.config/equser/config.yaml(XDG config)/etc/equser/config.yaml(system-wide)
The first file found is used.
Example configuration
sensor:
address: "192.168.10.10"
port: 1535
pmon:
connection:
retry_delay: 3
parquet:
interval: 86400
compression:
method: ZSTD
level: 4
Configuration sections
sensor
Connection settings for EQ Wave sensor hardware.
| Key | Type | Default | Description |
|---|---|---|---|
address | string | "192.168.10.10" | Sensor IP address |
port | int | 1535 | Sensor TCP port |
pmon
Power monitor acquisition settings.
| Key | Type | Default | Description |
|---|---|---|---|
connection.retry_delay | int | 3 | Seconds between reconnect attempts |
parquet.interval | int | 86400 | Seconds per Parquet file (86400 = 24 hours) |
parquet.compression.method | string | "ZSTD" | Compression method (ZSTD, SNAPPY, GZIP, or NONE) |
parquet.compression.level | int | 4 | Compression level |
Data paths
Default paths follow XDG conventions:
| Path | Contents |
|---|---|
~/.local/share/equser/pmon/ | PMon Parquet files |
~/.config/equser/config.yaml | User configuration |
On EQ Synapse gateways, data is stored at:
| Path | Contents |
|---|---|
/var/lib/eq-synapse/data/pmon/ | PMon files (YYYYMMDD_HHMM.parquet) |
/var/lib/eq-synapse/data/cpow/ | CPOW files (YYYYMMDD_HHMMSS.parquet) |
Programmatic access
from equser.core.config import load_config, get_sensor_address
config = load_config()
address = get_sensor_address(config)
print(f"Sensor at {address}")
Data Loading
Module: equser.data
Dependencies: base (numpy, pyarrow)
Load CPOW and PMon Parquet files with automatic scaling and timestamp parsing.
CPOW data
load_cpow_scaled(file_path) -> dict
Load a CPOW Parquet file and return scaled voltage/current arrays.
Handles both formats automatically:
- int32 (current): raw ADC counts scaled by
vscale/iscalefrom Parquet user metadata. - float (legacy): values already in V/A; scaling factors are 1.0.
Returns a dict with:
| Key | Type | Description |
|---|---|---|
table | pa.Table | Raw PyArrow Table |
VA, VB, VC | np.ndarray | Scaled voltage arrays (float64) |
IA, IB, IC, IN | np.ndarray | Scaled current arrays (float64) |
vscale | float | Voltage scaling factor applied |
iscale | float | Current scaling factor applied |
start_time | datetime or None | Parsed from metadata |
sample_rate | int | Always 32000 |
from equser.data import load_cpow_scaled
result = load_cpow_scaled('20250623_075056.parquet')
print(f"Peak voltage A: {result['VA'].max():.1f} V")
print(f"Samples: {len(result['VA'])}")
load_cpow(file_path) -> pa.Table
Load a CPOW Parquet file as a raw PyArrow Table with no scaling applied. Use this when you need the raw integer ADC values or want to handle scaling yourself.
Constants
| Constant | Value | Description |
|---|---|---|
SAMPLE_RATE_HZ | 32000 | CPOW sample rate |
CHANNELS | ['VA', 'VB', 'VC', 'IA', 'IB', 'IC', 'IN'] | Channel names |
NEUTRAL_CT_RATIO | 30 | Neutral CT sensitivity ratio vs. phase CTs |
PMon data
load_pmon(file_path) -> pa.Table
Load a PMon Parquet file as a PyArrow Table.
PMon files contain 10/12-cycle RMS measurements (10 cycles for 50 Hz grids, 12 cycles for 60 Hz). Common columns include:
| Column | Description |
|---|---|
time_us | Timestamp in microseconds |
FREQ | Line frequency (Hz) |
AVRMS, BVRMS, CVRMS | Phase RMS voltage |
AIRMS, BIRMS, CIRMS | Phase RMS current |
NIRMS | Neutral RMS current |
AWATT, BWATT, CWATT | Phase active power |
from equser.data import load_pmon
table = load_pmon('20250623_0750.parquet')
freq = table.column('FREQ').to_numpy()
print(f"Mean frequency: {freq.mean():.3f} Hz")
Timestamp parsing
parse_start_time(s) -> datetime
Parse an ISO 8601 timestamp string from CPOW metadata. Handles nanosecond precision by truncating to microseconds (Python datetime limit).
from equser.data import parse_start_time
dt = parse_start_time("2025-06-23T07:50:56.123456789Z")
print(dt) # 2025-06-23 07:50:56.123456+00:00
parse_filename_timestamp(filename) -> datetime | None
Extract a timestamp from an EQ data filename pattern.
Supports:
YYYYMMDD_HHMM(PMon files)YYYYMMDD_HHMMSS(CPOW files)
from equser.data import parse_filename_timestamp
dt = parse_filename_timestamp("20250623_075056.parquet")
print(dt) # 2025-06-23 07:50:56
Waveform Analysis
Module: equser.analysis
Dependencies: base (numpy only)
Functions for zero-crossing detection, cycle extraction, and waveform analysis.
find_zero_crossings(signal, time_array)
Find negative-to-positive zero crossings in a signal using linear interpolation between adjacent samples.
Args:
signal(ndarray): Voltage or current waveform array.time_array(ndarray): Corresponding time array (same length).
Returns: (crossing_times, crossing_indices)
crossing_times: interpolated times of each zero crossingcrossing_indices: index of the sample just before each crossing
import numpy as np
from equser.data import load_cpow_scaled, SAMPLE_RATE_HZ
from equser.analysis import find_zero_crossings
result = load_cpow_scaled('cpow_data.parquet')
time = np.arange(len(result['VA'])) / SAMPLE_RATE_HZ
crossings, indices = find_zero_crossings(result['VA'], time)
print(f"Found {len(crossings)} zero crossings")
# Frequency estimation
periods = np.diff(crossings)
print(f"Mean frequency: {1.0 / np.mean(periods):.2f} Hz")
print(f"Frequency std: {np.std(1.0 / periods):.4f} Hz")
extract_complete_cycles(signal, time_array, start_times, num_cycles=1)
Extract complete AC cycles (zero-crossing to zero-crossing) starting from specified times.
Args:
signal(ndarray): Waveform array.time_array(ndarray): Time array.start_times(list of float): Times to start extracting from.num_cycles(int): Number of cycles per start time (default 1).
Returns: List of tuples (cycle_time, cycle_signal, start, end) where:
cycle_time: time array normalized to start at 0cycle_signal: signal values for the windowstart: actual start time (first zero crossing)end: actual end time (last zero crossing)
Returns an empty list if there are not enough zero crossings for the requested number of cycles.
from equser.analysis import extract_complete_cycles
# Extract single cycles at 3 different times
cycles = extract_complete_cycles(result['VA'], time, [0.0, 0.05, 0.1])
for cycle_time, cycle_signal, start, end in cycles:
duration_ms = (end - start) * 1000
peak = np.max(np.abs(cycle_signal))
print(f"Cycle at {start:.4f}s: {duration_ms:.2f} ms, peak {peak:.1f} V")
plot_extracted_cycles(signal_dict, time_array, start_times, ...)
Plot extracted cycles for one or more phases side-by-side. Requires
matplotlib ([analysis] extra).
Args:
signal_dict: Dict mapping phase names to arrays (e.g.{'VA': va, 'VB': vb}), or a single array (treated as{'VA': array}).time_array: Time array.start_times: Times to extract cycles from.num_cycles(int): Cycles per window (default 1).epoch_start_time(datetime, optional): Absolute start time for labels.
Returns: (fig, axes, window_times)
from equser.analysis.waveform import plot_extracted_cycles
fig, axes, windows = plot_extracted_cycles(
{'VA': result['VA'], 'VB': result['VB'], 'VC': result['VC']},
time,
start_times=[0.0, 0.1, 0.2],
num_cycles=2,
epoch_start_time=result['start_time'],
)
fig.savefig('cycles.png', dpi=150, bbox_inches='tight')
Plotting
Module: equser.plotting
Dependencies: [analysis] extra (matplotlib)
Static matplotlib plots for PMon and CPOW data. Install with:
pip install equser[analysis]
PowerMonitorPlotter
Plot power monitor (PMon) time-series data.
from equser.plotting import PowerMonitorPlotter
plotter = PowerMonitorPlotter()
# Plot from a file
plotter.plot_file('pmon_data.parquet')
# Plot from a PyArrow Table
from equser.data import load_pmon
table = load_pmon('pmon_data.parquet')
plotter.plot(table)
Methods
| Method | Description |
|---|---|
plot_file(path) | Load and plot a PMon Parquet file |
plot(table) | Plot from a PyArrow Table |
Output
Generates a multi-panel figure with:
- Frequency vs. time
- Phase RMS voltages vs. time
- Phase RMS currents vs. time
- Phase active power vs. time
Color scheme follows standard power systems convention (black/red/blue for phases A/B/C).
WaveformPlotter
Plot CPOW waveform captures.
from equser.plotting import WaveformPlotter
plotter = WaveformPlotter()
# Plot from a file
plotter.plot_file('cpow_data.parquet')
Methods
| Method | Description |
|---|---|
plot_file(path) | Load and plot a CPOW Parquet file |
Output
Generates voltage and current waveform plots with proper scaling. The neutral
current is automatically adjusted by the CT sensitivity ratio
(NEUTRAL_CT_RATIO = 30).
Color schemes
Both plotters support named color schemes:
| Scheme | Description |
|---|---|
"standard" | Black/Red/Blue (default, power systems convention) |
"colorblind" | Colorblind-friendly palette |
"monochrome" | Grayscale with line style variation |
Saving plots
All plotters return matplotlib figure objects. Save in any format:
fig = plotter.plot_file('data.parquet')
fig.savefig('output.png', dpi=150, bbox_inches='tight')
fig.savefig('output.svg')
fig.savefig('output.pdf')
API Client
Module: equser.api
Dependencies: [analysis] extra (requests, websocket-client)
REST and WebSocket clients for EQ Synapse gateways. Install with:
pip install equser[analysis]
SynapseClient
REST client for the EQ Synapse API (Actix-web server at port 8080).
from equser.api import SynapseClient
client = SynapseClient('http://gateway:8080')
list_devices() -> list[dict]
List all registered devices.
devices = client.list_devices()
for d in devices:
print(f"{d['id']}: {d.get('name', 'unnamed')}")
get_pmon_data(device_id, **params) -> pa.Table
Fetch PMon data as an Arrow Table.
table = client.get_pmon_data('wave-001')
print(f"Rows: {table.num_rows}, Columns: {table.column_names}")
Optional query parameters: start_time, end_time, metrics, limit.
get_cpow_data(device_id, **params) -> pa.Table
Fetch CPOW waveform data as an Arrow Table.
table = client.get_cpow_data('wave-001')
Optional query parameters: start_time, end_time, limit.
get_events(device_id=None, limit=100) -> list[dict]
Fetch recent power quality events.
events = client.get_events(limit=10)
for e in events:
print(f"{e['timestamp']}: {e['type']}")
query_sql(query, device_id=None, limit=None) -> list[dict]
Execute a SELECT query via the SQL endpoint. Only SELECT statements are allowed by the server.
rows = client.query_sql(
"SELECT time_us, FREQ, AVRMS FROM pmon ORDER BY time_us DESC",
limit=10,
)
WebSocket streaming
connect_cpow_stream(gateway_url) -> Generator
Connect to the CPOW waveform WebSocket and yield data in real time.
Each binary message is an Arrow IPC RecordBatch (~512 rows, 16 ms at 32 kHz). Text messages are JSON gap markers indicating dropped samples.
from equser.api import connect_cpow_stream
for item in connect_cpow_stream('http://gateway:8080'):
if isinstance(item, dict):
print(f"Gap: {item['skipped_samples']} samples")
else:
# item is a pyarrow.RecordBatch
va = item.column('VA').to_numpy()
print(f"Batch: {item.num_rows} rows, VA peak: {va.max()}")
connect_spectral_stream(device_id, ...) -> Generator
Connect to the spectral analysis WebSocket and yield JSON frames.
Args:
| Parameter | Default | Description |
|---|---|---|
device_id | (required) | Device identifier |
phase | 'va' | Channel: va, vb, vc, ia, ib, ic |
fft_size | 4096 | FFT window size (power of 2) |
update_rate | 10.0 | Frames per second |
freq_min | 0 | Minimum frequency (Hz) |
freq_max | 3000 | Maximum frequency (Hz) |
from equser.api import connect_spectral_stream
for frame in connect_spectral_stream('wave-001', phase='va', fft_size=8192):
print(f"Spectral frame: {len(frame.get('magnitudes', []))} bins")
API endpoints
The EQ Synapse server (Actix-web) exposes these endpoints:
| Endpoint | Method | Description |
|---|---|---|
/api/v1/devices | GET | List devices |
/api/v1/devices/{id}/pmon/data | GET | PMon data (Arrow IPC) |
/api/v1/devices/{id}/cpow/data | GET | CPOW data (Arrow IPC) |
/api/v1/events | GET | Power quality events |
/api/v1/events/stream | GET | Event stream (SSE) |
/api/v1/query/sql | POST | SQL query (SELECT only) |
/api/ws/cpow_stream | WS | Real-time waveform stream |
/api/ws/spectral | WS | Real-time spectral stream |
Data endpoints return Arrow IPC binary format for efficient transfer.
Live Acquisition
Module: equser.pmon
Dependencies: [daq] extra (avro, fastavro)
Real-time power monitor data acquisition from EQ Wave sensor hardware. Install with:
pip install equser[daq]
Note: Live acquisition requires physical EQ Wave sensor hardware connected via Ethernet.
PowerMonitor
The main acquisition class. Connects to an EQ Wave sensor, reads 10/12-cycle power quality measurements, and writes them to Parquet files.
from equser.pmon import PowerMonitor
monitor = PowerMonitor(config)
monitor.run() # Blocks until interrupted
acquire(config_path)
Convenience function to load configuration and start monitoring.
from equser.pmon import acquire
acquire('equser.yaml')
Avro-to-Parquet conversion
Convert legacy Avro data files to Parquet format.
from equser.pmon import convert_avro_to_parquet
convert_avro_to_parquet('input.avro', 'output.parquet')
Schema
The PMon schema defines 10/12-cycle power quality measurements:
from equser.pmon.schema import create_schema
schema, schema_str, field_names = create_schema(num_phases=3)
print(field_names)
# ['time_us', 'FREQ', 'AVRMS', 'BVRMS', 'CVRMS', ...]
Supports 1, 2, or 3-phase configurations. Fields include:
| Field | Description |
|---|---|
time_us | Measurement timestamp (microseconds) |
FREQ | Line frequency (Hz) |
{A,B,C}VRMS | Phase RMS voltage |
{A,B,C}IRMS | Phase RMS current |
NIRMS | Neutral RMS current |
{A,B,C}WATT | Phase active power |
{A,B,C}VFUND | Phase fundamental voltage |
{A,B,C}IFUND | Phase fundamental current |
Error hierarchy
equser.pmon.errors.PMonError
├── ConnectionError - Sensor connection failures
├── DataError - Invalid or corrupt data
└── ConfigError - Configuration problems
CLI
# Start acquisition
equser pmon acquire -c config.yaml
# Convert Avro to Parquet
equser pmon convert data/*.avro --remove
CLI Reference
equser provides a command-line interface for common operations.
Usage
equser <command> [options]
Commands
equser pmon acquire
Start power monitoring from an EQ Wave sensor. Requires [daq] extra and
physical sensor hardware.
equser pmon acquire -c config.yaml
| Option | Description |
|---|---|
-c, --config | Path to YAML configuration file |
Reads 10/12-cycle RMS measurements and writes Parquet files to the configured data directory. Runs continuously until interrupted (Ctrl+C).
equser pmon convert
Convert Avro data files to Parquet format. Requires [daq] extra.
equser pmon convert data/*.avro
equser pmon convert data/*.avro --remove
| Option | Description |
|---|---|
--remove | Delete source Avro files after successful conversion |
equser plot
Plot a PMon or CPOW data file. Requires [analysis] extra.
equser plot data.parquet
Automatically detects the file type (PMon vs. CPOW) and uses the appropriate plotter.
equser notebooks list
List the reference notebooks bundled in the equser package.
equser notebooks list
equser notebooks copy
Copy reference notebooks to a directory.
equser notebooks copy --dest ./notebooks
equser notebooks copy --dest ./notebooks --category tutorials
equser notebooks copy --dest ./notebooks --overwrite
| Option | Description |
|---|---|
--dest | Destination directory (default: current directory) |
--category | Only copy notebooks from this category (tutorials or analysis) |
--overwrite | Overwrite existing files |
equser snapshot
Capture live waveform data from a gateway and save to a Parquet file.
equser snapshot
equser snapshot --host 192.168.1.10 --duration 10 --output capture.parquet
| Option | Description |
|---|---|
--host | Gateway hostname or IP (default: localhost) |
--port | Gateway port (default: 8080) |
--duration | Capture duration in seconds (default: 5.0) |
--output | Output Parquet file path (auto-generated if omitted) |
Tab completion
Install tab completion with the [cli] extra:
pip install equser[cli]
eval "$(register-python-argcomplete equser)"
Add the eval line to your shell profile for persistent completion.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
0.1.0 - 2026-02-06
Initial public release. User toolkit for EQ Wave power quality data.
Modules
- equser.data - Load CPOW and PMon Parquet files with automatic scaling, timestamp parsing
- equser.analysis - Waveform analysis: zero-crossing detection, AC cycle extraction
- equser.api - REST and WebSocket clients for EQ Synapse gateways (requires
[analysis]) - equser.plotting - Static matplotlib plots for PMon and CPOW data (requires
[analysis]) - equser.pmon - Live sensor acquisition and Avro-to-Parquet conversion (requires
[daq]) - equser.core - YAML configuration loading, XDG-compliant path resolution
- equser.utils - Logging with optional color, DateTime with floor-division
- equser.notebooks - Bundled reference notebooks with list/copy API
- equser.widgets - Interactive file selector for JupyterLab (requires
[jupyter]) - equser.snapshot - Waveform capture via gateway WebSocket
Dependency Tiers
- Base: numpy, pyarrow, pyyaml, argcomplete, colorlog (data loading, analysis, CLI)
- [daq]: avro, fastavro (live sensor acquisition)
- [analysis]: matplotlib, requests, websocket-client (plotting + API)
- [jupyter]:
[analysis]+ jupyterlab, duckdb, ipywidgets, ipykernel, nbconvert - [full]: all of the above
CLI
equser pmon acquire- Start power monitoring from EQ Wave sensorequser pmon convert- Convert Avro files to Parquetequser plot- Plot PMon or CPOW data filesequser notebooks list- List bundled reference notebooksequser notebooks copy- Copy reference notebooks to a directoryequser snapshot- Capture live waveform data to a Parquet file
MIT License
Copyright (c) 2026 EQ Systems Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.