Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[submodule "mist_openapi"]
path = mist_openapi
url = https://github.com/mistsys/mist_openapi.git
branch = master
branch = 2602.1.7
54 changes: 54 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,58 @@
# CHANGELOG
## Version 0.61.2 (March 2026)

**Released**: March 17, 2026

This release adds automatic reconnection support for WebSocket streams, updates the OpenAPI specification, and includes minor bug fixes.

---

### 1. NEW FEATURES

#### **WebSocket Auto-Reconnect**
`_MistWebsocket` now supports automatic reconnection with configurable parameters:
- `auto_reconnect` — Enable/disable auto-reconnect (default: `False`)
- `max_reconnect_attempts` — Maximum reconnect attempts before giving up (default: `5`)
- `reconnect_backoff` — Base backoff delay in seconds, with exponential increase (default: `2.0`)

When enabled, the WebSocket automatically reconnects on transient failures using exponential backoff. User-initiated `disconnect()` calls are respected during reconnection attempts.
Comment thread
tmunzer-AIDE marked this conversation as resolved.

```python
ws = mistapi.websockets.sites.DeviceStatsEvents(
apisession,
site_ids=["<site_id>"],
auto_reconnect=True,
max_reconnect_attempts=5,
reconnect_backoff=2.0
Comment thread
tmunzer-AIDE marked this conversation as resolved.
)
ws.connect(run_in_background=True)
```

---

### 2. API CHANGES (OpenAPI 2602.1.7)

Updated to mist_openapi spec version 2602.1.7.

#### **Insights API**
- **`getSiteInsightMetrics()`** — Now uses `metrics` as a query parameter instead of a path parameter
- **`getSiteInsightMetricsForAP()`** — New function to retrieve insight metrics for a specific AP
- **`getSiteInsightMetricsForClient()`** — Changed `metric` path parameter to `metrics` query parameter
- **`getSiteInsightMetricsForGateway()`** — Changed `metric` path parameter to `metrics` query parameter

#### **Stats API**
- **`getOrgStats()`** — Removed `start`, `end`, `duration`, `limit`, `page` query parameters
- **`listOrgSiteStats()`** — Removed `start`, `end`, `duration` query parameters

---

### 3. BUG FIXES
- Fixed `ShellSession.recv()` to gracefully handle socket timeout reset when the connection is already closed
- Fixed thread-safety (TOCTOU) race conditions in `ShellSession` by capturing WebSocket reference in local variables across `disconnect()`, `connected`, `send()`, `recv()`, and `resize()` methods
- Fixed thread-safety race condition in `_MistWebsocket.disconnect()` with local variable capture

---

## Version 0.61.1 (March 2026)

**Released**: March 15, 2026
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,19 +579,23 @@ The package provides a WebSocket client for real-time event streaming from the M

### Connection Parameters

All channel classes accept the following optional keyword arguments to control the WebSocket keep-alive behaviour:
All channel classes accept the following optional keyword arguments:

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `ping_interval` | `int` | `30` | Seconds between automatic ping frames. Set to `0` to disable pings. |
| `ping_timeout` | `int` | `10` | Seconds to wait for a pong response before treating the connection as dead. |
| `auto_reconnect` | `bool` | `False` | Automatically reconnect on transient failures using exponential backoff. |
| `max_reconnect_attempts` | `int` | `5` | Maximum number of reconnect attempts before giving up. |
| `reconnect_backoff` | `float` | `2.0` | Base backoff delay in seconds. Doubles after each failed attempt (2s, 4s, 8s, ...). Resets on successful reconnection. |
Comment thread
tmunzer-AIDE marked this conversation as resolved.

```python
ws = mistapi.websockets.sites.DeviceStatsEvents(
apisession,
site_ids=["<site_id>"],
ping_interval=60, # ping every 60 s
ping_timeout=20, # wait up to 20 s for pong
ping_interval=60, # ping every 60 s
ping_timeout=20, # wait up to 20 s for pong
auto_reconnect=True, # reconnect on transient failures
)
ws.connect()
```
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "mistapi"
version = "0.61.1"
version = "0.61.2"
authors = [{ name = "Thomas Munzer", email = "tmunzer@juniper.net" }]
description = "Python package to simplify the Mist System APIs usage"
keywords = ["Mist", "Juniper", "API"]
Expand Down
2 changes: 1 addition & 1 deletion src/mistapi/__version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version__ = "0.61.1"
__version__ = "0.61.2"
__author__ = "Thomas Munzer <tmunzer@juniper.net>"
40 changes: 1 addition & 39 deletions src/mistapi/api/v1/orgs/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,7 @@
from mistapi.__api_response import APIResponse as _APIResponse


def getOrgStats(
mist_session: _APISession,
org_id: str,
start: str | None = None,
end: str | None = None,
duration: str | None = None,
limit: int | None = None,
page: int | None = None,
) -> _APIResponse:
def getOrgStats(mist_session: _APISession, org_id: str) -> _APIResponse:
Comment thread
tmunzer-AIDE marked this conversation as resolved.
Comment thread
tmunzer marked this conversation as resolved.
"""
API doc: https://www.juniper.net/documentation/us/en/software/mist/api/http/api/orgs/stats/get-org-stats

Expand All @@ -35,14 +27,6 @@ def getOrgStats(
-----------
org_id : str

QUERY PARAMS
------------
start : str
end : str
duration : str, default: 1d
limit : int, default: 100
page : int, default: 1

RETURN
-----------
mistapi.APIResponse
Expand All @@ -51,16 +35,6 @@ def getOrgStats(

uri = f"/api/v1/orgs/{org_id}/stats"
query_params: dict[str, str] = {}
if start:
query_params["start"] = str(start)
if end:
query_params["end"] = str(end)
if duration:
query_params["duration"] = str(duration)
if limit:
query_params["limit"] = str(limit)
if page:
query_params["page"] = str(page)
resp = mist_session.mist_get(uri=uri, query=query_params)
return resp

Expand Down Expand Up @@ -1017,9 +991,6 @@ def searchOrgSwOrGwPorts(
def listOrgSiteStats(
mist_session: _APISession,
org_id: str,
start: str | None = None,
end: str | None = None,
duration: str | None = None,
limit: int | None = None,
page: int | None = None,
) -> _APIResponse:
Comment thread
tmunzer-AIDE marked this conversation as resolved.
Expand All @@ -1037,9 +1008,6 @@ def listOrgSiteStats(

QUERY PARAMS
------------
start : str
end : str
duration : str, default: 1d
limit : int, default: 100
page : int, default: 1

Expand All @@ -1051,12 +1019,6 @@ def listOrgSiteStats(

uri = f"/api/v1/orgs/{org_id}/stats/sites"
query_params: dict[str, str] = {}
if start:
query_params["start"] = str(start)
if end:
query_params["end"] = str(end)
if duration:
query_params["duration"] = str(duration)
if limit:
query_params["limit"] = str(limit)
if page:
Expand Down
Loading
Loading