Browse Source

add bleak python implementation to master branch

Thomas Buck 3 months ago
parent
commit
956f5e43e4

+ 106
- 0
test_bleak/flow.py View File

@@ -0,0 +1,106 @@
1
+#!/usr/bin/env python
2
+
3
+import sys
4
+import os
5
+import asyncio
6
+from poll import (
7
+    ble_conn,
8
+    get_current_temp,
9
+    get_target_temp, set_target_temp,
10
+    get_unit_is_fahrenheit,
11
+    get_state, set_state
12
+)
13
+
14
+terminal_width = os.get_terminal_size().columns - 15
15
+
16
+def print_bar(value, start, end, unit):
17
+    width = terminal_width
18
+    s = "\r"
19
+    s += "#" * int((value - start) / (end - start) * width)
20
+    s += "-" * (width - int((value - start) / (end - start) * width))
21
+    s += " {}{}".format(value, unit)
22
+    print(s, end="", flush=True)
23
+
24
+async def sleep(t):
25
+    w = terminal_width
26
+    if t < w:
27
+        w = int(t)
28
+    print_bar(0, 0, w, "s")
29
+    for i in range(0, w):
30
+        await asyncio.sleep(t / w)
31
+        print_bar(i + 1, 0, w, "s")
32
+    print()
33
+
34
+async def wait_for_temp(client, temp):
35
+    print("Setting temperature {}".format(temp))
36
+    await set_target_temp(client, temp)
37
+
38
+    print("Waiting for temperature to rise...")
39
+    start = await get_current_temp(client)
40
+    curr = start
41
+    print_bar(curr, start, temp, " degC")
42
+    while curr < temp:
43
+        await asyncio.sleep(1.0)
44
+        curr = await get_current_temp(client)
45
+        print_bar(curr, start, temp, " degC")
46
+    print()
47
+
48
+    print("Reached temperature {}".format(temp))
49
+
50
+async def flow_step(client, temp, t_wait, t_pump):
51
+    await wait_for_temp(client, temp)
52
+
53
+    print("Waiting {}s for heat to settle...".format(t_wait))
54
+    await sleep(t_wait)
55
+
56
+    print("Pumping for {}s".format(t_pump))
57
+    await set_state(client, (True, True)) # turn on pump
58
+    await sleep(t_pump)
59
+    await set_state(client, (True, False)) # turn off pump
60
+
61
+async def flow(client):
62
+    print("Turning on heater")
63
+    await set_state(client, (True, False))
64
+
65
+    await flow_step(client, 185.0, 10.0, 5.0)
66
+    await flow_step(client, 195.0, 5.0, 20.0)
67
+    await flow_step(client, 205.0, 5.0, 20.0)
68
+
69
+    print("Notification by pumping three times...")
70
+    for i in range(0, 3):
71
+        await asyncio.sleep(1.0)
72
+        await set_state(client, (True, True)) # turn on pump
73
+        await asyncio.sleep(1.0)
74
+        await set_state(client, (True, False)) # turn off pump
75
+
76
+    print("Turning heater off")
77
+    await set_state(client, (False, False)) # turn off heater and pump
78
+
79
+    print("Resetting temperature")
80
+    await set_target_temp(client, 190.0)
81
+
82
+async def main(address):
83
+    device = await ble_conn(address)
84
+
85
+    print("Connecting...")
86
+    async with device as client:
87
+        try:
88
+            if await get_unit_is_fahrenheit(client):
89
+                print("Imperial American scum is currently not supported :P")
90
+                sys.exit(42)
91
+
92
+            print("Starting Workflow")
93
+            await flow(client)
94
+        except:
95
+            print("\nTurning heater and pump off")
96
+            await set_state(client, (False, False)) # turn off heater and pump
97
+            raise
98
+
99
+        print("Disconnecting...")
100
+
101
+if __name__ == "__main__":
102
+    if len(sys.argv) <= 1:
103
+        print("Please pass MAC address of device")
104
+        sys.exit(1)
105
+
106
+    asyncio.run(main(sys.argv[1]))

+ 111
- 0
test_bleak/poll.py View File

@@ -0,0 +1,111 @@
1
+#!/usr/bin/env python
2
+
3
+import sys
4
+import asyncio
5
+from bleak import BleakClient, BleakScanner
6
+from bleak.exc import BleakDBusError
7
+
8
+async def ble_conn(address):
9
+    attempts = 10
10
+    print("Opening device..", end = "", flush=True)
11
+    while attempts > 0:
12
+        try:
13
+            if (attempts % 10) == 0:
14
+                print(".", end = "", flush=True)
15
+
16
+            device = await BleakScanner.find_device_by_address(address)
17
+            client = BleakClient(device)
18
+            print("", flush=True)
19
+            return client
20
+        except BleakDBusError as e:
21
+            attempts -= 1
22
+            if attempts == 0:
23
+                print("", flush=True)
24
+                print(e)
25
+            else:
26
+                await asyncio.sleep(0.1)
27
+
28
+    print("Could not connect to device")
29
+    return None
30
+
31
+async def get_current_temp(client):
32
+    val = await client.read_gatt_char("10110001-5354-4f52-5a26-4249434b454c")
33
+    num = int.from_bytes(val, byteorder="little")
34
+    return num / 10.0
35
+
36
+async def get_target_temp(client):
37
+    val = await client.read_gatt_char("10110003-5354-4f52-5a26-4249434b454c")
38
+    num = int.from_bytes(val, byteorder="little")
39
+    return num / 10.0
40
+
41
+async def set_target_temp(client, temp):
42
+    val = int(temp * 10.0)
43
+    d = val.to_bytes(4, byteorder="little")
44
+    await client.write_gatt_char("10110003-5354-4f52-5a26-4249434b454c", d)
45
+
46
+async def get_unit_is_fahrenheit(client):
47
+    val = await client.read_gatt_char("1010000d-5354-4f52-5a26-4249434b454c")
48
+    num = int.from_bytes(val, byteorder="little")
49
+    return (num & 0x200) != 0
50
+
51
+async def get_state(client):
52
+    val = await client.read_gatt_char("1010000c-5354-4f52-5a26-4249434b454c")
53
+    num = int.from_bytes(val, byteorder="little")
54
+    heater = (num & 0x0020) != 0
55
+    pump = (num & 0x2000) != 0
56
+    return (heater, pump)
57
+
58
+async def set_state(client, state):
59
+    heater, pump = state
60
+    if heater:
61
+        await client.write_gatt_char("1011000f-5354-4f52-5a26-4249434b454c", 0)
62
+    else:
63
+        await client.write_gatt_char("10110010-5354-4f52-5a26-4249434b454c", 0)
64
+    if pump:
65
+        await client.write_gatt_char("10110013-5354-4f52-5a26-4249434b454c", 0)
66
+    else:
67
+        await client.write_gatt_char("10110014-5354-4f52-5a26-4249434b454c", 0)
68
+
69
+async def test_poll(client):
70
+    temp = await get_current_temp(client)
71
+    print("Current Temperature: {}".format(temp))
72
+
73
+    target = await get_target_temp(client)
74
+    print("Target Temperature: {}".format(target))
75
+
76
+    fahrenheit = await get_unit_is_fahrenheit(client)
77
+    if fahrenheit:
78
+        print("Unit is Fahrenheit")
79
+    else:
80
+        print("Unit is Celsius")
81
+
82
+    heater, pump = await get_state(client)
83
+    if heater:
84
+        print("Heater is On")
85
+    else:
86
+        print("Heater is Off")
87
+    if pump:
88
+        print("Pump is On")
89
+    else:
90
+        print("Pump is Off")
91
+
92
+async def test(address):
93
+    device = await ble_conn(address)
94
+
95
+    print("Connecting...")
96
+    async with device as client:
97
+        print("Writing...")
98
+        await set_target_temp(client, 190.0)
99
+
100
+        print("Reading...")
101
+        for i in range(0, 5):
102
+            await test_poll(client)
103
+            print()
104
+            await asyncio.sleep(2.0)
105
+
106
+if __name__ == "__main__":
107
+    if len(sys.argv) <= 1:
108
+        print("Please pass MAC address of device")
109
+        sys.exit(1)
110
+
111
+    asyncio.run(test(sys.argv[1]))

+ 16
- 0
test_bleak/scan.py View File

@@ -0,0 +1,16 @@
1
+#!/usr/bin/env python
2
+
3
+import asyncio
4
+from bleak import BleakScanner
5
+
6
+async def scan():
7
+    print("scanning for 5 seconds, please wait...")
8
+    devices = await BleakScanner.discover(return_adv=True)
9
+    for d, a in devices.values():
10
+        print()
11
+        print(d)
12
+        print("-" * len(str(d)))
13
+        print(a)
14
+
15
+if __name__ == "__main__":
16
+    asyncio.run(scan())

python-test/.gitignore → test_micropython/.gitignore View File


python-test/copy.sh → test_micropython/copy.sh View File


python-test/lcd.py → test_micropython/lcd.py View File


python-test/ota.py → test_micropython/ota.py View File


python-test/poll.py → test_micropython/poll.py View File


python-test/scan.py → test_micropython/scan.py View File


python-test/state_connect.py → test_micropython/state_connect.py View File


python-test/state_heat.py → test_micropython/state_heat.py View File


python-test/state_notify.py → test_micropython/state_notify.py View File


python-test/state_pump.py → test_micropython/state_pump.py View File


python-test/state_scan.py → test_micropython/state_scan.py View File


python-test/state_select.py → test_micropython/state_select.py View File


python-test/state_wait_temp.py → test_micropython/state_wait_temp.py View File


python-test/state_wait_time.py → test_micropython/state_wait_time.py View File


python-test/states.py → test_micropython/states.py View File


python-test/workflows.py → test_micropython/workflows.py View File


Loading…
Cancel
Save