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.
- flowerpower/__init__.py +17 -2
- flowerpower/cfg/__init__.py +201 -149
- flowerpower/cfg/base.py +122 -24
- flowerpower/cfg/pipeline/__init__.py +254 -0
- flowerpower/cfg/pipeline/adapter.py +66 -0
- flowerpower/cfg/pipeline/run.py +40 -11
- flowerpower/cfg/pipeline/schedule.py +69 -79
- flowerpower/cfg/project/__init__.py +149 -0
- flowerpower/cfg/project/adapter.py +57 -0
- flowerpower/cfg/project/job_queue.py +165 -0
- flowerpower/cli/__init__.py +92 -37
- flowerpower/cli/job_queue.py +878 -0
- flowerpower/cli/mqtt.py +32 -1
- flowerpower/cli/pipeline.py +559 -406
- flowerpower/cli/utils.py +29 -18
- flowerpower/flowerpower.py +12 -8
- flowerpower/fs/__init__.py +20 -2
- flowerpower/fs/base.py +350 -26
- flowerpower/fs/ext.py +797 -216
- flowerpower/fs/storage_options.py +1097 -55
- flowerpower/io/base.py +13 -18
- flowerpower/io/loader/__init__.py +28 -0
- flowerpower/io/loader/deltatable.py +7 -10
- flowerpower/io/metadata.py +1 -0
- flowerpower/io/saver/__init__.py +28 -0
- flowerpower/io/saver/deltatable.py +4 -3
- flowerpower/job_queue/__init__.py +252 -0
- flowerpower/job_queue/apscheduler/__init__.py +11 -0
- flowerpower/job_queue/apscheduler/_setup/datastore.py +110 -0
- flowerpower/job_queue/apscheduler/_setup/eventbroker.py +93 -0
- flowerpower/job_queue/apscheduler/manager.py +1063 -0
- flowerpower/job_queue/apscheduler/setup.py +524 -0
- flowerpower/job_queue/apscheduler/trigger.py +169 -0
- flowerpower/job_queue/apscheduler/utils.py +309 -0
- flowerpower/job_queue/base.py +382 -0
- flowerpower/job_queue/rq/__init__.py +10 -0
- flowerpower/job_queue/rq/_trigger.py +37 -0
- flowerpower/job_queue/rq/concurrent_workers/gevent_worker.py +226 -0
- flowerpower/job_queue/rq/concurrent_workers/thread_worker.py +231 -0
- flowerpower/job_queue/rq/manager.py +1449 -0
- flowerpower/job_queue/rq/setup.py +150 -0
- flowerpower/job_queue/rq/utils.py +69 -0
- flowerpower/pipeline/__init__.py +5 -0
- flowerpower/pipeline/base.py +118 -0
- flowerpower/pipeline/io.py +407 -0
- flowerpower/pipeline/job_queue.py +505 -0
- flowerpower/pipeline/manager.py +1586 -0
- flowerpower/pipeline/registry.py +560 -0
- flowerpower/pipeline/runner.py +560 -0
- flowerpower/pipeline/visualizer.py +142 -0
- flowerpower/plugins/mqtt/__init__.py +12 -0
- flowerpower/plugins/mqtt/cfg.py +16 -0
- flowerpower/plugins/mqtt/manager.py +789 -0
- flowerpower/settings.py +110 -0
- flowerpower/utils/logging.py +21 -0
- flowerpower/utils/misc.py +57 -9
- flowerpower/utils/sql.py +122 -24
- flowerpower/utils/templates.py +2 -142
- flowerpower-1.0.0b2.dist-info/METADATA +324 -0
- flowerpower-1.0.0b2.dist-info/RECORD +94 -0
- flowerpower/_web/__init__.py +0 -61
- flowerpower/_web/routes/config.py +0 -103
- flowerpower/_web/routes/pipelines.py +0 -173
- flowerpower/_web/routes/scheduler.py +0 -136
- flowerpower/cfg/pipeline/tracker.py +0 -14
- flowerpower/cfg/project/open_telemetry.py +0 -8
- flowerpower/cfg/project/tracker.py +0 -11
- flowerpower/cfg/project/worker.py +0 -19
- flowerpower/cli/scheduler.py +0 -309
- flowerpower/cli/web.py +0 -44
- flowerpower/event_handler.py +0 -23
- flowerpower/mqtt.py +0 -609
- flowerpower/pipeline.py +0 -2499
- flowerpower/scheduler.py +0 -680
- flowerpower/tui.py +0 -79
- flowerpower/utils/datastore.py +0 -186
- flowerpower/utils/eventbroker.py +0 -127
- flowerpower/utils/executor.py +0 -58
- flowerpower/utils/trigger.py +0 -140
- flowerpower-0.9.13.1.dist-info/METADATA +0 -586
- flowerpower-0.9.13.1.dist-info/RECORD +0 -76
- /flowerpower/{cfg/pipeline/params.py → cli/worker.py} +0 -0
- {flowerpower-0.9.13.1.dist-info → flowerpower-1.0.0b2.dist-info}/WHEEL +0 -0
- {flowerpower-0.9.13.1.dist-info → flowerpower-1.0.0b2.dist-info}/entry_points.txt +0 -0
- {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()
|
flowerpower/event_handler.py
DELETED
@@ -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
|
-
)
|