FlowerPower 0.9.13.1__py3-none-any.whl → 1.0.0b2__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.
Files changed (85) hide show
  1. flowerpower/__init__.py +17 -2
  2. flowerpower/cfg/__init__.py +201 -149
  3. flowerpower/cfg/base.py +122 -24
  4. flowerpower/cfg/pipeline/__init__.py +254 -0
  5. flowerpower/cfg/pipeline/adapter.py +66 -0
  6. flowerpower/cfg/pipeline/run.py +40 -11
  7. flowerpower/cfg/pipeline/schedule.py +69 -79
  8. flowerpower/cfg/project/__init__.py +149 -0
  9. flowerpower/cfg/project/adapter.py +57 -0
  10. flowerpower/cfg/project/job_queue.py +165 -0
  11. flowerpower/cli/__init__.py +92 -37
  12. flowerpower/cli/job_queue.py +878 -0
  13. flowerpower/cli/mqtt.py +32 -1
  14. flowerpower/cli/pipeline.py +559 -406
  15. flowerpower/cli/utils.py +29 -18
  16. flowerpower/flowerpower.py +12 -8
  17. flowerpower/fs/__init__.py +20 -2
  18. flowerpower/fs/base.py +350 -26
  19. flowerpower/fs/ext.py +797 -216
  20. flowerpower/fs/storage_options.py +1097 -55
  21. flowerpower/io/base.py +13 -18
  22. flowerpower/io/loader/__init__.py +28 -0
  23. flowerpower/io/loader/deltatable.py +7 -10
  24. flowerpower/io/metadata.py +1 -0
  25. flowerpower/io/saver/__init__.py +28 -0
  26. flowerpower/io/saver/deltatable.py +4 -3
  27. flowerpower/job_queue/__init__.py +252 -0
  28. flowerpower/job_queue/apscheduler/__init__.py +11 -0
  29. flowerpower/job_queue/apscheduler/_setup/datastore.py +110 -0
  30. flowerpower/job_queue/apscheduler/_setup/eventbroker.py +93 -0
  31. flowerpower/job_queue/apscheduler/manager.py +1063 -0
  32. flowerpower/job_queue/apscheduler/setup.py +524 -0
  33. flowerpower/job_queue/apscheduler/trigger.py +169 -0
  34. flowerpower/job_queue/apscheduler/utils.py +309 -0
  35. flowerpower/job_queue/base.py +382 -0
  36. flowerpower/job_queue/rq/__init__.py +10 -0
  37. flowerpower/job_queue/rq/_trigger.py +37 -0
  38. flowerpower/job_queue/rq/concurrent_workers/gevent_worker.py +226 -0
  39. flowerpower/job_queue/rq/concurrent_workers/thread_worker.py +231 -0
  40. flowerpower/job_queue/rq/manager.py +1449 -0
  41. flowerpower/job_queue/rq/setup.py +150 -0
  42. flowerpower/job_queue/rq/utils.py +69 -0
  43. flowerpower/pipeline/__init__.py +5 -0
  44. flowerpower/pipeline/base.py +118 -0
  45. flowerpower/pipeline/io.py +407 -0
  46. flowerpower/pipeline/job_queue.py +505 -0
  47. flowerpower/pipeline/manager.py +1586 -0
  48. flowerpower/pipeline/registry.py +560 -0
  49. flowerpower/pipeline/runner.py +560 -0
  50. flowerpower/pipeline/visualizer.py +142 -0
  51. flowerpower/plugins/mqtt/__init__.py +12 -0
  52. flowerpower/plugins/mqtt/cfg.py +16 -0
  53. flowerpower/plugins/mqtt/manager.py +789 -0
  54. flowerpower/settings.py +110 -0
  55. flowerpower/utils/logging.py +21 -0
  56. flowerpower/utils/misc.py +57 -9
  57. flowerpower/utils/sql.py +122 -24
  58. flowerpower/utils/templates.py +2 -142
  59. flowerpower-1.0.0b2.dist-info/METADATA +324 -0
  60. flowerpower-1.0.0b2.dist-info/RECORD +94 -0
  61. flowerpower/_web/__init__.py +0 -61
  62. flowerpower/_web/routes/config.py +0 -103
  63. flowerpower/_web/routes/pipelines.py +0 -173
  64. flowerpower/_web/routes/scheduler.py +0 -136
  65. flowerpower/cfg/pipeline/tracker.py +0 -14
  66. flowerpower/cfg/project/open_telemetry.py +0 -8
  67. flowerpower/cfg/project/tracker.py +0 -11
  68. flowerpower/cfg/project/worker.py +0 -19
  69. flowerpower/cli/scheduler.py +0 -309
  70. flowerpower/cli/web.py +0 -44
  71. flowerpower/event_handler.py +0 -23
  72. flowerpower/mqtt.py +0 -609
  73. flowerpower/pipeline.py +0 -2499
  74. flowerpower/scheduler.py +0 -680
  75. flowerpower/tui.py +0 -79
  76. flowerpower/utils/datastore.py +0 -186
  77. flowerpower/utils/eventbroker.py +0 -127
  78. flowerpower/utils/executor.py +0 -58
  79. flowerpower/utils/trigger.py +0 -140
  80. flowerpower-0.9.13.1.dist-info/METADATA +0 -586
  81. flowerpower-0.9.13.1.dist-info/RECORD +0 -76
  82. /flowerpower/{cfg/pipeline/params.py → cli/worker.py} +0 -0
  83. {flowerpower-0.9.13.1.dist-info → flowerpower-1.0.0b2.dist-info}/WHEEL +0 -0
  84. {flowerpower-0.9.13.1.dist-info → flowerpower-1.0.0b2.dist-info}/entry_points.txt +0 -0
  85. {flowerpower-0.9.13.1.dist-info → flowerpower-1.0.0b2.dist-info}/top_level.txt +0 -0
flowerpower/cli/web.py DELETED
@@ -1,44 +0,0 @@
1
- import importlib
2
- import os
3
- import sys
4
- import uvicorn
5
- import typer
6
- from pathlib import Path
7
-
8
- web_app = typer.Typer(help="Web UI commands")
9
-
10
-
11
- @web_app.command()
12
- def start(
13
- host: str = "127.0.0.1",
14
- port: int = 8080,
15
- reload: bool = True,
16
- base_dir: str = None,
17
- ):
18
- """
19
- Start the FlowerPower Web UI.
20
-
21
- Args:
22
- host: Host address to run the server on
23
- port: Port to run the server on
24
- reload: Enable auto-reload on code changes
25
- base_dir: Base directory for the FlowerPower project
26
- """
27
- # Set the base directory in environment variable so web app can access it
28
- if base_dir:
29
- os.environ["FLOWERPOWER_BASE_DIR"] = base_dir
30
-
31
- # Import after setting environment vars
32
- from .._web import app as fastapi_app
33
-
34
- # Start the server
35
- uvicorn.run(
36
- "flowerpower.web:app",
37
- host=host,
38
- port=port,
39
- reload=reload
40
- )
41
-
42
-
43
- if __name__ == "__main__":
44
- web_app()
@@ -1,23 +0,0 @@
1
- # from apscheduler import (
2
- # Event,
3
- # JobAcquired,
4
- # JobAdded,
5
- # JobRemoved,
6
- # JobDeserializationFailed,
7
- # ScheduleAdded,
8
- # ScheduleDeserializationFailed,
9
- # ScheduleRemoved,
10
- # SchedulerStopped,
11
- # SchedulerStarted,
12
- # ScheduleUpdated,
13
- # TaskAdded,
14
- # TaskRemoved,
15
- # TaskUpdated,
16
- # )
17
- # from loguru import logger
18
-
19
- # def job_added(event: Event):
20
- # logger.info(event.timestamp)
21
- # logger.info(event.job_id)
22
- # logger.info(event.task_id)
23
- # logger.info(event.schedule_id)
flowerpower/mqtt.py DELETED
@@ -1,609 +0,0 @@
1
- import datetime as dt
2
- import random
3
- import time
4
- from pathlib import Path
5
- from types import TracebackType
6
- from typing import Callable
7
- import mmh3
8
- import socket
9
-
10
- from fsspec import AbstractFileSystem
11
- from loguru import logger
12
- from munch import Munch
13
- from paho.mqtt.client import CallbackAPIVersion, Client
14
-
15
- from .cfg import Config
16
- from .pipeline import Pipeline
17
-
18
-
19
- class MQTTManager:
20
- def __init__(
21
- self,
22
- username: str | None = None,
23
- password: str | None = None,
24
- host: str | None = "localhost",
25
- port: int | None = 1883,
26
- topic: str | None = None,
27
- first_reconnect_delay: int = 1,
28
- max_reconnect_count: int = 5,
29
- reconnect_rate: int = 2,
30
- max_reconnect_delay: int = 60,
31
- transport: str = "tcp",
32
- clean_session: bool = True,
33
- client_id: str | None = None,
34
- client_id_suffix: str | None = None,
35
- **kwargs,
36
- ):
37
- if "user" in kwargs:
38
- username = kwargs["user"]
39
- if "pw" in kwargs:
40
- password = kwargs["pw"]
41
-
42
- self.topic = topic
43
-
44
- self._username = username
45
- self._password = password
46
- self._host = host
47
- self._port = port
48
- self._first_reconnect_delay = first_reconnect_delay
49
- self._max_reconnect_count = max_reconnect_count
50
- self._reconnect_rate = reconnect_rate
51
- self._max_reconnect_delay = max_reconnect_delay
52
- self._transport = transport
53
-
54
- self._clean_session = clean_session
55
- self._client_id = client_id
56
- self._client_id_suffix = client_id_suffix
57
-
58
- self._client = None
59
-
60
- def __enter__(self) -> "MQTTManager":
61
- self.connect()
62
- return self
63
-
64
- def __exit__(
65
- self,
66
- exc_type: type[BaseException] | None,
67
- exc_val: BaseException | None,
68
- exc_tb: TracebackType | None,
69
- ) -> None:
70
- # Add any cleanup code here if needed
71
- self.disconnect()
72
-
73
- @staticmethod
74
- def _on_connect(client, userdata, flags, rc, properties):
75
- if rc == 0:
76
- logger.info(f"Connected to MQTT Broker {userdata.host}!")
77
- logger.info(f"Connected as {userdata.client_id} with clean session {userdata.clean_session}")
78
- else:
79
- logger.error(f"Failed to connect, return code {rc}")
80
-
81
- @staticmethod
82
- def _on_disconnect(client, userdata, disconnect_flags, rc, properties=None):
83
- reconnect_count, reconnect_delay = 0, userdata.first_reconnect_delay
84
-
85
- if userdata.max_reconnect_count == 0:
86
- logger.info("Disconnected successfully!")
87
- return
88
-
89
- while reconnect_count < userdata.max_reconnect_count:
90
- logger.info(f"Reconnecting in {reconnect_delay} seconds...")
91
- time.sleep(reconnect_delay)
92
-
93
- try:
94
- client.reconnect()
95
- logger.info("Reconnected successfully!")
96
- return
97
- except Exception as err:
98
- logger.error(f"{err}. Reconnect failed. Retrying...")
99
-
100
- reconnect_delay *= userdata.reconnect_rate
101
- reconnect_delay = min(reconnect_delay, userdata.max_reconnect_delay)
102
- reconnect_count += 1
103
- logger.info(f"Reconnect failed after {reconnect_count} attempts. Exiting...")
104
-
105
- @staticmethod
106
- def _on_publish(client, userdata, mid, rc, properties):
107
- logger.info(f"Published message id: {mid}")
108
-
109
- @staticmethod
110
- def _on_subscribe(client, userdata, mid, qos, properties):
111
- if isinstance(qos, list):
112
- qos_msg = str(qos[0])
113
- else:
114
- qos_msg = f"and granted QoS {qos[0]}"
115
- logger.info(f"Subscribed {qos_msg}")
116
-
117
- def connect(self) -> Client:
118
- if self._client_id is None and self._clean_session:
119
- #Random Client ID when clean session is True
120
- self._client_id = f"flowerpower-client-{random.randint(0, 10000)}"
121
- elif self._client_id is None and not self._clean_session:
122
- #Deterministic Client ID when clean session is False
123
- self._client_id = f"flowerpower-client-{mmh3.hash_bytes(
124
- str(self._host) + str(self._port) + str(self.topic) + str(socket.gethostname())
125
- ).hex()}"
126
-
127
- if self._client_id_suffix:
128
- self._client_id = f"{self._client_id}-{self._client_id_suffix}"
129
-
130
- logger.debug(f"Client ID: {self._client_id} - Clean session: {self._clean_session}")
131
- client = Client(
132
- CallbackAPIVersion.VERSION2,
133
- client_id=self._client_id,
134
- transport=self._transport,
135
- clean_session=self._clean_session,
136
- userdata=Munch(
137
- user=self._username,
138
- pw=self._password,
139
- host=self._host,
140
- port=self._port,
141
- topic=self.topic,
142
- first_reconnect_delay=self._first_reconnect_delay,
143
- max_reconnect_count=self._max_reconnect_count,
144
- reconnect_rate=self._reconnect_rate,
145
- max_reconnect_delay=self._max_reconnect_delay,
146
- transport=self._transport,
147
- client_id=self._client_id,
148
- clean_session=self._clean_session,
149
- ),
150
- )
151
- if self._password != "" and self._username != "":
152
- client.username_pw_set(self._username, self._password)
153
-
154
- client.on_connect = self._on_connect # self._on_connect
155
- client.on_disconnect = self._on_disconnect # self._on_disconnect
156
- client.on_publish = self._on_publish
157
- client.on_subscribe = self._on_subscribe
158
-
159
- client.connect(self._host, self._port)
160
- self._client = client
161
- # topic = topic or topic
162
- if self.topic:
163
- self.subscribe()
164
-
165
-
166
-
167
- def disconnect(self):
168
- self._max_reconnect_count = 0
169
- self._client._userdata.max_reconnect_count = 0
170
- self._client.disconnect()
171
-
172
- def reconnect(self):
173
- self._client.reconnect()
174
-
175
- def publish(self, topic, payload):
176
- if self._client is None:
177
- self.connect()
178
- # elif self._client.is_connected() is False:
179
- # self.reconnect()
180
- self._client.publish(topic, payload)
181
-
182
- def subscribe(self, topic: str | None = None, qos: int = 0):
183
- if topic is not None:
184
- self.topic = topic
185
- self._client.subscribe(self.topic, qos=qos)
186
-
187
- def unsubscribe(self, topic: str | None = None):
188
- if topic is not None:
189
- self.topic = topic
190
- self._client.unsubscribe(self.topic)
191
-
192
- def register_on_message(self, on_message: Callable):
193
- self._client.on_message = on_message
194
-
195
- def run_in_background(
196
- self,
197
- on_message: Callable,
198
- topic: str | None = None,
199
- qos: int = 0,
200
- ) -> None:
201
- """
202
- Run the MQTT client in the background.
203
-
204
- Args:
205
- on_message: Callback function to run when a message is received
206
- topic: MQTT topic to listen to
207
-
208
- Returns:
209
- None
210
- """
211
- if self._client is None or not self._client.is_connected():
212
- self.connect()
213
-
214
- if topic:
215
- self.subscribe(topic, qos=qos)
216
-
217
- self._client.on_message = on_message
218
- self._client.loop_start()
219
-
220
- def run_until_break(
221
- self,
222
- on_message: Callable,
223
- topic: str | None = None,
224
- qos: int = 0,
225
- ):
226
- """
227
- Run the MQTT client until a break signal is received.
228
-
229
- Args:
230
- on_message: Callback function to run when a message is received
231
- topic: MQTT topic to listen to
232
-
233
- Returns:
234
- None
235
- """
236
- if self._client is None or not self._client.is_connected():
237
- self.connect()
238
-
239
- if topic:
240
- self.subscribe(topic, qos=qos)
241
-
242
- self._client.on_message = on_message
243
- self._client.loop_forever()
244
-
245
- def start_listener(
246
- self, on_message: Callable, topic: str | None = None, background: bool = False, qos: int = 0
247
- ) -> None:
248
- """
249
- Start the MQTT listener.
250
-
251
- Args:
252
- on_message: Callback function to run when a message is received
253
- topic: MQTT topic to listen to
254
- background: Run the listener in the background
255
-
256
- Returns:
257
- None
258
- """
259
- if background:
260
- self.run_in_background(on_message, topic, qos)
261
- else:
262
- self.run_until_break(on_message, topic, qos)
263
-
264
- def stop_listener(
265
- self,
266
- ) -> None:
267
- """
268
- Stop the MQTT listener.
269
-
270
- Returns:
271
- None
272
- """
273
- self._client.loop_stop()
274
- logger.info("Client stopped.")
275
-
276
- @classmethod
277
- def from_event_broker(cls, base_dir: str | None = None):
278
- base_dir = base_dir or str(Path.cwd())
279
-
280
- event_broker_cfg = Config.load(base_dir=base_dir).project.worker.event_broker
281
- if event_broker_cfg is not None:
282
- if event_broker_cfg.get("type", None) == "mqtt":
283
- logger.debug(f"{event_broker_cfg}")
284
- return cls(
285
- user=event_broker_cfg.get("username", None),
286
- pw=event_broker_cfg.get("password", None),
287
- host=event_broker_cfg.get("host", "localhost"),
288
- port=event_broker_cfg.get("port", 1883),
289
- transport=event_broker_cfg.get("transport", "tcp"),
290
- clean_session=event_broker_cfg.get("clean_session", True),
291
- client_id=event_broker_cfg.get("client_id", None),
292
- client_id_suffix=event_broker_cfg.get("client_id_suffix", None),
293
- topic=event_broker_cfg.get("topic", None),
294
- qos=event_broker_cfg.get("qos", 0),
295
- )
296
- raise ValueError("No event broker configuration found in config file.")
297
- else:
298
- raise ValueError("No event broker configuration found in config file.")
299
-
300
- @classmethod
301
- def from_config(cls, cfg: dict):
302
- return cls(
303
- user=cfg.get("user", None),
304
- pw=cfg.get("pw", None),
305
- host=cfg.get("host", "localhost"),
306
- port=cfg.get("port", 1883),
307
- transport=cfg.get("transport", "tcp"),
308
- )
309
-
310
- @classmethod
311
- def from_dict(cls, cfg: dict):
312
- return cls(
313
- user=cfg.get("user", None),
314
- pw=cfg.get("pw", None),
315
- host=cfg.get("host", "localhost"),
316
- port=cfg.get("port", 1883),
317
- transport=cfg.get("transport", "tcp"),
318
- clean_session=cfg.get("clean_session", True),
319
- client_id=cfg.get("client_id", None),
320
- client_id_suffix=cfg.get("client_id_suffix", None),
321
- topic=cfg.get("topic", None),
322
- qos=cfg.get("qos", 0),
323
- )
324
-
325
- def run_pipeline_on_message(
326
- self,
327
- name: str,
328
- topic: str | None = None,
329
- inputs: dict | None = None,
330
- final_vars: list | None = None,
331
- config: dict | None = None,
332
- executor: str | None = None,
333
- with_tracker: bool | None = None,
334
- with_opentelemetry: bool | None = None,
335
- with_progressbar: bool | None = None,
336
- reload: bool = False,
337
- result_expiration_time: float | dt.timedelta = 0,
338
- as_job: bool = False,
339
- base_dir: str | None = None,
340
- storage_options: dict = {},
341
- fs: AbstractFileSystem | None = None,
342
- background: bool = False,
343
- qos: int = 0,
344
- config_hook: Callable[[bytes, int], dict] | None = None,
345
- **kwargs,
346
- ):
347
- """
348
- Start a pipeline listener that listens to a topic and processes the message using a pipeline.
349
-
350
- Args:
351
- name: Name of the pipeline
352
- topic: MQTT topic to listen to
353
- inputs: Inputs for the pipeline
354
- final_vars: Final variables for the pipeline
355
- config: Configuration for the pipeline driver
356
- executor: Executor to use for the pipeline
357
- with_tracker: Use tracker for the pipeline
358
- with_opentelemetry: Use OpenTelemetry for the pipeline
359
- with_progressbar: Use progress for the pipeline
360
- reload: Reload the pipeline
361
- result_expiration_time: Result expiration time for the pipeline
362
- as_job: Run the pipeline as a job
363
- base_dir: Base directory for the pipeline
364
- storage_options: Storage options for the pipeline
365
- fs: File system for the pipeline
366
- background: Run the listener in the background
367
- **kwargs: Additional keyword arguments
368
-
369
- Returns:
370
- MQTTClient: MQTT client
371
- """
372
- if inputs is None:
373
- inputs = {}
374
-
375
- if config_hook is not None and not callable(config_hook):
376
- raise ValueError("config_hook must be a callable function")
377
-
378
- def on_message(client, userdata, msg):
379
- logger.info(f"Received message on topic {topic}")
380
-
381
- inputs["payload"] = msg.payload
382
- inputs["topic"] = msg.topic
383
-
384
- if config_hook is not None:
385
- config = config_hook(inputs["payload"], inputs["topic"])
386
- logger.debug(f"Config from hook: {config}")
387
- with Pipeline(
388
- name=name, storage_options=storage_options, fs=fs, base_dir=base_dir
389
- ) as pipeline:
390
- try:
391
- if as_job:
392
- pipeline.add_job(
393
- inputs=inputs,
394
- final_vars=final_vars,
395
- executor=executor,
396
- config=config,
397
- with_tracker=with_tracker,
398
- with_opentelemetry=with_opentelemetry,
399
- with_progressbar=with_progressbar,
400
- reload=reload,
401
- result_expiration_time=result_expiration_time,
402
- **kwargs,
403
- )
404
- else:
405
- pipeline.run(
406
- inputs=inputs,
407
- final_vars=final_vars,
408
- executor=executor,
409
- config=config,
410
- with_tracker=with_tracker,
411
- with_opentelemetry=with_opentelemetry,
412
- with_progressbar=with_progressbar,
413
- reload=reload,
414
- result_expiration_time=result_expiration_time,
415
- **kwargs,
416
- )
417
- logger.success("Message processed successfully")
418
- return
419
- except Exception as e:
420
- _ = e
421
- logger.exception(e)
422
-
423
- logger.warning("Message processing failed")
424
-
425
- self.start_listener(on_message=on_message, topic=topic, background=background, qos=qos)
426
-
427
-
428
- def start_listener(
429
- on_message: Callable,
430
- topic: str | None = None,
431
- background: bool = False,
432
- mqtt_cfg: dict = {},
433
- base_dir: str | None = None,
434
- username: str | None = None,
435
- password: str | None = None,
436
- host: str | None = None,
437
- port: int | None = None,
438
- ) -> None:
439
- """
440
- Start the MQTT listener.
441
-
442
- The connection to the MQTT broker is established using the provided configuration of a
443
- MQTT event broker defined in the project configuration file `conf/project.toml`.
444
- If no configuration is found, you have to provide either the argument `mqtt_cfg`, dict with the
445
- connection parameters or the arguments `username`, `password`, `host`, and `port`.
446
-
447
- Args:
448
- on_message: Callback function to run when a message is received
449
- topic: MQTT topic to listen to
450
- background: Run the listener in the background
451
- mqtt_cfg: MQTT client configuration. Use either this or arguments
452
- username, password, host, and port.
453
- base_dir: Base directory for the MQTT client
454
- username: Username for the MQTT client
455
- password: Password for the MQTT client
456
- host: Host for the MQTT client
457
- port: Port for the MQTT client
458
-
459
- Returns:
460
- None
461
- """
462
- try:
463
- client = MQTTManager.from_event_broker(base_dir)
464
- except ValueError:
465
- if mqtt_cfg:
466
- client = MQTTManager.from_dict(mqtt_cfg)
467
- elif host and port:
468
- client = MQTTManager(
469
- username=username,
470
- password=password,
471
- host=host,
472
- port=port,
473
- )
474
- else:
475
- raise ValueError(
476
- "No client configuration found. Please provide a client configuration "
477
- "or a FlowerPower project base directory, in which a event broker is "
478
- "configured in the `config/project.yml` file."
479
- )
480
-
481
- client.start_listener(on_message=on_message, topic=topic, background=background)
482
-
483
-
484
- def run_pipeline_on_message(
485
- name: str,
486
- topic: str | None = None,
487
- inputs: dict | None = None,
488
- final_vars: list | None = None,
489
- config: dict | None = None,
490
- executor: str | None = None,
491
- with_tracker: bool | None = None,
492
- with_opentelemetry: bool | None = None,
493
- with_progressbar: bool | None = None,
494
- reload: bool = False,
495
- result_expiration_time: float | dt.timedelta = 0,
496
- as_job: bool = False,
497
- base_dir: str | None = None,
498
- storage_options: dict = {},
499
- fs: AbstractFileSystem | None = None,
500
- background: bool = False,
501
- mqtt_cfg: dict = {},
502
- host: str | None = None,
503
- port: int | None = None,
504
- username: str | None = None,
505
- password: str | None = None,
506
- clean_session: bool = True,
507
- qos: int = 0,
508
- client_id: str | None = None,
509
- client_id_suffix: str | None = None,
510
- config_hook: Callable[[bytes, int], dict] | None = None,
511
- **kwargs,
512
- ):
513
- """
514
- Start a pipeline listener that listens to a topic and processes the message using a pipeline.
515
-
516
- Args:
517
- name: Name of the pipeline
518
- topic: MQTT topic to listen to
519
- inputs: Inputs for the pipeline
520
- final_vars: Final variables for the pipeline
521
- config: Configuration for the pipeline driver
522
- executor: Executor to use for the pipeline
523
- with_tracker: Use tracker for the pipeline
524
- with_opentelemetry: Use OpenTelemetry for the pipeline
525
- reload: Reload the pipeline
526
- result_expiration_time: Result expiration time for the pipeline
527
- as_job: Run the pipeline as a job
528
- base_dir: Base directory for the pipeline
529
- storage_options: Storage options for the pipeline
530
- fs: File system for the pipeline
531
- background: Run the listener in the background
532
- mqtt_cfg: MQTT client configuration. Use either this or arguments
533
- username, password, host, and port.
534
- host: Host for the MQTT client
535
- port: Port for the MQTT client
536
- username: Username for the MQTT client
537
- password: Password for the MQTT Client
538
- clean_session: Clean session for the MQTT client
539
- qos: Quality of Service for the MQTT client
540
- client_id: Client ID for the MQTT client
541
- client_id_suffix: Client ID suffix for the MQTT client
542
- config_hook: Hook function to modify the configuration of the pipeline
543
- **kwargs: Additional keyword arguments
544
- """
545
- try:
546
- client = MQTTManager.from_event_broker(base_dir)
547
- except ValueError:
548
- if mqtt_cfg:
549
- client = MQTTManager.from_dict(mqtt_cfg)
550
- elif host and port:
551
- client = MQTTManager(
552
- user=username,
553
- pw=password,
554
- host=host,
555
- port=port,
556
- clean_session=clean_session,
557
- client_id=client_id,
558
- topic=topic,
559
- qos=qos,
560
- client_id_suffix=client_id_suffix,
561
- )
562
- else:
563
- raise ValueError(
564
- "No client configuration found. Please provide a client configuration "
565
- "or a FlowerPower project base directory, in which a event broker is "
566
- "configured in the `config/project.yml` file."
567
- )
568
-
569
- if client._client_id is None and client_id is not None:
570
- client._client_id = client_id
571
-
572
- if client._client_id_suffix is None and client_id_suffix is not None:
573
- client._client_id_suffix = client_id_suffix
574
-
575
- '''
576
- cli_clean_session | config_clean_session | result
577
- TRUE TRUE TRUE
578
- FALSE FALSE FALSE
579
- FALSE TRUE FALSE
580
- TRUE FALSE FALSE
581
-
582
- Clean session should only use default value if neither cli nor config source says otherwise
583
- '''
584
- client._clean_session = client._clean_session and clean_session
585
-
586
- if client.topic is None and topic is not None:
587
- client.topic = topic
588
-
589
- client.run_pipeline_on_message(
590
- name=name,
591
- topic=topic,
592
- inputs=inputs,
593
- final_vars=final_vars,
594
- config=config,
595
- executor=executor,
596
- with_tracker=with_tracker,
597
- with_opentelemetry=with_opentelemetry,
598
- with_progressbar=with_progressbar,
599
- reload=reload,
600
- result_expiration_time=result_expiration_time,
601
- as_job=as_job,
602
- base_dir=base_dir,
603
- storage_options=storage_options,
604
- fs=fs,
605
- background=background,
606
- qos=qos,
607
- config_hook=config_hook,
608
- **kwargs,
609
- )