Skip to content
This repository was archived by the owner on Jan 5, 2024. It is now read-only.

Commit 3ae4941

Browse files
authored
Merge pull request #3 from rccoleman/async
Improve compatibility with the Home Assistant integration
2 parents e48ab3c + c3075a3 commit 3ae4941

File tree

3 files changed

+64
-33
lines changed

3 files changed

+64
-33
lines changed

lmdirect/__init__.py

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@
1313
class LMDirect:
1414
def __init__(self, key):
1515
"""Init LMDirect"""
16-
self.run = True
17-
self.cipher = AESCipher(key)
18-
self.response_task = None
19-
self.status_task = None
20-
self.current_status = {}
16+
self._run = True
17+
self._cipher = AESCipher(key)
18+
self._read_response_task = None
19+
self._poll_status_task = None
20+
self._current_status = {}
21+
self._callback = None
22+
23+
def register_callback(self, callback):
24+
"""Register callback for updates"""
25+
if callable(callback):
26+
self._callback = callback
2127

2228
async def connect(self, addr):
2329
"""Conmnect to espresso machine"""
@@ -29,34 +35,40 @@ async def connect(self, addr):
2935
loop = asyncio.get_event_loop()
3036

3137
"""Start listening for responses & sending status requests"""
32-
self.response_task = loop.create_task(self.response())
33-
34-
"""Start sending status requests"""
35-
self.status_task = loop.create_task(self.status())
38+
self._read_response_task = loop.create_task(
39+
self.read_response_task(), name="Response Task"
40+
)
3641

3742
async def close(self):
3843
"""Stop listening for responses and close the socket"""
39-
self.run = False
44+
self._run = False
45+
tasks = [self._read_response_task]
46+
47+
if self._poll_status_task:
48+
tasks.append(self._poll_status_task)
4049

41-
await asyncio.gather(*[self.response_task, self.status_task])
50+
await asyncio.gather(*tasks)
4251

4352
"""Close the connection"""
4453
self.writer.close()
4554

46-
async def response(self):
55+
async def read_response_task(self):
4756
"""Start thread to receive responses"""
4857
BUFFER_SIZE = 1000
4958

50-
while self.run:
59+
while self._run:
5160
encoded_data = await self.reader.read(BUFFER_SIZE)
5261

5362
_LOGGER.debug(encoded_data)
5463
if encoded_data is not None:
55-
loop = asyncio.get_running_loop()
56-
fn = partial(self.cipher.decrypt, encoded_data[1:-1])
64+
loop = asyncio.get_event_loop()
65+
fn = partial(self._cipher.decrypt, encoded_data[1:-1])
5766
plaintext = await loop.run_in_executor(None, fn)
5867
await self.process_data(plaintext)
5968

69+
if self._callback is not None:
70+
self._callback(self._current_status)
71+
6072
async def process_data(self, plaintext):
6173
"""Process incoming packet"""
6274

@@ -77,36 +89,50 @@ async def process_data(self, plaintext):
7789

7890
if any(preamble in x for x in CMD.PREAMBLES):
7991
await self.populate_items(data, CMD.RESP_MAP[preamble])
80-
_LOGGER.debug(self.current_status)
92+
_LOGGER.debug(self._current_status)
8193

8294
async def populate_items(self, data, map):
8395
for elem in map:
8496
index = elem.index * 2
8597
size = elem.size * 2
8698

87-
value = int(data[index: index + size], 16)
88-
if any(x in map[elem] for x in ["TEMP", "PREBREWING_K"]):
99+
value = int(data[index : index + size], 16)
100+
if any(x in map[elem] for x in ["TSET", "TEMP", "PREBREWING_K"]):
89101
value = value / 10
90102
elif "AUTO_BITFIELD" in map[elem]:
91103
for i in range(0, 7):
92104
setting = ENABLED if value & 0x01 else DISABLED
93-
self.current_status[CMD.AUTO_BITFIELD_MAP[i]] = setting
105+
self._current_status[CMD.AUTO_BITFIELD_MAP[i]] = setting
94106
value = value >> 1
95107
continue
96-
self.current_status[map[elem]] = value
108+
self._current_status[map[elem]] = value
109+
110+
@property
111+
def current_status(self):
112+
"""Return a dict of all the properties that have been received"""
113+
return self._current_status
97114

98-
async def status(self):
115+
async def create_polling_task(self):
116+
"""Start a polling task"""
117+
self._poll_status_task = asyncio.get_event_loop().create_task(
118+
self.poll_status_task(), name="Request Status Task"
119+
)
120+
121+
async def poll_status_task(self):
99122
"""Send periodic status requests"""
100-
while self.run:
101-
await self.send_cmd(CMD.STATUS)
102-
await self.send_cmd(CMD.CONFIG)
103-
await self.send_cmd(CMD.AUTO_SCHED)
123+
while self._run:
124+
await self.request_status()
104125
await asyncio.sleep(5)
105126

127+
async def request_status(self):
128+
await self.send_cmd(CMD.STATUS)
129+
await self.send_cmd(CMD.CONFIG)
130+
await self.send_cmd(CMD.AUTO_SCHED)
131+
106132
async def send_cmd(self, cmd):
107133
"""Send command to espresso machine"""
108-
loop = asyncio.get_running_loop()
109-
fn = partial(self.cipher.encrypt, cmd)
134+
loop = asyncio.get_event_loop()
135+
fn = partial(self._cipher.encrypt, cmd)
110136
ciphertext = "@" + (await loop.run_in_executor(None, fn)).decode("utf-8") + "%"
111137
_LOGGER.debug(ciphertext)
112138

lmdirect/cmds.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def size(self):
5050
# 04 D8: Steam Temp (124.0C)
5151
# B6
5252

53-
SHORT_MAP = {Element(4, 2): "COFFEE_TEMP", Element(6, 2): "STEAM_TEMP"}
53+
SHORT_MAP = {Element(4, 2): "TEMP_COFFEE", Element(6, 2): "TEMP_STEAM"}
5454

5555
# R
5656
# 40 00 00 20: Preamble
@@ -63,7 +63,7 @@ def size(self):
6363
# 70
6464

6565
D8_MAP = {
66-
Element(27, 1): "POWER",
66+
Element(27, 1): "MACHINE_STATUS",
6767
# Element(32, 2): "COFFEE_TEMP",
6868
# Element(34, 2): "STEAM_TEMP",
6969
}
@@ -91,9 +91,8 @@ def size(self):
9191
# 35: 66: Degrees f/c
9292

9393
E9_MAP = {
94-
Element(0): "STBY_TIMER",
95-
Element(11, 2): "COFFEE_TEMP_SET",
96-
Element(13, 2): "STEAM_TEMP_SET",
94+
Element(11, 2): "TSET_COFFEE",
95+
Element(13, 2): "TSET_STEAM",
9796
Element(15): "ENABLE_PREBREWING",
9897
Element(16): "TON_PREBREWING_K1",
9998
Element(17): "TON_PREBREWING_K2",
@@ -109,7 +108,6 @@ def size(self):
109108
Element(30, 2): "DOSE_K4",
110109
Element(32, 2): "DOSE_K5",
111110
Element(34): "DOSE_TEA",
112-
# Element(35): "T_UNITS",
113111
}
114112

115113
# Response to R 03 10 00 1D EB

test.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,19 @@ def read_config():
2525
return key, ip_addr
2626

2727

28+
def update(data):
29+
print("Updated: {}".format(data))
30+
31+
2832
async def main():
2933
"""Main execution loop"""
3034
loop = asyncio.get_event_loop()
3135
key, ip_addr = await loop.run_in_executor(None, read_config)
3236

3337
lmdirect = LMDirect(key)
38+
lmdirect.register_callback(update)
3439
await lmdirect.connect(ip_addr)
40+
# await lmdirect.create_polling_task()
3541

3642
while True:
3743
try:
@@ -51,4 +57,5 @@ async def main():
5157

5258
await lmdirect.close()
5359

60+
5461
asyncio.run(main())

0 commit comments

Comments
 (0)