Skip to content

Commit dd643fd

Browse files
committed
Clean up discovery and H.A. style
1 parent fb35b0b commit dd643fd

2 files changed

Lines changed: 133 additions & 97 deletions

File tree

lib/device.py

Lines changed: 81 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
import socket
2-
import asyncio
3-
import time
4-
from sys import argv
52

63
PH803W_DEFAULT_TCP_PORT = 12416
74
PH803W_PING_INTERVAL = 4000
@@ -13,36 +10,42 @@ class Device(object):
1310
def __init__(self, host):
1411
self.result = {}
1512
self.host = host
16-
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
13+
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
14+
self._loop = True
1715

1816
async def run_async(self):
1917
self.run()
2018

21-
def run(self):
22-
self.loop = True
23-
self.socket.connect((self.host, PH803W_DEFAULT_TCP_PORT))
19+
def run(self, once: bool = False) -> bool:
20+
self._loop = True
21+
self._socket.connect((self.host, PH803W_DEFAULT_TCP_PORT))
2422

25-
data = bytes.fromhex('0000000303000006')
26-
self.socket.sendall(data)
27-
response = self.socket.recv(1024)
23+
data = bytes.fromhex("0000000303000006")
24+
self._socket.sendall(data)
25+
response = self._socket.recv(1024)
2826
passcode_lenth = response[9]
2927
passcode_raw = response[10 : 10 + passcode_lenth]
3028
passcode = passcode_raw.decode("utf-8")
31-
print(passcode)
32-
33-
data = bytes.fromhex('000000030f00000800') + passcode_lenth.to_bytes(1, 'little') + passcode_raw
34-
self.socket.sendall(data)
35-
response = self.socket.recv(1024)
29+
# print(passcode)
30+
31+
data = (
32+
bytes.fromhex("000000030f00000800")
33+
+ passcode_lenth.to_bytes(1, "little")
34+
+ passcode_raw
35+
)
36+
self._socket.sendall(data)
37+
response = self._socket.recv(1024)
3638
if response[8] != 0:
37-
print('Error connecting')
39+
# print("Error connecting")
40+
return False
3841

3942
# Connection established, from now on some cyclig bahavior
40-
data = bytes.fromhex('000000030400009002')
41-
self.socket.sendall(data)
43+
data = bytes.fromhex("000000030400009002")
44+
self._socket.sendall(data)
4245
empty_counter = 0
43-
data = bytes.fromhex('0000000303000015')
44-
while self.loop and empty_counter < 10:
45-
response = self.socket.recv(1024)
46+
data = bytes.fromhex("0000000303000015")
47+
while self._loop and empty_counter < 10:
48+
response = self._socket.recv(1024)
4649
if len(response) == 0:
4750
empty_counter += 1
4851
continue
@@ -51,44 +54,58 @@ def run(self):
5154
if len(response) == 18:
5255
flag1 = response[8]
5356
if flag1 & 0b0000_0100:
54-
print('In water')
57+
print("In water")
5558
flag2 = response[9]
5659
if flag2 & 0b0000_0010:
57-
print('ORP on')
60+
print("ORP on")
5861
if flag2 & 0b0000_0001:
59-
print('PH on')
60-
#state_raw = response[8 : 9]
61-
ph_raw = response[10 : 12]
62-
ph = int.from_bytes(ph_raw, 'big') * 0.01
63-
redox_raw = response[12 : 14]
64-
redox = int.from_bytes(redox_raw, 'big') - 2000
65-
unknown1_raw = response[14 : 16]
66-
unknown1 = int.from_bytes(unknown1_raw, 'big')
67-
unknown2_raw = response[15 : 18]
68-
unknown2 = int.from_bytes(unknown2_raw, 'big')
69-
print('pH: %s, Redox: %s, U1: %s, U2: %s' % (ph, redox, unknown1, unknown2), flush=True)
70-
71-
72-
73-
self.socket.sendall(data)
74-
response = self.socket.recv(1024)
75-
62+
print("PH on")
63+
# state_raw = response[8 : 9]
64+
ph_raw = response[10:12]
65+
ph = int.from_bytes(ph_raw, "big") * 0.01
66+
redox_raw = response[12:14]
67+
redox = int.from_bytes(redox_raw, "big") - 2000
68+
unknown1_raw = response[14:16]
69+
unknown1 = int.from_bytes(unknown1_raw, "big")
70+
unknown2_raw = response[15:18]
71+
unknown2 = int.from_bytes(unknown2_raw, "big")
72+
print(
73+
"pH: %s, Redox: %s, U1: %s, U2: %s"
74+
% (ph, redox, unknown1, unknown2),
75+
flush=True,
76+
)
77+
self._socket.sendall(data)
78+
response = self._socket.recv(1024)
79+
if once:
80+
break
7681

7782
def _handle_response(self, data):
7883
if data[0] != 0 and data[1] != 0 and data[2] != 0 and data[2] != 3:
79-
print('Ignore data package because invalid prefix: %s' % data[0:3])
80-
self.result['status'] = ['Error', 'Ignore data package because invalid prefix']
84+
# print("Ignore data package because invalid prefix: %s" % data[0:3])
85+
self.result["status"] = [
86+
"Error",
87+
"Ignore data package because invalid prefix",
88+
]
8189
return
8290
data_length = data[4]
8391
if len(data) != data_length + 5:
8492
if len(data) > data_length:
85-
additional_data = data[data_length: len(data)]
86-
data = data[0 : data_length]
87-
print('Split into two data packages because additional data detected. First %s - Second %s}' % (data.toString('hex'), additional_data.toString('hex')))
93+
additional_data = data[data_length : len(data)]
94+
data = data[0:data_length]
95+
# print(
96+
# "Split into two data packages because additional data detected. First %s - Second %s}"
97+
# % (data.toString("hex"), additional_data.toString("hex"))
98+
# )
8899
self._handle_response(additional_data)
89100
else:
90-
print('Ignore data package because invalid length(%s): %s' % (data_length, data))
91-
self.result['status'] = ['Error', 'Ignore data package because invalid length']
101+
# print(
102+
# "Ignore data package because invalid length(%s): %s"
103+
# % (data_length, data)
104+
# )
105+
self.result["status"] = [
106+
"Error",
107+
"Ignore data package because invalid length",
108+
]
92109
return
93110

94111
message_type = data[7]
@@ -103,20 +120,32 @@ def _handle_response(self, data):
103120
elif message_type == 0x94:
104121
self._handle_data_extended_response(data)
105122
else:
106-
print('Ignore data package because invalid message type %s: %s' % (message_type, data))
107-
self.result['status'] = ['Ignore', 'Ignore data package because invalid length', message_type, data]
123+
# print(
124+
# "Ignore data package because invalid message type %s: %s"
125+
# % (message_type, data)
126+
# )
127+
self.result["status"] = [
128+
"Ignore",
129+
"Ignore data package because invalid length",
130+
message_type,
131+
data,
132+
]
108133

109134
def _handle_passcode_response(self, data):
110135
pass
136+
111137
def _handle_login_response(self, data):
112138
pass
139+
113140
def _handle_ping_pong_response(self):
114141
pass
142+
115143
def _handle_data_extended_response(self, data):
116144
pass
117145

118146
def _send_ping(self):
119147
pass
148+
120149
# if (this.pingWaitTimeout) {
121150
# clearTimeout(this.pingWaitTimeout);
122151
# this.pingWaitTimeout = null;
@@ -133,10 +162,10 @@ def _send_ping(self):
133162
# } def _handle_data_response(self, data):
134163

135164
def abort(self):
136-
self.loop = False
165+
self._loop = False
137166

138167
def close(self):
139-
self.socket.close()
168+
self._socket.close()
140169

141170
def get_result(self):
142171
return str(self.result)
@@ -146,4 +175,4 @@ def __enter__(self):
146175
return self
147176

148177
def __exit__(self, type, value, traceback):
149-
self.socket.close()
178+
self._socket.close()

lib/discovery.py

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,81 +4,88 @@
44
PH803W_UDP_PORT = 12414
55

66

7+
class DiscoveryError(ConnectionError):
8+
pass
9+
10+
711
class Discovery(object):
812
def __init__(self):
9-
self.result = {}
10-
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
13+
self.device = None
14+
self._socket = socket.socket(
15+
socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP
16+
)
1117
# Enable broadcasting mode
12-
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
18+
self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
1319
# Set a timeout so the socket does not block
1420
# indefinitely when trying to receive data.
15-
self.socket.settimeout(1)
21+
self._socket.settimeout(1)
1622

1723
async def run_async(self):
1824
self.run()
1925

2026
def run(self):
21-
data = bytes.fromhex('0000000303000003')
22-
self.socket.sendto(data, ('<broadcast>', PH803W_UDP_PORT))
23-
print("Sent request message!")
24-
25-
data, remote = self.socket.recvfrom(1024)
27+
# Send dicovery request broadcast
28+
data = bytes.fromhex("0000000303000003")
29+
self._socket.sendto(data, ("<broadcast>", PH803W_UDP_PORT))
30+
# print("Sent request message!")
2631

32+
# Receive device reaponse to discovery
33+
data, remote = self._socket.recvfrom(1024)
2734
if data[0] != 0 and data[1] != 0 and data[2] != 0 and data[2] != 3:
28-
print('Ignore data package because invalid prefix: %s' % data[0:3])
29-
self.result['status'] = ['Error', 'Ignore data package because invalid prefix']
30-
return
35+
# print('Ignore data package because invalid prefix: %s' % data[0:3])
36+
raise DiscoveryError("Ignore data package because invalid prefix")
3137
data_length = data[4]
3238
if len(data) != data_length + 5:
33-
print('Ignore data package because invalid length(%s): %s' % (data_length, data))
34-
self.result['status'] = ['Error', 'Ignore data package because invalid length']
35-
return
39+
# print('Ignore data package because invalid length(%s): %s' % (data_length, data))
40+
raise DiscoveryError("Ignore data package because invalid length")
3641
if data[7] == 3:
37-
self.result['status'] = ['Unknown']
38-
return
42+
raise DiscoveryError("Unknown response message type")
3943
if data[7] != 4:
40-
print('Ignore data package because invalid message type ${data[7]}')
41-
self.result['status'] = ['Error', 'Ignore data package because invalid message type']
42-
return
44+
# print("Ignore data package because invalid message type ${data[7]}")
45+
raise DiscoveryError("Ignore data package because invalid message type")
46+
47+
# Parsing result of correct type
48+
# print(
49+
# "Parsing discovered device: %s: %s - %s" % (remote[0], remote[1], data[7:])
50+
# )
51+
self.device = Device(remote[0], data)
52+
53+
def close(self):
54+
self._socket.close()
55+
56+
def get_result(self):
57+
return self.device
58+
59+
def __enter__(self):
60+
self.run()
61+
return self
62+
63+
def __exit__(self, type, value, traceback):
64+
self.close()
4365

44-
print('Parsing discovered device: %s: %s - %s' % (remote[0], remote[1], data[7:]))
45-
self.result['status'] = ['Error'] # Temporary until all pass
46-
self.result['result'] = {'ip' : remote[0]}
66+
67+
class Device:
68+
def __init__(self, ip: str, data):
69+
self.ip = ip
4770

4871
id1_length = data[9]
4972
id1_raw = data[10 : 10 + id1_length]
50-
self.result['result']['id1'] = id1_raw.decode("utf-8")
73+
self.id1 = id1_raw.decode("utf-8")
5174

5275
id2_length = data[9 + id1_length + 12]
5376
id2_raw = data[9 + id1_length + 13 : 9 + id1_length + 13 + id2_length]
54-
self.result['result']['id2'] = id2_raw.decode("utf-8")
77+
self.id2 = id2_raw.decode("utf-8")
5578

5679
idx = 9 + id1_length + 13 + id2_length + 8
5780
idx_start = idx
5881
while data[idx] != 0:
5982
idx += 1
60-
api_server_raw = data[idx_start: idx]
61-
self.result['result']['api_server'] = api_server_raw.decode("utf-8")
83+
api_server_raw = data[idx_start:idx]
84+
self.api_server = api_server_raw.decode("utf-8")
6285

6386
idx += 1
64-
6587
idx_start = idx
6688
while data[idx] != 0:
6789
idx += 1
68-
version_raw = data[idx_start: idx]
69-
self.result['result']['version_server'] = version_raw.decode("utf-8")
70-
71-
self.result['status'] = ['Success']
72-
73-
def close(self):
74-
self.socket.close()
75-
76-
def get_result(self):
77-
return self.result
78-
79-
def __enter__(self):
80-
self.run()
81-
return self
82-
83-
def __exit__(self, type, value, traceback):
84-
self.close()
90+
version_raw = data[idx_start:idx]
91+
self.version_server = version_raw.decode("utf-8")

0 commit comments

Comments
 (0)