orb-cloud-client 1.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ """Orb Cloud API Client
2
+
3
+ A Python client library for interacting with the Orb Cloud API.
4
+ """
5
+
6
+ try:
7
+ from .client import OrbCloudClient
8
+ from .models import *
9
+ except ImportError:
10
+ # Handle case where package is not installed
11
+ from orb_cloud_client.client import OrbCloudClient
12
+ from orb_cloud_client.models import *
13
+
14
+ __version__ = "1.0.0"
15
+ __all__ = ["OrbCloudClient"]
@@ -0,0 +1,107 @@
1
+ """
2
+ Simple HTTP client for Orb Cloud API.
3
+ """
4
+
5
+ import httpx
6
+ from typing import Dict, Any, Optional, List
7
+
8
+ try:
9
+ from .models import Device, TempDatasetsRequest, Organization
10
+ except ImportError:
11
+ # Handle direct execution
12
+ from models import Device, TempDatasetsRequest, Organization
13
+
14
+ class OrbCloudClientBase(object):
15
+ def __init__(self, base_url: str = "https://panel.orb.net", token: Optional[str] = None):
16
+ self.base_url = base_url.rstrip('/')
17
+ self.headers = {"Content-Type": "application/json"}
18
+ if token:
19
+ self.headers["Authorization"] = f"Bearer {token}"
20
+
21
+ def _get_organization_devices(self, response: httpx.Response) -> List[Device]:
22
+ """Get list of devices for the organization from a response."""
23
+ response.raise_for_status()
24
+ return [Device(**device) for device in response.json()]
25
+
26
+ def _configure_temporary_datasets(self, response: httpx.Response) -> Dict[str, Any]:
27
+ """Process the response for configuring temporary datasets."""
28
+ response.raise_for_status()
29
+ return response.json()
30
+
31
+ def _get_organizations(self, response: httpx.Response) -> List[Organization]:
32
+ """Get list of organizations accessible with the provided token."""
33
+ response.raise_for_status()
34
+ return [Organization(**org) for org in response.json()]
35
+
36
+ class OrbCloudClient(OrbCloudClientBase):
37
+ """Simple HTTP client for Orb Cloud API."""
38
+
39
+ def __init__(self, base_url: str = "https://panel.orb.net", token: Optional[str] = None):
40
+ super().__init__(base_url, token)
41
+ self.client = httpx.Client(base_url=self.base_url, headers=self.headers)
42
+
43
+ def __enter__(self):
44
+ return self
45
+
46
+ def __exit__(self, exc_type, exc_val, exc_tb):
47
+ self.client.close()
48
+
49
+ def close(self):
50
+ """Close the HTTP client."""
51
+ self.client.close()
52
+
53
+ def get_organization_devices(self, organization_id: str) -> List[Device]:
54
+ """Get list of devices for the organization."""
55
+ response = self.client.get(f"/api/v2/organization/{organization_id}/devices")
56
+ return self._get_organization_devices(response)
57
+
58
+ def configure_temporary_datasets(self, device_id: str, temp_datasets_request: TempDatasetsRequest) -> Dict[str, Any]:
59
+ """Enable temporary data reporting to a custom endpoint with a given config for a specified duration."""
60
+ response = self.client.post(f"/api/v1/device/{device_id}/temp-datasets", json=temp_datasets_request.model_dump())
61
+ return self._configure_temporary_datasets(response)
62
+
63
+ def get_organizations(self) -> List[Organization]:
64
+ """Get list of organizations accessible with the provided token."""
65
+ response = self.client.get("/api/v2/organizations")
66
+ return self._get_organizations(response)
67
+
68
+ def request(self, method: str, endpoint: str, **kwargs) -> httpx.Response:
69
+ """Make a raw HTTP request for any other endpoints."""
70
+ return self.client.request(method, endpoint, **kwargs)
71
+
72
+
73
+ class OrbCloudClientAsync(OrbCloudClientBase):
74
+ """Simple async HTTP client for Orb Cloud API."""
75
+
76
+ def __init__(self, base_url: str = "https://panel.orb.net", token: Optional[str] = None):
77
+ super().__init__(base_url, token)
78
+ self.client = httpx.AsyncClient(base_url=self.base_url, headers=self.headers)
79
+
80
+ async def __aenter__(self):
81
+ return self
82
+
83
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
84
+ await self.client.aclose()
85
+
86
+ async def close(self):
87
+ """Close the HTTP client."""
88
+ await self.client.aclose()
89
+
90
+ async def get_organization_devices(self, organization_id: str) -> List[Device]:
91
+ """Get list of devices for the organization."""
92
+ response = await self.client.get(f"/api/v2/organization/{organization_id}/devices")
93
+ return self._get_organization_devices(response)
94
+
95
+ async def configure_temporary_datasets(self, device_id: str, temp_datasets_request: TempDatasetsRequest) -> Dict[str, Any]:
96
+ """Enable temporary data reporting to a custom endpoint with a given config for a specified duration."""
97
+ response = await self.client.post(f"/api/v1/device/{device_id}/temp-datasets", json=temp_datasets_request.model_dump())
98
+ return self._configure_temporary_datasets(response)
99
+
100
+ async def get_organizations(self) -> List[Organization]:
101
+ """Get list of organizations accessible with the provided token."""
102
+ response = await self.client.get("/api/v2/organizations")
103
+ return self._get_organizations(response)
104
+
105
+ async def request(self, method: str, endpoint: str, **kwargs) -> httpx.Response:
106
+ """Make a raw async HTTP request for any other endpoints."""
107
+ return await self.client.request(method, endpoint, **kwargs)
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Example usage of the Orb Cloud API Client.
4
+
5
+ This example demonstrates:
6
+ 1. Getting a list of devices for an organization
7
+ 2. Displaying the devices and letting the user select one
8
+ 3. Asking the user for a URL to push data to
9
+ 4. Configuring temporary datasets for the selected device with data push
10
+ """
11
+
12
+ import os
13
+ import sys
14
+ from typing import Optional, List
15
+
16
+ # Add the client to the path (for running this example directly)
17
+ sys.path.insert(0, os.path.dirname(__file__))
18
+
19
+ from client import OrbCloudClient
20
+ from models import TempDatasetsRequest, Datasets, DataPush, Device
21
+
22
+
23
+ def get_and_display_devices(client: OrbCloudClient, organization_id: str) -> List[Device]:
24
+ """Get and display all devices for the organization."""
25
+ print(f"Getting devices for organization: {organization_id}")
26
+
27
+ try:
28
+ devices = client.get_organization_devices(organization_id)
29
+ print(f"Found {len(devices)} devices:\n")
30
+
31
+ for i, device in enumerate(devices, 1):
32
+ connection_status = "Connected" if device.is_connected.value == 1 else "Disconnected"
33
+ print(f"{i:2}. {device.name}")
34
+ print(f" orb_id: {device.orb_id}")
35
+ print(f" status: {connection_status}")
36
+ print()
37
+
38
+ return devices
39
+
40
+ except Exception as e:
41
+ print(f"Error getting devices: {e}")
42
+ return []
43
+
44
+
45
+ def select_device(devices: List[Device]) -> Optional[Device]:
46
+ """Allow user to select a device from the list."""
47
+ if not devices:
48
+ print("No devices available to select from.")
49
+ return None
50
+
51
+ while True:
52
+ try:
53
+ choice = input(f"Select a device (1-{len(devices)}) or 'q' to quit: ").strip()
54
+
55
+ if choice.lower() == 'q':
56
+ print("Exiting...")
57
+ return None
58
+
59
+ device_index = int(choice) - 1
60
+
61
+ if 0 <= device_index < len(devices):
62
+ selected_device = devices[device_index]
63
+ print(f"Selected: {selected_device.name}")
64
+ return selected_device
65
+ else:
66
+ print(f"Please enter a number between 1 and {len(devices)}")
67
+
68
+ except ValueError:
69
+ print("Please enter a valid number or 'q' to quit")
70
+ except KeyboardInterrupt:
71
+ print("\nExiting...")
72
+ return None
73
+
74
+
75
+ def get_push_url() -> Optional[str]:
76
+ """Get the push URL from the user."""
77
+ while True:
78
+ try:
79
+ url = input("\nEnter the URL to push data to (or 'q' to quit): ").strip()
80
+
81
+ if url.lower() == 'q':
82
+ print("Exiting...")
83
+ return None
84
+
85
+ if not url:
86
+ print("Please enter a valid URL")
87
+ continue
88
+
89
+ # Basic URL validation
90
+ if not (url.startswith('http://') or url.startswith('https://')):
91
+ print("URL should start with http:// or https://")
92
+ continue
93
+
94
+ print(f"Will push data to: {url}")
95
+ return url
96
+
97
+ except KeyboardInterrupt:
98
+ print("\nExiting...")
99
+ return None
100
+
101
+
102
+ def configure_temp_datasets(client: OrbCloudClient, device_id: str, push_url: str) -> bool:
103
+ """Configure temporary datasets for the specified device."""
104
+ print(f"Configuring temporary datasets for device: {device_id}")
105
+
106
+ # Create the temporary datasets configuration
107
+ temp_config = TempDatasetsRequest(
108
+ duration="5m", # Run for 5 minutes
109
+ datasets_config=Datasets(
110
+ enabled=True,
111
+ datasets=["responsiveness_1s", "scores_1s"], # Collect these datasets
112
+ push=DataPush(
113
+ enabled=True,
114
+ url=push_url, # Use the user-provided URL
115
+ datasets=["responsiveness_1s", "scores_1s"],
116
+ format="json",
117
+ interval_ms=500 # Push new data every 500ms (when available)
118
+ )
119
+ )
120
+ )
121
+
122
+ try:
123
+ result = client.configure_temporary_datasets(device_id, temp_config)
124
+ print(f"Temporary datasets configured successfully: {result}")
125
+ return True
126
+
127
+ except Exception as e:
128
+ print(f"Error configuring temporary datasets: {e}")
129
+ return False
130
+
131
+
132
+ def main():
133
+ """Main example function."""
134
+ # Get configuration from environment variables
135
+ api_token = os.getenv("ORB_API_TOKEN")
136
+ organization_id = os.getenv("ORB_ORGANIZATION_ID")
137
+
138
+ if not api_token:
139
+ print("Error: Please set ORB_API_TOKEN environment variable")
140
+ sys.exit(1)
141
+
142
+ if not organization_id:
143
+ print("Error: Please set ORB_ORGANIZATION_ID environment variable")
144
+ sys.exit(1)
145
+
146
+ print("Orb Cloud API Client Example")
147
+ print("=" * 30)
148
+
149
+ # Create the client
150
+ with OrbCloudClient(token=api_token) as client:
151
+ # Step 1: Get and display all devices
152
+ devices = get_and_display_devices(client, organization_id)
153
+
154
+ if not devices:
155
+ print("No devices found. Exiting.")
156
+ sys.exit(1)
157
+
158
+ # Step 2: Let user select a device
159
+ selected_device = select_device(devices)
160
+
161
+ if not selected_device:
162
+ sys.exit(0)
163
+
164
+ # Step 3: Get the push URL from user
165
+ push_url = get_push_url()
166
+
167
+ if not push_url:
168
+ sys.exit(0)
169
+
170
+ # Step 4: Configure temporary datasets for the selected device
171
+ success = configure_temp_datasets(client, selected_device.orb_id, push_url)
172
+
173
+ if success:
174
+ print("\nExample completed successfully!")
175
+ print(f"Device '{selected_device.name}' is now configured to collect temporary datasets.")
176
+ print(f"Data will be pushed to: {push_url}")
177
+ else:
178
+ print("\nExample failed to configure temporary datasets.")
179
+ sys.exit(1)
180
+
181
+
182
+ if __name__ == "__main__":
183
+ main()
@@ -0,0 +1,47 @@
1
+ from client import OrbCloudClientAsync
2
+ import asyncio
3
+ import os
4
+
5
+
6
+ async def main():
7
+ """
8
+ Example of using the OrbCloudClientAsync to fetch organization devices.
9
+
10
+ Requires ORB_CLOUD_TOKEN and ORB_CLOUD_ORGANIZATION_ID environment variables to be set.
11
+ """
12
+ token = os.environ.get("ORB_CLOUD_TOKEN")
13
+
14
+ if not token:
15
+ print("Please set ORB_CLOUD_TOKEN environment variables.")
16
+ return
17
+
18
+ print("Getting organization...")
19
+ async with OrbCloudClientAsync(token=token) as client:
20
+ organizations = await client.get_organizations()
21
+ if not organizations:
22
+ print("No organizations found for this token.")
23
+ return
24
+
25
+ print("Organizations found:")
26
+ for org in organizations:
27
+ print(f" - ID: {org.organization_id}, Name: {org.name}")
28
+
29
+ print(f"Fetching devices for organization: {org.organization_id}")
30
+ try:
31
+ devices = await client.get_organization_devices(org.organization_id)
32
+ if not devices:
33
+ print("No devices found for this organization.")
34
+ return
35
+
36
+ print("Devices found:")
37
+ for device in devices:
38
+ print(f" - ID: {device.orb_id}, Name: {device.name}")
39
+ except Exception as e:
40
+ print(f"An error occurred: {e}")
41
+
42
+
43
+ if __name__ == "__main__":
44
+ asyncio.run(main())
45
+
46
+
47
+
@@ -0,0 +1,245 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: openapi.yaml
3
+ # timestamp: 2025-10-16T14:05:24+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from enum import Enum
8
+ from typing import Dict, List, Optional
9
+
10
+ from pydantic import BaseModel, Field
11
+
12
+
13
+ class IsConnected(Enum):
14
+ integer_0 = 0
15
+ integer_1 = 1
16
+
17
+
18
+ class GeoIPInfo(BaseModel):
19
+ city: Optional[str] = Field(None, examples=['Kirkland'])
20
+ state: Optional[str] = Field(None, examples=['Washington'])
21
+ country: Optional[str] = Field(None, examples=['United States'])
22
+ isp_name: Optional[str] = Field(None, examples=['Comcast Cable'])
23
+ latitude: Optional[float] = Field(None, examples=[47.6784])
24
+ longitude: Optional[float] = Field(None, examples=[-122.1857])
25
+ country_code: Optional[str] = Field(None, examples=['US'])
26
+
27
+
28
+ class DeviceInfo(BaseModel):
29
+ name: Optional[str] = Field(None, examples=['hardy-nca1515'])
30
+ version: Optional[str] = Field(None, examples=['v1.2.3'])
31
+ cpu_count: Optional[int] = Field(None, examples=[4])
32
+ full_name: Optional[str] = Field(
33
+ None, description='device full hostname', examples=['hardy-nca1515']
34
+ )
35
+ operating_system: Optional[str] = Field(None, examples=['linux'])
36
+
37
+
38
+ class NetworkInterface(BaseModel):
39
+ name: Optional[str] = Field(None, examples=[''])
40
+ type: Optional[str] = Field(None, examples=['Ethernet'])
41
+ local_ip: Optional[str] = Field(
42
+ None,
43
+ description='Local IP address of the network interface',
44
+ examples=['192.168.207.172'],
45
+ )
46
+ mac_address: Optional[str] = Field(
47
+ None,
48
+ description='MAC address of the network interface',
49
+ examples=['00:90:0b:a5:de:2a'],
50
+ )
51
+
52
+
53
+ class OrbScore(BaseModel):
54
+ score: Optional[float] = Field(
55
+ None, description='Raw score value', examples=[0.8283297399160232]
56
+ )
57
+ display: Optional[int] = Field(
58
+ None, description='Display score (0-100)', examples=[91]
59
+ )
60
+ included: Optional[bool] = Field(
61
+ None,
62
+ description='Whether there are enough measurements on the subject score that it is conclusive',
63
+ examples=[True],
64
+ )
65
+ value: Optional[float] = Field(
66
+ None,
67
+ description='Raw measurement value (present in leaf components)',
68
+ examples=[38965.44888204175],
69
+ )
70
+ components: Optional[Dict[str, OrbScore]] = Field(
71
+ None, description='Score components breakdown (nested OrbScore objects)'
72
+ )
73
+ duration_ms: Optional[int] = Field(
74
+ None, description='Duration of measurement in milliseconds', examples=[60000]
75
+ )
76
+ score_version: Optional[str] = Field(
77
+ None, description='Version of scoring algorithm', examples=['1.2.0']
78
+ )
79
+
80
+
81
+ class DataPush(BaseModel):
82
+ enabled: bool = Field(
83
+ ..., description='Whether data push is enabled', examples=[True]
84
+ )
85
+ identifiable: Optional[bool] = Field(
86
+ None, description='Whether data should be identifiable', examples=[True]
87
+ )
88
+ datasets: Optional[List[str]] = Field(
89
+ None,
90
+ description='List of datasets to push',
91
+ examples=[['responsiveness_1s', 'speed_results']],
92
+ )
93
+ url: Optional[str] = Field(
94
+ None,
95
+ description='URL to push data to',
96
+ examples=['https://api.example.com/data'],
97
+ )
98
+ format: Optional[str] = Field(
99
+ None, description='Data format for pushing', examples=['json']
100
+ )
101
+ buffer_kb: Optional[int] = Field(
102
+ None, description='Buffer size in KB', examples=[64]
103
+ )
104
+ interval_ms: Optional[int] = Field(
105
+ None, description='Push interval in milliseconds', examples=[5000]
106
+ )
107
+
108
+
109
+ class DataAPI(BaseModel):
110
+ enabled: bool = Field(
111
+ ..., description='Whether data API is enabled', examples=[True]
112
+ )
113
+ identifiable: Optional[bool] = Field(
114
+ None, description='Whether data should be identifiable', examples=[True]
115
+ )
116
+ datasets: Optional[List[str]] = Field(
117
+ None,
118
+ description='List of datasets to expose via API',
119
+ examples=[['responsiveness_1s', 'speed_results']],
120
+ )
121
+ api_key: Optional[str] = Field(
122
+ None,
123
+ description='API key for accessing the data API',
124
+ examples=['your-api-key-here'],
125
+ )
126
+ port: Optional[int] = Field(
127
+ None, description='Port for the data API', examples=[8080]
128
+ )
129
+ buffer: Optional[int] = Field(None, description='Buffer size', examples=[1000])
130
+
131
+
132
+ class Error(BaseModel):
133
+ message: str = Field(
134
+ ..., description='Error message', examples=['Invalid organization ID']
135
+ )
136
+ code: Optional[str] = Field(
137
+ None, description='Error code', examples=['INVALID_ORG_ID']
138
+ )
139
+
140
+
141
+ class AccountConfigEntitlements(BaseModel):
142
+ entitlements: Optional[List[str]] = None
143
+
144
+
145
+ class AccountPlanLimits(BaseModel):
146
+ deployment_tokens: Optional[int] = None
147
+ devices: Optional[int] = None
148
+ users: Optional[int] = None
149
+
150
+
151
+ class SummaryTags(BaseModel):
152
+ geoip: Optional[GeoIPInfo] = None
153
+ device_info: Optional[DeviceInfo] = None
154
+ network_interface: Optional[NetworkInterface] = None
155
+
156
+
157
+ class Datasets(BaseModel):
158
+ enabled: bool = Field(
159
+ ..., description='Whether datasets are enabled', examples=[True]
160
+ )
161
+ datasets: Optional[List[str]] = Field(
162
+ None,
163
+ description='List of datasets to collect',
164
+ examples=[['responsiveness_1s', 'speed_results']],
165
+ )
166
+ cloud_push: Optional[DataPush] = None
167
+ push: Optional[DataPush] = None
168
+ api: Optional[DataAPI] = None
169
+
170
+
171
+ class AccountPlan(BaseModel):
172
+ config: Optional[AccountConfigEntitlements] = None
173
+ features: Optional[List[str]] = None
174
+ limits: Optional[AccountPlanLimits] = None
175
+
176
+
177
+ class DeviceSummary(BaseModel):
178
+ tags: Optional[SummaryTags] = None
179
+ version: Optional[str] = Field(
180
+ None, description='Summary version', examples=['0.1.0']
181
+ )
182
+ orb_score: Optional[OrbScore] = None
183
+ created_ts: Optional[int] = Field(
184
+ None, description='Timestamp when summary was created', examples=[1757391432134]
185
+ )
186
+ orb_scores: Optional[List[OrbScore]] = Field(
187
+ None, description='Array of Orb scores for different time windows'
188
+ )
189
+
190
+
191
+ class TempDatasetsRequest(BaseModel):
192
+ duration: str = Field(
193
+ ..., description='Duration for temporary dataset configuration', examples=['1h']
194
+ )
195
+ datasets_config: Datasets
196
+
197
+
198
+ class Organization(BaseModel):
199
+ customer_id: Optional[str] = None
200
+ name: Optional[str] = None
201
+ organization_id: Optional[str] = None
202
+ plan: Optional[AccountPlan] = None
203
+ subscription_id: Optional[str] = None
204
+
205
+
206
+ class Device(BaseModel):
207
+ orb_id: str = Field(
208
+ ...,
209
+ description='Unique Orb device identifier',
210
+ examples=['cjwbntmuy97ta4rx3mbrjf2gj8j7'],
211
+ )
212
+ name: str = Field(..., description='Device name', examples=['hardy-house'])
213
+ is_connected: IsConnected = Field(
214
+ ...,
215
+ description='Connection status (0 = disconnected, 1 = connected)',
216
+ examples=[1],
217
+ )
218
+ is_connected_updated_at: Optional[int] = Field(
219
+ None,
220
+ description='Timestamp when connection status was last updated',
221
+ examples=[1757368500851],
222
+ )
223
+ can_notify: Optional[bool] = Field(
224
+ None, description='Whether the device can send notifications', examples=[False]
225
+ )
226
+ summary: DeviceSummary
227
+ created_ts: Optional[int] = Field(
228
+ None, description='Timestamp when device was created', examples=[1757368560803]
229
+ )
230
+ tags: Optional[List[str]] = Field(
231
+ None, description='Device tags', examples=[['dt=Default Configuration']]
232
+ )
233
+ config: Optional[Dict[str, List[str]]] = Field(
234
+ None,
235
+ description='Device configuration user overrides (map of strings to arrays of strings)',
236
+ examples=[
237
+ {
238
+ 'datasets.datasets': ['responsiveness_1s', 'speed_results'],
239
+ 'datasets.cloud_push': ['identifiable=true', 'responsiveness_1s'],
240
+ }
241
+ ],
242
+ )
243
+
244
+
245
+ OrbScore.model_rebuild()
@@ -0,0 +1,426 @@
1
+ Metadata-Version: 2.4
2
+ Name: orb-cloud-client
3
+ Version: 1.2.1
4
+ Summary: Python client library for Orb Cloud API
5
+ Author-email: Orb Networks <support@orb.net>
6
+ License: MIT
7
+ Project-URL: Documentation, https://api.orb.net/docs
8
+ Project-URL: Homepage, https://github.com/orbforge/orb-cloud
9
+ Project-URL: Repository, https://github.com/orbforge/orb-cloud
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: build>=1.2.2.post1
22
+ Requires-Dist: httpx>=0.24.0
23
+ Requires-Dist: pydantic>=2.0.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: build; extra == "dev"
26
+ Requires-Dist: pytest; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # Orb Cloud API Client
30
+
31
+ A Python client library for interacting with the Orb Cloud API. This package provides a simple interface to manage devices, configure data collection, and interact with Orb Cloud services.
32
+
33
+ ## Installation
34
+
35
+ Install the package from the wheel file:
36
+
37
+ ```bash
38
+ pip install orb-cloud-client
39
+ ```
40
+
41
+ The package will automatically install its dependencies:
42
+ - `httpx>=0.24.0` (for HTTP requests)
43
+ - `pydantic>=2.0.0` (for data models)
44
+
45
+ ## Quick Start
46
+
47
+ ```python
48
+ from orb_cloud_client import OrbCloudClient
49
+
50
+ # Create client
51
+ client = OrbCloudClient(token="your-api-token")
52
+
53
+ # Get organization devices
54
+ devices = client.get_organization_devices("your-org-id")
55
+ print(f"Found {len(devices)} devices")
56
+
57
+ # Use as context manager (recommended)
58
+ with OrbCloudClient(token="your-api-token") as client:
59
+ devices = client.get_organization_devices("your-org-id")
60
+ ```
61
+
62
+ ## API Reference
63
+
64
+ ### OrbCloudClient
65
+
66
+ #### Constructor
67
+
68
+ ```python
69
+ OrbCloudClient(base_url="https://panel.orb.net", token=None)
70
+ ```
71
+
72
+ **Parameters:**
73
+ - `base_url` (str, optional): API base URL. Defaults to production environment.
74
+ - `token` (str, optional): Bearer authentication token.
75
+
76
+ #### Methods
77
+
78
+ ##### get_organization_devices(organization_id)
79
+
80
+ Get a list of all devices for an organization.
81
+
82
+ ```python
83
+ devices = client.get_organization_devices("org-123")
84
+ ```
85
+
86
+ **Parameters:**
87
+ - `organization_id` (str): The organization ID
88
+
89
+ **Returns:**
90
+ - `List[Device]`: List of Device objects
91
+
92
+ **Example:**
93
+ ```python
94
+ devices = client.get_organization_devices("org-123")
95
+ for device in devices:
96
+ print(f"Device: {device.name} (ID: {device.id})")
97
+ print(f" Status: {'Connected' if device.is_connected else 'Disconnected'}")
98
+ print(f" Location: {device.geo_ip_info.city}, {device.geo_ip_info.state}")
99
+ ```
100
+
101
+ ##### configure_temporary_datasets(device_id, temp_datasets_request)
102
+
103
+ Configure temporary data collection for a device with custom data push endpoint.
104
+
105
+ ```python
106
+ from orb_cloud_client.models import TempDatasetsRequest, Datasets, DataPush
107
+
108
+ temp_config = TempDatasetsRequest(
109
+ duration="5m", # Run for 5 minutes
110
+ datasets_config=Datasets(
111
+ enabled=True,
112
+ datasets=["responsiveness_1s", "scores_1s"], # Collect these datasets
113
+ push=DataPush(
114
+ enabled=True,
115
+ url="https://your-endpoint.com/data", # Your webhook URL
116
+ datasets=["responsiveness_1s", "scores_1s"],
117
+ format="json",
118
+ interval_ms=500 # Push new data every 500ms (when available)
119
+ )
120
+ )
121
+ )
122
+
123
+ result = client.configure_temporary_datasets("device-456", temp_config)
124
+ ```
125
+
126
+ **Parameters:**
127
+ - `device_id` (str): The device ID
128
+ - `temp_datasets_request` (TempDatasetsRequest): Configuration object
129
+
130
+ **Returns:**
131
+ - `Dict[str, Any]`: API response
132
+
133
+ ##### request(method, endpoint, **kwargs)
134
+
135
+ Make a custom HTTP request to any API endpoint.
136
+
137
+ ```python
138
+ response = client.request("GET", "/api/v2/custom-endpoint")
139
+ data = response.json()
140
+ ```
141
+
142
+ **Parameters:**
143
+ - `method` (str): HTTP method (GET, POST, PUT, DELETE, etc.)
144
+ - `endpoint` (str): API endpoint path
145
+ - `**kwargs`: Additional arguments passed to httpx
146
+
147
+ **Returns:**
148
+ - `httpx.Response`: Raw HTTP response
149
+
150
+ ##### close()
151
+
152
+ Close the HTTP client connection.
153
+
154
+ ```python
155
+ client.close()
156
+ ```
157
+
158
+ ### Data Models
159
+
160
+ All API models are Pydantic models with full type validation and serialization.
161
+
162
+ #### Device
163
+
164
+ Represents an Orb device with all its properties.
165
+
166
+ ```python
167
+ from orb_cloud_client.models import Device
168
+
169
+ # Device properties
170
+ device.id # str: Device UUID
171
+ device.name # str: Device name
172
+ device.is_connected # IsConnected: Connection status (0 or 1)
173
+ device.last_seen # Optional[str]: Last seen timestamp
174
+ device.geo_ip_info # Optional[GeoIPInfo]: Geographic information
175
+ device.device_info # Optional[DeviceInfo]: Hardware/software info
176
+ device.network_interfaces # Optional[List[NetworkInterface]]: Network info
177
+ device.orb_score # Optional[OrbScore]: Performance metrics
178
+ device.summary # Optional[DeviceSummary]: Device summary
179
+ ```
180
+
181
+ #### TempDatasetsRequest
182
+
183
+ Configuration for temporary data collection.
184
+
185
+ ```python
186
+ from orb_cloud_client.models import TempDatasetsRequest, Datasets, DataPush
187
+
188
+ temp_request = TempDatasetsRequest(
189
+ duration="5m", # Duration (e.g., "5m", "1h", "30m", "2h")
190
+ datasets_config=Datasets(
191
+ enabled=True,
192
+ datasets=["responsiveness_1s", "scores_1s"], # Dataset types to collect
193
+ push=DataPush( # Data push configuration
194
+ enabled=True,
195
+ url="https://your-server.com/webhook",
196
+ datasets=["responsiveness_1s", "scores_1s"],
197
+ format="json",
198
+ interval_ms=500 # Push interval in milliseconds
199
+ )
200
+ )
201
+ )
202
+ ```
203
+
204
+ #### Datasets
205
+
206
+ Data collection configuration with push settings.
207
+
208
+ ```python
209
+ datasets = Datasets(
210
+ enabled=True, # bool: Enable data collection
211
+ datasets=[ # List[str]: Dataset types to collect
212
+ "responsiveness_1s",
213
+ "scores_1s",
214
+ "latency_1s",
215
+ "throughput_1s"
216
+ ],
217
+ push=DataPush( # Optional: Push configuration
218
+ enabled=True,
219
+ url="https://api.example.com/webhook",
220
+ datasets=["responsiveness_1s", "scores_1s"],
221
+ format="json", # Data format
222
+ interval_ms=500 # Push interval in milliseconds
223
+ )
224
+ )
225
+ ```
226
+
227
+ #### DataPush
228
+
229
+ Configuration for pushing data to external endpoints.
230
+
231
+ ```python
232
+ data_push = DataPush(
233
+ enabled=True, # bool: Enable data pushing
234
+ url="https://api.example.com/webhook", # str: Webhook URL
235
+ datasets=["responsiveness_1s", "scores_1s"], # List[str]: Datasets to push
236
+ format="json", # str: Data format
237
+ interval_ms=500 # int: Push interval in milliseconds
238
+ )
239
+ ```
240
+
241
+ #### GeoIPInfo
242
+
243
+ Geographic information from device IP address.
244
+
245
+ ```python
246
+ geo_info.city # Optional[str]: City name
247
+ geo_info.state # Optional[str]: State/region
248
+ geo_info.country # Optional[str]: Country name
249
+ geo_info.country_code # Optional[str]: Country code (e.g., "US")
250
+ geo_info.isp_name # Optional[str]: Internet service provider
251
+ geo_info.latitude # Optional[float]: Latitude coordinate
252
+ geo_info.longitude # Optional[float]: Longitude coordinate
253
+ ```
254
+
255
+ #### DeviceInfo
256
+
257
+ Device hardware and software information.
258
+
259
+ ```python
260
+ device_info.name # Optional[str]: Device hostname
261
+ device_info.version # Optional[str]: Software version
262
+ device_info.cpu_count # Optional[int]: Number of CPU cores
263
+ device_info.full_name # Optional[str]: Full hostname
264
+ device_info.operating_system # Optional[str]: OS name
265
+ ```
266
+
267
+ #### NetworkInterface
268
+
269
+ Network interface information.
270
+
271
+ ```python
272
+ interface.name # Optional[str]: Interface name
273
+ interface.type # Optional[str]: Interface type (e.g., "Ethernet")
274
+ interface.local_ip # Optional[str]: Local IP address
275
+ interface.mac_address # Optional[str]: MAC address
276
+ ```
277
+
278
+ ## Usage Examples
279
+
280
+ ### Basic Device Management
281
+
282
+ ```python
283
+ from orb_cloud_client import OrbCloudClient
284
+
285
+ with OrbCloudClient(token="your-token") as client:
286
+ # Get all devices
287
+ devices = client.get_organization_devices("org-123")
288
+
289
+ # Filter connected devices
290
+ connected_devices = [d for d in devices if d.is_connected == 1]
291
+
292
+ # Find specific device
293
+ target_device = next((d for d in devices if "hardy" in d.name.lower()), None)
294
+ if target_device:
295
+ print(f"Found device: {target_device.name}")
296
+ ```
297
+
298
+ ### Configure Data Collection
299
+
300
+ ```python
301
+ from orb_cloud_client import OrbCloudClient
302
+ from orb_cloud_client.models import TempDatasetsRequest, Datasets, DataPush
303
+
304
+ with OrbCloudClient(token="your-token") as client:
305
+ # Configure temporary datasets with custom endpoint
306
+ config = TempDatasetsRequest(
307
+ duration="5m", # Run for 5 minutes
308
+ datasets_config=Datasets(
309
+ enabled=True,
310
+ datasets=["responsiveness_1s", "scores_1s"],
311
+ push=DataPush(
312
+ enabled=True,
313
+ url="https://your-analytics.com/orb-data",
314
+ datasets=["responsiveness_1s", "scores_1s"],
315
+ format="json",
316
+ interval_ms=500 # Push every 500ms when data available
317
+ )
318
+ )
319
+ )
320
+
321
+ result = client.configure_temporary_datasets("device-456", config)
322
+ print("Configuration successful:", result)
323
+ ```
324
+
325
+ ### Error Handling
326
+
327
+ ```python
328
+ from orb_cloud_client import OrbCloudClient
329
+ import httpx
330
+
331
+ with OrbCloudClient(token="your-token") as client:
332
+ try:
333
+ devices = client.get_organization_devices("org-123")
334
+ except httpx.HTTPStatusError as e:
335
+ print(f"HTTP error {e.response.status_code}: {e.response.text}")
336
+ except httpx.RequestError as e:
337
+ print(f"Request error: {e}")
338
+ ```
339
+
340
+ ### Working with Device Data
341
+
342
+ ```python
343
+ from orb_cloud_client import OrbCloudClient
344
+
345
+ with OrbCloudClient(token="your-token") as client:
346
+ devices = client.get_organization_devices("org-123")
347
+
348
+ for device in devices:
349
+ print(f"\n--- {device.name} ---")
350
+ print(f"ID: {device.id}")
351
+ print(f"Connected: {'Yes' if device.is_connected else 'No'}")
352
+
353
+ if device.geo_ip_info:
354
+ print(f"Location: {device.geo_ip_info.city}, {device.geo_ip_info.state}")
355
+ print(f"ISP: {device.geo_ip_info.isp_name}")
356
+
357
+ if device.device_info:
358
+ print(f"OS: {device.device_info.operating_system}")
359
+ print(f"Version: {device.device_info.version}")
360
+
361
+ if device.network_interfaces:
362
+ for interface in device.network_interfaces:
363
+ print(f"Interface: {interface.name} ({interface.local_ip})")
364
+ ```
365
+
366
+ ## Interactive Example
367
+
368
+ The package includes an interactive example that demonstrates the complete workflow:
369
+
370
+ ```bash
371
+ export ORB_API_TOKEN="your-api-token"
372
+ export ORB_ORGANIZATION_ID="your-org-id"
373
+ python3 -m orb_cloud_client.example
374
+ ```
375
+
376
+ The example will:
377
+ 1. Fetch and display all devices
378
+ 2. Configure temporary datasets for the selected device
379
+
380
+
381
+ See `example.py` for example code.
382
+
383
+
384
+ ## Available Dataset Types
385
+
386
+ Common dataset types you can collect:
387
+ - `responsiveness_{timeframe}` - time-binned responsiveness (example `responiveness_1s`, `responsiveness_15s`)
388
+ - `scores_{timeframe}` - time-binned scores (example `scores_1s`, `scores_1m`)
389
+ - `speed_results` - Result-level speed measurements
390
+ - `web_responsiveness_results` - Result-level DNS resolve and TTFB measurements
391
+
392
+ ## Authentication
393
+
394
+ The client uses Bearer token authentication. Get your API token from the Orb Cloud dashboard and pass it to the client constructor:
395
+
396
+ ```python
397
+ client = OrbCloudClient(token="your-bearer-token")
398
+ ```
399
+
400
+ ## Environment Support
401
+
402
+ - **Staging**: `https://api.staging.orb.net` (default)
403
+ - **Production**: `https://api.orb.net`
404
+
405
+ ```python
406
+ # Production client
407
+ client = OrbCloudClient(
408
+ base_url="https://api.orb.net",
409
+ token="your-token"
410
+ )
411
+ ```
412
+
413
+ ## Requirements
414
+
415
+ - Python 3.8+
416
+ - httpx >= 0.24.0
417
+ - pydantic >= 2.0.0
418
+
419
+ ## Error Handling
420
+
421
+ The client raises standard httpx exceptions:
422
+ - `httpx.HTTPStatusError` - For HTTP error responses (4xx, 5xx)
423
+ - `httpx.RequestError` - For network/connection errors
424
+ - `httpx.TimeoutException` - For request timeouts
425
+
426
+ Always wrap API calls in try-catch blocks for production use.
@@ -0,0 +1,11 @@
1
+ orb_cloud_client/__init__.py,sha256=zLbPCBXomXt5df4vyQVNVVRyRcM9ic4Fv93Vc9dOxN4,382
2
+ orb_cloud_client/client.py,sha256=z2LWiKEakOP-kC4ElXmSPs-VVoWmPSz8WMVAVnWSIn8,4781
3
+ orb_cloud_client/example.py,sha256=2izr6hF7gJyV_Gjqa3v0j9rkDwCjCYJsLXRgJTI_pIU,6046
4
+ orb_cloud_client/example_async.py,sha256=OZb55zsYL_-Lo2TEgbBLVW1rdKldeWOmWI_HwPV1LMg,1427
5
+ orb_cloud_client/models.py,sha256=5TTJBKunvLnvGBNAGmUFXepTi6K44AFzrSOXpDcoQKg,7955
6
+ orb_cloud_client-1.2.1.dist-info/licenses/LICENSE,sha256=eUe4cBliuPkBMknHKwcgVbFlu53hoolIo-YrsynYrug,1069
7
+ orb_cloud_client-1.2.1.dist-info/METADATA,sha256=uHkzxpK5pq-KF-mNyBqJjNNTcAI-btjpnoQdZ1omc1s,12482
8
+ orb_cloud_client-1.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ orb_cloud_client-1.2.1.dist-info/entry_points.txt,sha256=3i38Vp8ALWRx-BCL66C8m5BXCIF3vAJq8VMpOu_wT8Q,62
10
+ orb_cloud_client-1.2.1.dist-info/top_level.txt,sha256=WMPN64HVd0BGfTV4BnMef1lroq7nUv_ZVf8wjYH8DdE,17
11
+ orb_cloud_client-1.2.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ orb-example = orb_cloud_client.example:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Orb Networks
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ orb_cloud_client