diff --git a/README.md b/README.md index bfb8c9b..fae142f 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,18 @@ -Meshview -======== - -Now running at https://meshview.bayme.sh +# Meshview This project watches a MQTT topic for meshtastic messages, imports them to a database and has a web UI to view them. -Requires Python 3.12 -Running -------- +Requires **`python3.12`** and **`graphviz`**. + +## Preparing + Clone the repo from github with: ``` bash git clone --recurse-submodules https://github.com/pablorevilla-meshtastic/meshview.git ``` -It is important to include the `--recurse-submodules` flag or the meshtastic protobufs wont be included +> [!NOTE] +> It is important to include the `--recurse-submodules` flag or the meshtastic protobufs won't be included. Create a python virtual environment: ``` bash @@ -28,23 +27,14 @@ You also need to install `graphviz`: ``` bash sudo apt-get install graphviz ``` +Edit `config.ini` to change the MQTT server, username, password, and topic(s) as necessary. + +You may also change the web server port from the ***default 8081***. +https://github.com/madeofstown/meshview/blob/c9d65a078af5e71a6815c142dbb11e5868f8885b/config.ini#L1-L15 + +## Running Meshview -To run Meshview: ``` bash ./env/bin/python main.py ``` -Now you can hit http://localhost/ - -Other Options: -* `--port` - - Web server port, default is `8081` - -* `--mqtt-server` - - MQTT Server, default is `mqtt.bayme.sh` - -* `--topic` - - MQTT Topic, default is `msh/US/bayarea/#` - +Now you can hit http://localhost:8081/ diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..693429b --- /dev/null +++ b/config.ini @@ -0,0 +1,15 @@ +[server] +bind = * +port = 8081 +tls_cert = +acme_challenge = + +[mqtt] +server = mqtt.bayme.sh +topics = ['msh/US/bayarea/#', 'msh/US/CA/mrymesh/#'] +port = 1883 +username = meshdev +password = large4cats + +[database] +connection_string = sqlite+aiosqlite:///packets.db \ No newline at end of file diff --git a/main.py b/main.py index adf44dc..7accc67 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,6 @@ import asyncio import argparse - +import configparser from meshview import mqtt_reader from meshview import database from meshview import store @@ -8,34 +8,56 @@ from meshview import web from meshview import http -async def load_database_from_mqtt(mqtt_server, topic): - async for topic, env in mqtt_reader.get_topic_envelopes(mqtt_server, topic): +async def load_database_from_mqtt(mqtt_server: str , mqtt_port: int, topic: str, mqtt_user: str | None = None, mqtt_passwd: str | None = None): + async for topic, env in mqtt_reader.get_topic_envelopes(mqtt_server, mqtt_port, topic, mqtt_user, mqtt_passwd): await store.process_envelope(topic, env) -async def main(args): - database.init_database(args.database) +async def main(config): + database.init_database(config["database"]["connection_string"]) await database.create_tables() + mqtt_user = None + mqtt_passwd = None + if config["mqtt"]["username"] != "": + mqtt_user: str = config["mqtt"]["username"] + if config["mqtt"]["password"] != "": + mqtt_passwd: str = config["mqtt"]["password"] + async with asyncio.TaskGroup() as tg: - tg.create_task(load_database_from_mqtt(args.mqtt_server, args.topic)) - tg.create_task(web.run_server(args.bind, args.port, args.tls_cert)) - if args.acme_challenge: - tg.create_task(http.run_server(args.bind, args.acme_challenge)) + tg.create_task( + load_database_from_mqtt(config["mqtt"]["server"], int(config["mqtt"]["port"]), config["mqtt"]["topics"], mqtt_user, mqtt_passwd) + ) + tg.create_task( + web.run_server( + config["server"]["bind"], + int(config["server"]["port"]), + config["server"].get("tls_cert"), + ) + ) + if config["server"].get("acme_challenge"): + tg.create_task( + http.run_server( + config["server"]["bind"], config["server"]["acme_challenge"] + ) + ) + + +def load_config(file_path): + """Load configuration from an INI-style text file.""" + config_parser = configparser.ConfigParser() + config_parser.read(file_path) + + # Convert to a dictionary for easier access + config = {section: dict(config_parser.items(section)) for section in config_parser.sections()} + return config if __name__ == '__main__': - parser = argparse.ArgumentParser('meshview') - parser.add_argument('--bind', nargs='*', default=['*']) - parser.add_argument('--acme-challenge') - parser.add_argument('--port', default=8081, type=int) - parser.add_argument('--tls-cert') - - parser.add_argument('--mqtt-server', default='mqtt.bayme.sh') - parser.add_argument('--topic', nargs='*', default=['msh/US/bayarea/#']) - - parser.add_argument('--database', default='sqlite+aiosqlite:///packets.db') - + parser = argparse.ArgumentParser("meshview") + parser.add_argument("--config", help="Path to the configuration file.", default='config.ini') args = parser.parse_args() - asyncio.run(main(args)) + config = load_config(args.config) + + asyncio.run(main(config)) \ No newline at end of file diff --git a/meshview/mqtt_reader.py b/meshview/mqtt_reader.py index 5453f5c..5215f98 100644 --- a/meshview/mqtt_reader.py +++ b/meshview/mqtt_reader.py @@ -25,12 +25,12 @@ def decrypt(packet): pass -async def get_topic_envelopes(mqtt_server, topics): +async def get_topic_envelopes(mqtt_server, mqtt_port, topics, mqtt_user, mqtt_passwd): identifier = str(random.getrandbits(16)) while True: try: async with aiomqtt.Client( - mqtt_server, username="meshdev", password="large4cats" , identifier=identifier, + mqtt_server, port=mqtt_port , username=mqtt_user, password=mqtt_passwd , identifier=identifier, ) as client: for topic in topics: await client.subscribe(topic)