piphi-runtime-kit-python 0.3.1__py3-none-any.whl → 0.4.2__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.
@@ -49,6 +49,7 @@ from .runtime.events import (
49
49
  normalize_event_payload,
50
50
  )
51
51
  from .runtime.health import build_runtime_diagnostics_response, build_runtime_health_response
52
+ from .runtime.identity import build_runtime_identity, resolve_config_id, resolve_device_id
52
53
  from .runtime.lifespan import (
53
54
  DEFAULT_CORE_CLIENT_TIMEOUT_SECONDS,
54
55
  DEFAULT_RUNTIME_CONTAINER_ID_ENV_NAME,
@@ -70,7 +71,7 @@ from .runtime.mqtt import (
70
71
  )
71
72
  from .runtime.registry import RuntimeRegistry
72
73
  from .runtime.state import RuntimeProcessState
73
- from .runtime.starter import RuntimeStarter, create_runtime_starter
74
+ from .runtime.starter import RuntimeStarter, build_entities_response, create_runtime_starter
74
75
  from .runtime.tasks import create_tracked_task, shutdown_background_tasks, track_background_task
75
76
  from .runtime.telemetry import TelemetryClient, build_core_auth_headers
76
77
  from .schemas import (
@@ -80,6 +81,11 @@ from .schemas import (
80
81
  IntegrationCommandRequest,
81
82
  IntegrationDiscoveryRequest,
82
83
  IntegrationDiscoveryResponse,
84
+ RuntimeEntityCommandFieldResponse,
85
+ RuntimeEntityCommandResponse,
86
+ RuntimeEntityDashboardResponse,
87
+ RuntimeEntitiesResponse,
88
+ RuntimeEntityResponse,
83
89
  IntegrationEventIngestResponse,
84
90
  IntegrationEventListResponse,
85
91
  IntegrationEventRequest,
@@ -110,6 +116,11 @@ __all__ = [
110
116
  "IntegrationCommandRequest",
111
117
  "IntegrationDiscoveryRequest",
112
118
  "IntegrationDiscoveryResponse",
119
+ "RuntimeEntityCommandFieldResponse",
120
+ "RuntimeEntityCommandResponse",
121
+ "RuntimeEntityDashboardResponse",
122
+ "RuntimeEntitiesResponse",
123
+ "RuntimeEntityResponse",
113
124
  "IntegrationEventRequest",
114
125
  "RuntimeAuthContext",
115
126
  "RuntimeAuthHeaders",
@@ -138,9 +149,11 @@ __all__ = [
138
149
  "build_event_ingest_response",
139
150
  "build_event_list_response",
140
151
  "build_local_event_record",
152
+ "build_runtime_identity",
141
153
  "build_core_auth_headers",
142
154
  "build_config_apply_response",
143
155
  "build_config_remove_response",
156
+ "build_entities_response",
144
157
  "build_runtime_auth_headers",
145
158
  "build_runtime_diagnostics_response",
146
159
  "build_runtime_health_response",
@@ -168,6 +181,8 @@ __all__ = [
168
181
  "schedule_telemetry_delivery",
169
182
  "shutdown_background_tasks",
170
183
  "track_background_task",
184
+ "resolve_config_id",
185
+ "resolve_device_id",
171
186
  "validate_typed_config",
172
187
  "validate_typed_configs",
173
188
  "DEFAULT_SOURCE_TOPIC_PREFIX",
@@ -2,8 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  """Helpers for background dispatch of telemetry and event delivery."""
4
4
 
5
+ import asyncio
5
6
  from typing import Any, Callable, Mapping
6
7
 
8
+ from .errors import CoreDeliveryError
7
9
  from .events import EventClient
8
10
  from .tasks import create_tracked_task
9
11
  from .telemetry import TelemetryClient
@@ -14,6 +16,9 @@ EventRecorder = Callable[[dict[str, Any]], Any]
14
16
  SkipHandler = Callable[[str, dict[str, Any]], Any]
15
17
  ErrorHandler = Callable[[Exception, dict[str, Any]], Any]
16
18
 
19
+ DEFAULT_RETRYABLE_DELIVERY_ATTEMPTS = 5
20
+ DEFAULT_RETRYABLE_DELIVERY_INITIAL_DELAY_SECONDS = 0.5
21
+
17
22
 
18
23
  def build_local_event_record(
19
24
  *,
@@ -36,6 +41,30 @@ def build_local_event_record(
36
41
  }
37
42
 
38
43
 
44
+ async def _run_with_retryable_core_delivery_backoff(
45
+ delivery: Callable[[], Any],
46
+ *,
47
+ max_attempts: int = DEFAULT_RETRYABLE_DELIVERY_ATTEMPTS,
48
+ initial_delay_seconds: float = DEFAULT_RETRYABLE_DELIVERY_INITIAL_DELAY_SECONDS,
49
+ ) -> Any:
50
+ """Retry transient Core delivery failures with a short exponential backoff."""
51
+
52
+ attempt = 1
53
+ delay_seconds = initial_delay_seconds
54
+ while True:
55
+ try:
56
+ result = delivery()
57
+ if asyncio.iscoroutine(result):
58
+ return await result
59
+ return result
60
+ except CoreDeliveryError as exc:
61
+ if not exc.retryable or attempt >= max_attempts:
62
+ raise
63
+ await asyncio.sleep(delay_seconds)
64
+ attempt += 1
65
+ delay_seconds *= 2
66
+
67
+
39
68
  async def dispatch_telemetry_delivery(
40
69
  *,
41
70
  telemetry_client: TelemetryClient,
@@ -59,13 +88,15 @@ async def dispatch_telemetry_delivery(
59
88
  return False
60
89
 
61
90
  try:
62
- await telemetry_client.send_metrics(
63
- device_id=device_id,
64
- metrics=metrics,
65
- auth_context=auth_context,
66
- container_id=resolved_container_id,
67
- units=units,
68
- timestamp=timestamp,
91
+ await _run_with_retryable_core_delivery_backoff(
92
+ lambda: telemetry_client.send_metrics(
93
+ device_id=device_id,
94
+ metrics=metrics,
95
+ auth_context=auth_context,
96
+ container_id=resolved_container_id,
97
+ units=units,
98
+ timestamp=timestamp,
99
+ )
69
100
  )
70
101
  return True
71
102
  except Exception as exc:
@@ -119,16 +150,18 @@ async def dispatch_event_delivery(
119
150
  ) -> bool:
120
151
  """Send one event to Core and return whether delivery succeeded."""
121
152
  try:
122
- event_sent = await event_client.send_event(
123
- event_type=event_type,
124
- integration_id=device.get("integration_id"),
125
- config_id=device.get("config_id") or device.get("device_id"),
126
- auth_context=auth_context,
127
- container_id=device.get("container_id"),
128
- device_id=device.get("device_id"),
129
- payload=payload,
130
- source=source,
131
- severity=severity,
153
+ event_sent = await _run_with_retryable_core_delivery_backoff(
154
+ lambda: event_client.send_event(
155
+ event_type=event_type,
156
+ integration_id=device.get("integration_id"),
157
+ config_id=device.get("config_id"),
158
+ auth_context=auth_context,
159
+ container_id=device.get("container_id"),
160
+ device_id=device.get("device_id"),
161
+ payload=payload,
162
+ source=source,
163
+ severity=severity,
164
+ )
132
165
  )
133
166
  if not event_sent and on_skipped is not None:
134
167
  on_skipped(
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ """Helpers for keeping runtime config and device identity explicit."""
4
+
5
+ from typing import Any, Mapping
6
+
7
+
8
+ def _coerce_optional_string(value: Any) -> str | None:
9
+ if value is None:
10
+ return None
11
+ text = str(value).strip()
12
+ return text or None
13
+
14
+
15
+ def _first_present(payload: Mapping[str, Any], *keys: str) -> str | None:
16
+ for key in keys:
17
+ resolved = _coerce_optional_string(payload.get(key))
18
+ if resolved:
19
+ return resolved
20
+ return None
21
+
22
+
23
+ def resolve_config_id(payload: Mapping[str, Any] | Any) -> str:
24
+ """Resolve the Core-owned config id from a config-like payload."""
25
+
26
+ if isinstance(payload, Mapping):
27
+ return _first_present(payload, "config_id", "configId", "id") or "missing"
28
+ return (
29
+ _coerce_optional_string(getattr(payload, "config_id", None))
30
+ or _coerce_optional_string(getattr(payload, "configId", None))
31
+ or _coerce_optional_string(getattr(payload, "id", None))
32
+ or "missing"
33
+ )
34
+
35
+
36
+ def resolve_device_id(payload: Mapping[str, Any] | Any) -> str:
37
+ """Resolve the integration-owned device id from a config-like payload."""
38
+
39
+ if isinstance(payload, Mapping):
40
+ return _first_present(payload, "device_id", "deviceId", "id") or "missing"
41
+ return (
42
+ _coerce_optional_string(getattr(payload, "device_id", None))
43
+ or _coerce_optional_string(getattr(payload, "deviceId", None))
44
+ or _coerce_optional_string(getattr(payload, "id", None))
45
+ or "missing"
46
+ )
47
+
48
+
49
+ def build_runtime_identity(
50
+ payload: Mapping[str, Any] | Any,
51
+ *,
52
+ container_id: str | None = None,
53
+ integration_id: str | None = None,
54
+ ) -> dict[str, Any]:
55
+ """Build a canonical runtime identity record for one config-backed device."""
56
+
57
+ resolved = {
58
+ "config_id": resolve_config_id(payload),
59
+ "device_id": resolve_device_id(payload),
60
+ }
61
+ payload_container_id = (
62
+ _first_present(payload, "container_id", "containerId")
63
+ if isinstance(payload, Mapping)
64
+ else (
65
+ _coerce_optional_string(getattr(payload, "container_id", None))
66
+ or _coerce_optional_string(getattr(payload, "containerId", None))
67
+ )
68
+ )
69
+ payload_integration_id = (
70
+ _first_present(payload, "integration_id", "integrationId")
71
+ if isinstance(payload, Mapping)
72
+ else (
73
+ _coerce_optional_string(getattr(payload, "integration_id", None))
74
+ or _coerce_optional_string(getattr(payload, "integrationId", None))
75
+ )
76
+ )
77
+
78
+ resolved["container_id"] = _coerce_optional_string(container_id) or payload_container_id
79
+ resolved["integration_id"] = _coerce_optional_string(integration_id) or payload_integration_id
80
+ return resolved
@@ -50,18 +50,30 @@ class RuntimeRegistry(Generic[TEntry, TState, TEvent]):
50
50
  first_entry_id = next(iter(self.entries))
51
51
  return self.entries[first_entry_id]
52
52
 
53
- def update_state(self, entry_id: str, state: dict[str, Any]) -> dict[str, Any]:
53
+ def update_state(
54
+ self,
55
+ entry_id: str,
56
+ state: dict[str, Any],
57
+ *,
58
+ device_id: str | None = None,
59
+ ) -> dict[str, Any]:
54
60
  """Store the latest state snapshot and mirror it onto the active entry."""
55
61
  timestamp = dt.datetime.now(dt.timezone.utc).isoformat()
62
+ entry = self.entries.get(entry_id)
63
+ resolved_device_id = (
64
+ device_id
65
+ or (str(entry.get("device_id")) if entry and entry.get("device_id") else None)
66
+ or entry_id
67
+ )
56
68
  latest_state = {
57
- "device_id": entry_id,
69
+ "device_id": resolved_device_id,
58
70
  "state": state,
59
71
  "last_updated": timestamp,
60
72
  }
61
73
  self.state_snapshots[entry_id] = latest_state
62
- if entry_id in self.entries:
63
- self.entries[entry_id]["latest_state"] = state
64
- self.entries[entry_id]["last_updated"] = timestamp
74
+ if entry is not None:
75
+ entry["latest_state"] = state
76
+ entry["last_updated"] = timestamp
65
77
  return latest_state
66
78
 
67
79
  def append_event(self, event: TEvent) -> TEvent:
@@ -11,6 +11,7 @@ from .events import DEFAULT_CORE_BASE_URL as DEFAULT_EVENTS_CORE_BASE_URL, Event
11
11
  from .health import build_runtime_diagnostics_response, build_runtime_health_response
12
12
  from .registry import RuntimeRegistry
13
13
  from .telemetry import DEFAULT_CORE_BASE_URL as DEFAULT_TELEMETRY_CORE_BASE_URL, TelemetryClient
14
+ from ..schemas import RuntimeEntitiesResponse
14
15
 
15
16
 
16
17
  @dataclass(slots=True)
@@ -77,6 +78,34 @@ class RuntimeStarter:
77
78
  },
78
79
  )
79
80
 
81
+ def entities_response(
82
+ self,
83
+ *,
84
+ entities: list[dict[str, Any]],
85
+ capabilities: dict[str, Any] | None = None,
86
+ commands: dict[str, Any] | None = None,
87
+ ) -> RuntimeEntitiesResponse:
88
+ """Build the standard /entities response body for this runtime."""
89
+ return build_entities_response(
90
+ entities=entities,
91
+ capabilities=capabilities,
92
+ commands=commands,
93
+ )
94
+
95
+
96
+ def build_entities_response(
97
+ *,
98
+ entities: list[dict[str, Any]],
99
+ capabilities: dict[str, Any] | None = None,
100
+ commands: dict[str, Any] | None = None,
101
+ ) -> RuntimeEntitiesResponse:
102
+ """Return the standard runtime /entities response wrapper."""
103
+ return RuntimeEntitiesResponse(
104
+ entities=entities,
105
+ capabilities=capabilities or {},
106
+ commands=commands or {},
107
+ )
108
+
80
109
 
81
110
  def create_runtime_starter(
82
111
  *,
@@ -6,13 +6,31 @@ from datetime import datetime, timezone
6
6
  from enum import Enum
7
7
  from typing import Any
8
8
 
9
- from pydantic import BaseModel, ConfigDict, Field
9
+ from pydantic import AliasChoices, BaseModel, ConfigDict, Field
10
10
 
11
11
 
12
12
  class RuntimeConfig(BaseModel):
13
13
  """Base runtime config model with a required config id and extensible fields."""
14
+
15
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
16
+
14
17
  id: str
15
- model_config = ConfigDict(extra="allow")
18
+ config_id: str | None = Field(
19
+ default=None,
20
+ validation_alias=AliasChoices("config_id", "configId"),
21
+ )
22
+ container_id: str | None = Field(
23
+ default=None,
24
+ validation_alias=AliasChoices("container_id", "containerId"),
25
+ )
26
+ integration_id: str | None = Field(
27
+ default=None,
28
+ validation_alias=AliasChoices("integration_id", "integrationId"),
29
+ )
30
+ device_id: str | None = Field(
31
+ default=None,
32
+ validation_alias=AliasChoices("device_id", "deviceId"),
33
+ )
16
34
 
17
35
 
18
36
  class RuntimeConfigSnapshot(BaseModel):
@@ -74,6 +92,115 @@ class IntegrationCommandRequest(BaseModel):
74
92
  args: dict[str, Any] = Field(default_factory=dict)
75
93
 
76
94
 
95
+ class RuntimeEntityDashboardResponse(BaseModel):
96
+ """Optional per-entity dashboard hints returned by a runtime."""
97
+
98
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
99
+
100
+ allowed_widgets: list[str] = Field(
101
+ default_factory=list,
102
+ validation_alias=AliasChoices("allowed_widgets", "allowedWidgets"),
103
+ )
104
+ default_widget: str | None = Field(
105
+ default=None,
106
+ validation_alias=AliasChoices("default_widget", "defaultWidget"),
107
+ )
108
+ recommended_widgets: list[str] = Field(
109
+ default_factory=list,
110
+ validation_alias=AliasChoices("recommended_widgets", "recommendedWidgets"),
111
+ )
112
+ metadata: dict[str, Any] = Field(default_factory=dict)
113
+
114
+
115
+ class RuntimeEntityCommandFieldResponse(BaseModel):
116
+ """Optional per-command argument metadata returned by a runtime entity."""
117
+
118
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
119
+
120
+ type: str
121
+ label: str | None = None
122
+ description: str | None = None
123
+ required: bool = False
124
+ default: Any | None = None
125
+ minimum: float | None = Field(
126
+ default=None,
127
+ validation_alias=AliasChoices("minimum", "min"),
128
+ )
129
+ maximum: float | None = Field(
130
+ default=None,
131
+ validation_alias=AliasChoices("maximum", "max"),
132
+ )
133
+ step: float | None = None
134
+ unit: str | None = None
135
+ options: list[Any] = Field(default_factory=list)
136
+
137
+
138
+ class RuntimeEntityCommandResponse(BaseModel):
139
+ """One device-scoped command exposed by a runtime entity."""
140
+
141
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
142
+
143
+ id: str
144
+ label: str | None = None
145
+ description: str | None = None
146
+ kind: str | None = None
147
+ args_schema: dict[str, RuntimeEntityCommandFieldResponse] = Field(
148
+ default_factory=dict,
149
+ validation_alias=AliasChoices("args_schema", "argsSchema"),
150
+ )
151
+
152
+
153
+ class RuntimeEntityResponse(BaseModel):
154
+ """One live runtime entity tied to a configured device/account."""
155
+
156
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
157
+
158
+ id: str
159
+ name: str
160
+ capabilities: list[str] = Field(default_factory=list)
161
+ config_id: str | None = Field(
162
+ default=None,
163
+ validation_alias=AliasChoices("config_id", "configId"),
164
+ )
165
+ device_id: str | None = Field(
166
+ default=None,
167
+ validation_alias=AliasChoices("device_id", "deviceId"),
168
+ )
169
+ device_type: str | None = Field(
170
+ default=None,
171
+ validation_alias=AliasChoices("device_type", "deviceType"),
172
+ )
173
+ device_class: str | None = Field(
174
+ default=None,
175
+ validation_alias=AliasChoices("device_class", "deviceClass"),
176
+ )
177
+ entity_type: str | None = Field(
178
+ default=None,
179
+ validation_alias=AliasChoices("entity_type", "entityType"),
180
+ )
181
+ available_commands: list[RuntimeEntityCommandResponse | str] = Field(
182
+ default_factory=list,
183
+ validation_alias=AliasChoices(
184
+ "available_commands",
185
+ "availableCommands",
186
+ "supported_commands",
187
+ "supportedCommands",
188
+ ),
189
+ )
190
+ dashboard: RuntimeEntityDashboardResponse | None = None
191
+ metadata: dict[str, Any] = Field(default_factory=dict)
192
+
193
+
194
+ class RuntimeEntitiesResponse(BaseModel):
195
+ """Standard response body returned by runtime /entities endpoints."""
196
+
197
+ model_config = ConfigDict(populate_by_name=True, extra="allow")
198
+
199
+ entities: list[RuntimeEntityResponse] = Field(default_factory=list)
200
+ capabilities: dict[str, Any] = Field(default_factory=dict)
201
+ commands: dict[str, Any] = Field(default_factory=dict)
202
+
203
+
77
204
  class IntegrationEventRequest(BaseModel):
78
205
  """Generic event payload emitted by a runtime integration."""
79
206
  event_type: str
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: piphi-runtime-kit-python
3
- Version: 0.3.1
3
+ Version: 0.4.2
4
4
  Summary: PiPhi Network runtime integration helpers
5
5
  Keywords: piphi,runtime,integration,iot
6
6
  Author-Email: KelvinSan <support@piphi.network>
@@ -170,9 +170,71 @@ These are the most common runtime routes and what they are for.
170
170
  | `/deconfigure` | `POST` | Usually | Remove one config | `RuntimeConfigRemoveResponse` |
171
171
  | `/events` | `GET` | Common | Show recent local runtime events | `build_event_list_response(...)` |
172
172
  | `/state` | `GET` | Common | Show current runtime state | `registry.entries`, `registry.state_snapshots` |
173
- | `/entities` | `GET` | Integration-specific | Show normalized entity list | integration-owned |
173
+ | `/entities` | `GET` | Integration-specific | Show normalized entity list | `build_entities_response(...)`, `starter.entities_response(...)` |
174
174
  | `/ui` or `/ui-config` | `GET` | Optional | Return config UI metadata | integration-owned |
175
175
 
176
+ ## Modeling `/entities`
177
+
178
+ For simple integrations, `/entities` can still be a plain list of generic
179
+ entities.
180
+
181
+ For smart-home and multi-device integrations, PiPhi now recommends a richer
182
+ runtime-owned entity shape that ties each entity to a real saved config or
183
+ device. That lets Core make better device-first dashboard suggestions, and it
184
+ gives the frontend enough context to render better widget defaults for plugs,
185
+ bulbs, thermostats, and other device-specific entities.
186
+
187
+ Recommended fields:
188
+
189
+ - `id`: stable runtime entity id
190
+ - `name`: user-facing label
191
+ - `capabilities`: actual capabilities for that specific device
192
+ - `config_id` / `configId`: PiPhi config UUID when available
193
+ - `device_id` / `deviceId`: integration-native device id
194
+ - `device_type` / `device_class`: values like `plug`, `bulb`, `sensor`, `climate`
195
+ - `entity_type`: values like `switch`, `light`, `sensor`, `media`
196
+ - `dashboard.allowed_widgets`, `dashboard.default_widget`, `dashboard.recommended_widgets`: optional UI hints
197
+
198
+ The SDK now includes `RuntimeEntityResponse`, `RuntimeEntitiesResponse`,
199
+ `build_entities_response(...)`, and `starter.entities_response(...)` to make
200
+ that payload easier to return.
201
+
202
+ The helper returns the standard wrapper shape:
203
+
204
+ - `entities`: the runtime-owned list you generated
205
+ - `capabilities`: optional manifest capability metadata
206
+ - `commands`: optional manifest command metadata
207
+
208
+ ```python
209
+ from piphi_runtime_kit_python import build_entities_response
210
+
211
+ @app.get("/entities")
212
+ async def entities() -> dict[str, Any]:
213
+ return build_entities_response(
214
+ entities=[
215
+ {
216
+ "id": "office-plug",
217
+ "name": "Office Plug",
218
+ "config_id": "core-config-uuid",
219
+ "device_id": "office-plug",
220
+ "device_class": "plug",
221
+ "entity_type": "switch",
222
+ "capabilities": ["switch", "power", "energy_today"],
223
+ "dashboard": {
224
+ "allowed_widgets": ["tile", "button", "stat"],
225
+ "default_widget": "tile",
226
+ },
227
+ }
228
+ ],
229
+ capabilities=manifest["capabilities"],
230
+ commands=manifest.get("commands", {}),
231
+ ).model_dump(exclude_none=True)
232
+ ```
233
+
234
+ If you are already using the starter object, the same response can be built with
235
+ `starter.entities_response(...)` instead of calling the standalone helper
236
+ directly.
237
+
176
238
  ## UI Config Endpoints
177
239
 
178
240
  Many integrations expose `/ui` or `/ui-config` so the PiPhi frontend knows how
@@ -300,15 +362,15 @@ async def health():
300
362
  async def config(payload: DemoConfig, request: Request):
301
363
  sync_runtime_auth_from_fastapi_payload(starter.runtime, request, payload)
302
364
  typed_payload = validate_typed_config(payload, DemoConfig)
365
+ identity = build_runtime_identity(typed_payload)
303
366
  starter.registry.set(
304
367
  typed_payload.id,
305
368
  {
306
- "config_id": typed_payload.config_id or typed_payload.id,
307
- "device_id": typed_payload.device_id or typed_payload.id,
369
+ **identity,
308
370
  "host": typed_payload.host,
309
371
  },
310
372
  )
311
- return build_config_apply_response(config_id=typed_payload.config_id or typed_payload.id)
373
+ return build_config_apply_response(config_id=identity["config_id"])
312
374
 
313
375
 
314
376
  @app.post("/telemetry/example")
@@ -1,8 +1,8 @@
1
- piphi_runtime_kit_python-0.3.1.dist-info/METADATA,sha256=bINE7fw8dM0xAx0z8UPlBApHCgrtTyXQCGb9dGOfE0k,28493
2
- piphi_runtime_kit_python-0.3.1.dist-info/WHEEL,sha256=Z36eTX6lG3PITRleSd5hAZHCcz52yg3c0JQVxKBbLW0,90
3
- piphi_runtime_kit_python-0.3.1.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
- piphi_runtime_kit_python-0.3.1.dist-info/licenses/LICENSE,sha256=pS6szQI4X7qAin1ODdoDp-XwZvsFy6ZZ28-JAwGHgzo,1070
5
- piphi_runtime_kit_python/__init__.py,sha256=ghbPOQY3QMbEZrH8xSH8ZS9rbpYwkuRITReOUtZ9NsU,5446
1
+ piphi_runtime_kit_python-0.4.2.dist-info/METADATA,sha256=th_AhsVfZ_at7LDyKQv7ZA1EVxiasOUwoSdTxQvdulI,30849
2
+ piphi_runtime_kit_python-0.4.2.dist-info/WHEEL,sha256=Z36eTX6lG3PITRleSd5hAZHCcz52yg3c0JQVxKBbLW0,90
3
+ piphi_runtime_kit_python-0.4.2.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
4
+ piphi_runtime_kit_python-0.4.2.dist-info/licenses/LICENSE,sha256=pS6szQI4X7qAin1ODdoDp-XwZvsFy6ZZ28-JAwGHgzo,1070
5
+ piphi_runtime_kit_python/__init__.py,sha256=P8yx6C1P88xuXWuf3D58d5zhUD-pprhBhbVeyNbDaJQ,6013
6
6
  piphi_runtime_kit_python/adapters/__init__.py,sha256=3jBhPZSBN3Qo7MKHlxAol2jPU3cK23BXHR2oTlircsw,361
7
7
  piphi_runtime_kit_python/adapters/fastapi.py,sha256=1QcOBYprg6AmQVrkI8g61NIelBB_7BTbdvNaMvG2-s8,2098
8
8
  piphi_runtime_kit_python/fastapi.py,sha256=OZ-ds2-g32kB4YkF90D5bM4vVVjpOWPtrNC-WdhDNyo,361
@@ -12,16 +12,17 @@ piphi_runtime_kit_python/runtime/config_sync.py,sha256=IzsH5AGxnISISWx8OYfNMTk2O
12
12
  piphi_runtime_kit_python/runtime/configuration.py,sha256=4kkoJeENOhJHeNvC9QDu81ejDJUQJG7S5GmQG1sv3YQ,3631
13
13
  piphi_runtime_kit_python/runtime/context.py,sha256=CwXgrivciBd7eTREn0u0zjqkDPy6VthASW-gsoZqCTg,1109
14
14
  piphi_runtime_kit_python/runtime/discovery.py,sha256=uH6rJ4BnLVukTCxu2QwvN8yJhsI_fcT29VEat62oBcY,2274
15
- piphi_runtime_kit_python/runtime/dispatch.py,sha256=N0Fxqd9QLkareAOX4aOGBBzQpPU3-v5vDxbWEill0zI,6218
15
+ piphi_runtime_kit_python/runtime/dispatch.py,sha256=QcbA9B5izyyg0znlKHq94dbr-oqHVxKW2LCjrQKYfkY,7341
16
16
  piphi_runtime_kit_python/runtime/errors.py,sha256=_mjqmJLsdM5NfrTwb6YFkhheqPfn6Df-QtAKgl9U6QU,4120
17
17
  piphi_runtime_kit_python/runtime/events.py,sha256=JcvrTZlJ-vgY237jTDVOAVTbSxbcuh9u2QCdOGyuduA,6543
18
18
  piphi_runtime_kit_python/runtime/health.py,sha256=9YO0K_UCJKTe6uSFLgb1kStr5dAgtIUc8Ha0dnRi2vA,1880
19
+ piphi_runtime_kit_python/runtime/identity.py,sha256=cth0_QHR9R5LTdp-6DPD1IhM8cYJT5HB1OUQYW7kLGs,2762
19
20
  piphi_runtime_kit_python/runtime/lifespan.py,sha256=mVSdZZAS66xlcnlzAwkztFjzw_l2pyLrJqULJsb0Ezk,2850
20
21
  piphi_runtime_kit_python/runtime/mqtt.py,sha256=JKMN5zxg1BcOidu7TaYnuRD3HuoAJ_WcvXMTTSlAteo,6069
21
- piphi_runtime_kit_python/runtime/registry.py,sha256=AOpd5NgwSAhN8Us8nLW6084GrMtitMMaQP916EpMZrk,3103
22
- piphi_runtime_kit_python/runtime/starter.py,sha256=K62F_3qTy-HAjWYAs3TADeR_wwC0u9kCUbQbNK66ThI,3747
22
+ piphi_runtime_kit_python/runtime/registry.py,sha256=8rKLCWt1_0SlDm3TepEUGDliOhVEDTv_zYR0kxLpSHM,3373
23
+ piphi_runtime_kit_python/runtime/starter.py,sha256=j5C4GOOcRHNLJ-HYu8fmf3bE00baDOzAfuq1MefT-Jg,4651
23
24
  piphi_runtime_kit_python/runtime/state.py,sha256=4xvQGTT59DF9rXHAtM3kDyV_UGg2a9tLPvGiCp_PFMc,674
24
25
  piphi_runtime_kit_python/runtime/tasks.py,sha256=gMd-2K96jMU8tCPvI12xdWxuyNt5gsLDss2QWOoM94k,1241
25
26
  piphi_runtime_kit_python/runtime/telemetry.py,sha256=RN6SFrhmAZ7aJLyioQMBzJEFsxQgOjjA1pV5qNeaR4E,4279
26
- piphi_runtime_kit_python/schemas.py,sha256=NDG6g0BZZOEy35O5sz9_A4HlH-BnHyztOQMLbpKqF0E,4924
27
- piphi_runtime_kit_python-0.3.1.dist-info/RECORD,,
27
+ piphi_runtime_kit_python/schemas.py,sha256=LGz53OHmWwUEQb_nHbVBgRDGVGowJPT53S2P7otP3lo,9073
28
+ piphi_runtime_kit_python-0.4.2.dist-info/RECORD,,