Skip to content

Commit 109e248

Browse files
committed
Validate received messages
for correct header, footer and checksum
1 parent 02ee78f commit 109e248

File tree

4 files changed

+75
-20
lines changed

4 files changed

+75
-20
lines changed

plugwise/exceptions.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,20 @@ class CirclePlusError(PlugwiseException):
2424
"""Connection to Circle+ node failed"""
2525

2626

27-
class ProtocolError(PlugwiseException):
28-
"""Error while decode received data"""
27+
class InvalidMessageLength(PlugwiseException):
28+
"""Invalid message length"""
29+
30+
31+
class InvalidMessageHeader(PlugwiseException):
32+
"""Invalid message header"""
33+
34+
35+
class InvalidMessageFooter(PlugwiseException):
36+
"""Invalid message footer"""
37+
38+
39+
class InvalidMessageChecksum(PlugwiseException):
40+
"""Invalid data checksum"""
2941

3042

3143
class TimeoutException(PlugwiseException):

plugwise/messages/__init__.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,20 @@
77
class PlugwiseMessage:
88
"""Plugwise message base."""
99

10-
# TODO: none of the self objects are predefined
10+
def __init__(self):
11+
self.mac = ""
12+
self.checksum = None
13+
self.args = []
1114

1215
def serialize(self):
1316
"""Return message in a serialized format that can be sent out on wire."""
14-
args = b"".join(a.serialize() for a in self.args)
17+
_args = b"".join(a.serialize() for a in self.args)
1518
msg = self.ID
1619
if self.mac != "":
1720
msg += self.mac
18-
msg += args
19-
checksum = self.calculate_checksum(msg)
20-
return MESSAGE_HEADER + msg + checksum + MESSAGE_FOOTER
21+
msg += _args
22+
self.checksum = self.calculate_checksum(msg)
23+
return MESSAGE_HEADER + msg + self.checksum + MESSAGE_FOOTER
2124

2225
def calculate_checksum(self, s):
2326
"""Calculate crc checksum."""

plugwise/messages/responses.py

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
MESSAGE_LARGE,
88
MESSAGE_SMALL,
99
)
10-
from plugwise.exceptions import ProtocolError
10+
from plugwise.exceptions import (
11+
InvalidMessageChecksum,
12+
InvalidMessageFooter,
13+
InvalidMessageHeader,
14+
InvalidMessageLength,
15+
)
1116
from plugwise.messages import PlugwiseMessage
1217
from plugwise.util import (
1318
DateTime,
@@ -31,7 +36,6 @@ def __init__(self, format_size=None):
3136
super().__init__()
3237
self.format_size = format_size
3338
self.params = []
34-
self.mac = None
3539
self.timestamp = None
3640
self.seq_id = None
3741
self.msg_id = None
@@ -45,13 +49,36 @@ def __init__(self, format_size=None):
4549

4650
def deserialize(self, response):
4751
self.timestamp = datetime.now()
48-
if len(response) != len(self):
49-
raise ProtocolError(
50-
"message doesn't have expected length, expected %d bytes got %d"
51-
% (len(self), len(response))
52+
_msg_length = len(response)
53+
if _msg_length != len(self):
54+
raise InvalidMessageLength(
55+
"Invalid message length received for %s, expected %s bytes got %s",
56+
self.__class__.__name__,
57+
str(len(self)),
58+
str(_msg_length),
5259
)
5360
if response[:4] != MESSAGE_HEADER:
54-
raise ProtocolError("Invalid message header")
61+
raise InvalidMessageHeader(
62+
"Invalid message header %s for %s",
63+
str(response[:4]),
64+
self.__class__.__name__,
65+
)
66+
if response[_msg_length - 2 :] != MESSAGE_FOOTER:
67+
raise InvalidMessageFooter(
68+
"Invalid message footer %s for %s",
69+
str(response[_msg_length - 2 :]),
70+
self.__class__.__name__,
71+
)
72+
_calculated_checksum = self.calculate_checksum(response[4 : _msg_length - 6])
73+
_message_checksum = response[_msg_length - 6 : _msg_length - 2]
74+
if _calculated_checksum != _message_checksum:
75+
raise InvalidMessageChecksum(
76+
"Invalid checksum for %s, expected %s got %s",
77+
self.__class__.__name__,
78+
str(_calculated_checksum),
79+
str(_message_checksum),
80+
)
81+
5582
self.msg_id = response[4:8]
5683
self.seq_id = response[8:12]
5784
response = response[12:]
@@ -61,13 +88,13 @@ def deserialize(self, response):
6188
if self.format_size != MESSAGE_SMALL:
6289
self.mac = response[:16]
6390
response = response[16:]
64-
6591
response = self._parse_params(response)
66-
# TODO: unused crc
67-
# crc = response[:4]
6892

69-
if response[4:] != MESSAGE_FOOTER:
70-
raise ProtocolError("Invalid message footer")
93+
_args = b"".join(a.serialize() for a in self.args)
94+
msg = self.ID
95+
if self.mac != "":
96+
msg += self.mac
97+
msg += _args
7198

7299
def _parse_params(self, response):
73100
for p in self.params:

plugwise/parser.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
import logging
44

55
from plugwise.constants import MESSAGE_FOOTER, MESSAGE_HEADER
6+
from plugwise.exceptions import (
7+
InvalidMessageChecksum,
8+
InvalidMessageFooter,
9+
InvalidMessageHeader,
10+
InvalidMessageLength,
11+
)
612
from plugwise.messages.responses import get_message_response
713

814
_LOGGER = logging.getLogger(__name__)
@@ -86,9 +92,16 @@ def parse_data(self):
8692
self._message.deserialize(
8793
self._buffer[: footer_index + 2]
8894
)
95+
except (
96+
InvalidMessageChecksum,
97+
InvalidMessageFooter,
98+
InvalidMessageHeader,
99+
InvalidMessageLength,
100+
) as e:
101+
_LOGGER.warning(e)
89102
except Exception as e:
90103
_LOGGER.error(
91-
"Error while decoding %s message (%s)",
104+
"Failed to parse %s message (%s)",
92105
self._message.__class__.__name__,
93106
str(self._buffer[: footer_index + 2]),
94107
)

0 commit comments

Comments
 (0)