koios-client 1.0.0__tar.gz

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.
Files changed (163) hide show
  1. koios_client-1.0.0/PKG-INFO +608 -0
  2. koios_client-1.0.0/README.md +578 -0
  3. koios_client-1.0.0/pyproject.toml +138 -0
  4. koios_client-1.0.0/setup.cfg +4 -0
  5. koios_client-1.0.0/src/koios_client/__init__.py +85 -0
  6. koios_client-1.0.0/src/koios_client/_auth.py +229 -0
  7. koios_client-1.0.0/src/koios_client/_generated/__init__.py +1447 -0
  8. koios_client-1.0.0/src/koios_client/_generated/acknowledge_all_events.py +40 -0
  9. koios_client-1.0.0/src/koios_client/_generated/acknowledge_event.py +40 -0
  10. koios_client-1.0.0/src/koios_client/_generated/acknowledge_events.py +40 -0
  11. koios_client-1.0.0/src/koios_client/_generated/base_client.py +211 -0
  12. koios_client-1.0.0/src/koios_client/_generated/base_model.py +30 -0
  13. koios_client-1.0.0/src/koios_client/_generated/client.py +4367 -0
  14. koios_client-1.0.0/src/koios_client/_generated/create_component_environment.py +45 -0
  15. koios_client-1.0.0/src/koios_client/_generated/create_component_instance.py +41 -0
  16. koios_client-1.0.0/src/koios_client/_generated/create_device.py +39 -0
  17. koios_client-1.0.0/src/koios_client/_generated/create_device_set.py +39 -0
  18. koios_client-1.0.0/src/koios_client/_generated/create_device_set_item.py +38 -0
  19. koios_client-1.0.0/src/koios_client/_generated/create_endpoint_client.py +41 -0
  20. koios_client-1.0.0/src/koios_client/_generated/create_local_value.py +39 -0
  21. koios_client-1.0.0/src/koios_client/_generated/create_model.py +39 -0
  22. koios_client-1.0.0/src/koios_client/_generated/create_scan_group.py +39 -0
  23. koios_client-1.0.0/src/koios_client/_generated/create_tag.py +39 -0
  24. koios_client-1.0.0/src/koios_client/_generated/create_trend.py +38 -0
  25. koios_client-1.0.0/src/koios_client/_generated/create_trend_item.py +38 -0
  26. koios_client-1.0.0/src/koios_client/_generated/create_wire.py +37 -0
  27. koios_client-1.0.0/src/koios_client/_generated/delete_component_environment.py +45 -0
  28. koios_client-1.0.0/src/koios_client/_generated/delete_component_instance.py +41 -0
  29. koios_client-1.0.0/src/koios_client/_generated/delete_component_library.py +43 -0
  30. koios_client-1.0.0/src/koios_client/_generated/delete_device.py +38 -0
  31. koios_client-1.0.0/src/koios_client/_generated/delete_device_set.py +39 -0
  32. koios_client-1.0.0/src/koios_client/_generated/delete_device_set_item.py +37 -0
  33. koios_client-1.0.0/src/koios_client/_generated/delete_device_set_items.py +19 -0
  34. koios_client-1.0.0/src/koios_client/_generated/delete_device_sets.py +19 -0
  35. koios_client-1.0.0/src/koios_client/_generated/delete_devices.py +17 -0
  36. koios_client-1.0.0/src/koios_client/_generated/delete_endpoint_client.py +38 -0
  37. koios_client-1.0.0/src/koios_client/_generated/delete_events.py +17 -0
  38. koios_client-1.0.0/src/koios_client/_generated/delete_local_value.py +39 -0
  39. koios_client-1.0.0/src/koios_client/_generated/delete_local_values.py +19 -0
  40. koios_client-1.0.0/src/koios_client/_generated/delete_model.py +38 -0
  41. koios_client-1.0.0/src/koios_client/_generated/delete_models.py +17 -0
  42. koios_client-1.0.0/src/koios_client/_generated/delete_scan_group.py +39 -0
  43. koios_client-1.0.0/src/koios_client/_generated/delete_scan_groups.py +19 -0
  44. koios_client-1.0.0/src/koios_client/_generated/delete_tag.py +38 -0
  45. koios_client-1.0.0/src/koios_client/_generated/delete_tags.py +17 -0
  46. koios_client-1.0.0/src/koios_client/_generated/delete_trend.py +38 -0
  47. koios_client-1.0.0/src/koios_client/_generated/delete_trend_item.py +38 -0
  48. koios_client-1.0.0/src/koios_client/_generated/delete_wire.py +37 -0
  49. koios_client-1.0.0/src/koios_client/_generated/duplicate_device.py +39 -0
  50. koios_client-1.0.0/src/koios_client/_generated/duplicate_model.py +39 -0
  51. koios_client-1.0.0/src/koios_client/_generated/duplicate_tag.py +38 -0
  52. koios_client-1.0.0/src/koios_client/_generated/duplicate_trend.py +39 -0
  53. koios_client-1.0.0/src/koios_client/_generated/enums.py +528 -0
  54. koios_client-1.0.0/src/koios_client/_generated/exceptions.py +85 -0
  55. koios_client-1.0.0/src/koios_client/_generated/get_bindings.py +99 -0
  56. koios_client-1.0.0/src/koios_client/_generated/get_component_definition.py +44 -0
  57. koios_client-1.0.0/src/koios_client/_generated/get_component_definitions.py +42 -0
  58. koios_client-1.0.0/src/koios_client/_generated/get_component_environment.py +71 -0
  59. koios_client-1.0.0/src/koios_client/_generated/get_component_environment_live.py +30 -0
  60. koios_client-1.0.0/src/koios_client/_generated/get_component_environments.py +40 -0
  61. koios_client-1.0.0/src/koios_client/_generated/get_component_environments_live.py +37 -0
  62. koios_client-1.0.0/src/koios_client/_generated/get_component_instance.py +55 -0
  63. koios_client-1.0.0/src/koios_client/_generated/get_component_instances.py +50 -0
  64. koios_client-1.0.0/src/koios_client/_generated/get_component_libraries.py +38 -0
  65. koios_client-1.0.0/src/koios_client/_generated/get_component_library.py +73 -0
  66. koios_client-1.0.0/src/koios_client/_generated/get_component_library_groups.py +36 -0
  67. koios_client-1.0.0/src/koios_client/_generated/get_device.py +116 -0
  68. koios_client-1.0.0/src/koios_client/_generated/get_device_live.py +32 -0
  69. koios_client-1.0.0/src/koios_client/_generated/get_device_set.py +39 -0
  70. koios_client-1.0.0/src/koios_client/_generated/get_device_sets.py +45 -0
  71. koios_client-1.0.0/src/koios_client/_generated/get_devices.py +55 -0
  72. koios_client-1.0.0/src/koios_client/_generated/get_devices_live.py +37 -0
  73. koios_client-1.0.0/src/koios_client/_generated/get_endpoint_client.py +50 -0
  74. koios_client-1.0.0/src/koios_client/_generated/get_endpoint_clients.py +51 -0
  75. koios_client-1.0.0/src/koios_client/_generated/get_event_count_summary.py +20 -0
  76. koios_client-1.0.0/src/koios_client/_generated/get_events.py +61 -0
  77. koios_client-1.0.0/src/koios_client/_generated/get_failed_counts.py +19 -0
  78. koios_client-1.0.0/src/koios_client/_generated/get_license_detail.py +28 -0
  79. koios_client-1.0.0/src/koios_client/_generated/get_local_values.py +45 -0
  80. koios_client-1.0.0/src/koios_client/_generated/get_model.py +124 -0
  81. koios_client-1.0.0/src/koios_client/_generated/get_model_files.py +46 -0
  82. koios_client-1.0.0/src/koios_client/_generated/get_model_live.py +59 -0
  83. koios_client-1.0.0/src/koios_client/_generated/get_models.py +70 -0
  84. koios_client-1.0.0/src/koios_client/_generated/get_models_live.py +64 -0
  85. koios_client-1.0.0/src/koios_client/_generated/get_platform_version.py +31 -0
  86. koios_client-1.0.0/src/koios_client/_generated/get_protocol.py +34 -0
  87. koios_client-1.0.0/src/koios_client/_generated/get_protocols.py +34 -0
  88. koios_client-1.0.0/src/koios_client/_generated/get_scan_group.py +53 -0
  89. koios_client-1.0.0/src/koios_client/_generated/get_scan_group_live.py +33 -0
  90. koios_client-1.0.0/src/koios_client/_generated/get_scan_groups.py +40 -0
  91. koios_client-1.0.0/src/koios_client/_generated/get_scan_groups_live.py +38 -0
  92. koios_client-1.0.0/src/koios_client/_generated/get_services.py +33 -0
  93. koios_client-1.0.0/src/koios_client/_generated/get_system_health.py +17 -0
  94. koios_client-1.0.0/src/koios_client/_generated/get_system_info.py +35 -0
  95. koios_client-1.0.0/src/koios_client/_generated/get_tag.py +139 -0
  96. koios_client-1.0.0/src/koios_client/_generated/get_tag_history.py +29 -0
  97. koios_client-1.0.0/src/koios_client/_generated/get_tag_live.py +39 -0
  98. koios_client-1.0.0/src/koios_client/_generated/get_tags.py +78 -0
  99. koios_client-1.0.0/src/koios_client/_generated/get_tags_history.py +32 -0
  100. koios_client-1.0.0/src/koios_client/_generated/get_tags_live.py +44 -0
  101. koios_client-1.0.0/src/koios_client/_generated/get_trend.py +72 -0
  102. koios_client-1.0.0/src/koios_client/_generated/get_trend_items.py +58 -0
  103. koios_client-1.0.0/src/koios_client/_generated/get_trends.py +32 -0
  104. koios_client-1.0.0/src/koios_client/_generated/get_wires.py +74 -0
  105. koios_client-1.0.0/src/koios_client/_generated/input_types.py +1930 -0
  106. koios_client-1.0.0/src/koios_client/_generated/mark_all_events_seen.py +39 -0
  107. koios_client-1.0.0/src/koios_client/_generated/mark_event_seen.py +39 -0
  108. koios_client-1.0.0/src/koios_client/_generated/obtain_client_token.py +40 -0
  109. koios_client-1.0.0/src/koios_client/_generated/refresh_token.py +40 -0
  110. koios_client-1.0.0/src/koios_client/_generated/regenerate_client_secret.py +41 -0
  111. koios_client-1.0.0/src/koios_client/_generated/set_device_set_active_device.py +43 -0
  112. koios_client-1.0.0/src/koios_client/_generated/set_endpoint_client_permissions.py +56 -0
  113. koios_client-1.0.0/src/koios_client/_generated/set_protocol_visible.py +39 -0
  114. koios_client-1.0.0/src/koios_client/_generated/update_binding.py +46 -0
  115. koios_client-1.0.0/src/koios_client/_generated/update_component_environment.py +45 -0
  116. koios_client-1.0.0/src/koios_client/_generated/update_component_instance.py +41 -0
  117. koios_client-1.0.0/src/koios_client/_generated/update_device.py +39 -0
  118. koios_client-1.0.0/src/koios_client/_generated/update_device_set.py +39 -0
  119. koios_client-1.0.0/src/koios_client/_generated/update_device_set_item.py +38 -0
  120. koios_client-1.0.0/src/koios_client/_generated/update_device_sets.py +21 -0
  121. koios_client-1.0.0/src/koios_client/_generated/update_devices.py +20 -0
  122. koios_client-1.0.0/src/koios_client/_generated/update_endpoint_client.py +40 -0
  123. koios_client-1.0.0/src/koios_client/_generated/update_local_value.py +39 -0
  124. koios_client-1.0.0/src/koios_client/_generated/update_local_values.py +21 -0
  125. koios_client-1.0.0/src/koios_client/_generated/update_model.py +39 -0
  126. koios_client-1.0.0/src/koios_client/_generated/update_models.py +20 -0
  127. koios_client-1.0.0/src/koios_client/_generated/update_scan_group.py +39 -0
  128. koios_client-1.0.0/src/koios_client/_generated/update_scan_groups.py +22 -0
  129. koios_client-1.0.0/src/koios_client/_generated/update_tag.py +39 -0
  130. koios_client-1.0.0/src/koios_client/_generated/update_tags.py +20 -0
  131. koios_client-1.0.0/src/koios_client/_generated/update_trend.py +38 -0
  132. koios_client-1.0.0/src/koios_client/_generated/update_trend_item.py +38 -0
  133. koios_client-1.0.0/src/koios_client/_generated/update_wire.py +37 -0
  134. koios_client-1.0.0/src/koios_client/_generated/verify_token.py +10 -0
  135. koios_client-1.0.0/src/koios_client/client.py +1112 -0
  136. koios_client-1.0.0/src/koios_client/exceptions.py +83 -0
  137. koios_client-1.0.0/src/koios_client/py.typed +0 -0
  138. koios_client-1.0.0/src/koios_client/resources/__init__.py +33 -0
  139. koios_client-1.0.0/src/koios_client/resources/_base.py +93 -0
  140. koios_client-1.0.0/src/koios_client/resources/_helpers.py +82 -0
  141. koios_client-1.0.0/src/koios_client/resources/bindings.py +44 -0
  142. koios_client-1.0.0/src/koios_client/resources/bindings.pyi +33 -0
  143. koios_client-1.0.0/src/koios_client/resources/device_sets.py +104 -0
  144. koios_client-1.0.0/src/koios_client/resources/device_sets.pyi +25 -0
  145. koios_client-1.0.0/src/koios_client/resources/devices.py +167 -0
  146. koios_client-1.0.0/src/koios_client/resources/devices.pyi +52 -0
  147. koios_client-1.0.0/src/koios_client/resources/models.py +191 -0
  148. koios_client-1.0.0/src/koios_client/resources/models.pyi +71 -0
  149. koios_client-1.0.0/src/koios_client/resources/protocols.py +54 -0
  150. koios_client-1.0.0/src/koios_client/resources/protocols.pyi +27 -0
  151. koios_client-1.0.0/src/koios_client/resources/scan_groups.py +112 -0
  152. koios_client-1.0.0/src/koios_client/resources/scan_groups.pyi +36 -0
  153. koios_client-1.0.0/src/koios_client/resources/tags.py +134 -0
  154. koios_client-1.0.0/src/koios_client/resources/tags.pyi +47 -0
  155. koios_client-1.0.0/src/koios_client/types.py +265 -0
  156. koios_client-1.0.0/src/koios_client.egg-info/PKG-INFO +608 -0
  157. koios_client-1.0.0/src/koios_client.egg-info/SOURCES.txt +161 -0
  158. koios_client-1.0.0/src/koios_client.egg-info/dependency_links.txt +1 -0
  159. koios_client-1.0.0/src/koios_client.egg-info/requires.txt +10 -0
  160. koios_client-1.0.0/src/koios_client.egg-info/top_level.txt +1 -0
  161. koios_client-1.0.0/tests/test_auth.py +205 -0
  162. koios_client-1.0.0/tests/test_client.py +473 -0
  163. koios_client-1.0.0/tests/test_resources.py +850 -0
@@ -0,0 +1,608 @@
1
+ Metadata-Version: 2.4
2
+ Name: koios-client
3
+ Version: 1.0.0
4
+ Summary: Python client library for the Koios IoT platform — typed GraphQL & REST wrapper
5
+ Author: Ai-OPs, Inc.
6
+ License: Proprietary
7
+ Project-URL: Homepage, https://www.ai-op.com
8
+ Project-URL: Documentation, https://docs.ai-op.com
9
+ Project-URL: Repository, https://github.com/Ai-Ops-Inc/koios-client
10
+ Keywords: koios,api,graphql,client,iot,industrial
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: httpx==0.28.1
22
+ Requires-Dist: pydantic<3.0.0,>=2.0.0
23
+ Requires-Dist: python-dateutil==2.9.0.post0
24
+ Provides-Extra: dev
25
+ Requires-Dist: ariadne-codegen>=0.14.0; extra == "dev"
26
+ Requires-Dist: basedpyright>=1.29.0; extra == "dev"
27
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
28
+ Requires-Dist: pytest-httpx>=0.30.0; extra == "dev"
29
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
30
+
31
+ # Koios Client
32
+
33
+ Typed Python SDK for the [Koios IoT platform](https://ai-op.com). Provides two API layers:
34
+
35
+ - **High-level resource API** — `Device`, `Tag`, `Model` objects with intuitive methods like `.enable()`, `.update()`, `.delete()`
36
+ - **Low-level GraphQL client** — fully-typed generated client via `client.gql` with IDE autocomplete
37
+
38
+ Plus REST helpers for file operations, import/export, backups, and trend data.
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ pip install koios-client
44
+ ```
45
+
46
+ Or install from source:
47
+
48
+ ```bash
49
+ pip install -e ".[dev]"
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ```python
55
+ from koios_client import KoiosClient
56
+
57
+ client = KoiosClient(
58
+ hostname="koios.example.com",
59
+ client_id="your-client-uuid",
60
+ client_secret="your-client-secret",
61
+ )
62
+
63
+ # High-level resource API — fetch by ID (default) or slug
64
+ device = client.device(1)
65
+ device.enable()
66
+ for tag in device.tags():
67
+ print(tag.name, tag.value)
68
+
69
+ # Low-level GraphQL (fully typed — IDE autocomplete works)
70
+ result = client.gql.get_devices()
71
+ for device in result.devices.results:
72
+ print(device.name, device.status)
73
+
74
+ client.close()
75
+ ```
76
+
77
+ Use as a context manager for automatic cleanup:
78
+
79
+ ```python
80
+ with KoiosClient("koios.example.com", "client-id", "secret") as client:
81
+ device = client.device(1)
82
+ print(device.name, device.enabled)
83
+ ```
84
+
85
+ ## High-Level Resource API
86
+
87
+ The resource layer wraps the generated GraphQL client with intuitive Python objects. All resource classes are importable from the top-level package:
88
+
89
+ ```python
90
+ from koios_client import Device, Tag, Model, PaginatedList
91
+ ```
92
+
93
+ ### Fetching Resources
94
+
95
+ Every resource can be fetched by **ID** (default positional arg) or **slug** (UUID).
96
+
97
+ ```python
98
+ # By ID (default — integer database primary key)
99
+ device = client.device(1)
100
+ tag = client.tag(42)
101
+ model = client.model(5)
102
+
103
+ # By slug (UUID — useful when you already have the identifier)
104
+ device = client.device(slug="550e8400-e29b-41d4-a716-446655440000")
105
+
106
+ # By name — use the list query with filters
107
+ from koios_client.types import DeviceFilter, StrFilterLookup
108
+ devices = client.devices(filters=DeviceFilter(name=StrFilterLookup(exact="My Device")))
109
+ device = devices[0]
110
+
111
+ # List with filtering, ordering, and pagination
112
+ from koios_client.types import OffsetPaginationInput
113
+
114
+ devices = client.devices(
115
+ filters=DeviceFilter(enabled=True),
116
+ pagination=OffsetPaginationInput(limit=50),
117
+ )
118
+ print(f"{devices.total_count} total devices")
119
+ for device in devices:
120
+ print(device.name, device.slug, device.enabled)
121
+ ```
122
+
123
+ `PaginatedList` supports iteration, indexing, `len()`, and `bool()`:
124
+
125
+ ```python
126
+ tags = client.tags()
127
+ print(len(tags)) # Number of items on this page
128
+ print(tags.total_count) # Total matching items on server
129
+ first = tags[0] # Index access
130
+ ```
131
+
132
+ ### CRUD Operations
133
+
134
+ ```python
135
+ from koios_client.types import DeviceInput, OneToManyInput
136
+
137
+ # Create — `protocol` takes a OneToManyInput pointing at the protocol's ID
138
+ device = client.create_device(DeviceInput(
139
+ name="New Device",
140
+ protocol=OneToManyInput(set="1"),
141
+ scan_rate=5.0,
142
+ ))
143
+
144
+ # Update (returns a fresh object with all fields refreshed)
145
+ device = device.update(name="Renamed Device", scan_rate=5.0, enabled=True)
146
+
147
+ # Enable / Disable (convenience wrappers around update)
148
+ device = device.enable()
149
+ device = device.disable()
150
+
151
+ # Duplicate
152
+ copy = device.duplicate("Device Copy", description="Cloned from original")
153
+
154
+ # Delete
155
+ device.delete()
156
+ ```
157
+
158
+ ### Parent-Child Accessors
159
+
160
+ ```python
161
+ # Device → Tags
162
+ device = client.device(1)
163
+ tags = device.tags(
164
+ filters=TagFilter(enabled=True),
165
+ pagination=OffsetPaginationInput(limit=100),
166
+ )
167
+
168
+ # Model → Bindings
169
+ model = client.model(5)
170
+ for binding in model.bindings():
171
+ print(binding.name, binding.usage, binding.normalization_type)
172
+ binding.update(normalization_type="MIN_MAX")
173
+ ```
174
+
175
+ ### Bulk Operations
176
+
177
+ ```python
178
+ # Bulk enable / disable
179
+ client.enable_devices(["1", "2", "3"])
180
+ client.disable_tags(["10", "11", "12"])
181
+
182
+ # Bulk delete
183
+ client.delete_devices(["1", "2", "3"])
184
+ client.delete_tags(["10", "11", "12"])
185
+ client.delete_models(["20", "21"])
186
+ client.delete_scan_groups(["30", "31"])
187
+ client.delete_device_sets(["40", "41"])
188
+ ```
189
+
190
+ ### Live Data
191
+
192
+ Fetch real-time values from the Redis cache:
193
+
194
+ ```python
195
+ device = client.device(1)
196
+ live = device.live()
197
+ print(live.get("status"), live.get("error_message"))
198
+
199
+ tag = client.tag(42)
200
+ live = tag.live()
201
+ print(live.get("value"), live.get("timestamp"), live.get("quality"))
202
+ ```
203
+
204
+ ### Import / Export with Resources
205
+
206
+ ```python
207
+ # Import with dry_run (default) — returns preview without applying
208
+ preview = client.import_devices("devices.csv")
209
+ print(preview)
210
+
211
+ # Import for real
212
+ result = client.import_devices("devices.csv", dry_run=False)
213
+
214
+ # Export a single device/model
215
+ csv_bytes = device.export_csv()
216
+ zip_bytes = model.export()
217
+ ```
218
+
219
+ ### All Resource Types
220
+
221
+ All singular methods accept `id` (positional, integer) or `slug=` (UUID). Exactly one must be provided.
222
+
223
+ | Method | Returns | Notes |
224
+ |--------|---------|-------|
225
+ | `client.device(id, slug=)` | `Device` | |
226
+ | `client.devices(...)` | `PaginatedList[Device]` | filters, order, pagination |
227
+ | `client.create_device(data)` | `Device` | |
228
+ | `client.enable_devices(ids)` | `list[Device]` | |
229
+ | `client.disable_devices(ids)` | `list[Device]` | |
230
+ | `client.delete_devices(ids)` | `None` | |
231
+ | `client.import_devices(file)` | `dict` | dry_run=True by default |
232
+ | `client.tag(id, slug=)` | `Tag` | |
233
+ | `client.tags(...)` | `PaginatedList[Tag]` | |
234
+ | `client.create_tag(data)` | `Tag` | |
235
+ | `client.enable_tags(ids)` | `list[Tag]` | |
236
+ | `client.disable_tags(ids)` | `list[Tag]` | |
237
+ | `client.delete_tags(ids)` | `None` | |
238
+ | `client.import_tags(file)` | `dict` | dry_run=True by default |
239
+ | `client.model(id, slug=)` | `Model` | |
240
+ | `client.models(...)` | `PaginatedList[Model]` | |
241
+ | `client.create_model(data)` | `Model` | |
242
+ | `client.enable_models(ids)` | `list[Model]` | |
243
+ | `client.disable_models(ids)` | `list[Model]` | |
244
+ | `client.delete_models(ids)` | `None` | |
245
+ | `client.import_models(file)` | `dict` | dry_run=True by default |
246
+ | `client.scan_group(id, slug=)` | `ScanGroup` | |
247
+ | `client.scan_groups(...)` | `PaginatedList[ScanGroup]` | |
248
+ | `client.create_scan_group(data)` | `ScanGroup` | |
249
+ | `client.enable_scan_groups(ids)` | `list[ScanGroup]` | |
250
+ | `client.disable_scan_groups(ids)` | `list[ScanGroup]` | |
251
+ | `client.delete_scan_groups(ids)` | `None` | |
252
+ | `client.device_set(id, slug=)` | `DeviceSet` | supports `.update()`, `.delete()`, `.set_active_device(item_id)` |
253
+ | `client.device_sets(...)` | `PaginatedList[DeviceSet]` | |
254
+ | `client.create_device_set(data)` | `DeviceSet` | |
255
+ | `client.delete_device_sets(ids)` | `None` | |
256
+ | `client.protocol(id, slug=)` | `Protocol` | read-only, supports `.set_visible(bool)` |
257
+ | `client.protocols()` | `list[Protocol]` | not paginated |
258
+
259
+ #### Device Sets (Redundancy Groups)
260
+
261
+ Device sets group multiple devices for failover. Member devices are added as
262
+ `DeviceSetItem` records via the low-level GraphQL client, and the active
263
+ member is controlled by `set_active_device`:
264
+
265
+ ```python
266
+ from koios_client.types import (
267
+ DeviceSetInput,
268
+ DeviceSetItemInput,
269
+ OneToManyInput,
270
+ )
271
+
272
+ ds = client.create_device_set(DeviceSetInput(
273
+ name="Redundant Pair",
274
+ protocol=OneToManyInput(set="1"),
275
+ ))
276
+
277
+ # Add two member devices with priority 1 (primary) and 2 (backup).
278
+ item1 = KoiosClient.check_operation(
279
+ client.gql.create_device_set_item(
280
+ data=DeviceSetItemInput(
281
+ device_set=OneToManyInput(set=ds.id),
282
+ device=OneToManyInput(set="10"),
283
+ priority=1,
284
+ )
285
+ ).create_device_set_item
286
+ )
287
+ item2 = KoiosClient.check_operation(
288
+ client.gql.create_device_set_item(
289
+ data=DeviceSetItemInput(
290
+ device_set=OneToManyInput(set=ds.id),
291
+ device=OneToManyInput(set="11"),
292
+ priority=2,
293
+ )
294
+ ).create_device_set_item
295
+ )
296
+
297
+ # Switch which member is active — identified by the DeviceSetItem ID, not the Device ID.
298
+ ds = ds.set_active_device(item2.id)
299
+ ```
300
+
301
+ ### Endpoint Clients
302
+
303
+ ```python
304
+ # List endpoint clients
305
+ clients = client.endpoint_clients()
306
+
307
+ # Get a single endpoint client by ID
308
+ ec = client.endpoint_client("123")
309
+
310
+ # Create a new endpoint client (returns one-time secret)
311
+ result = client.create_endpoint_client("My Integration", description="Data pipeline")
312
+
313
+ # Set permissions
314
+ client.set_endpoint_client_permissions(
315
+ client_id="123",
316
+ permission_ids=["1", "2", "3"],
317
+ )
318
+ ```
319
+
320
+ ## Low-Level GraphQL Client
321
+
322
+ All GraphQL queries and mutations are available via `client.gql` with fully typed parameters and return values.
323
+
324
+ ### Querying Data
325
+
326
+ ```python
327
+ from koios_client.types import (
328
+ DeviceFilter,
329
+ OffsetPaginationInput,
330
+ StatusChoices,
331
+ )
332
+
333
+ # Paginated queries
334
+ devices = client.gql.get_devices(
335
+ pagination=OffsetPaginationInput(limit=50),
336
+ )
337
+ print(f"Total: {devices.devices.total_count}")
338
+ for device in devices.devices.results:
339
+ print(device.name, device.status, device.protocol.name)
340
+
341
+ # Filtered queries
342
+ tags = client.gql.get_tags(
343
+ filters=TagFilter(enabled=True),
344
+ pagination=OffsetPaginationInput(limit=100),
345
+ )
346
+
347
+ # Single item by slug
348
+ device = client.gql.get_device(slug="my-device")
349
+ tag = client.gql.get_tag(slug="my-tag")
350
+ ```
351
+
352
+ ### Mutations
353
+
354
+ Single-item mutations return a union of the entity type or `OperationInfo`. Use `check_operation` to unwrap:
355
+
356
+ ```python
357
+ from koios_client.types import DeviceInput, OneToManyInput
358
+
359
+ raw = client.gql.create_device(data=DeviceInput(
360
+ name="New Device",
361
+ protocol=OneToManyInput(set="1"),
362
+ ))
363
+
364
+ # Raises OperationError if the server returned validation/permission errors
365
+ device = KoiosClient.check_operation(raw.create_device)
366
+ print(f"Created: {device.name} ({device.slug})")
367
+ ```
368
+
369
+ ### Available Enums
370
+
371
+ All GraphQL enums are available as typed Python enums:
372
+
373
+ ```python
374
+ from koios_client.types import (
375
+ StatusChoices, # RUNNING, STOPPED, FAILED
376
+ UsageChoices, # INPUT, OUTPUT, VIRTUAL
377
+ AggregateFunction, # MEAN, SUM, MIN, MAX, FIRST, LAST, COUNT
378
+ DeviceErrorCodeChoices,
379
+ TagErrorCodeChoices,
380
+ ProtocolReferenceCodeChoices, # OPCUA, MODBUS_TCP, ETHERNET_IP, ...
381
+ )
382
+ ```
383
+
384
+ ## REST API
385
+
386
+ Operations that involve file uploads, background tasks, or binary responses use REST endpoints exposed directly on the client.
387
+
388
+ ### Export / Import
389
+
390
+ ```python
391
+ # Export devices as CSV
392
+ csv_bytes = client.export_devices_csv()
393
+ with open("devices.csv", "wb") as f:
394
+ f.write(csv_bytes)
395
+
396
+ # Export specific tags
397
+ csv_bytes = client.export_tags_csv(ids=[1, 2, 3])
398
+
399
+ # Export models as ZIP (includes bindings)
400
+ zip_bytes = client.export_models()
401
+
402
+ # Two-step import: preview then confirm
403
+ preview = client.import_tags_preview("tags.csv")
404
+ print(f"Will import {preview['result']['total_rows']} rows")
405
+ if preview["can_import"]:
406
+ result = client.import_tags_confirm(
407
+ preview["tmp_storage_name"],
408
+ preview["file_name"],
409
+ )
410
+ print(f"Imported {result['imported_count']} tags")
411
+ ```
412
+
413
+ ### Model Files
414
+
415
+ ```python
416
+ # Upload a model file
417
+ result = client.upload_model_file(
418
+ "model.onnx",
419
+ model_slug="my-model",
420
+ version="1.0",
421
+ set_active=True,
422
+ )
423
+
424
+ # Download a model file
425
+ model_bytes = client.download_model_file("model-file-slug")
426
+
427
+ # Get model structure (weights stripped for ONNX)
428
+ structure = client.get_model_file_structure("model-file-slug")
429
+ ```
430
+
431
+ ### Backups
432
+
433
+ ```python
434
+ # Create a backup
435
+ task = client.create_backup(tier="full")
436
+
437
+ # Poll for completion
438
+ import time
439
+ while True:
440
+ status = client.get_backup_status(task["task_id"])
441
+ if status["status"] == "completed":
442
+ break
443
+ time.sleep(2)
444
+
445
+ # List and download
446
+ backups = client.list_backups()
447
+ for backup in backups["backups"]:
448
+ print(backup["filename"], backup["size_bytes"])
449
+
450
+ data = client.download_backup(backups["backups"][0]["filename"])
451
+
452
+ # Restore from backup
453
+ upload = client.upload_restore_file("backup.tar.gz")
454
+ task = client.start_restore(upload["restore_file_path"])
455
+ ```
456
+
457
+ ### Trend Data Export
458
+
459
+ ```python
460
+ # Start an export task
461
+ task = client.create_trend_export(
462
+ tag_ids=[1, 2, 3],
463
+ start="2026-01-01T00:00:00Z",
464
+ stop="2026-02-01T00:00:00Z",
465
+ mode="resampled",
466
+ resample_interval="5m",
467
+ aggregate_fn="mean",
468
+ output_format="csv",
469
+ )
470
+
471
+ # Poll and download
472
+ status = client.get_trend_export_status(task["task_id"])
473
+ if status["status"] == "completed":
474
+ data = client.download_trend_export(status["filename"])
475
+
476
+ # Estimate before exporting
477
+ estimate = client.estimate_trend_export(
478
+ tag_ids=[1, 2, 3],
479
+ export_all=True,
480
+ )
481
+ print(f"~{estimate['estimated_size_bytes'] / 1e6:.1f} MB")
482
+ ```
483
+
484
+ ### Component Libraries
485
+
486
+ ```python
487
+ result = client.upload_component_library("my-component.kcl")
488
+ print(f"Uploaded: {result['name']} v{result['version']}")
489
+ ```
490
+
491
+ ### OPC-UA Certificates
492
+
493
+ ```python
494
+ # Upload a certificate + key pair
495
+ result = client.upload_opcua_certificate(
496
+ "cert.der", "key.pem", name="My Certificate"
497
+ )
498
+
499
+ # Download cert or key
500
+ cert_bytes = client.download_opcua_certificate("cert-slug")
501
+ key_bytes = client.download_opcua_key("cert-slug")
502
+ ```
503
+
504
+ ### EDS Files
505
+
506
+ ```python
507
+ # Upload an EDS file for a device
508
+ result = client.upload_eds_file("device.eds")
509
+
510
+ # Delete the EDS file from a device
511
+ client.delete_eds_file(device_id=1)
512
+ ```
513
+
514
+ ### Log Downloads
515
+
516
+ ```python
517
+ log = client.download_device_log("my-device")
518
+ log = client.download_model_log("my-model")
519
+ log = client.download_scan_group_log("scan-group-1")
520
+ log = client.download_component_log("instance-1")
521
+ log = client.download_service_log("datacollector")
522
+ ```
523
+
524
+ ## Connection Options
525
+
526
+ ```python
527
+ client = KoiosClient(
528
+ hostname="koios.example.com",
529
+ client_id="your-uuid",
530
+ client_secret="your-secret",
531
+ port=443, # Default: 443 (HTTPS) or 80 (HTTP)
532
+ ssl=True, # Default: True
533
+ verify_ssl=True, # Default: True (set False for self-signed certs)
534
+ timeout=30.0, # Default: 30 seconds
535
+ )
536
+ ```
537
+
538
+ ## Error Handling
539
+
540
+ ```python
541
+ from koios_client import (
542
+ KoiosError, # Base exception
543
+ AuthenticationError, # Bad credentials or expired token
544
+ NotFoundError, # Resource not found (e.g. client.device(999))
545
+ OperationError, # Mutation returned OperationInfo
546
+ GraphQLError, # GraphQL response errors
547
+ KoiosConnectionError, # Network connectivity issues
548
+ KoiosPermissionError, # Insufficient permissions
549
+ ValidationError, # Input validation failures
550
+ )
551
+
552
+ try:
553
+ device = client.device(1)
554
+ device.update(name="New Name")
555
+ except NotFoundError:
556
+ print("Device not found")
557
+ except OperationError as e:
558
+ for msg in e.messages:
559
+ print(f"{msg.kind}: {msg.field}: {msg.message}")
560
+ except AuthenticationError:
561
+ print("Check your client credentials")
562
+ ```
563
+
564
+ ## Development
565
+
566
+ ```bash
567
+ # Install with dev dependencies
568
+ make install
569
+
570
+ # Format and lint
571
+ make format # ruff check --fix + format
572
+ make lint # ruff check + format --check
573
+
574
+ # Type check and run unit tests
575
+ make typecheck # basedpyright
576
+ make test # pytest (mocked HTTP; no live server needed)
577
+ make ci # lint + typecheck + test
578
+
579
+ # Regenerate GraphQL client (requires a running Koios dev server at :8080)
580
+ make codegen
581
+ ```
582
+
583
+ ### Integration tests
584
+
585
+ `make test-integration` runs the suite under `tests/integration/` against a live
586
+ Koios server. These tests are skipped by the default `make test` /
587
+ `make ci` runs — they exist so releases can be verified against a real
588
+ container.
589
+
590
+ Copy `tests/integration/.env.example` to `tests/integration/.env` and fill in
591
+ an endpoint client ID/secret from the target server:
592
+
593
+ ```bash
594
+ cp tests/integration/.env.example tests/integration/.env
595
+ # Edit tests/integration/.env with KOIOS_HOST, KOIOS_CLIENT_ID, KOIOS_CLIENT_SECRET
596
+ make test-integration
597
+ ```
598
+
599
+ ### Releasing
600
+
601
+ ```bash
602
+ # Bump pyproject.toml version, land via PR, then tag:
603
+ make tag # reads version from pyproject.toml, creates matching git tag
604
+ ```
605
+
606
+ ## License
607
+
608
+ Copyright 2024-2026 Ai-OPs, Inc. All rights reserved.