Skip to content

Commit 1d01bc2

Browse files
committed
adding websocket client
1 parent 9015c27 commit 1d01bc2

23 files changed

Lines changed: 1237 additions & 47 deletions

README.md

Lines changed: 150 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,26 @@ A comprehensive Python package to interact with the Mist Cloud APIs, built from
1414
- [Installation](#installation)
1515
- [Quick Start](#quick-start)
1616
- [Configuration](#configuration)
17+
- [Using Environment File](#using-environment-file)
18+
- [Environment Variables](#environment-variables)
1719
- [Authentication](#authentication)
18-
- [Usage](#usage)
19-
- [CLI Helper Functions](#cli-helper-functions)
20-
- [Pagination](#pagination-support)
21-
- [Examples](#examples)
20+
- [Interactive Authentication](#interactive-authentication)
21+
- [Environment File Authentication](#environment-file-authentication)
22+
- [HashiCorp Vault Authentication](#hashicorp-vault-authentication)
23+
- [System Keyring Authentication](#system-keyring-authentication)
24+
- [Direct Parameter Authentication](#direct-parameter-authentication)
25+
- [API Requests Usage](#api-requests-usage)
26+
- [Basic API Calls](#basic-api-calls)
27+
- [Error Handling](#error-handling)
28+
- [Log Sanitization](#log-sanitization)
29+
- [Getting Help](#getting-help)
30+
- [CLI Helper Functions](#cli-helper-functions)
31+
- [Pagination](#pagination-support)
32+
- [Examples](#examples)
33+
- [WebSocket Streaming](#websocket-streaming)
34+
- [Available Channels](#available-channels)
35+
- [Callbacks](#callbacks)
36+
- [Usage Patterns](#usage-patterns)
2237
- [Development](#development-and-testing)
2338
- [Contributing](#contributing)
2439
- [License](#license)
@@ -150,22 +165,24 @@ MIST_APITOKEN=your_api_token_here
150165
# LOGGING_LOG_LEVEL=10
151166
```
152167

153-
### All Configuration Options
168+
### Configuration Options
169+
170+
| Environment Variable | APISession Parameter | Type | Default | Description |
171+
|---|---|---|---|---|
172+
| `MIST_HOST` | `host` | string | None | Mist Cloud API endpoint (e.g., `api.mist.com`) |
173+
| `MIST_APITOKEN` | `apitoken` | string | None | API Token for authentication (recommended) |
174+
| `MIST_USER` | `email` | string | None | Username/email for authentication |
175+
| `MIST_PASSWORD` | `password` | string | None | Password for authentication |
176+
| `MIST_KEYRING_SERVICE` | `keyring_service` | string | None | System keyring service name |
177+
| `MIST_VAULT_URL` | `vault_url` | string | https://127.0.0.1:8200 | HashiCorp Vault URL |
178+
| `MIST_VAULT_PATH` | `vault_path` | string | None | Path to secret in Vault |
179+
| `MIST_VAULT_MOUNT_POINT` | `vault_mount_point` | string | secret | Vault mount point |
180+
| `MIST_VAULT_TOKEN` | `vault_token` | string | None | Vault authentication token |
181+
| `CONSOLE_LOG_LEVEL` | `console_log_level` | int | 20 | Console log level (0-50) |
182+
| `LOGGING_LOG_LEVEL` | `logging_log_level` | int | 10 | File log level (0-50) |
183+
| `HTTPS_PROXY` | `https_proxy` | string | None | HTTP/HTTPS proxy URL |
184+
| | `env_file` | str | None | Path to `.env` file |
154185

155-
| Variable | Type | Default | Description |
156-
|----------|------|---------|-------------|
157-
| `MIST_HOST` | string | None | Mist Cloud API endpoint (e.g., `api.mist.com`) |
158-
| `MIST_APITOKEN` | string | None | API Token for authentication (recommended) |
159-
| `MIST_USER` | string | None | Username if not using API token |
160-
| `MIST_PASSWORD` | string | None | Password if not using API token |
161-
| `MIST_KEYRING_SERVICE` | string | None | Load credentials from system keyring |
162-
| `MIST_VAULT_URL` | string | https://127.0.0.1:8200 | HashiCorp Vault URL |
163-
| `MIST_VAULT_PATH` | string | None | Path to secret in Vault |
164-
| `MIST_VAULT_MOUNT_POINT` | string | secret | Vault mount point |
165-
| `MIST_VAULT_TOKEN` | string | None | Vault authentication token |
166-
| `CONSOLE_LOG_LEVEL` | int | 20 | Console log level (0-50) |
167-
| `LOGGING_LOG_LEVEL` | int | 10 | File log level (0-50) |
168-
| `HTTPS_PROXY` | string | None | HTTP/HTTPS proxy URL |
169186

170187
---
171188

@@ -253,27 +270,10 @@ apisession = mistapi.APISession(
253270
apisession.login()
254271
```
255272

256-
### APISession Parameters
257-
258-
| Parameter | Type | Default | Description |
259-
|-----------|------|---------|-------------|
260-
| `email` | str | None | Email for login/password authentication |
261-
| `password` | str | None | Password for login/password authentication |
262-
| `apitoken` | str | None | API token (recommended method) |
263-
| `host` | str | None | Mist Cloud endpoint (e.g., "api.mist.com") |
264-
| `keyring_service` | str | None | System keyring service name |
265-
| `vault_url` | str | https://127.0.0.1:8200 | HashiCorp Vault URL |
266-
| `vault_path` | str | None | Path to secret in Vault |
267-
| `vault_mount_point` | str | secret | Vault mount point |
268-
| `vault_token` | str | None | Vault authentication token |
269-
| `env_file` | str | None | Path to `.env` file |
270-
| `console_log_level` | int | 20 | Console logging level (0-50) |
271-
| `logging_log_level` | int | 10 | File logging level (0-50) |
272-
| `https_proxy` | str | None | Proxy URL |
273273

274274
---
275275

276-
## Usage
276+
## API Requests Usage
277277

278278
### Basic API Calls
279279

@@ -345,11 +345,11 @@ help(mistapi.api.v1.orgs.stats.getOrgStats)
345345

346346
---
347347

348-
## CLI Helper Functions
348+
### CLI Helper Functions
349349

350350
Interactive functions for selecting organizations and sites.
351351

352-
### Organization Selection
352+
#### Organization Selection
353353

354354
```python
355355
# Select single organization
@@ -368,7 +368,7 @@ Available organizations:
368368
Select an Org (0 to 1, or q to exit): 0
369369
```
370370

371-
### Site Selection
371+
#### Site Selection
372372

373373
```python
374374
# Select site within an organization
@@ -386,9 +386,9 @@ Select a Site (0 to 1, or q to exit): 0
386386

387387
---
388388

389-
## Pagination Support
389+
### Pagination Support
390390

391-
### Get Next Page
391+
#### Get Next Page
392392

393393
```python
394394
# Get first page
@@ -403,7 +403,7 @@ if response.next:
403403
print(f"Second page: {len(response_2.data['results'])} results")
404404
```
405405

406-
### Get All Pages Automatically
406+
#### Get All Pages Automatically
407407

408408
```python
409409
# Get all pages with a single call
@@ -419,11 +419,11 @@ print(f"Total results across all pages: {len(all_data)}")
419419

420420
---
421421

422-
## Examples
422+
### Examples
423423

424424
Comprehensive examples are available in the [Mist Library repository](https://github.com/tmunzer/mist_library).
425425

426-
### Device Management
426+
#### Device Management
427427

428428
```python
429429
# List all devices in an organization
@@ -441,7 +441,7 @@ result = mistapi.api.v1.orgs.devices.updateOrgDevice(
441441
)
442442
```
443443

444-
### Site Management
444+
#### Site Management
445445

446446
```python
447447
# Create a new site
@@ -458,7 +458,7 @@ new_site = mistapi.api.v1.orgs.sites.createOrgSite(
458458
site_stats = mistapi.api.v1.sites.stats.getSiteStats(apisession, new_site.id)
459459
```
460460

461-
### Client Analytics
461+
#### Client Analytics
462462

463463
```python
464464
# Search for wireless clients
@@ -478,6 +478,109 @@ events = mistapi.api.v1.orgs.clients.searchOrgClientsEvents(
478478

479479
---
480480

481+
## WebSocket Streaming
482+
483+
The package provides a WebSocket client for real-time event streaming from the Mist API (`wss://{host}/api-ws/v1/stream`). Authentication is handled automatically using the same session credentials (API token or login/password).
484+
485+
### Available Channels
486+
487+
#### Organization Channels
488+
489+
| Class | Channel | Description |
490+
|-------|---------|-------------|
491+
| `mistapi.websockets.orgs.OrgInsightsEvents` | `/orgs/{org_id}/insights/summary` | Real-time insights events for an organization |
492+
| `mistapi.websockets.orgs.OrgMxEdgesStatsEvents` | `/orgs/{org_id}/stats/mxedges` | Real-time MX edges stats for an organization |
493+
| `mistapi.websockets.orgs.OrgMxEdgesUpgradesEvents` | `/orgs/{org_id}/mxedges` | Real-time MX edges upgrades events for an organization |
494+
495+
#### Site Channels
496+
497+
| Class | Channel | Description |
498+
|-------|---------|-------------|
499+
| `mistapi.websockets.sites.SiteClientsStatsEvents` | `/sites/{site_id}/stats/clients` | Real-time clients stats for a site |
500+
| `mistapi.websockets.sites.SiteDeviceCmdEvents` | `/sites/{site_id}/devices/{device_id}/cmd` | Real-time device command events for a site |
501+
| `mistapi.websockets.sites.SiteDeviceStatsEvents` | `/sites/{site_id}/stats/devices` | Real-time device stats for a site |
502+
| `mistapi.websockets.sites.SiteDeviceUpgradesEvents` | `/sites/{site_id}/devices` | Real-time device upgrades events for a site |
503+
| `mistapi.websockets.sites.SitePcapEvents` | `/sites/{site_id}/pcap` | Real-time PCAP events for a site |
504+
505+
#### Location Channels
506+
507+
| Class | Channel | Description |
508+
|-------|---------|-------------|
509+
| `mistapi.websockets.location.LocationBleAssetsEvents` | `/sites/{site_id}/stats/maps/{map_id}/assets` | Real-time BLE assets location events |
510+
| `mistapi.websockets.location.LocationConnectedClientsEvents` | `/sites/{site_id}/stats/maps/{map_id}/clients` | Real-time connected clients location events |
511+
| `mistapi.websockets.location.LocationSdkClientsEvents` | `/sites/{site_id}/stats/maps/{map_id}/sdkclients` | Real-time SDK clients location events |
512+
| `mistapi.websockets.location.LocationUnconnectedClientsEvents` | `/sites/{site_id}/stats/maps/{map_id}/unconnected_clients` | Real-time unconnected clients location events |
513+
| `mistapi.websockets.location.LocationDiscoveredBleAssetsEvents` | `/sites/{site_id}/stats/maps/{map_id}/discovered_assets` | Real-time discovered BLE assets location events |
514+
515+
### Callbacks
516+
517+
| Method | Signature | Description |
518+
|--------|-----------|-------------|
519+
| `ws.on_open(cb)` | `cb()` | Called when the connection is established |
520+
| `ws.on_message(cb)` | `cb(data: dict)` | Called for every incoming message |
521+
| `ws.on_error(cb)` | `cb(error: Exception)` | Called on WebSocket errors |
522+
| `ws.on_close(cb)` | `cb(status_code: int, msg: str)` | Called when the connection closes |
523+
524+
### Usage Patterns
525+
526+
#### Callback style (recommended)
527+
528+
`connect()` returns immediately; messages are delivered to the registered callback in a background thread.
529+
530+
```python
531+
import mistapi
532+
533+
apisession = mistapi.APISession(env_file="~/.mist_env")
534+
apisession.login()
535+
536+
ws = mistapi.websockets.sites.SiteDeviceStatsEvents(apisession, site_id="<site_id>")
537+
ws.on_message(lambda data: print(data))
538+
ws.connect() # non-blocking
539+
540+
input("Press Enter to stop")
541+
ws.disconnect()
542+
```
543+
544+
#### Generator style
545+
546+
Iterate over incoming messages as a blocking generator. Useful when you want to process messages sequentially in a loop.
547+
548+
```python
549+
ws = mistapi.websockets.sites.SiteDeviceStatsEvents(apisession, site_id="<site_id>")
550+
ws.connect(run_in_background=True)
551+
552+
for msg in ws.receive(): # blocks, yields each message as a dict
553+
print(msg)
554+
if some_condition:
555+
ws.disconnect() # stops the generator cleanly
556+
```
557+
558+
#### Blocking style
559+
560+
`connect(run_in_background=False)` blocks the calling thread until the connection closes. Useful for simple scripts.
561+
562+
```python
563+
ws = mistapi.websockets.sites.SiteDeviceStatsEvents(apisession, site_id="<site_id>")
564+
ws.on_message(lambda data: print(data))
565+
ws.connect(run_in_background=False) # blocks until disconnected
566+
```
567+
568+
#### Context manager
569+
570+
`disconnect()` is called automatically on exit, even if an exception is raised.
571+
572+
```python
573+
import time
574+
575+
with mistapi.websockets.sites.SiteDeviceStatsEvents(apisession, site_id="<site_id>") as ws:
576+
ws.on_message(lambda data: print(data))
577+
ws.connect()
578+
time.sleep(60)
579+
# ws.disconnect() called automatically here
580+
```
581+
582+
---
583+
481584
## Development and Testing
482585

483586
### Development Setup

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dependencies = [
2727
"deprecation>=2.1.0",
2828
"hvac>=2.3.0",
2929
"keyring>=24.3.0",
30+
"websocket-client>=1.8.0",
3031
]
3132

3233
[project.urls]

src/mistapi/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
--------------------------------------------------------------------------------
1111
"""
1212

13+
# isort: skip_file
1314
from mistapi.__api_session import APISession as APISession
1415
from mistapi import api as api
1516
from mistapi import cli as cli
17+
from mistapi import websockets as websockets
1618
from mistapi.__pagination import get_all as get_all
1719
from mistapi.__pagination import get_next as get_next
1820
from mistapi.__version import __author__ as __author__

src/mistapi/websockets/__init__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
--------------------------------------------------------------------------------
3+
------------------------- Mist API Python CLI Session --------------------------
4+
5+
Written by: Thomas Munzer (tmunzer@juniper.net)
6+
Github : https://github.com/tmunzer/mistapi_python
7+
8+
This package is licensed under the MIT License.
9+
10+
--------------------------------------------------------------------------------
11+
WebSocket channel classes for real-time Mist API streaming.
12+
13+
Usage example::
14+
15+
import mistapi
16+
session = mistapi.APISession(...)
17+
session.login()
18+
19+
ws = mistapi.websockets.sites.SiteDeviceStatsEvents(session, site_id="<site_id>")
20+
ws.on_message(lambda data: print(data))
21+
ws.connect()
22+
"""
23+
24+
from mistapi.websockets import location, orgs, sites

0 commit comments

Comments
 (0)