перевел все на mqtt 5, оно вроде и работало на unix-порте, но в составе прошивки не завелось. вечером разберусь почему
This commit is contained in:
+214
@@ -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()
|
||||||
+7
-6
@@ -1,6 +1,6 @@
|
|||||||
import machine
|
import machine
|
||||||
import time
|
import time
|
||||||
from machine import Pin, PWM
|
# from machine import Pin, PWM
|
||||||
|
|
||||||
_U16_MAX = 0xFFFF
|
_U16_MAX = 0xFFFF
|
||||||
|
|
||||||
@@ -92,11 +92,12 @@ class ServoInterface(PwmInterface):
|
|||||||
|
|
||||||
|
|
||||||
_interfaces = [
|
_interfaces = [
|
||||||
PwmInterface(f"/dorm/66/esp32-{machine.unique_id().hex()}/pwm0n0", Pin(13)),
|
# PwmInterface(f"/dorm/66/esp32-{machine.unique_id().hex()}/pwm0n0", Pin(13)),
|
||||||
PwmInterface(f"/dorm/66/esp32-{machine.unique_id().hex()}/pwm0n1", Pin(12)),
|
# PwmInterface(f"/dorm/66/esp32-{machine.unique_id().hex()}/pwm0n1", Pin(12)),
|
||||||
PwmInterface(f"/dorm/66/esp32-{machine.unique_id().hex()}/pwm0n2", Pin(14)),
|
# PwmInterface(f"/dorm/66/esp32-{machine.unique_id().hex()}/pwm0n2", Pin(14)),
|
||||||
PwmInterface(f"/dorm/66/esp32-{machine.unique_id().hex()}/pwm0n3", Pin(27)),
|
# PwmInterface(f"/dorm/66/esp32-{machine.unique_id().hex()}/pwm0n3", Pin(27)),
|
||||||
ServoInterface(f"/dorm/66/esp32-{machine.unique_id().hex()}/pwm1n0", Pin(32)),
|
# ServoInterface(f"/dorm/66/esp32-{machine.unique_id().hex()}/pwm1n0", Pin(32)),
|
||||||
|
BaseInterface(f"/home/38/esp8266/pwm0n0"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -1,5 +1,5 @@
|
|||||||
import errno
|
import errno
|
||||||
import network
|
# import network
|
||||||
import interfaces
|
import interfaces
|
||||||
import utime as time
|
import utime as time
|
||||||
from wssmqtt import *
|
from wssmqtt import *
|
||||||
@@ -7,9 +7,9 @@ from env import *
|
|||||||
|
|
||||||
interfaces.init()
|
interfaces.init()
|
||||||
|
|
||||||
wifi = network.WLAN(network.STA_IF)
|
# wifi = network.WLAN(network.STA_IF)
|
||||||
wifi.active(1)
|
# wifi.active(1)
|
||||||
wifi.connect(WIFI_SSID, WIFI_PASSWORD)
|
# wifi.connect(WIFI_SSID, WIFI_PASSWORD)
|
||||||
|
|
||||||
|
|
||||||
def do_connection():
|
def do_connection():
|
||||||
|
|||||||
+146
-125
@@ -8,24 +8,15 @@ import machine
|
|||||||
from websocket import websocket
|
from websocket import websocket
|
||||||
|
|
||||||
|
|
||||||
class WebSocketClient(websocket):
|
def create_ssl_socket(hostname):
|
||||||
def __init__(self, sock):
|
|
||||||
super().__init__(sock)
|
|
||||||
self.__sock = sock
|
|
||||||
|
|
||||||
def setblocking(self, value):
|
|
||||||
self.__sock.setblocking(value)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create_ssl_socket(hostname):
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
addr = socket.getaddrinfo(hostname, 443)[0][-1]
|
addr = socket.getaddrinfo(hostname, 443)[0][-1]
|
||||||
s.connect(addr)
|
s.connect(addr)
|
||||||
return ssl.wrap_socket(s)
|
return ssl.wrap_socket(s)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def create_websocket(hostname, path, protocol=None):
|
def create_websocket(hostname, path, protocol=None):
|
||||||
sock = WebSocketClient.create_ssl_socket(hostname)
|
sock = create_ssl_socket(hostname)
|
||||||
|
|
||||||
query = f"GET {path} HTTP/1.1\r\n"
|
query = f"GET {path} HTTP/1.1\r\n"
|
||||||
query += f"Host: {hostname}\r\n"
|
query += f"Host: {hostname}\r\n"
|
||||||
@@ -60,7 +51,7 @@ class WebSocketClient(websocket):
|
|||||||
print(header)
|
print(header)
|
||||||
header = sock.readline()
|
header = sock.readline()
|
||||||
|
|
||||||
return WebSocketClient(sock)
|
return websocket(sock)
|
||||||
|
|
||||||
|
|
||||||
class WSSMQTTException(Exception):
|
class WSSMQTTException(Exception):
|
||||||
@@ -68,15 +59,7 @@ class WSSMQTTException(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class WSSMQTTClient:
|
class WSSMQTTClient:
|
||||||
def __init__(
|
def __init__(self, host, path='/', client_id=f"micropython-unix", user=None, password=None, keepalive=15):
|
||||||
self,
|
|
||||||
host,
|
|
||||||
path='/',
|
|
||||||
client_id=f"client-{machine.unique_id().hex()}",
|
|
||||||
user=None,
|
|
||||||
password=None,
|
|
||||||
keepalive=0,
|
|
||||||
):
|
|
||||||
self.client_id = client_id
|
self.client_id = client_id
|
||||||
self.sock = None
|
self.sock = None
|
||||||
self.host = host
|
self.host = host
|
||||||
@@ -90,80 +73,132 @@ class WSSMQTTClient:
|
|||||||
self.lw_msg = None
|
self.lw_msg = None
|
||||||
self.lw_qos = 0
|
self.lw_qos = 0
|
||||||
self.lw_retain = False
|
self.lw_retain = False
|
||||||
|
self._packet_id_counter = 1
|
||||||
|
|
||||||
def _send_str(self, s):
|
def _encode_length(self, length):
|
||||||
self.sock.write(struct.pack("!H", len(s)))
|
enc = bytearray()
|
||||||
self.sock.write(s)
|
while True:
|
||||||
|
digit = length & 0x7F
|
||||||
|
length >>= 7
|
||||||
|
if length > 0:
|
||||||
|
digit |= 0x80
|
||||||
|
enc.append(digit)
|
||||||
|
if length == 0:
|
||||||
|
break
|
||||||
|
return bytes(enc)
|
||||||
|
|
||||||
def _recv_len(self):
|
def _decode_length(self, data, start = 0):
|
||||||
n = 0
|
"""Декодирует остающуюся длину из data, возвращает (значение, количество_прочитанных_байт)."""
|
||||||
sh = 0
|
length = 0
|
||||||
while 1:
|
multiplier = 1
|
||||||
b = self.sock.read(1)[0]
|
pos = start
|
||||||
n |= (b & 0x7F) << sh
|
while True:
|
||||||
if not b & 0x80:
|
if pos >= len(data):
|
||||||
return n
|
raise WSSMQTTException("Value error: Incomplete length encoding")
|
||||||
sh += 7
|
digit = data[pos]
|
||||||
|
length += (digit & 0x7F) * multiplier
|
||||||
|
multiplier <<= 7
|
||||||
|
pos += 1
|
||||||
|
if not (digit & 0x80):
|
||||||
|
break
|
||||||
|
return length, pos - start
|
||||||
|
|
||||||
|
def _build_connect(self):
|
||||||
|
protocol_name = b"MQTT"
|
||||||
|
protocol_level = 5
|
||||||
|
connect_flags = 0
|
||||||
|
connect_flags |= 0x02 # clean start
|
||||||
|
if self.user is not None:
|
||||||
|
connect_flags |= 0x80
|
||||||
|
if self.pswd is not None:
|
||||||
|
connect_flags |= 0x40
|
||||||
|
|
||||||
|
properties = b"\x00" # без свойств
|
||||||
|
|
||||||
|
client_id_bytes = self.client_id.encode('utf-8')
|
||||||
|
payload = struct.pack("!H", len(client_id_bytes)) + client_id_bytes
|
||||||
|
|
||||||
|
if self.user is not None:
|
||||||
|
user_bytes = self.user.encode('utf-8')
|
||||||
|
payload += struct.pack("!H", len(user_bytes)) + user_bytes
|
||||||
|
if self.pswd is not None:
|
||||||
|
pass_bytes = self.pswd.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", self.keepalive) + properties
|
||||||
|
|
||||||
|
remaining_length = len(variable_header) + len(payload)
|
||||||
|
fixed_header = b"\x10" + self._encode_length(remaining_length)
|
||||||
|
return fixed_header + variable_header + payload
|
||||||
|
|
||||||
|
def _parse_publish(self, data):
|
||||||
|
"""
|
||||||
|
Распарсить 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 set_callback(self, f):
|
def set_callback(self, f):
|
||||||
self.cb = f
|
self.cb = f
|
||||||
|
|
||||||
def set_last_will(self, topic, msg, retain=False, qos=0):
|
def connect(self):
|
||||||
assert 0 <= qos <= 2
|
|
||||||
assert topic
|
|
||||||
self.lw_topic = topic
|
|
||||||
self.lw_msg = msg
|
|
||||||
self.lw_qos = qos
|
|
||||||
self.lw_retain = retain
|
|
||||||
|
|
||||||
def connect(self, clean_session=True):
|
|
||||||
if self.sock is not None:
|
if self.sock is not None:
|
||||||
raise WSSMQTTException('Already connected')
|
raise WSSMQTTException('Already connected')
|
||||||
|
|
||||||
self.sock = WebSocketClient.create_websocket(self.host, self.path, protocol='mqtt')
|
self.sock = create_websocket(self.host, self.path, protocol='mqtt')
|
||||||
if self.sock is None:
|
if self.sock is None:
|
||||||
raise WSSMQTTException('Failed to create websocket')
|
raise WSSMQTTException('Failed to create websocket')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
premsg = bytearray(b"\x10\0\0\0\0\0")
|
self.sock.write(self._build_connect())
|
||||||
msg = bytearray(b"\x04MQTT\x04\x02\0\0")
|
data = self.sock.read()
|
||||||
|
if data and data[0] == 0x20:
|
||||||
sz = 10 + 2 + len(self.client_id)
|
if len(data) >= 4:
|
||||||
msg[6] = clean_session << 1
|
reason_code = data[3]
|
||||||
if self.user:
|
if reason_code != 0:
|
||||||
sz += 2 + len(self.user) + 2 + len(self.pswd)
|
raise WSSMQTTException(f'Connection refused, reason code: {reason_code}')
|
||||||
msg[6] |= 0xC0
|
else:
|
||||||
if self.keepalive:
|
raise WSSMQTTException(f'Malformed CONNACK {data}')
|
||||||
assert self.keepalive < 65536
|
else:
|
||||||
msg[7] |= self.keepalive >> 8
|
raise WSSMQTTException(f'Not a CONNACK received, received {data}')
|
||||||
msg[8] |= self.keepalive & 0x00FF
|
|
||||||
if self.lw_topic:
|
|
||||||
sz += 2 + len(self.lw_topic) + 2 + len(self.lw_msg)
|
|
||||||
msg[6] |= 0x4 | (self.lw_qos & 0x1) << 3 | (self.lw_qos & 0x2) << 3
|
|
||||||
msg[6] |= self.lw_retain << 5
|
|
||||||
|
|
||||||
i = 1
|
|
||||||
while sz > 0x7F:
|
|
||||||
premsg[i] = (sz & 0x7F) | 0x80
|
|
||||||
sz >>= 7
|
|
||||||
i += 1
|
|
||||||
premsg[i] = sz
|
|
||||||
|
|
||||||
self.sock.write(premsg, i + 2)
|
|
||||||
self.sock.write(msg)
|
|
||||||
# print(hex(len(msg)), hexlify(msg, ":"))
|
|
||||||
self._send_str(self.client_id)
|
|
||||||
if self.lw_topic:
|
|
||||||
self._send_str(self.lw_topic)
|
|
||||||
self._send_str(self.lw_msg)
|
|
||||||
if self.user:
|
|
||||||
self._send_str(self.user)
|
|
||||||
self._send_str(self.pswd)
|
|
||||||
resp = self.sock.read(4)
|
|
||||||
assert resp[0] == 0x20 and resp[1] == 0x02
|
|
||||||
if resp[3] != 0:
|
|
||||||
raise WSSMQTTException(resp[3])
|
|
||||||
return resp[2] & 1
|
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
self.sock = None
|
self.sock = None
|
||||||
@@ -183,53 +218,39 @@ class WSSMQTTClient:
|
|||||||
self.sock.write(b"\xc0\0")
|
self.sock.write(b"\xc0\0")
|
||||||
|
|
||||||
def publish(self, topic, msg, retain=False, qos=0):
|
def publish(self, topic, msg, retain=False, qos=0):
|
||||||
pkt = bytearray(b"\x30\0\0\0")
|
if self.sock is None:
|
||||||
pkt[0] |= qos << 1 | retain
|
raise WSSMQTTException("Not connected")
|
||||||
sz = 2 + len(topic) + len(msg)
|
# Для QoS 0 packet_id не нужен
|
||||||
if qos > 0:
|
flags = (qos << 1) | (1 if retain else 0)
|
||||||
sz += 2
|
fixed = bytes([0x30 | flags])
|
||||||
assert sz < 2097152
|
topic_bytes = topic.encode('utf-8')
|
||||||
i = 1
|
msg_bytes = msg.encode('utf-8')
|
||||||
while sz > 0x7F:
|
properties = b"\x00" # пустые свойства
|
||||||
pkt[i] = (sz & 0x7F) | 0x80
|
# Переменный заголовок: topic length + topic + properties
|
||||||
sz >>= 7
|
variable = struct.pack("!H", len(topic_bytes)) + topic_bytes + properties
|
||||||
i += 1
|
payload = msg_bytes
|
||||||
pkt[i] = sz
|
remaining = len(variable) + len(payload)
|
||||||
# print(hex(len(pkt)), hexlify(pkt, ":"))
|
pkt = fixed + self._encode_length(remaining) + variable + payload
|
||||||
self.sock.write(pkt, i + 1)
|
self.sock.write(pkt)
|
||||||
self._send_str(topic)
|
# Для QoS 1 и 2 нужно ждать подтверждения, но пока опускаем
|
||||||
if qos > 0:
|
|
||||||
self.pid += 1
|
|
||||||
pid = self.pid
|
|
||||||
struct.pack_into("!H", pkt, 0, pid)
|
|
||||||
self.sock.write(pkt, 2)
|
|
||||||
self.sock.write(msg)
|
|
||||||
if qos == 1:
|
|
||||||
while 1:
|
|
||||||
op = self.wait_msg()
|
|
||||||
if op == 0x40:
|
|
||||||
sz = self.sock.read(1)
|
|
||||||
assert sz == b"\x02"
|
|
||||||
rcv_pid = self.sock.read(2)
|
|
||||||
rcv_pid = rcv_pid[0] << 8 | rcv_pid[1]
|
|
||||||
if pid == rcv_pid:
|
|
||||||
return
|
|
||||||
elif qos == 2:
|
|
||||||
assert 0
|
|
||||||
|
|
||||||
def subscribe(self, topic, qos=0):
|
def subscribe(self, topic, qos=0):
|
||||||
assert self.cb is not None, "Subscribe callback is not set"
|
assert self.cb is not None, "Subscribe callback is not set"
|
||||||
pkt = bytearray(b"\x82\0\0\0")
|
fixed_header = b"\x82"
|
||||||
self.pid += 1
|
topic_bytes = topic.encode('utf-8')
|
||||||
struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid)
|
properties = b"\x00"
|
||||||
# print(hex(len(pkt)), hexlify(pkt, ":"))
|
payload = struct.pack("!H", self._packet_id_counter) + properties
|
||||||
|
payload += struct.pack("!H", len(topic_bytes)) + topic_bytes
|
||||||
|
payload += bytes([qos])
|
||||||
|
remaining_length = len(payload)
|
||||||
|
self._packet_id_counter = (self._packet_id_counter + 1) & 0xffff
|
||||||
|
|
||||||
|
pkt = fixed_header + self._encode_length(remaining_length) + payload
|
||||||
self.sock.write(pkt)
|
self.sock.write(pkt)
|
||||||
self._send_str(topic)
|
|
||||||
self.sock.write(qos.to_bytes(1, "little"))
|
|
||||||
while 1:
|
while 1:
|
||||||
op = self.wait_msg()
|
op = self.wait_msg()
|
||||||
if op == 0x90:
|
if op == 0x90:
|
||||||
resp = self.sock.read(4)
|
resp = self.sock.read()
|
||||||
# print(resp)
|
# print(resp)
|
||||||
assert resp[1] == pkt[2] and resp[2] == pkt[3]
|
assert resp[1] == pkt[2] and resp[2] == pkt[3]
|
||||||
if resp[3] == 0x80:
|
if resp[3] == 0x80:
|
||||||
@@ -241,7 +262,7 @@ class WSSMQTTClient:
|
|||||||
# set by .set_callback() method. Other (internal) MQTT
|
# set by .set_callback() method. Other (internal) MQTT
|
||||||
# messages processed internally.
|
# messages processed internally.
|
||||||
def wait_msg(self):
|
def wait_msg(self):
|
||||||
res = self.sock.read(1)
|
res = self.sock.read()
|
||||||
if res is None:
|
if res is None:
|
||||||
return None
|
return None
|
||||||
if res == b"":
|
if res == b"":
|
||||||
|
|||||||
Reference in New Issue
Block a user