перевел все на mqtt 5, оно вроде и работало на unix-порте, но в составе прошивки не завелось. вечером разберусь почему

This commit is contained in:
2026-06-10 18:47:58 +03:00
parent 59d159d683
commit 1786760a46
4 changed files with 401 additions and 165 deletions
+214
View File
@@ -0,0 +1,214 @@
import websocket
import struct
import random
# --- Подключение ---
URL = "wss://insert-me.ru/mqtt"
USER = "esp8266-insert-me"
PASS = "insert-me"
CLIENT_ID = f"pyclient_{random.randint(0, 10000)}"
TOPIC = "#"
def encode_length(length: int) -> bytes:
enc = bytearray()
while True:
digit = length & 0x7F
length >>= 7
if length > 0:
digit |= 0x80
enc.append(digit)
if length == 0:
break
return bytes(enc)
def decode_length(data: bytes, start: int = 0) -> tuple:
"""Декодирует остающуюся длину из data, возвращает (значение, количество_прочитанных_байт)."""
length = 0
multiplier = 1
pos = start
while True:
if pos >= len(data):
raise ValueError("Incomplete length encoding")
digit = data[pos]
length += (digit & 0x7F) * multiplier
multiplier <<= 7
pos += 1
if not (digit & 0x80):
break
return length, pos - start
def build_connect(client_id: str, username: str = None, password: str = None,
keepalive: int = 60, clean_start: bool = True) -> bytes:
protocol_name = b"MQTT"
protocol_level = 5
connect_flags = 0
if clean_start:
connect_flags |= 0x02
if username is not None:
connect_flags |= 0x80
if password is not None:
connect_flags |= 0x40
properties = b"\x00" # без свойств
client_id_bytes = client_id.encode('utf-8')
payload = struct.pack("!H", len(client_id_bytes)) + client_id_bytes
if username is not None:
user_bytes = username.encode('utf-8')
payload += struct.pack("!H", len(user_bytes)) + user_bytes
if password is not None:
pass_bytes = password.encode('utf-8')
payload += struct.pack("!H", len(pass_bytes)) + pass_bytes
variable_header = (
struct.pack("!H", len(protocol_name)) +
protocol_name +
bytes([protocol_level]) +
bytes([connect_flags]) +
struct.pack("!H", keepalive) +
properties
)
remaining_length = len(variable_header) + len(payload)
fixed_header = b"\x10" + encode_length(remaining_length)
return fixed_header + variable_header + payload
def build_subscribe(topic: str, packet_id: int = 1, qos: int = 0) -> bytes:
fixed_header = b"\x82"
topic_bytes = topic.encode('utf-8')
properties = b"\x00"
payload = struct.pack("!H", packet_id) + properties
payload += struct.pack("!H", len(topic_bytes)) + topic_bytes
payload += bytes([qos])
remaining_length = len(payload)
return fixed_header + encode_length(remaining_length) + payload
def parse_publish(data: bytes) -> tuple:
"""
Распарсить PUBLISH пакет MQTT 5.
Возвращает (topic, payload, qos, packet_id) или (None, None, None, None) если ошибка.
"""
if not data or data[0] & 0xF0 != 0x30:
return None, None, None, None
# Пропускаем фиксированный заголовок (1 байт) и декодируем длину
pos = 1
remaining_length, consumed = decode_length(data, pos)
pos += consumed
# Читаем топик
if pos + 2 > len(data):
return None, None, None, None
topic_len = struct.unpack("!H", data[pos:pos+2])[0]
pos += 2
if pos + topic_len > len(data):
return None, None, None, None
topic = data[pos:pos+topic_len].decode('utf-8', errors='replace')
pos += topic_len
# QoS и Packet ID
qos = (data[0] >> 1) & 0x03
packet_id = None
if qos > 0:
if pos + 2 > len(data):
return None, None, None, None
packet_id = struct.unpack("!H", data[pos:pos+2])[0]
pos += 2
# Свойства: сначала длина свойств (1 байт, т.к. у нас всегда 0)
if pos >= len(data):
return None, None, None, None
prop_len = data[pos]
pos += 1
if prop_len > 0:
# Если есть свойства, пропускаем их (упрощённо)
pos += prop_len
# Всё что осталось – payload
payload = data[pos:].decode('utf-8', errors='replace')
return topic, payload, qos, packet_id
def main():
# Создаём WebSocket
ws = websocket.create_connection(URL, subprotocols=["mqtt"], timeout=10)
# 1. CONNECT
connect_pkt = build_connect(CLIENT_ID, username=USER, password=PASS, keepalive=60)
print("Sending CONNECT:", connect_pkt.hex())
ws.send_binary(connect_pkt)
# 2. Читаем CONNACK
try:
data = ws.recv()
print("Received:", data.hex() if data else "(empty)")
if data and data[0] == 0x20:
if len(data) >= 4:
reason_code = data[3]
if reason_code == 0:
print("✅ Connected successfully")
else:
print(f"❌ Connection refused, reason code: {reason_code}")
ws.close()
return
else:
print("Malformed CONNACK")
ws.close()
return
else:
print("Not a CONNACK received")
ws.close()
return
except Exception as e:
print(f"Error receiving CONNACK: {e}")
ws.close()
return
# 3. Подписка
sub_pkt = build_subscribe(TOPIC, packet_id=1, qos=0)
print("Sending SUBSCRIBE:", sub_pkt.hex())
ws.send_binary(sub_pkt)
suback = ws.recv()
print("SUBACK:", suback.hex())
if suback and suback[0] == 0x90:
reason = suback[-1]
if reason == 0:
print(f"✅ Subscribed to '{TOPIC}'")
else:
print(f"❌ Subscribe failed, reason code: {reason}")
ws.close()
return
# 4. Цикл приёма
print("Waiting for messages... (Ctrl+C to stop)")
try:
ws.settimeout(5)
while True:
try:
msg = ws.recv()
except websocket.WebSocketTimeoutException:
ws.send_binary(b"\xC0\x00") # PINGREQ
continue
if not msg:
break
# Если PINGRESP – игнорируем
if msg[0] == 0xD0 and len(msg) == 2 and msg[1] == 0x00:
continue
# Парсим PUBLISH
topic, payload, qos, pid = parse_publish(msg)
if topic is not None:
print(f"📨 Topic: {topic}, Payload: {payload}")
if qos > 0:
print(f" (QoS={qos}, PacketID={pid})")
else:
print(f"Other MQTT packet: {msg.hex()}")
except KeyboardInterrupt:
pass
finally:
ws.close()
if __name__ == "__main__":
main()