feat: add connect-mqtt command
This commit is contained in:
22
README.md
22
README.md
@@ -16,8 +16,7 @@ Copyright (C) 2023 Vladimir Vukicevic
|
||||
|
||||
## Usage
|
||||
|
||||
Python 3 is required. There are no other requirements, but you should
|
||||
install the `alive-progress` package for nicer progress bars (`pip3 install alive-progress`).
|
||||
Python 3 is required. Use `pip install -r requirements.txt` to install dependencies.
|
||||
|
||||
### Printer status
|
||||
|
||||
@@ -28,6 +27,15 @@ $ ./cassini.py status
|
||||
File Transfer Status: 0
|
||||
```
|
||||
|
||||
### Printer(s) full status
|
||||
|
||||
```
|
||||
$ ./cassini.py status-full
|
||||
```
|
||||
|
||||
Will print out the full json status of all printers found.
|
||||
|
||||
|
||||
### Watch live print progress
|
||||
|
||||
```
|
||||
@@ -49,6 +57,16 @@ MyFile.goo |██████████████████████
|
||||
$ ./cassini.py [--printer printer_ip] print Myfile.goo
|
||||
```
|
||||
|
||||
### Connect printer(s) to particular MQTT server
|
||||
|
||||
```
|
||||
$ [sudo] python cassini.py [--printer printer_ip] connect-mqtt mqtt.local:1883
|
||||
```
|
||||
|
||||
Probably you need to use `sudo` (at least on MacOS, or you'll get `Permission denied: could not open /dev/bpf0. Make sure to be running Scapy as root`).
|
||||
|
||||
If `--printer` is not specified, all printers found will be connected to the same MQTT server.
|
||||
|
||||
## Protocol Description
|
||||
|
||||
The protocol is pretty simple. There is no encryption or any obfuscation that I could find.
|
||||
|
24
cassini.py
24
cassini.py
@@ -7,6 +7,8 @@
|
||||
# License: MIT
|
||||
#
|
||||
import os
|
||||
import pprint
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import asyncio
|
||||
@@ -63,6 +65,11 @@ def do_status(printers):
|
||||
print(f" File: {print_info['Filename']}")
|
||||
print(f" File Transfer Status: {FileStatus(file_info['Status']).name}")
|
||||
|
||||
def do_status_full(printers):
|
||||
for i, p in enumerate(printers):
|
||||
pprint.pprint(p.desc)
|
||||
|
||||
|
||||
def do_watch(printer, interval=5, broadcast=None):
|
||||
status = printer.status()
|
||||
with alive_bar(total=status['totalLayers'], manual=True, elapsed=False, title=status['filename']) as bar:
|
||||
@@ -135,6 +142,7 @@ def main():
|
||||
subparsers = parser.add_subparsers(title="commands", dest="command", required=True)
|
||||
|
||||
parser_status = subparsers.add_parser('status', help='Discover and display status of all printers')
|
||||
parser_status_full = subparsers.add_parser('status-full', help='Discover and display full status of all printers')
|
||||
|
||||
parser_watch = subparsers.add_parser('watch', help='Continuously update the status of the selected printer')
|
||||
parser_watch.add_argument('--interval', type=int, help='Status update interval (seconds)', default=5)
|
||||
@@ -146,6 +154,9 @@ def main():
|
||||
parser_print = subparsers.add_parser('print', help='Start printing a file already present on the printer')
|
||||
parser_print.add_argument('filename', help='File to print')
|
||||
|
||||
parser_connect_mqtt = subparsers.add_parser('connect-mqtt', help='Connect printer to particular MQTT server')
|
||||
parser_connect_mqtt.add_argument('address', help='MQTT host and port, e.g. "192.168.1.33:1883" or "mqtt.local:1883"')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.debug:
|
||||
@@ -171,6 +182,19 @@ def main():
|
||||
do_status(printers)
|
||||
sys.exit(0)
|
||||
|
||||
if args.command == "status-full":
|
||||
do_status_full(printers)
|
||||
sys.exit(0)
|
||||
|
||||
if args.command == "connect-mqtt":
|
||||
mqtt_host, mqtt_port = args.address.split(':')
|
||||
try:
|
||||
mqtt_host = socket.gethostbyname(mqtt_host)
|
||||
except socket.gaierror:
|
||||
pass
|
||||
for p in printers:
|
||||
p.connect_mqtt(mqtt_host, mqtt_port)
|
||||
|
||||
if args.command == "watch":
|
||||
do_watch(printer, interval=args.interval, broadcast=broadcast)
|
||||
sys.exit(0)
|
||||
|
@@ -1 +1,2 @@
|
||||
alive-progress==3.1.4
|
||||
scapy==2.5.0
|
@@ -14,6 +14,7 @@ import asyncio
|
||||
import logging
|
||||
import random
|
||||
from enum import Enum
|
||||
from scapy.all import IP, UDP, send
|
||||
|
||||
SATURN_UDP_PORT = 3000
|
||||
|
||||
@@ -320,3 +321,16 @@ class SaturnPrinter:
|
||||
}
|
||||
self.mqtt.publish('/sdcp/request/' + self.id, json.dumps(cmd_data))
|
||||
return hexstr
|
||||
|
||||
def connect_mqtt(self, mqtt_host, mqtt_port):
|
||||
"""
|
||||
Connect printer to MQTT server
|
||||
|
||||
It spoofs UDP packet to the printer to make it connect to particular MQTT server
|
||||
"""
|
||||
ip = IP(dst=self.addr[0], src=mqtt_host)
|
||||
any_src_port = random.randint(1024, 65535)
|
||||
udp = UDP(sport=any_src_port, dport=SATURN_UDP_PORT)
|
||||
payload = f"M66666 {mqtt_port}"
|
||||
packet = ip / udp / payload
|
||||
send(packet)
|
||||
|
Reference in New Issue
Block a user