Parcourir la source

simplepyble, untested

Thomas Buck il y a 1 an
Parent
révision
78c9daca6b
4 fichiers modifiés avec 142 ajouts et 130 suppressions
  1. 1
    0
      python-test/.gitignore
  2. 38
    40
      python-test/flow.py
  3. 68
    78
      python-test/poll.py
  4. 35
    12
      python-test/scan.py

+ 1
- 0
python-test/.gitignore Voir le fichier

@@ -0,0 +1 @@
1
+venv

+ 38
- 40
python-test/flow.py Voir le fichier

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

+ 68
- 78
python-test/poll.py Voir le fichier

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

+ 35
- 12
python-test/scan.py Voir le fichier

@@ -1,16 +1,39 @@
1
-#!/usr/bin/env python
1
+import simplepyble
2 2
 
3
-import asyncio
4
-from bleak import BleakScanner
3
+def ble_scan(addr):
4
+    adapters = simplepyble.Adapter.get_adapters()
5 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)
6
+    if len(adapters) == 0:
7
+        print("No adapters found")
8
+        return None
9
+
10
+    # TODO allow selection of bluetooth adapter
11
+    adapter = adapters[0]
12
+    print("Selected adapter: {} [{}]".format(adapter.identifier(), adapter.address()))
13
+
14
+    # TODO support longer scans?
15
+    print("Scanning for '{}' for 1s...".format(addr))
16
+    adapter.scan_for(1000)
17
+
18
+    peripherals = adapter.scan_get_results()
19
+    for peripheral in peripherals:
20
+        if addr != None:
21
+            if addr == peripheral.address():
22
+                return peripheral
23
+        else:
24
+            if peripheral.identifier() == "S&B VOLCANO H":
25
+                return peripheral
26
+
27
+    print("No device found")
28
+    return None
14 29
 
15 30
 if __name__ == "__main__":
16
-    asyncio.run(scan())
31
+    import sys
32
+
33
+    arg = None
34
+    if len(sys.argv) > 1:
35
+        arg = sys.argv[1]
36
+
37
+    dev = ble_scan(arg)
38
+    if dev != None:
39
+        print("{} {}".format(dev.identifier(), dev.address()))

Chargement…
Annuler
Enregistrer