diff --git a/.gitmodules b/.gitmodules index 6d8647c..4fd1c93 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "mqtt"] path = mqtt url = https://git.mount-mockery.de/pylib/mqtt.git +[submodule "report"] + path = report + url = https://git.mount-mockery.de/pylib/report.git diff --git a/config.py b/config.py new file mode 100644 index 0000000..cecd594 --- /dev/null +++ b/config.py @@ -0,0 +1,3 @@ +APP_NAME = 'mqtt_sniffer' + +DEBUG = True \ No newline at end of file diff --git a/mqtt_sniffer.py b/mqtt_sniffer.py index 15324b9..3388f83 100644 --- a/mqtt_sniffer.py +++ b/mqtt_sniffer.py @@ -1,19 +1,24 @@ import argparse +import config import getpass -import json import logging import mqtt import re +import report import time from textual.app import App, ComposeResult from textual.containers import Vertical -from textual.widgets import Footer, Header, Input, RichLog +from textual.widgets import Footer, Header, Input, RichLog, Button + +try: + from config import APP_NAME as ROOT_LOGGER_NAME +except ImportError: + ROOT_LOGGER_NAME = 'root' +logger = logging.getLogger(ROOT_LOGGER_NAME) -# TODO: Integrate sending of message (topic + payload) - -class MqttReceiver(object): +class MqttHandler(object): def __init__(self, app): self.app = app args = app.args @@ -22,9 +27,18 @@ class MqttReceiver(object): self.mqtt_client = mqtt.mqtt_client('mqtt_sniffer', args.hostname, args.port, username=args.username, password=password) self.mqtt_client.add_callback("#", self.__rx__) + def __get_logger__(self, prefix, topic): + return logging.getLogger(ROOT_LOGGER_NAME).getChild(prefix + '.' + topic.replace('/', '.')) + def __rx__(self, client, userdata, message): + logger = self.__get_logger__('rx', message.topic) + logger.debug("Received message: topic=%s, payload=%s type=%s", message.topic, repr(message.payload), repr(type(message.payload))) self.app.call_from_thread(self.app.add_log, message) + def send(self, topic, payload): + logger = self.__get_logger__('tx', topic) + logger.debug("Sending message: topic=%s, payload=%s type=%s", topic, repr(payload), repr(type(payload))) + self.mqtt_client.send(topic, payload) class MqttSniffer(App): """a textual application for viewing mqtt messages.""" @@ -38,8 +52,11 @@ class MqttSniffer(App): self.args = args self.password = password # + self.mqtt = None self.all_logs = [] self.topic_filter = "" + self.send_topic = "" + self.send_payload = "" self.log_display = RichLog(highlight=True, markup=True) def compose(self) -> ComposeResult: @@ -49,11 +66,14 @@ class MqttSniffer(App): yield self.log_display with Vertical(id="bottom-bar"): yield Input(placeholder="topic filter...", id="topic_filter") + yield Input(placeholder="topic...", id="send_topic") + yield Input(placeholder="payload...", id="send_payload") + yield Button("Send", variant="success", id="send_button") yield Footer() def on_mount(self) -> None: """start the mqtt receiver.""" - MqttReceiver(self) + self.mqtt = MqttHandler(self) def add_log(self, record) -> None: """Add new mqt messages and update the tui.""" @@ -74,15 +94,10 @@ class MqttSniffer(App): pass # No valid regular expression if topic_match: - try: - payload = json.loads(record.payload) - except: - payload = record.payload.decode('utf-8') - message = ( f"[[dim]{asctime}[/dim]] " f"[bold]{record.topic}[/bold] - " - f"{repr(payload)}" + f"{repr(record.payload)}" ) self.log_display.write(message) @@ -94,14 +109,29 @@ class MqttSniffer(App): self._apply_filters_to_log(record) def on_input_changed(self, message: Input.Changed) -> None: - """Update the filter and filtered messages after the user did changes.""" + """Update the tui inputs and execute task, if requireed.""" if message.input.id == "topic_filter": self.topic_filter = message.value + self._update_display() + elif message.input.id == "send_topic": + self.send_topic = message.value + elif message.input.id == "send_payload": + self.send_payload = message.value - self._update_display() + def on_button_pressed(self, event: Button.Pressed) -> None: + """Event handler called when a button is pressed.""" + if event.button.id == "send_button": + if self.mqtt is not None: + self.mqtt.send(self.send_topic, self.send_payload) if __name__ == "__main__": + # + # Logging + # + if config.DEBUG: + report.appLoggingConfigure(None, None, ((config.APP_NAME, logging.DEBUG), ), target_level=logging.WARNING, fmt=report.SHORT_FMT, host='localhost', port=19996) + # # Parse Arguments # diff --git a/report b/report new file mode 160000 index 0000000..74e6f20 --- /dev/null +++ b/report @@ -0,0 +1 @@ +Subproject commit 74e6f20d09cf76b3fbbdfa04c192b01708e50d5d diff --git a/style.tcss b/style.tcss index 159b353..44441e6 100644 --- a/style.tcss +++ b/style.tcss @@ -2,16 +2,30 @@ layout: vertical; overflow-y: scroll; scrollbar-gutter: stable; - height: 89%; + height: 1fr; } #bottom-bar { layout: grid; - grid-size: 1; - height: 11%; + grid-size: 4; + grid-columns: 3fr 2fr 2fr 1fr; + height: 4; border-top: solid $primary; } -#module_filter { +#topic_filter { width: 100%; } + +#send_button { + width: 100%; +} + +#send_topic { + width: 100%; +} + +#send_payload { + width: 100%; +} +