Fix bugs in pytuya

This handles responses without a return code and also fixes support for
unencrypted clients. Made a few tweaks to debug logs as well.
This commit is contained in:
Pierre Ståhl
2020-10-19 10:20:38 +02:00
committed by rospogrigio
parent 1d830552df
commit e6538a4681

View File

@@ -228,19 +228,25 @@ class MessageDispatcher:
break break
# length includes payload length, retcode, crc and suffix # length includes payload length, retcode, crc and suffix
payload_length = length - 4 - struct.calcsize(MESSAGE_END_FMT) if (retcode & 0xFFFFFF00) != 0:
payload = self.buffer[header_len : header_len + payload_length] payload_start = header_len - 4
payload_length = length - struct.calcsize(MESSAGE_END_FMT)
else:
payload_start = header_len
payload_length = length - 4 - struct.calcsize(MESSAGE_END_FMT)
payload = self.buffer[payload_start : payload_start + payload_length]
crc, _ = struct.unpack_from( crc, _ = struct.unpack_from(
MESSAGE_END_FMT, MESSAGE_END_FMT,
self.buffer[header_len + payload_length : header_len + length], self.buffer[payload_start + payload_length : payload_start + length],
) )
self.buffer = self.buffer[header_len + length - 4 :] self.buffer = self.buffer[header_len - 4 + length :]
self._dispatch(TuyaMessage(seqno, cmd, retcode, payload, crc)) self._dispatch(TuyaMessage(seqno, cmd, retcode, payload, crc))
def _dispatch(self, msg): def _dispatch(self, msg):
"""Dispatch a message to someone that is listening.""" """Dispatch a message to someone that is listening."""
_LOGGER.debug("Dispatching message %s", msg)
if msg.seqno in self.listeners: if msg.seqno in self.listeners:
self.log.debug("Dispatching sequence number %d", msg.seqno) self.log.debug("Dispatching sequence number %d", msg.seqno)
sem = self.listeners[msg.seqno] sem = self.listeners[msg.seqno]
@@ -258,7 +264,7 @@ class MessageDispatcher:
else: else:
self.log.debug( self.log.debug(
"Got message type %d for unknown listener %d: %s", "Got message type %d for unknown listener %d: %s",
msg.command, msg.cmd,
msg.seqno, msg.seqno,
msg, msg,
) )
@@ -459,14 +465,14 @@ class TuyaProtocol(asyncio.Protocol):
try: try:
data = await self.status() data = await self.status()
except Exception as e: except Exception as e:
self.log.warning("Failed to get status: %s", e) self.log.exception("Failed to get status: %s", e)
raise raise
if "dps" in data: if "dps" in data:
self.dps_cache.update(data["dps"]) self.dps_cache.update(data["dps"])
if self.dev_type == "type_0a": if self.dev_type == "type_0a":
return self.dps_cache return self.dps_cache
self.log.debug("detected dps: %s", self.dps_cache) self.log.debug("Detected dps: %s", self.dps_cache)
return self.dps_cache return self.dps_cache
def add_dps_to_request(self, dp_indicies): def add_dps_to_request(self, dp_indicies):
@@ -477,10 +483,10 @@ class TuyaProtocol(asyncio.Protocol):
self.dps_to_request.update({str(index): None for index in dp_indicies}) self.dps_to_request.update({str(index): None for index in dp_indicies})
def _decode_payload(self, payload): def _decode_payload(self, payload):
self.log.debug("Decode payload: %s", payload)
if not payload: if not payload:
payload = "{}" payload = "{}"
elif payload.startswith(b"{"):
payload = payload
elif payload.startswith(PROTOCOL_VERSION_BYTES_31): elif payload.startswith(PROTOCOL_VERSION_BYTES_31):
payload = payload[len(PROTOCOL_VERSION_BYTES_31) :] # remove version header payload = payload[len(PROTOCOL_VERSION_BYTES_31) :] # remove version header
# remove (what I'm guessing, but not confirmed is) 16-bytes of MD5 # remove (what I'm guessing, but not confirmed is) 16-bytes of MD5
@@ -500,7 +506,7 @@ class TuyaProtocol(asyncio.Protocol):
self.dev_type, self.dev_type,
) )
return None return None
elif not payload.startswith(b"{"): else:
raise Exception(f"Unexpected payload={payload} (id: {self.id})") raise Exception(f"Unexpected payload={payload} (id: {self.id})")
if not isinstance(payload, str): if not isinstance(payload, str):
@@ -537,7 +543,7 @@ class TuyaProtocol(asyncio.Protocol):
json_data["dps"] = self.dps_to_request json_data["dps"] = self.dps_to_request
payload = json.dumps(json_data).replace(" ", "").encode("utf-8") payload = json.dumps(json_data).replace(" ", "").encode("utf-8")
self.log.debug("Paylod: %s", payload) self.log.debug("Send payload: %s", payload)
if self.version == 3.3: if self.version == 3.3:
payload = self.cipher.encrypt(payload, False) payload = self.cipher.encrypt(payload, False)