ironflock 1.2.0__tar.gz → 1.2.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ironflock
3
- Version: 1.2.0
3
+ Version: 1.2.1
4
4
  Summary: IronFlock Python SDK for connecting to the IronFlock Platform
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ironflock"
3
- version = "1.2.0"
3
+ version = "1.2.1"
4
4
  description = "IronFlock Python SDK for connecting to the IronFlock Platform"
5
5
  authors = [
6
6
  {name = "Marko Petzold, IronFlock GmbH", email = "info@ironflock.com"}
@@ -138,7 +138,7 @@ class CrossbarConnection:
138
138
  self.session = session
139
139
  self._is_connected = True
140
140
  await self._resubscribe_all()
141
- print(f"Connection to realm '{session._realm}' established")
141
+ print(f"Connection to IronFlock app realm '{session._realm}' established")
142
142
 
143
143
  if self._first_connection_future and not self._first_connection_future.done():
144
144
  self._first_connection_future.set_result(None)
@@ -147,7 +147,7 @@ class CrossbarConnection:
147
147
  """Handle session close event"""
148
148
  self.session = None
149
149
  self._is_connected = False
150
- print(f"Connection to realm {self.realm} closed: {details.reason}")
150
+ print(f"Connection to IronFlock app realm {self.realm} closed: {details.reason}")
151
151
 
152
152
  if self._first_connection_future and not self._first_connection_future.done():
153
153
  self._first_connection_future.set_exception(
@@ -158,8 +158,8 @@ class CrossbarConnection:
158
158
  """Handle disconnect event"""
159
159
  self.session = None
160
160
  self._is_connected = False
161
- print(f"Disconnected from realm {self.realm}, clean: {was_clean}")
162
-
161
+ print(f"Disconnected from IronFlock app realm {self.realm}, clean: {was_clean}")
162
+
163
163
  async def _session_wait(self) -> None:
164
164
  """Wait for session to be available"""
165
165
  start_time = time.time()
@@ -177,13 +177,13 @@ class CrossbarConnection:
177
177
  """Start the connection"""
178
178
  if not self.component:
179
179
  raise ValueError("Must call configure() before start()")
180
-
181
- print(f'Starting connection for realm {self.realm}')
182
-
183
- # Start the component
184
- await self.component.start()
180
+
181
+ print(f'Starting connection for IronFlock app realm {self.realm}')
182
+
183
+ # Start the component (non-blocking in autobahn asyncio)
184
+ self.component.start()
185
185
 
186
- # Wait for first connection
186
+ # Wait for first connection to be established
187
187
  if self._first_connection_future:
188
188
  await self._first_connection_future
189
189
 
@@ -3,6 +3,7 @@ import asyncio
3
3
  from typing import Optional, Any
4
4
 
5
5
  from ironflock.CrossbarConnection import CrossbarConnection, Stage, getSerialNumber
6
+ from autobahn.wamp.types import PublishOptions, RegisterOptions, SubscribeOptions, CallOptions
6
7
 
7
8
 
8
9
  class IronFlock:
@@ -106,8 +107,8 @@ class IronFlock:
106
107
  # Merge device metadata with user kwargs
107
108
  combined_kwargs = {**device_metadata, **kwargs}
108
109
 
109
- # Use acknowledged publish
110
- options = {"acknowledge": True}
110
+ # Use acknowledged publish with proper PublishOptions
111
+ options = PublishOptions(acknowledge=True)
111
112
 
112
113
  try:
113
114
  pub = await self._connection.publish(
@@ -159,60 +160,83 @@ class IronFlock:
159
160
  print(f"Set location failed: {e}")
160
161
  return None
161
162
 
162
- async def register_function(self, topic: str, func):
163
- """Registers a function to be called when a message is received on the given topic.
164
-
163
+ async def register(self, topic: str, endpoint, options: Optional[dict] = None) -> Optional[Any]:
164
+ """Registers a function with the IronFlock Platform Message Router
165
+
165
166
  Args:
166
- topic (str): The URI of the topic to register the function for, e.g. "example.mytopic1".
167
- func (callable): The function to call when a message is received on the topic.
167
+ topic (str): The URI of the topic to register, e.g. "com.myapp.myprocedure1"
168
+ endpoint: The function to register
169
+ options (dict, optional): Registration options
170
+
171
+ Returns:
172
+ Optional[Any]: Object representing a registration
168
173
  """
169
174
  if not self.is_connected:
170
- print("cannot register function, not connected")
175
+ print("cannot register, not connected")
171
176
  return None
172
-
173
- swarm_key = os.environ.get("SWARM_KEY")
174
- app_key = os.environ.get("APP_KEY")
175
- env_value = os.environ.get("ENV")
176
-
177
- full_topic = f"{swarm_key}.{self._device_key}.{app_key}.{env_value}.{topic}"
178
-
177
+
178
+ # Convert options dict to RegisterOptions if provided
179
+ register_options = RegisterOptions(**options) if options else None
180
+
179
181
  try:
180
- # Note: CrossbarConnection doesn't support force_reregister option directly
181
- # but it handles resubscription automatically on reconnect
182
- registration = await self._connection.register(full_topic, func)
183
- return registration
182
+ reg = await self._connection.register(topic, endpoint, options=register_options)
183
+ return reg
184
184
  except Exception as e:
185
- print(f"Register function failed: {e}")
185
+ print(f"Register failed: {e}")
186
186
  return None
187
187
 
188
- async def call(self, device_key: str, topic: str, args: list = None, kwargs: dict = None):
189
- """Calls a remote procedure on the IronFlock platform.
188
+ async def subscribe(self, topic: str, handler, options: Optional[dict] = None) -> Optional[Any]:
189
+ """Subscribes to a topic on the IronFlock Platform Message Router
190
190
 
191
191
  Args:
192
- device_key (str): The key of the device to call the procedure on.
193
- topic (str): The URI of the topic to call, e.g. "com.myprocedure".
194
- args (list): The arguments to pass to the procedure.
195
- kwargs (dict): The keyword arguments to pass to the procedure.
192
+ topic (str): The URI of the topic to subscribe to, e.g. "com.myapp.mytopic1"
193
+ handler: The function to call when a message is received
194
+ options (dict, optional): Subscription options
196
195
 
197
196
  Returns:
198
- The result of the remote procedure call.
197
+ Optional[Any]: Object representing a subscription
198
+ """
199
+ if not self.is_connected:
200
+ print("cannot subscribe, not connected")
201
+ return None
202
+
203
+ # Convert options dict to SubscribeOptions if provided
204
+ subscribe_options = SubscribeOptions(**options) if options else None
205
+
206
+ try:
207
+ sub = await self._connection.subscribe(topic, handler, options=subscribe_options)
208
+ return sub
209
+ except Exception as e:
210
+ print(f"Subscribe failed: {e}")
211
+ return None
212
+
213
+ async def call(self, device_key: str, topic: str, args: list = None, kwargs: dict = None, options: Optional[dict] = None):
214
+ """Calls a remote procedure registered by another IronFlock device
215
+
216
+ Args:
217
+ device_key (str): The device key of the target device
218
+ topic (str): The URI of the topic to call, e.g. "com.myapp.myprocedure1"
219
+ args (list, optional): Positional arguments for the call. Defaults to None.
220
+ kwargs (dict, optional): Keyword arguments for the call. Defaults to None.
221
+ options (dict, optional): Call options. Defaults to None.
222
+
223
+ Returns:
224
+ Any: The result of the remote procedure call
199
225
  """
200
226
  if not self.is_connected:
201
227
  print("cannot call, not connected")
202
228
  return None
203
-
204
- swarm_key = os.environ.get("SWARM_KEY")
205
- app_key = os.environ.get("APP_KEY")
206
- env_value = os.environ.get("ENV")
207
-
208
- full_topic = f"{swarm_key}.{device_key}.{app_key}.{env_value}.{topic}"
209
-
229
+
230
+ args = args or []
231
+ kwargs = kwargs or {}
232
+
233
+ # Convert options dict to CallOptions if provided
234
+ call_options = CallOptions(**options) if options else None
235
+
236
+ call_topic = f"{device_key}.{topic}"
237
+
210
238
  try:
211
- result = await self._connection.call(
212
- full_topic,
213
- args=args or [],
214
- kwargs=kwargs or {}
215
- )
239
+ result = await self._connection.call(call_topic, args=args, kwargs=kwargs, options=call_options)
216
240
  return result
217
241
  except Exception as e:
218
242
  print(f"Call failed: {e}")
@@ -275,7 +299,8 @@ class IronFlock:
275
299
  pass
276
300
  self._main_task = None
277
301
 
278
- await self._connection.stop()
302
+ if self._connection:
303
+ await self._connection.stop()
279
304
 
280
305
  async def run(self):
281
306
  """Start the connection and keep it running"""
@@ -291,6 +316,9 @@ class IronFlock:
291
316
  await asyncio.sleep(1)
292
317
  except KeyboardInterrupt:
293
318
  print("Shutting down...")
319
+ except Exception as e:
320
+ print(f"Exception in run(): {e}")
321
+ raise
294
322
  finally:
295
323
  await self.stop()
296
324
 
File without changes
File without changes