update
Showing
18 changed files
with
2167 additions
and
74 deletions
a.log
0 → 100644
This diff could not be displayed because it is too large.
b.log
0 → 100644
| 1 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 2 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": null, "peripheralId": "68:67:11:27:78:49", "rssi": -28}} | ||
| 3 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 4 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"68:67:11:27:78:49"},"id":1} | ||
| 5 | 下发: {"jsonrpc": "2.0", "result": {"message": ""}, "id": 1} | ||
| 6 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"68:67:11:27:78:49"},"id":2} | ||
| 7 | 下发: {"jsonrpc": "2.0", "result": {"message": ""}, "id": 2} | ||
| 8 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 9 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "Chinese_spik", "peripheralId": "68:67:11:27:78:49", "rssi": -36}} | ||
| 10 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 11 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 12 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "Chinese_spik", "peripheralId": "68:67:11:27:78:49", "rssi": -36}} | ||
| 13 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 14 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"68:67:11:27:78:49"},"id":1} | ||
| 15 | 下发: {"jsonrpc": "2.0", "result": null, "id": 1} | ||
| 16 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"68:67:11:27:78:49"},"id":2} | ||
| 17 | 下发: {"jsonrpc": "2.0", "result": {"message": "Device with address 68:67:11:27:78:49 was not found."}, "id": 2} | ||
| 18 | 接收: {"jsonrpc":"2.0","method":"startNotifications","params":{"serviceId":"00b6c4bc-8170-268e-4627-e07f37ed6744","characteristicId":"01b6c4bc-8170-268e-4627-e07f37ed6744"},"id":3} | ||
| 19 | 下发: {"jsonrpc": "2.0", "result": {"message": "Not connected"}, "id": 3} | ||
| 20 | 接收: {"jsonrpc":"2.0","method":"write","params":{"serviceId":"01b6c4bc-8170-268e-4627-e07f37ed6744","characteristicId":"01B6C4BC-8170-268E-4627-E07F37ED6744","message":"WwEAXF0=","encoding":"base64"},"id":4} | ||
| 21 | 下发: {"jsonrpc": "2.0", "result": {"message": "Service Discovery has not been performed yet"}, "id": 4} | ||
| 22 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 23 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "Chinese_spik", "peripheralId": "68:67:11:27:78:49", "rssi": -28}} | ||
| 24 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 25 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"68:67:11:27:78:49"},"id":1} | ||
| 26 | 下发: {"jsonrpc": "2.0", "result": {"message": ""}, "id": 1} | ||
| 27 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 28 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "Chinese_spik", "peripheralId": "68:67:11:27:78:49", "rssi": -28}} | ||
| 29 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 30 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"68:67:11:27:78:49"},"id":1} | ||
| 31 | 下发: {"jsonrpc": "2.0", "result": {"message": ""}, "id": 1} | ||
| 32 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"68:67:11:27:78:49"},"id":2} | ||
| 33 | 下发: {"jsonrpc": "2.0", "result": {"message": ""}, "id": 2} | ||
| 34 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 35 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "Chinese_spik", "peripheralId": "68:67:11:27:78:49", "rssi": -34}} | ||
| 36 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 37 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"68:67:11:27:78:49"},"id":1} | ||
| 38 | 下发: {"jsonrpc": "2.0", "result": null, "id": 1} | ||
| 39 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"68:67:11:27:78:49"},"id":2} | ||
| 40 | 下发: {"jsonrpc": "2.0", "result": {"message": "Device with address 68:67:11:27:78:49 was not found."}, "id": 2} | ||
| 41 | 接收: {"jsonrpc":"2.0","method":"startNotifications","params":{"serviceId":"00b6c4bc-8170-268e-4627-e07f37ed6744","characteristicId":"01b6c4bc-8170-268e-4627-e07f37ed6744"},"id":3} | ||
| 42 | 下发: {"jsonrpc": "2.0", "result": {"message": "Not connected"}, "id": 3} | ||
| 43 | 接收: {"jsonrpc":"2.0","method":"write","params":{"serviceId":"01b6c4bc-8170-268e-4627-e07f37ed6744","characteristicId":"01B6C4BC-8170-268E-4627-E07F37ED6744","message":"WwEAXF0=","encoding":"base64"},"id":4} | ||
| 44 | 下发: {"jsonrpc": "2.0", "result": {"message": "Service Discovery has not been performed yet"}, "id": 4} | ||
| 45 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 46 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "Chinese_spik", "peripheralId": "68:67:11:27:78:4D", "rssi": -30}} | ||
| 47 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 48 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"68:67:11:27:78:4D"},"id":1} | ||
| 49 | 下发: {"jsonrpc": "2.0", "result": null, "id": 1} | ||
| 50 | 接收: {"jsonrpc":"2.0","method":"write","params":{"serviceId":"00004f0e-1212-efde-1523-785feabcd123","characteristicId":"00001563-1212-efde-1523-785feabcd123","message":"AQIGFwEAAAAAAAA=","encoding":"base64"},"id":2} | ||
| 51 | 下发: {"jsonrpc": "2.0", "result": {"message": "Characteristic 00001563-1212-efde-1523-785feabcd123 was not found!"}, "id": 2} | ||
| 52 | 接收: {"jsonrpc":"2.0","method":"write","params":{"serviceId":"00004f0e-1212-efde-1523-785feabcd123","characteristicId":"00001565-1212-efde-1523-785feabcd123","message":"BgQDAAD/","encoding":"base64"},"id":3} | ||
| 53 | 下发: {"jsonrpc": "2.0", "result": {"message": "Characteristic 00001565-1212-efde-1523-785feabcd123 was not found!"}, "id": 3} | ||
| 54 | 接收: {"jsonrpc":"2.0","method":"startNotifications","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001527-1212-efde-1523-785feabcd123"},"id":4} | ||
| 55 | 下发: {"jsonrpc": "2.0", "result": {"message": "Characteristic 00001527-1212-efde-1523-785feabcd123 was not found!"}, "id": 4} | ||
| 56 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":5} | ||
| 57 | 下发: {"jsonrpc": "2.0", "result": {"message": "Characteristic 00001528-1212-efde-1523-785feabcd123 was not found!"}, "id": 5} | ||
| 58 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":6} | ||
| 59 | 下发: {"jsonrpc": "2.0", "result": {"message": "Characteristic 00001528-1212-efde-1523-785feabcd123 was not found!"}, "id": 6} | ||
| 60 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":7} | ||
| 61 | 下发: {"jsonrpc": "2.0", "result": {"message": "Characteristic 00001528-1212-efde-1523-785feabcd123 was not found!"}, "id": 7} | ||
| 62 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 63 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "Chinese_spik", "peripheralId": "68:67:11:27:78:4D", "rssi": -26}} | ||
| 64 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 65 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 66 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "Chinese_spik", "peripheralId": "68:67:11:27:78:4D", "rssi": -26}} | ||
| 67 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 68 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 69 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "LPF2 Smart Hub", "peripheralId": "3D:6F:89:AB:DA:FE", "rssi": -24}} | ||
| 70 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 71 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"3D:6F:89:AB:DA:FE"},"id":1} | ||
| 72 | 下发: {"jsonrpc": "2.0", "result": null, "id": 1} | ||
| 73 | 接收: {"jsonrpc":"2.0","method":"write","params":{"serviceId":"00004f0e-1212-efde-1523-785feabcd123","characteristicId":"00001563-1212-efde-1523-785feabcd123","message":"AQIGFwEAAAAAAAA=","encoding":"base64"},"id":2} | ||
| 74 | 下发: {"jsonrpc": "2.0", "result": null, "id": 2} | ||
| 75 | 接收: {"jsonrpc":"2.0","method":"write","params":{"serviceId":"00004f0e-1212-efde-1523-785feabcd123","characteristicId":"00001565-1212-efde-1523-785feabcd123","message":"BgQDAAD/","encoding":"base64"},"id":3} | ||
| 76 | 下发: {"jsonrpc": "2.0", "result": null, "id": 3} | ||
| 77 | 接收: {"jsonrpc":"2.0","method":"startNotifications","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001527-1212-efde-1523-785feabcd123"},"id":4} | ||
| 78 | 下发: {"jsonrpc": "2.0", "result": null, "id": 4} | ||
| 79 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "AwEzFQEAAAABAAAA"}} | ||
| 80 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "BQE1FgEAAAABAAAA"}} | ||
| 81 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "BAE0FAEAAAABAAAA"}} | ||
| 82 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "BgE2FwEAAAABAAAA"}} | ||
| 83 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":5} | ||
| 84 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 5} | ||
| 85 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":6} | ||
| 86 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 6} | ||
| 87 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":7} | ||
| 88 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 7} | ||
| 89 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":8} | ||
| 90 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 8} | ||
| 91 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":9} | ||
| 92 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 9} | ||
| 93 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":10} | ||
| 94 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 10} | ||
| 95 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":11} | ||
| 96 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 11} | ||
| 97 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":12} | ||
| 98 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 12} | ||
| 99 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":13} | ||
| 100 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 13} | ||
| 101 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":14} | ||
| 102 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 14} | ||
| 103 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":15} | ||
| 104 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 15} | ||
| 105 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":16} | ||
| 106 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 16} | ||
| 107 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":17} | ||
| 108 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 17} | ||
| 109 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 110 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "Chinese_spik", "peripheralId": "68:67:11:27:78:4D", "rssi": -28}} | ||
| 111 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 112 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 113 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "Chinese_spik", "peripheralId": "68:67:11:27:78:4D", "rssi": -28}} | ||
| 114 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 115 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 116 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "Chinese_spik", "peripheralId": "68:67:11:27:78:4D", "rssi": -30}} | ||
| 117 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 118 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 119 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "LPF2 Smart Hub", "peripheralId": "3D:6F:89:AB:DA:FE", "rssi": -26}} | ||
| 120 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 121 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"3D:6F:89:AB:DA:FE"},"id":1} | ||
| 122 | 下发: {"jsonrpc": "2.0", "result": null, "id": 1} | ||
| 123 | 接收: {"jsonrpc":"2.0","method":"write","params":{"serviceId":"00004f0e-1212-efde-1523-785feabcd123","characteristicId":"00001563-1212-efde-1523-785feabcd123","message":"AQIGFwEAAAAAAAA=","encoding":"base64"},"id":2} | ||
| 124 | 下发: {"jsonrpc": "2.0", "result": null, "id": 2} | ||
| 125 | 接收: {"jsonrpc":"2.0","method":"write","params":{"serviceId":"00004f0e-1212-efde-1523-785feabcd123","characteristicId":"00001565-1212-efde-1523-785feabcd123","message":"BgQDAAD/","encoding":"base64"},"id":3} | ||
| 126 | 下发: {"jsonrpc": "2.0", "result": null, "id": 3} | ||
| 127 | 接收: {"jsonrpc":"2.0","method":"startNotifications","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001527-1212-efde-1523-785feabcd123"},"id":4} | ||
| 128 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "AwEzFQEAAAABAAAA"}} | ||
| 129 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "BAE0FAEAAAABAAAA"}} | ||
| 130 | 下发: {"jsonrpc": "2.0", "result": null, "id": 4} | ||
| 131 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "BQE1FgEAAAABAAAA"}} | ||
| 132 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "BgE2FwEAAAABAAAA"}} | ||
| 133 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":5} | ||
| 134 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 5} | ||
| 135 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":6} | ||
| 136 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 6} | ||
| 137 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":7} | ||
| 138 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 7} | ||
| 139 | 接收: {"jsonrpc":"2.0","method":"discover","params":{"filters":[{"services":["00001523-1212-efde-1523-785feabcd123"]},{"services":["00b6c4bc-8170-268e-4627-e07f37ed6744"]}],"optionalServices":["00004f0e-1212-efde-1523-785feabcd123","00001524-1212-efde-1523-785feabcd123","00001527-1212-efde-1523-785feabcd123","00001528-1212-efde-1523-785feabcd123","00001560-1212-efde-1523-785feabcd123","00001563-1212-efde-1523-785feabcd123","00001565-1212-efde-1523-785feabcd123","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","01b6c4bc-8170-268e-4627-e07f37ed6744","02b6c4bc-8170-268e-4627-e07f37ed6744","01B6C4BC-8170-268E-4627-E07F37ED6744","02B6C4BC-8170-268E-4627-E07F37ED6744"]},"id":0} | ||
| 140 | 下发: {"jsonrpc": "2.0", "method": "didDiscoverPeripheral", "params": {"name": "ccc", "peripheralId": "07:00:00:00:00:00", "rssi": -34}} | ||
| 141 | 下发: {"jsonrpc": "2.0", "result": null, "id": 0} | ||
| 142 | 接收: {"jsonrpc":"2.0","method":"connect","params":{"peripheralId":"07:00:00:00:00:00"},"id":1} | ||
| 143 | 下发: {"jsonrpc": "2.0", "result": null, "id": 1} | ||
| 144 | 接收: {"jsonrpc":"2.0","method":"write","params":{"serviceId":"00004f0e-1212-efde-1523-785feabcd123","characteristicId":"00001563-1212-efde-1523-785feabcd123","message":"AQIGFwEAAAAAAAA=","encoding":"base64"},"id":2} | ||
| 145 | 下发: {"jsonrpc": "2.0", "result": null, "id": 2} | ||
| 146 | 接收: {"jsonrpc":"2.0","method":"write","params":{"serviceId":"00004f0e-1212-efde-1523-785feabcd123","characteristicId":"00001565-1212-efde-1523-785feabcd123","message":"BgQDAAD/","encoding":"base64"},"id":3} | ||
| 147 | 下发: {"jsonrpc": "2.0", "result": null, "id": 3} | ||
| 148 | 接收: {"jsonrpc":"2.0","method":"startNotifications","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001527-1212-efde-1523-785feabcd123"},"id":4} | ||
| 149 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "AgEBAQEAAAABAAAA"}} | ||
| 150 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "AQEAAQEAAAABAAAA"}} | ||
| 151 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "AwEzFQEAAAABAAAA"}} | ||
| 152 | 下发: {"jsonrpc": "2.0", "result": null, "id": 4} | ||
| 153 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "BAE0FAEAAAABAAAA"}} | ||
| 154 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "BQE1FgEAAAABAAAA"}} | ||
| 155 | 下发: {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001527-1212-efde-1523-785feabcd123", "message": "BgE2FwEAAAABAAAA"}} | ||
| 156 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":5} | ||
| 157 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 5} | ||
| 158 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":6} | ||
| 159 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 6} | ||
| 160 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":7} | ||
| 161 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 7} | ||
| 162 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":8} | ||
| 163 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 8} | ||
| 164 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":9} | ||
| 165 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 9} | ||
| 166 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":10} | ||
| 167 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 10} | ||
| 168 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":11} | ||
| 169 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 11} | ||
| 170 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":12} | ||
| 171 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 12} | ||
| 172 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":13} | ||
| 173 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 13} | ||
| 174 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":14} | ||
| 175 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 14} | ||
| 176 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":15} | ||
| 177 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 15} | ||
| 178 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":16} | ||
| 179 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 16} | ||
| 180 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":17} | ||
| 181 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 17} | ||
| 182 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":18} | ||
| 183 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 18} | ||
| 184 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":19} | ||
| 185 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 19} | ||
| 186 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":20} | ||
| 187 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 20} | ||
| 188 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":21} | ||
| 189 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 21} | ||
| 190 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":22} | ||
| 191 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 22} | ||
| 192 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":23} | ||
| 193 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 23} | ||
| 194 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":24} | ||
| 195 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 24} | ||
| 196 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":25} | ||
| 197 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 25} | ||
| 198 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":26} | ||
| 199 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 26} | ||
| 200 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":27} | ||
| 201 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 27} | ||
| 202 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":28} | ||
| 203 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 28} | ||
| 204 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":29} | ||
| 205 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 29} | ||
| 206 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":30} | ||
| 207 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 30} | ||
| 208 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":31} | ||
| 209 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 31} | ||
| 210 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":32} | ||
| 211 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 32} | ||
| 212 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":33} | ||
| 213 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 33} | ||
| 214 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":34} | ||
| 215 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 34} | ||
| 216 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":35} | ||
| 217 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 35} | ||
| 218 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":36} | ||
| 219 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 36} | ||
| 220 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":37} | ||
| 221 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 37} | ||
| 222 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":38} | ||
| 223 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 38} | ||
| 224 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":39} | ||
| 225 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 39} | ||
| 226 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":40} | ||
| 227 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 40} | ||
| 228 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":41} | ||
| 229 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 41} | ||
| 230 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":42} | ||
| 231 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 42} | ||
| 232 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":43} | ||
| 233 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 43} | ||
| 234 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":44} | ||
| 235 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 44} | ||
| 236 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":45} | ||
| 237 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 45} | ||
| 238 | 接收: {"jsonrpc":"2.0","method":"read","params":{"serviceId":"00001523-1212-efde-1523-785feabcd123","characteristicId":"00001528-1212-efde-1523-785feabcd123"},"id":46} | ||
| 239 | 下发: {"jsonrpc": "2.0", "result": {"serviceId": "00001523-1212-efde-1523-785feabcd123", "characteristicId": "00001528-1212-efde-1523-785feabcd123", "message": "AA=="}, "id": 46} |
ble_client.spec
0 → 100644
| 1 | # -*- mode: python ; coding: utf-8 -*- | ||
| 2 | |||
| 3 | |||
| 4 | a = Analysis( | ||
| 5 | ['we.py'], | ||
| 6 | pathex=[], | ||
| 7 | binaries=[], | ||
| 8 | datas=[('data', 'data')], | ||
| 9 | hiddenimports=[], | ||
| 10 | hookspath=[], | ||
| 11 | hooksconfig={}, | ||
| 12 | runtime_hooks=[], | ||
| 13 | excludes=[], | ||
| 14 | noarchive=False, | ||
| 15 | optimize=0, | ||
| 16 | ) | ||
| 17 | pyz = PYZ(a.pure) | ||
| 18 | |||
| 19 | exe = EXE( | ||
| 20 | pyz, | ||
| 21 | a.scripts, | ||
| 22 | a.binaries, | ||
| 23 | a.datas, | ||
| 24 | [], | ||
| 25 | name='ble_client', | ||
| 26 | debug=False, | ||
| 27 | bootloader_ignore_signals=False, | ||
| 28 | strip=False, | ||
| 29 | upx=True, | ||
| 30 | upx_exclude=[], | ||
| 31 | runtime_tmpdir=None, | ||
| 32 | console=True, | ||
| 33 | disable_windowed_traceback=False, | ||
| 34 | argv_emulation=False, | ||
| 35 | target_arch=None, | ||
| 36 | codesign_identity=None, | ||
| 37 | entitlements_file=None, | ||
| 38 | icon=['data\\app.ico'], | ||
| 39 | ) |
m.py
0 → 100644
| 1 | import asyncio | ||
| 2 | import json | ||
| 3 | import websockets | ||
| 4 | |||
| 5 | # 预定义的下发通知列表 | ||
| 6 | NOTIFICATIONS = [ | ||
| 7 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W1EDAVwADF0="}}, | ||
| 8 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALV0="}}, | ||
| 9 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALQAvAC8ALV0="}}, | ||
| 10 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALF0="}}, | ||
| 11 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALQAvAC8ALV0="}}, | ||
| 12 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W1EDAVwADF0="}}, | ||
| 13 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALQAvAC8ALF0="}}, | ||
| 14 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W4ECAgDgXQ=="}}, | ||
| 15 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALQAvAC8ALF0="}}, | ||
| 16 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALF0="}}, | ||
| 17 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W1EDAVwADF0="}}, | ||
| 18 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALF0="}}, | ||
| 19 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W1EDAVwADF0="}}, | ||
| 20 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALV0="}}, | ||
| 21 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W4ECAgDgXQ=="}}, | ||
| 22 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALQAvAC8ALV0="}}, | ||
| 23 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALF0="}}, | ||
| 24 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALV0="}}, | ||
| 25 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W1EDAVwADF0="}}, | ||
| 26 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALV0="}}, | ||
| 27 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W4ECAgDgXQ=="}}, | ||
| 28 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALQAvAC8ALV0="}}, | ||
| 29 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALF0="}}, | ||
| 30 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W1EDAVwADF0="}}, | ||
| 31 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALQAvAC8ALV0="}}, | ||
| 32 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALQAvAC8ALF0="}}, | ||
| 33 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W1EDAVwADF0="}}, | ||
| 34 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALF0="}}, | ||
| 35 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W4ECAgDgXQ=="}}, | ||
| 36 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALV0="}}, | ||
| 37 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W1EDAVwADF0="}}, | ||
| 38 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALV0="}}, | ||
| 39 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALQAvAC8ALV0="}}, | ||
| 40 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "W4ECAgDgXQ=="}}, | ||
| 41 | {"jsonrpc": "2.0", "method": "characteristicDidChange", "params": {"serviceId": "00b6c4bc-8170-268e-4627-e07f37ed6744", "characteristicId": "01b6c4bc-8170-268e-4627-e07f37ed6744", "message": "WzEJAwkALgAvAC8ALF0="}} | ||
| 42 | ] | ||
| 43 | async def handle_client(websocket): | ||
| 44 | """处理客户端连接""" | ||
| 45 | client_ip = websocket.remote_address[0] | ||
| 46 | print(f"客户端 {client_ip} 已连接") | ||
| 47 | |||
| 48 | try: | ||
| 49 | async for message in websocket: | ||
| 50 | # 解析接收到的JSON消息 | ||
| 51 | try: | ||
| 52 | data = json.loads(message) | ||
| 53 | except json.JSONDecodeError: | ||
| 54 | print(f"无效的JSON数据: {message}") | ||
| 55 | continue | ||
| 56 | |||
| 57 | print(f"收到请求:\n{json.dumps(data, indent=2)}") | ||
| 58 | |||
| 59 | # 提取请求参数 | ||
| 60 | method = data.get("method") | ||
| 61 | req_id = data.get("id") | ||
| 62 | params = data.get("params", {}) | ||
| 63 | |||
| 64 | # 请求路由处理 | ||
| 65 | if method == "discover" and req_id == 0: | ||
| 66 | # 处理设备发现请求 | ||
| 67 | await websocket.send(json.dumps({ | ||
| 68 | "jsonrpc": "2.0", | ||
| 69 | "method": "didDiscoverPeripheral", | ||
| 70 | "params": { | ||
| 71 | "name": "12345", | ||
| 72 | "peripheralId": "E68744E9-02D5-42C1-AD3F-6BF6A7D4A5EC", | ||
| 73 | "rssi": -76 | ||
| 74 | } | ||
| 75 | })) | ||
| 76 | await send_response(websocket, None, 0) | ||
| 77 | |||
| 78 | elif method == "connect" and req_id == 1: | ||
| 79 | # 处理连接请求 | ||
| 80 | print(f"正在连接设备: {params.get('peripheralId')}") | ||
| 81 | await send_response(websocket, None, 1) | ||
| 82 | |||
| 83 | elif method == "write" and req_id == 2: | ||
| 84 | # 处理数据写入请求 | ||
| 85 | print(f"写入数据到特征: {params.get('characteristicId')}") | ||
| 86 | await send_response(websocket, None, 2) | ||
| 87 | |||
| 88 | elif method == "startNotifications" and req_id == 3: | ||
| 89 | # 处理通知订阅请求 | ||
| 90 | print("已启动特征通知") | ||
| 91 | await send_response(websocket, None, 3) | ||
| 92 | |||
| 93 | # 发送预定义通知流 | ||
| 94 | for idx, notification in enumerate(NOTIFICATIONS): | ||
| 95 | await websocket.send(json.dumps(notification)) | ||
| 96 | print(f"已发送通知 #{idx+1}") | ||
| 97 | await asyncio.sleep(1) # 每秒发送一个通知 | ||
| 98 | |||
| 99 | except websockets.exceptions.ConnectionClosed: | ||
| 100 | print(f"客户端 {client_ip} 已断开连接") | ||
| 101 | |||
| 102 | async def send_response(websocket, result, req_id): | ||
| 103 | """发送标准响应""" | ||
| 104 | response = { | ||
| 105 | "jsonrpc": "2.0", | ||
| 106 | "result": result, | ||
| 107 | "id": req_id | ||
| 108 | } | ||
| 109 | await websocket.send(json.dumps(response)) | ||
| 110 | print(f"已发送响应 ID {req_id}") | ||
| 111 | |||
| 112 | async def main(): | ||
| 113 | """启动WebSocket服务器""" | ||
| 114 | server = await websockets.serve( | ||
| 115 | handle_client, | ||
| 116 | host="0.0.0.0", | ||
| 117 | port=20111, | ||
| 118 | ping_interval=None | ||
| 119 | ) | ||
| 120 | |||
| 121 | print(f"WebSocket服务器已启动,监听 ws://0.0.0.0:20111") | ||
| 122 | print("按 Ctrl+C 停止服务器") | ||
| 123 | |||
| 124 | await server.wait_closed() | ||
| 125 | |||
| 126 | if __name__ == "__main__": | ||
| 127 | try: | ||
| 128 | asyncio.run(main()) | ||
| 129 | except KeyboardInterrupt: | ||
| 130 | print("\n服务器已关闭") | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
m.spec
0 → 100644
| 1 | # -*- mode: python ; coding: utf-8 -*- | ||
| 2 | |||
| 3 | |||
| 4 | a = Analysis( | ||
| 5 | ['m.py'], | ||
| 6 | pathex=[], | ||
| 7 | binaries=[], | ||
| 8 | datas=[('data', 'data')], | ||
| 9 | hiddenimports=[], | ||
| 10 | hookspath=[], | ||
| 11 | hooksconfig={}, | ||
| 12 | runtime_hooks=[], | ||
| 13 | excludes=[], | ||
| 14 | noarchive=False, | ||
| 15 | optimize=0, | ||
| 16 | ) | ||
| 17 | pyz = PYZ(a.pure) | ||
| 18 | |||
| 19 | exe = EXE( | ||
| 20 | pyz, | ||
| 21 | a.scripts, | ||
| 22 | a.binaries, | ||
| 23 | a.datas, | ||
| 24 | [], | ||
| 25 | name='m', | ||
| 26 | debug=False, | ||
| 27 | bootloader_ignore_signals=False, | ||
| 28 | strip=False, | ||
| 29 | upx=True, | ||
| 30 | upx_exclude=[], | ||
| 31 | runtime_tmpdir=None, | ||
| 32 | console=True, | ||
| 33 | disable_windowed_traceback=False, | ||
| 34 | argv_emulation=False, | ||
| 35 | target_arch=None, | ||
| 36 | codesign_identity=None, | ||
| 37 | entitlements_file=None, | ||
| 38 | icon=['data\\app.ico'], | ||
| 39 | ) |
pythonserver @ 65948850
| 1 | Subproject commit 659488506d3926a24a8012358231b8345c52e618 |
qt5.py
0 → 100644
| 1 | from PyQt5.QtCore import pyqtSlot, QObject, QTimer, pyqtSignal, QMutex, QMutexLocker, QThread, QEvent | ||
| 2 | from PyQt5.QtBluetooth import (QBluetoothDeviceDiscoveryAgent, QLowEnergyController, | ||
| 3 | QBluetoothUuid, QLowEnergyService, QLowEnergyCharacteristic, | ||
| 4 | QLowEnergyDescriptor, QBluetoothDeviceInfo, QBluetoothServiceDiscoveryAgent, | ||
| 5 | QBluetoothSocket, QBluetoothServiceInfo) | ||
| 6 | from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QVBoxLayout, QWidget, QPushButton, QHBoxLayout | ||
| 7 | from PyQt5.QtWebSockets import QWebSocketServer, QWebSocket | ||
| 8 | from PyQt5.QtNetwork import QHostAddress | ||
| 9 | |||
| 10 | class BluetoothDeviceScanner(QObject): | ||
| 11 | deviceDiscovered = pyqtSignal(QBluetoothDeviceInfo) | ||
| 12 | scanFinished = pyqtSignal() | ||
| 13 | deviceConnected = pyqtSignal(QLowEnergyController) | ||
| 14 | |||
| 15 | def __init__(self, parent=None, target_uuid="00001523-1212-efde-1523-785feabcd123"): | ||
| 16 | super().__init__(parent) | ||
| 17 | self.target_uuid = target_uuid | ||
| 18 | self.discovery_agent = QBluetoothDeviceDiscoveryAgent(self) | ||
| 19 | |||
| 20 | # 配置扫描参数 - 低功耗设备需要更长的扫描时间 | ||
| 21 | self.discovery_agent.setLowEnergyDiscoveryTimeout(10000) # 10秒的扫描时间 | ||
| 22 | |||
| 23 | # 配置扫描方式,包括低功耗设备和经典蓝牙设备 | ||
| 24 | self.discovery_agent.setInquiryType(QBluetoothDeviceDiscoveryAgent.GeneralUnlimitedInquiry) | ||
| 25 | |||
| 26 | # 连接信号 | ||
| 27 | self.discovery_agent.deviceDiscovered.connect(self.on_device_discovered) | ||
| 28 | self.discovery_agent.finished.connect(self.on_scan_finished) | ||
| 29 | self.discovery_agent.error.connect(self.on_error) | ||
| 30 | |||
| 31 | self.controllers = {} # Store controllers by device address | ||
| 32 | self.target_device = None | ||
| 33 | self.discovered_devices = {} # 存储所有发现的设备,避免重复处理 | ||
| 34 | |||
| 35 | # 添加自动重试机制 | ||
| 36 | self.scan_retry_count = 0 | ||
| 37 | self.max_scan_retries = 2 # 最多重试2次,加上第一次共3次 | ||
| 38 | self.connection_retry_count = 0 | ||
| 39 | self.max_connection_retries = 2 | ||
| 40 | |||
| 41 | # 创建计时器用于延迟重试 | ||
| 42 | self.retry_timer = QTimer(self) | ||
| 43 | self.retry_timer.setSingleShot(True) | ||
| 44 | self.retry_timer.timeout.connect(self.retry_scan) | ||
| 45 | |||
| 46 | # 记录已经尝试过连接的设备 | ||
| 47 | self.connection_attempts = set() | ||
| 48 | |||
| 49 | def start_scan(self): | ||
| 50 | """开始扫描蓝牙设备""" | ||
| 51 | print("开始蓝牙设备扫描...") | ||
| 52 | self.scan_retry_count = 0 | ||
| 53 | self.discovered_devices.clear() | ||
| 54 | self.connection_attempts.clear() | ||
| 55 | self.target_device = None | ||
| 56 | |||
| 57 | # 设置更广泛的设备类型过滤 | ||
| 58 | self.discovery_agent.start() | ||
| 59 | |||
| 60 | def stop_scan(self): | ||
| 61 | """停止设备扫描""" | ||
| 62 | self.discovery_agent.stop() | ||
| 63 | self.retry_timer.stop() | ||
| 64 | print("蓝牙设备扫描已停止") | ||
| 65 | |||
| 66 | def retry_scan(self): | ||
| 67 | """重试扫描,用于当扫描失败或未找到目标设备时""" | ||
| 68 | if self.scan_retry_count < self.max_scan_retries and not self.target_device: | ||
| 69 | self.scan_retry_count += 1 | ||
| 70 | print(f"重试扫描 ({self.scan_retry_count}/{self.max_scan_retries})...") | ||
| 71 | self.discovery_agent.start() | ||
| 72 | |||
| 73 | @pyqtSlot(QBluetoothDeviceInfo) | ||
| 74 | def on_device_discovered(self, device_info): | ||
| 75 | """当发现蓝牙设备时调用""" | ||
| 76 | device_name = device_info.name() or "未命名设备" | ||
| 77 | device_address = device_info.address().toString() | ||
| 78 | |||
| 79 | # 如果已经处理过这个设备,则直接返回,除非是我们感兴趣的目标设备 | ||
| 80 | if device_address in self.discovered_devices and "LPF2" not in device_name: | ||
| 81 | return | ||
| 82 | |||
| 83 | # 存储设备信息 | ||
| 84 | self.discovered_devices[device_address] = device_info | ||
| 85 | |||
| 86 | # 分析设备类型 | ||
| 87 | device_type = "未知" | ||
| 88 | # 获取主要和次要设备类别 | ||
| 89 | major_class = device_info.majorDeviceClass() | ||
| 90 | minor_class = device_info.minorDeviceClass() | ||
| 91 | |||
| 92 | print(f"调试信息 - 主类别: {major_class}, 次类别: {minor_class}") | ||
| 93 | |||
| 94 | # 识别设备类别 | ||
| 95 | if (major_class == QBluetoothDeviceInfo.MiscellaneousDevice or major_class == 0) and "LPF2" in device_name: | ||
| 96 | device_type = "LEGO智能集线器" | ||
| 97 | elif major_class == QBluetoothDeviceInfo.ToyDevice: | ||
| 98 | if minor_class == 0x01: | ||
| 99 | device_type = "玩具机器人" | ||
| 100 | elif minor_class == 0x02: | ||
| 101 | device_type = "玩具车辆" | ||
| 102 | else: | ||
| 103 | device_type = "玩具" | ||
| 104 | elif major_class == QBluetoothDeviceInfo.ComputerDevice: | ||
| 105 | device_type = "电脑" | ||
| 106 | elif major_class == QBluetoothDeviceInfo.PhoneDevice: | ||
| 107 | device_type = "手机" | ||
| 108 | elif major_class == QBluetoothDeviceInfo.AudioVideoDevice: | ||
| 109 | device_type = "音频/视频设备" | ||
| 110 | elif major_class == QBluetoothDeviceInfo.NetworkDevice: | ||
| 111 | device_type = "网络设备" | ||
| 112 | elif major_class == QBluetoothDeviceInfo.PeripheralDevice: | ||
| 113 | if minor_class & 0x04: # 键盘 | ||
| 114 | device_type = "键盘" | ||
| 115 | elif minor_class & 0x08: # 鼠标 | ||
| 116 | device_type = "鼠标" | ||
| 117 | elif minor_class & 0x10: # 组合键盘/鼠标 | ||
| 118 | device_type = "键盘/鼠标组合" | ||
| 119 | elif minor_class & 0x40: # 游戏杆 | ||
| 120 | device_type = "游戏杆" | ||
| 121 | elif minor_class & 0x80: # 游戏手柄 | ||
| 122 | device_type = "游戏手柄" | ||
| 123 | else: | ||
| 124 | device_type = "外围设备" | ||
| 125 | elif major_class == QBluetoothDeviceInfo.ImagingDevice: | ||
| 126 | device_type = "成像设备" | ||
| 127 | elif major_class == QBluetoothDeviceInfo.WearableDevice: | ||
| 128 | device_type = "可穿戴设备" | ||
| 129 | elif major_class == QBluetoothDeviceInfo.HealthDevice: | ||
| 130 | device_type = "健康设备" | ||
| 131 | |||
| 132 | # 基于设备名称的额外分类 | ||
| 133 | if device_type == "未知": | ||
| 134 | if "lego" in device_name.lower() or "lpf2" in device_name.lower(): | ||
| 135 | device_type = "LEGO智能集线器" | ||
| 136 | elif "hub" in device_name.lower(): | ||
| 137 | device_type = "智能集线器" | ||
| 138 | elif "watch" in device_name.lower(): | ||
| 139 | device_type = "智能手表" | ||
| 140 | elif "speaker" in device_name.lower() or "headphone" in device_name.lower() or "airpod" in device_name.lower(): | ||
| 141 | device_type = "音频设备" | ||
| 142 | elif "fitness" in device_name.lower() or "band" in device_name.lower(): | ||
| 143 | device_type = "健身追踪器" | ||
| 144 | |||
| 145 | print(f"发现设备: {device_name} ({device_address}) - 类型: {device_type}") | ||
| 146 | |||
| 147 | # 获取额外的设备信息 | ||
| 148 | rssi = device_info.rssi() | ||
| 149 | if rssi != 0: | ||
| 150 | print(f"信号强度 (RSSI): {rssi} dBm") | ||
| 151 | |||
| 152 | # 检查制造商特定数据 | ||
| 153 | manufacturer_data = device_info.manufacturerData() | ||
| 154 | if manufacturer_data: | ||
| 155 | print(f"制造商数据可用: {len(manufacturer_data)} 字节") | ||
| 156 | for manufacturer_id, data in manufacturer_data.items(): | ||
| 157 | # 将QByteArray转换为十六进制字符串 | ||
| 158 | data_bytes = bytes(data) | ||
| 159 | hex_string = data_bytes.hex() if hasattr(data_bytes, 'hex') else ' '.join([f'{b:02x}' for b in data_bytes]) | ||
| 160 | print(f"制造商ID: {manufacturer_id:04x}, 数据: {hex_string}") | ||
| 161 | |||
| 162 | # 获取服务UUID(如果可用) | ||
| 163 | service_uuids = device_info.serviceUuids() | ||
| 164 | if service_uuids: | ||
| 165 | print(f"服务UUID数量: {len(service_uuids)}") | ||
| 166 | for uuid_obj in service_uuids: | ||
| 167 | if isinstance(uuid_obj, QBluetoothUuid): | ||
| 168 | uuid_str = uuid_obj.toString() | ||
| 169 | print(f" - {uuid_str}") | ||
| 170 | |||
| 171 | # 连接决策逻辑 - 优先级从高到低 | ||
| 172 | should_connect = False | ||
| 173 | connection_reason = "" | ||
| 174 | |||
| 175 | # 优先级1: 检查设备是否有与目标UUID匹配的服务 | ||
| 176 | if service_uuids: | ||
| 177 | for uuid_obj in service_uuids: | ||
| 178 | if isinstance(uuid_obj, QBluetoothUuid): | ||
| 179 | uuid_str = uuid_obj.toString().lower() | ||
| 180 | if uuid_str == self.target_uuid.lower(): | ||
| 181 | should_connect = True | ||
| 182 | connection_reason = "匹配服务UUID" | ||
| 183 | break | ||
| 184 | |||
| 185 | # 优先级2: 特别处理LEGO设备 - 通常只是基于名称 | ||
| 186 | if not should_connect and "LPF2" in device_name: | ||
| 187 | should_connect = True | ||
| 188 | connection_reason = "LEGO智能集线器" | ||
| 189 | |||
| 190 | # 优先级3: 设备地址或名称包含目标UUID前缀 | ||
| 191 | if not should_connect and self.target_uuid: | ||
| 192 | if (self.target_uuid[:8].lower() in device_address.lower() or | ||
| 193 | (device_name and self.target_uuid[:8].lower() in device_name.lower())): | ||
| 194 | should_connect = True | ||
| 195 | connection_reason = "UUID前缀匹配名称/地址" | ||
| 196 | |||
| 197 | # 优先级4: 如果信号强度很好,并且是玩具类型设备 | ||
| 198 | if not should_connect and rssi > -60 and (device_type.startswith("玩具") or device_type.startswith("LEGO")): | ||
| 199 | should_connect = True | ||
| 200 | connection_reason = "强信号玩具设备" | ||
| 201 | |||
| 202 | # 如果决定连接,并且之前没有尝试过 | ||
| 203 | if should_connect and device_address not in self.connection_attempts: | ||
| 204 | print(f"尝试连接设备: {device_name} (原因: {connection_reason})") | ||
| 205 | self.connection_attempts.add(device_address) # 标记为已尝试 | ||
| 206 | self.target_device = device_info | ||
| 207 | self.connect_to_device(device_info) | ||
| 208 | |||
| 209 | # 找到了可能的目标,停止扫描 | ||
| 210 | if "LPF2" in device_name: | ||
| 211 | self.discovery_agent.stop() | ||
| 212 | |||
| 213 | self.deviceDiscovered.emit(device_info) | ||
| 214 | |||
| 215 | @pyqtSlot() | ||
| 216 | def on_scan_finished(self): | ||
| 217 | """当设备扫描完成时调用""" | ||
| 218 | print("蓝牙扫描完成") | ||
| 219 | |||
| 220 | # 如果我们还没有找到目标设备,但又发现了一些设备,则选择一个尝试连接 | ||
| 221 | if not self.target_device and self.discovered_devices: | ||
| 222 | print("没有通过UUID找到目标设备,尝试连接发现的设备...") | ||
| 223 | |||
| 224 | # 首先尝试连接任何包含"LPF2"的设备 | ||
| 225 | for addr, device_info in self.discovered_devices.items(): | ||
| 226 | if "LPF2" in device_info.name() and addr not in self.connection_attempts: | ||
| 227 | print(f"尝试连接: {device_info.name()} (LEGO设备)") | ||
| 228 | self.connection_attempts.add(addr) | ||
| 229 | self.target_device = device_info | ||
| 230 | self.connect_to_device(device_info) | ||
| 231 | break | ||
| 232 | |||
| 233 | # 如果还没有目标设备,尝试基于信号强度来选择设备 | ||
| 234 | if not self.target_device: | ||
| 235 | # 按信号强度排序设备 | ||
| 236 | devices_by_signal = sorted( | ||
| 237 | [(addr, info) for addr, info in self.discovered_devices.items() if addr not in self.connection_attempts], | ||
| 238 | key=lambda x: x[1].rssi(), reverse=True # 最强信号优先 | ||
| 239 | ) | ||
| 240 | |||
| 241 | if devices_by_signal: | ||
| 242 | addr, device_info = devices_by_signal[0] | ||
| 243 | print(f"尝试连接: {device_info.name()} (最强信号设备)") | ||
| 244 | self.connection_attempts.add(addr) | ||
| 245 | self.target_device = device_info | ||
| 246 | self.connect_to_device(device_info) | ||
| 247 | |||
| 248 | # 如果仍未找到设备,考虑重试扫描 | ||
| 249 | if not self.target_device and self.scan_retry_count < self.max_scan_retries: | ||
| 250 | print(f"没有找到合适的设备,将在2秒后重试扫描... (已尝试 {self.scan_retry_count+1}/{self.max_scan_retries+1})") | ||
| 251 | self.retry_timer.start(2000) # 2秒后重试 | ||
| 252 | else: | ||
| 253 | if not self.target_device: | ||
| 254 | print("经过多次尝试后仍未找到目标设备。") | ||
| 255 | self.scanFinished.emit() | ||
| 256 | |||
| 257 | @pyqtSlot(QBluetoothDeviceDiscoveryAgent.Error) | ||
| 258 | def on_error(self, error): | ||
| 259 | """处理发现错误""" | ||
| 260 | error_str = "未知错误" | ||
| 261 | if error == QBluetoothDeviceDiscoveryAgent.PoweredOffError: | ||
| 262 | error_str = "蓝牙已关闭" | ||
| 263 | elif error == QBluetoothDeviceDiscoveryAgent.InputOutputError: | ||
| 264 | error_str = "蓝牙I/O错误" | ||
| 265 | elif error == QBluetoothDeviceDiscoveryAgent.InvalidBluetoothAdapterError: | ||
| 266 | error_str = "无效的蓝牙适配器" | ||
| 267 | elif error == QBluetoothDeviceDiscoveryAgent.UnsupportedPlatformError: | ||
| 268 | error_str = "不支持的平台" | ||
| 269 | elif error == QBluetoothDeviceDiscoveryAgent.UnsupportedDiscoveryMethod: | ||
| 270 | error_str = "不支持的发现方法" | ||
| 271 | elif error == QBluetoothDeviceDiscoveryAgent.ResourceError: | ||
| 272 | error_str = "资源错误" | ||
| 273 | |||
| 274 | print(f"蓝牙发现错误: {error_str}") | ||
| 275 | |||
| 276 | # 如果遇到错误,也考虑重试 | ||
| 277 | if self.scan_retry_count < self.max_scan_retries: | ||
| 278 | print(f"因错误将在3秒后重试扫描...") | ||
| 279 | self.retry_timer.start(3000) # 3秒后重试 | ||
| 280 | |||
| 281 | def connect_to_device(self, device_info): | ||
| 282 | """连接到指定设备""" | ||
| 283 | print(f"正在连接 {device_info.name()}...") | ||
| 284 | |||
| 285 | # 创建设备控制器 | ||
| 286 | controller = QLowEnergyController.createCentral(device_info) | ||
| 287 | device_address = device_info.address().toString() | ||
| 288 | self.controllers[device_address] = controller | ||
| 289 | |||
| 290 | # 连接控制器信号 | ||
| 291 | controller.connected.connect(self.on_device_connected) | ||
| 292 | controller.disconnected.connect(self.on_device_disconnected) | ||
| 293 | controller.error.connect(self.on_controller_error) | ||
| 294 | controller.serviceDiscovered.connect(self.on_service_discovered) | ||
| 295 | controller.discoveryFinished.connect(self.on_service_discovery_finished) | ||
| 296 | |||
| 297 | # 连接超时计时器 | ||
| 298 | connection_timer = QTimer(self) | ||
| 299 | connection_timer.setSingleShot(True) | ||
| 300 | connection_timer.timeout.connect(lambda: self.on_connection_timeout(device_address, connection_timer)) | ||
| 301 | connection_timer.start(5000) # 5秒连接超时 | ||
| 302 | |||
| 303 | # 存储计时器以便稍后引用 | ||
| 304 | controller.property_connection_timer = connection_timer | ||
| 305 | |||
| 306 | # 连接到设备 | ||
| 307 | controller.connectToDevice() | ||
| 308 | |||
| 309 | def on_connection_timeout(self, device_address, timer): | ||
| 310 | """处理连接超时""" | ||
| 311 | if device_address in self.controllers: | ||
| 312 | controller = self.controllers[device_address] | ||
| 313 | # 在PyQt5中,QLowEnergyController没有isConnected()方法 | ||
| 314 | # 改用state()方法检查连接状态 | ||
| 315 | if controller.state() != QLowEnergyController.ConnectedState: | ||
| 316 | print(f"连接到 {controller.remoteName()} 超时") | ||
| 317 | |||
| 318 | # 删除连接尝试记录,以便后续可以再次尝试 | ||
| 319 | if device_address in self.connection_attempts: | ||
| 320 | self.connection_attempts.remove(device_address) | ||
| 321 | |||
| 322 | # 设置为null,这样我们可以尝试其他设备 | ||
| 323 | if self.target_device and self.target_device.address().toString() == device_address: | ||
| 324 | self.target_device = None | ||
| 325 | |||
| 326 | # 重新开始扫描,如果我们已经尝试了最大重试次数 | ||
| 327 | if not self.discovery_agent.isActive() and self.scan_retry_count < self.max_scan_retries: | ||
| 328 | print("将重新开始扫描...") | ||
| 329 | self.retry_timer.start(1000) # 1秒后重试 | ||
| 330 | |||
| 331 | @pyqtSlot() | ||
| 332 | def on_device_connected(self): | ||
| 333 | """当连接到设备时调用""" | ||
| 334 | controller = self.sender() | ||
| 335 | print(f"已连接到 {controller.remoteName()}") | ||
| 336 | self.deviceConnected.emit(controller) | ||
| 337 | |||
| 338 | # 停止连接超时计时器 | ||
| 339 | if hasattr(controller, 'property_connection_timer'): | ||
| 340 | controller.property_connection_timer.stop() | ||
| 341 | |||
| 342 | # 发现服务 | ||
| 343 | controller.discoverServices() | ||
| 344 | |||
| 345 | @pyqtSlot() | ||
| 346 | def on_device_disconnected(self): | ||
| 347 | """当从设备断开连接时调用""" | ||
| 348 | controller = self.sender() | ||
| 349 | device_address = "" | ||
| 350 | |||
| 351 | # 找到这个控制器的地址 | ||
| 352 | for addr, ctrl in self.controllers.items(): | ||
| 353 | if ctrl == controller: | ||
| 354 | device_address = addr | ||
| 355 | break | ||
| 356 | |||
| 357 | print(f"从 {controller.remoteName()} 断开连接") | ||
| 358 | |||
| 359 | # 如果这是我们当前的目标设备,清除它,以便我们可以尝试另一个 | ||
| 360 | if self.target_device and self.target_device.address().toString() == device_address: | ||
| 361 | self.target_device = None | ||
| 362 | |||
| 363 | # 如果当前没有在扫描,并且我们还有剩余的重试次数,则重新开始扫描 | ||
| 364 | if not self.discovery_agent.isActive() and self.scan_retry_count < self.max_scan_retries: | ||
| 365 | print("设备断开连接,将重新开始扫描...") | ||
| 366 | self.retry_timer.start(1000) # 1秒后重试 | ||
| 367 | |||
| 368 | @pyqtSlot(QLowEnergyController.Error) | ||
| 369 | def on_controller_error(self, error): | ||
| 370 | """处理控制器错误""" | ||
| 371 | controller = self.sender() | ||
| 372 | device_address = "" | ||
| 373 | |||
| 374 | # 找到这个控制器的地址 | ||
| 375 | for addr, ctrl in self.controllers.items(): | ||
| 376 | if ctrl == controller: | ||
| 377 | device_address = addr | ||
| 378 | break | ||
| 379 | |||
| 380 | error_str = "未知错误" | ||
| 381 | if error == QLowEnergyController.UnknownError: | ||
| 382 | error_str = "未知错误" | ||
| 383 | elif error == QLowEnergyController.RemoteHostClosedError: | ||
| 384 | error_str = "远程主机关闭了连接" | ||
| 385 | elif error == QLowEnergyController.ConnectionError: | ||
| 386 | error_str = "连接错误" | ||
| 387 | |||
| 388 | print(f"控制器错误 ({controller.remoteName()}): {error_str}") | ||
| 389 | |||
| 390 | # 如果是我们当前的目标设备,清除它并尝试另一个 | ||
| 391 | if self.target_device and self.target_device.address().toString() == device_address: | ||
| 392 | # 从尝试列表中移除,以便后续可以重新尝试 | ||
| 393 | if device_address in self.connection_attempts: | ||
| 394 | self.connection_attempts.remove(device_address) | ||
| 395 | |||
| 396 | self.target_device = None | ||
| 397 | |||
| 398 | # 如果当前没有在扫描,并且我们还有剩余的重试次数,则重新开始扫描 | ||
| 399 | if not self.discovery_agent.isActive() and self.scan_retry_count < self.max_scan_retries: | ||
| 400 | print("因控制器错误将重新开始扫描...") | ||
| 401 | self.retry_timer.start(1000) # 1秒后重试 | ||
| 402 | |||
| 403 | @pyqtSlot(QBluetoothUuid) | ||
| 404 | def on_service_discovered(self, uuid): | ||
| 405 | """当在连接的设备上发现服务时调用""" | ||
| 406 | controller = self.sender() | ||
| 407 | print(f"在 {controller.remoteName()} 上发现服务: {uuid.toString()}") | ||
| 408 | |||
| 409 | @pyqtSlot() | ||
| 410 | def on_service_discovery_finished(self): | ||
| 411 | """当服务发现完成时调用""" | ||
| 412 | controller = self.sender() | ||
| 413 | print(f"{controller.remoteName()} 的服务发现完成") | ||
| 414 | |||
| 415 | # 处理发现的服务 | ||
| 416 | services_found = False | ||
| 417 | for service_uuid in controller.services(): | ||
| 418 | service = controller.createServiceObject(service_uuid) | ||
| 419 | if service: | ||
| 420 | services_found = True | ||
| 421 | print(f"处理服务: {service_uuid.toString()}") | ||
| 422 | service.stateChanged.connect(self.on_service_state_changed) | ||
| 423 | service.characteristicChanged.connect( | ||
| 424 | lambda characteristic, value, service=service: | ||
| 425 | self.on_characteristic_changed(characteristic, value) | ||
| 426 | ) | ||
| 427 | service.discoverDetails() | ||
| 428 | |||
| 429 | if not services_found: | ||
| 430 | print("未发现服务,可能需要特定的连接协议") | ||
| 431 | |||
| 432 | @pyqtSlot(QLowEnergyService.ServiceState) | ||
| 433 | def on_service_state_changed(self, state): | ||
| 434 | """当服务状态改变时调用""" | ||
| 435 | service = self.sender() | ||
| 436 | if state == QLowEnergyService.ServiceDiscovered: | ||
| 437 | print(f"服务详情已发现: {service.serviceUuid().toString()}") | ||
| 438 | |||
| 439 | # 处理特性 | ||
| 440 | for characteristic in service.characteristics(): | ||
| 441 | print(f"发现特性: {characteristic.uuid().toString()}") | ||
| 442 | print(f" - 特性属性: {characteristic.properties()}") | ||
| 443 | |||
| 444 | # 如果这是一个可读特性,尝试读取它 | ||
| 445 | if characteristic.properties() & QLowEnergyCharacteristic.Read: | ||
| 446 | print(f" - 读取特性值...") | ||
| 447 | service.readCharacteristic(characteristic) | ||
| 448 | |||
| 449 | # 如果这是一个可通知特性,尝试开启通知 | ||
| 450 | if characteristic.properties() & QLowEnergyCharacteristic.Notify: | ||
| 451 | print(f" - 启用通知...") | ||
| 452 | try: | ||
| 453 | # PyQt5的QLowEnergyService没有descriptors()方法 | ||
| 454 | # 我们可以直接尝试获取CCCD描述符 | ||
| 455 | # 或者尝试直接写入特征来启用通知 | ||
| 456 | descriptor = characteristic.descriptor(QBluetoothUuid(QBluetoothUuid.ClientCharacteristicConfiguration)) | ||
| 457 | if descriptor.isValid(): | ||
| 458 | print(f" - 找到客户端特性配置描述符") | ||
| 459 | service.writeDescriptor(descriptor, b"\x01\x00") # 启用通知 | ||
| 460 | else: | ||
| 461 | print(f" - 未找到有效的客户端特性配置描述符") | ||
| 462 | # 某些实现可能支持以下方法 | ||
| 463 | if hasattr(service, 'setNotifyValue'): | ||
| 464 | print(f" - 尝试使用setNotifyValue方法") | ||
| 465 | service.setNotifyValue(characteristic, True) | ||
| 466 | except Exception as e: | ||
| 467 | print(f" - 启用通知时出错: {e}") | ||
| 468 | print(f" - 将尝试特殊处理LEGO设备...") | ||
| 469 | |||
| 470 | # LEGO设备特殊处理 - 这是一种常见的方法 | ||
| 471 | try: | ||
| 472 | if "LPF2" in service.controller().remoteName(): | ||
| 473 | print(f" - 检测到LEGO设备,尝试特殊处理") | ||
| 474 | # 有些LEGO设备需要写入一个特定值到特性以启用通知 | ||
| 475 | if characteristic.properties() & QLowEnergyCharacteristic.Write: | ||
| 476 | print(f" - 尝试写入特性来启用通知") | ||
| 477 | service.writeCharacteristic(characteristic, b"\x01\x00") | ||
| 478 | except Exception as e2: | ||
| 479 | print(f" - LEGO设备特殊处理失败: {e2}") | ||
| 480 | |||
| 481 | def on_characteristic_changed(self, characteristic, value): | ||
| 482 | """当特性值改变时调用""" | ||
| 483 | try: | ||
| 484 | # 将QByteArray转换为十六进制字符串 | ||
| 485 | if hasattr(value, 'data'): # 它是一个QByteArray | ||
| 486 | data_bytes = bytes(value) | ||
| 487 | hex_string = data_bytes.hex() if hasattr(data_bytes, 'hex') else ' '.join([f'{b:02x}' for b in data_bytes]) | ||
| 488 | else: # 它可能已经是bytes | ||
| 489 | hex_string = value.hex() if hasattr(value, 'hex') else ' '.join([f'{b:02x}' for b in value]) | ||
| 490 | |||
| 491 | print(f"特性 {characteristic.uuid().toString()} 值改变: {hex_string}") | ||
| 492 | |||
| 493 | # 解析数据 (示例) | ||
| 494 | print(f"数据解析: {' '.join([f'{b:02x}' for b in data_bytes])}") | ||
| 495 | except Exception as e: | ||
| 496 | print(f"处理特性变化时出错: {e}") | ||
| 497 | |||
| 498 | |||
| 499 | def main(): | ||
| 500 | """运行蓝牙扫描器的主函数""" | ||
| 501 | import sys | ||
| 502 | |||
| 503 | app = QApplication(sys.argv) | ||
| 504 | |||
| 505 | # 创建带有目标UUID的扫描器 | ||
| 506 | scanner = BluetoothDeviceScanner(target_uuid="00001523-1212-efde-1523-785feabcd123") | ||
| 507 | |||
| 508 | # 创建简单的UI | ||
| 509 | window = QMainWindow() | ||
| 510 | window.setWindowTitle("蓝牙扫描器") | ||
| 511 | window.resize(700, 500) | ||
| 512 | |||
| 513 | central_widget = QWidget() | ||
| 514 | layout = QVBoxLayout(central_widget) | ||
| 515 | |||
| 516 | # 添加文本编辑框 | ||
| 517 | text_edit = QTextEdit() | ||
| 518 | text_edit.setReadOnly(True) | ||
| 519 | layout.addWidget(text_edit) | ||
| 520 | |||
| 521 | # 添加按钮布局 | ||
| 522 | button_layout = QHBoxLayout() | ||
| 523 | |||
| 524 | # 开始扫描按钮 | ||
| 525 | start_scan_button = QPushButton("开始扫描") | ||
| 526 | start_scan_button.clicked.connect(scanner.start_scan) | ||
| 527 | button_layout.addWidget(start_scan_button) | ||
| 528 | |||
| 529 | # 停止扫描按钮 | ||
| 530 | stop_scan_button = QPushButton("停止扫描") | ||
| 531 | stop_scan_button.clicked.connect(scanner.stop_scan) | ||
| 532 | button_layout.addWidget(stop_scan_button) | ||
| 533 | |||
| 534 | # 清除按钮 | ||
| 535 | clear_button = QPushButton("清除日志") | ||
| 536 | clear_button.clicked.connect(text_edit.clear) | ||
| 537 | button_layout.addWidget(clear_button) | ||
| 538 | |||
| 539 | layout.addLayout(button_layout) | ||
| 540 | |||
| 541 | window.setCentralWidget(central_widget) | ||
| 542 | window.show() | ||
| 543 | |||
| 544 | # 重定向print语句到文本编辑框 | ||
| 545 | import sys | ||
| 546 | original_stdout = sys.stdout | ||
| 547 | |||
| 548 | class TextEditRedirector: | ||
| 549 | def __init__(self, text_edit): | ||
| 550 | self.text_edit = text_edit | ||
| 551 | |||
| 552 | def write(self, text): | ||
| 553 | self.text_edit.append(text) | ||
| 554 | original_stdout.write(text) | ||
| 555 | |||
| 556 | def flush(self): | ||
| 557 | pass | ||
| 558 | |||
| 559 | sys.stdout = TextEditRedirector(text_edit) | ||
| 560 | |||
| 561 | # 自动开始第一次扫描 | ||
| 562 | scanner.start_scan() | ||
| 563 | |||
| 564 | return app.exec_() | ||
| 565 | |||
| 566 | |||
| 567 | if __name__ == "__main__": | ||
| 568 | main() | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
qt5.spec
0 → 100644
| 1 | # -*- mode: python ; coding: utf-8 -*- | ||
| 2 | |||
| 3 | |||
| 4 | a = Analysis( | ||
| 5 | ['qt5.py'], | ||
| 6 | pathex=[], | ||
| 7 | binaries=[], | ||
| 8 | datas=[('data', 'data')], | ||
| 9 | hiddenimports=[], | ||
| 10 | hookspath=[], | ||
| 11 | hooksconfig={}, | ||
| 12 | runtime_hooks=[], | ||
| 13 | excludes=[], | ||
| 14 | noarchive=False, | ||
| 15 | optimize=0, | ||
| 16 | ) | ||
| 17 | pyz = PYZ(a.pure) | ||
| 18 | |||
| 19 | exe = EXE( | ||
| 20 | pyz, | ||
| 21 | a.scripts, | ||
| 22 | a.binaries, | ||
| 23 | a.datas, | ||
| 24 | [], | ||
| 25 | name='qt5', | ||
| 26 | debug=False, | ||
| 27 | bootloader_ignore_signals=False, | ||
| 28 | strip=False, | ||
| 29 | upx=True, | ||
| 30 | upx_exclude=[], | ||
| 31 | runtime_tmpdir=None, | ||
| 32 | console=True, | ||
| 33 | disable_windowed_traceback=False, | ||
| 34 | argv_emulation=False, | ||
| 35 | target_arch=None, | ||
| 36 | codesign_identity=None, | ||
| 37 | entitlements_file=None, | ||
| 38 | icon=['data\\app.ico'], | ||
| 39 | ) |
| ... | @@ -27,7 +27,9 @@ pip install pystray pillow | ... | @@ -27,7 +27,9 @@ pip install pystray pillow |
| 27 | 27 | ||
| 28 | ## | 28 | ## |
| 29 | 打包指令 产生一个带窗口的程序 打包完后可以随便改exe 名字 | 29 | 打包指令 产生一个带窗口的程序 打包完后可以随便改exe 名字 |
| 30 | C:\Users\Admin\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\Scripts\pyinstaller.exe --onefile --icon=data\app.ico we.py --add-data "data;data" | 30 | C:\Users\Admin\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\Scripts\pyinstaller.exe -F --hidden-import=bleak.backends --hidden-import=asyncio --win-private-assemblies --onefile --icon=data\app.ico we.py --add-data "data;data" |
| 31 | |||
| 32 | C:\Users\Admin\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\LocalCache\local-packages\Python313\Scripts\pyinstaller.exe --onefile --windowed --name=ble_client --icon=data\app.ico we.py --add-data "data;data" | ||
| 31 | 33 | ||
| 32 | ## | 34 | ## |
| 33 | 端口占用 | 35 | 端口占用 | ... | ... |
s6.10.py
0 → 100644
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | import sys | ||
| 3 | import asyncio | ||
| 4 | import websockets | ||
| 5 | from bleak import BleakScanner, BleakClient | ||
| 6 | import json | ||
| 7 | import base64 | ||
| 8 | import threading | ||
| 9 | from collections import defaultdict | ||
| 10 | |||
| 11 | import platform | ||
| 12 | |||
| 13 | # 方法1:通过sys模块快速判断(推荐) | ||
| 14 | if sys.platform.startswith('win32'): | ||
| 15 | import winrt.windows.foundation.collections # noqa | ||
| 16 | import winrt.windows.devices.bluetooth # noqa | ||
| 17 | import winrt.windows.devices.bluetooth.advertisement # noq | ||
| 18 | print("当前运行在Windows系统") | ||
| 19 | elif sys.platform.startswith('linux'): | ||
| 20 | print("当前运行在Linux系统") | ||
| 21 | elif sys.platform.startswith('darwin'): | ||
| 22 | print("当前运行在macOS系统") | ||
| 23 | # 添加线程锁以确保日志写入的原子性 | ||
| 24 | write_lock = threading.Lock() | ||
| 25 | |||
| 26 | def log_message_sync(direction, message): | ||
| 27 | """同步日志记录函数""" | ||
| 28 | log_entry = f"{direction}: {message}\n" | ||
| 29 | print(log_entry, end='') # 控制台仍然输出 | ||
| 30 | with write_lock: | ||
| 31 | with open('b.log', 'a', encoding='utf-8') as f: | ||
| 32 | f.write(log_entry) | ||
| 33 | |||
| 34 | async def log_message(direction, message): | ||
| 35 | """异步封装日志记录""" | ||
| 36 | loop = asyncio.get_event_loop() | ||
| 37 | await loop.run_in_executor(None, log_message_sync, direction, message) | ||
| 38 | |||
| 39 | class BLEClient: | ||
| 40 | def __init__(self): | ||
| 41 | self.target_device = None | ||
| 42 | self.client = None | ||
| 43 | self.services = [] | ||
| 44 | self.optional_services = [] | ||
| 45 | self.websocket = None | ||
| 46 | self.notification_records = defaultdict(lambda: (None, 0)) # 特征ID: (最后消息, 时间戳) | ||
| 47 | |||
| 48 | def on_disconnect(self, client): | ||
| 49 | print("BLE连接断开,关闭WebSocket") | ||
| 50 | if self.websocket and not self.websocket.closed: | ||
| 51 | asyncio.create_task(self.close_websocket()) | ||
| 52 | |||
| 53 | async def close_websocket(self): | ||
| 54 | await self.websocket.close() | ||
| 55 | self.websocket = None | ||
| 56 | |||
| 57 | def detection_callback(self, device, advertisement_data): | ||
| 58 | if any(service_uuid in advertisement_data.service_uuids for service_uuid in self.services): | ||
| 59 | self.target_device = (device, advertisement_data) | ||
| 60 | if not self.target_device: | ||
| 61 | print("未找到匹配设备") | ||
| 62 | return | ||
| 63 | else: | ||
| 64 | device, adv_data = self.target_device | ||
| 65 | print("\n找到目标设备:") | ||
| 66 | print(f"设备名称: {device.name}") | ||
| 67 | print(f"设备地址: {device.address}") | ||
| 68 | print(f"信号强度: {device.rssi} dBm") | ||
| 69 | print("\n广播信息:") | ||
| 70 | print(f"服务UUID列表: {adv_data.service_uuids}") | ||
| 71 | print(f"制造商数据: {adv_data.manufacturer_data}") | ||
| 72 | print(f"服务数据: {adv_data.service_data}") | ||
| 73 | print(f"本地名称: {adv_data.local_name}") | ||
| 74 | return self.target_device | ||
| 75 | |||
| 76 | async def handle_client(self, websocket, path): | ||
| 77 | self.websocket = websocket | ||
| 78 | if path != "/scratch/ble": | ||
| 79 | await websocket.close(code=1003, reason="Path not allowed") | ||
| 80 | return | ||
| 81 | |||
| 82 | try: | ||
| 83 | async for message in websocket: | ||
| 84 | try: | ||
| 85 | await log_message("接收", message) | ||
| 86 | request = json.loads(message) | ||
| 87 | |||
| 88 | if request["jsonrpc"] != "2.0": | ||
| 89 | continue | ||
| 90 | |||
| 91 | method = request.get("method") | ||
| 92 | params = request.get("params", {}) | ||
| 93 | request_id = request.get("id") | ||
| 94 | |||
| 95 | if method == "discover": | ||
| 96 | self.services = [] | ||
| 97 | for filt in params.get("filters", [{}]): | ||
| 98 | self.services.extend(filt.get("services", [])) | ||
| 99 | self.optional_services = params.get("optionalServices", []) | ||
| 100 | |||
| 101 | scanner = BleakScanner(scanning_mode="active") | ||
| 102 | scanner.register_detection_callback(self.detection_callback) | ||
| 103 | |||
| 104 | max_retries = 3 | ||
| 105 | found = False | ||
| 106 | for attempt in range(max_retries): | ||
| 107 | self.target_device = None | ||
| 108 | await scanner.start() | ||
| 109 | await asyncio.sleep(5) | ||
| 110 | await scanner.stop() | ||
| 111 | |||
| 112 | if self.target_device: | ||
| 113 | found = True | ||
| 114 | break | ||
| 115 | |||
| 116 | if attempt < max_retries - 1: | ||
| 117 | print(f"未找到设备,第{attempt+1}次重试...") | ||
| 118 | await asyncio.sleep(3) | ||
| 119 | |||
| 120 | if found: | ||
| 121 | device, adv_data = self.target_device | ||
| 122 | discover_response = json.dumps({ | ||
| 123 | "jsonrpc": "2.0", | ||
| 124 | "method": "didDiscoverPeripheral", | ||
| 125 | "params": { | ||
| 126 | "name": device.name, | ||
| 127 | "peripheralId": device.address, | ||
| 128 | "rssi": device.rssi | ||
| 129 | } | ||
| 130 | }) | ||
| 131 | await log_message("下发", discover_response) | ||
| 132 | await websocket.send(discover_response) | ||
| 133 | |||
| 134 | result_response = json.dumps({ | ||
| 135 | "jsonrpc": "2.0", | ||
| 136 | "result": None, | ||
| 137 | "id": request_id | ||
| 138 | }) | ||
| 139 | await log_message("下发", result_response) | ||
| 140 | await websocket.send(result_response) | ||
| 141 | |||
| 142 | elif method == "connect": | ||
| 143 | peripheral_id = params.get("peripheralId") | ||
| 144 | if peripheral_id: | ||
| 145 | self.client = BleakClient(peripheral_id) | ||
| 146 | self.client.set_disconnected_callback(self.on_disconnect) | ||
| 147 | await self.client.connect() | ||
| 148 | |||
| 149 | if self.client.is_connected: | ||
| 150 | response = json.dumps({ | ||
| 151 | "jsonrpc": "2.0", | ||
| 152 | "result": None, | ||
| 153 | "id": request_id | ||
| 154 | }) | ||
| 155 | await log_message("下发", response) | ||
| 156 | await websocket.send(response) | ||
| 157 | |||
| 158 | elif method == "write": | ||
| 159 | service_id = params.get("serviceId") | ||
| 160 | characteristic_id = params.get("characteristicId") | ||
| 161 | message = params.get("message") | ||
| 162 | encoding = params.get("encoding", "utf-8") | ||
| 163 | |||
| 164 | if all([service_id, characteristic_id, message]): | ||
| 165 | if encoding == "base64": | ||
| 166 | message_bytes = base64.b64decode(message) | ||
| 167 | else: | ||
| 168 | message_bytes = message.encode(encoding) | ||
| 169 | |||
| 170 | await self.client.write_gatt_char(characteristic_id, message_bytes) | ||
| 171 | response = json.dumps({ | ||
| 172 | "jsonrpc": "2.0", | ||
| 173 | "result": None, | ||
| 174 | "id": request_id | ||
| 175 | }) | ||
| 176 | await log_message("下发", response) | ||
| 177 | await websocket.send(response) | ||
| 178 | |||
| 179 | elif method == "read": | ||
| 180 | service_id = params.get("serviceId") | ||
| 181 | characteristic_id = params.get("characteristicId") | ||
| 182 | |||
| 183 | if all([service_id, characteristic_id]): | ||
| 184 | data = await self.client.read_gatt_char(characteristic_id) | ||
| 185 | response = json.dumps({ | ||
| 186 | "jsonrpc": "2.0", | ||
| 187 | "result": { | ||
| 188 | "serviceId": service_id, | ||
| 189 | "characteristicId": characteristic_id, | ||
| 190 | "message": base64.b64encode(data).decode("utf-8") | ||
| 191 | }, | ||
| 192 | "id": request_id | ||
| 193 | }) | ||
| 194 | await log_message("下发", response) | ||
| 195 | await websocket.send(response) | ||
| 196 | |||
| 197 | elif method == "startNotifications": | ||
| 198 | service_id = params.get("serviceId") | ||
| 199 | characteristic_id = params.get("characteristicId") | ||
| 200 | |||
| 201 | if all([service_id, characteristic_id]): | ||
| 202 | await self.client.start_notify( | ||
| 203 | characteristic_id, | ||
| 204 | self.notification_handler(websocket, service_id, characteristic_id) | ||
| 205 | ) | ||
| 206 | response = json.dumps({ | ||
| 207 | "jsonrpc": "2.0", | ||
| 208 | "result": None, | ||
| 209 | "id": request_id | ||
| 210 | }) | ||
| 211 | await log_message("下发", response) | ||
| 212 | await websocket.send(response) | ||
| 213 | |||
| 214 | except json.JSONDecodeError: | ||
| 215 | error_msg = json.dumps({ | ||
| 216 | "jsonrpc": "2.0", | ||
| 217 | "result": {"message": "Parse error"}, | ||
| 218 | "id": None | ||
| 219 | }) | ||
| 220 | await log_message("下发", error_msg) | ||
| 221 | except Exception as e: | ||
| 222 | error_msg = json.dumps({ | ||
| 223 | "jsonrpc": "2.0", | ||
| 224 | "result": {"message": str(e)}, | ||
| 225 | "id": request.get("id") if request else None | ||
| 226 | }) | ||
| 227 | await log_message("下发", error_msg) | ||
| 228 | |||
| 229 | except websockets.exceptions.ConnectionClosed: | ||
| 230 | print("WebSocket连接关闭") | ||
| 231 | finally: | ||
| 232 | if self.client and self.client.is_connected: | ||
| 233 | await self.client.disconnect() | ||
| 234 | self.client = None | ||
| 235 | self.target_device = None | ||
| 236 | |||
| 237 | def notification_handler(self, websocket, service_id, characteristic_id): | ||
| 238 | async def callback(sender, data): | ||
| 239 | current_time = asyncio.get_event_loop().time() | ||
| 240 | last_message, last_time = self.notification_records[characteristic_id] | ||
| 241 | |||
| 242 | # 解码当前数据用于比较 | ||
| 243 | current_message = base64.b64encode(data).decode('utf-8') | ||
| 244 | |||
| 245 | # 过滤逻辑 | ||
| 246 | if current_message == last_message and (current_time - last_time) < 0.5: | ||
| 247 | return | ||
| 248 | |||
| 249 | # 更新记录 | ||
| 250 | self.notification_records[characteristic_id] = (current_message, current_time) | ||
| 251 | |||
| 252 | response = json.dumps({ | ||
| 253 | "jsonrpc": "2.0", | ||
| 254 | "method": "characteristicDidChange", | ||
| 255 | "params": { | ||
| 256 | "serviceId": service_id, | ||
| 257 | "characteristicId": characteristic_id, | ||
| 258 | "message": current_message | ||
| 259 | } | ||
| 260 | }) | ||
| 261 | await log_message("下发", response) | ||
| 262 | await websocket.send(response) | ||
| 263 | return callback | ||
| 264 | |||
| 265 | async def main(): | ||
| 266 | async with websockets.serve( | ||
| 267 | lambda websocket, path: BLEClient().handle_client(websocket, path), | ||
| 268 | "localhost", 20111 | ||
| 269 | ): | ||
| 270 | print("WebSocket服务已启动: ws://localhost:20111/scratch/ble") | ||
| 271 | print("日志文件路径: ./b.log") | ||
| 272 | await asyncio.Future() | ||
| 273 | |||
| 274 | if __name__ == "__main__": | ||
| 275 | asyncio.run(main()) | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
s6.6.py
0 → 100644
| 1 | # -*- coding: utf-8 -*- | ||
| 2 | |||
| 3 | import asyncio | ||
| 4 | import websockets | ||
| 5 | from bleak import BleakScanner, BleakClient | ||
| 6 | import json | ||
| 7 | import base64 | ||
| 8 | import threading | ||
| 9 | import winrt.windows.foundation.collections # noqa | ||
| 10 | import winrt.windows.devices.bluetooth # noqa | ||
| 11 | import winrt.windows.devices.bluetooth.advertisement # noqa | ||
| 12 | |||
| 13 | # 添加线程锁以确保日志写入的原子性 | ||
| 14 | write_lock = threading.Lock() | ||
| 15 | |||
| 16 | def log_message_sync(direction, message): | ||
| 17 | """同步日志记录函数""" | ||
| 18 | log_entry = f"{direction}: {message}\n" | ||
| 19 | print(log_entry, end='') # 控制台仍然输出 | ||
| 20 | with write_lock: | ||
| 21 | with open('a.log', 'a', encoding='utf-8') as f: | ||
| 22 | f.write(log_entry) | ||
| 23 | |||
| 24 | async def log_message(direction, message): | ||
| 25 | """异步封装日志记录""" | ||
| 26 | loop = asyncio.get_event_loop() | ||
| 27 | await loop.run_in_executor(None, log_message_sync, direction, message) | ||
| 28 | |||
| 29 | class BLEClient: | ||
| 30 | def __init__(self): | ||
| 31 | self.target_device = None | ||
| 32 | self.client = None | ||
| 33 | self.services = [] | ||
| 34 | self.optional_services = [] | ||
| 35 | self.websocket = None | ||
| 36 | |||
| 37 | def on_disconnect(self, client): | ||
| 38 | print("BLE连接断开,关闭WebSocket") | ||
| 39 | if self.websocket and not self.websocket.closed: | ||
| 40 | asyncio.create_task(self.close_websocket()) | ||
| 41 | |||
| 42 | async def close_websocket(self): | ||
| 43 | await self.websocket.close() | ||
| 44 | self.websocket = None | ||
| 45 | |||
| 46 | def detection_callback(self, device, advertisement_data): | ||
| 47 | if any(service_uuid in advertisement_data.service_uuids for service_uuid in self.services): | ||
| 48 | self.target_device = (device, advertisement_data) | ||
| 49 | if not self.target_device: | ||
| 50 | print("未找到匹配设备") | ||
| 51 | return | ||
| 52 | else: | ||
| 53 | device, adv_data = self.target_device | ||
| 54 | print("\n找到目标设备:") | ||
| 55 | print(f"设备名称: {device.name}") | ||
| 56 | print(f"设备地址: {device.address}") | ||
| 57 | print(f"信号强度: {device.rssi} dBm") | ||
| 58 | print("\n广播信息:") | ||
| 59 | print(f"服务UUID列表: {adv_data.service_uuids}") | ||
| 60 | print(f"制造商数据: {adv_data.manufacturer_data}") | ||
| 61 | print(f"服务数据: {adv_data.service_data}") | ||
| 62 | print(f"本地名称: {adv_data.local_name}") | ||
| 63 | return self.target_device | ||
| 64 | |||
| 65 | async def handle_client(self, websocket, path): | ||
| 66 | self.websocket = websocket | ||
| 67 | if path != "/scratch/ble": | ||
| 68 | await websocket.close(code=1003, reason="Path not allowed") | ||
| 69 | return | ||
| 70 | |||
| 71 | try: | ||
| 72 | async for message in websocket: | ||
| 73 | try: | ||
| 74 | # 记录接收到的消息 | ||
| 75 | await log_message("接收", message) | ||
| 76 | |||
| 77 | request = json.loads(message) | ||
| 78 | if request["jsonrpc"] != "2.0": | ||
| 79 | response = json.dumps({ | ||
| 80 | "jsonrpc": "2.0", | ||
| 81 | "result": {"message": "Invalid Request"}, | ||
| 82 | "id": request.get("id", None) | ||
| 83 | }) | ||
| 84 | await log_message("下发", response) | ||
| 85 | await websocket.send(response) | ||
| 86 | continue | ||
| 87 | |||
| 88 | method = request.get("method") | ||
| 89 | params = request.get("params", {}) | ||
| 90 | request_id = request.get("id") | ||
| 91 | |||
| 92 | if method == "discover": | ||
| 93 | self.services = params.get("filters", [{}])[1].get("services", []) | ||
| 94 | self.optional_services = params.get("optionalServices", []) | ||
| 95 | |||
| 96 | scanner = BleakScanner() | ||
| 97 | scanner.register_detection_callback(self.detection_callback) | ||
| 98 | |||
| 99 | # 添加重试机制 | ||
| 100 | max_retries = 3 | ||
| 101 | found = False | ||
| 102 | for attempt in range(max_retries): | ||
| 103 | self.target_device = None # 重置目标设备 | ||
| 104 | await scanner.start() | ||
| 105 | await asyncio.sleep(5) | ||
| 106 | await scanner.stop() | ||
| 107 | |||
| 108 | if self.target_device: | ||
| 109 | found = True | ||
| 110 | break | ||
| 111 | |||
| 112 | if attempt < max_retries - 1: # 最后一次不等待 | ||
| 113 | print(f"未找到设备,第{attempt+1}次重试...") | ||
| 114 | await asyncio.sleep(3) | ||
| 115 | |||
| 116 | if not found: | ||
| 117 | # response = json.dumps({ | ||
| 118 | # "jsonrpc": "2.0", | ||
| 119 | # "result": {"message": "Device not found"}, | ||
| 120 | # "id": request_id | ||
| 121 | # }) | ||
| 122 | # await log_message("下发", response) | ||
| 123 | # await websocket.send(response) | ||
| 124 | continue | ||
| 125 | |||
| 126 | device, adv_data = self.target_device | ||
| 127 | discover_response = json.dumps({ | ||
| 128 | "jsonrpc": "2.0", | ||
| 129 | "method": "didDiscoverPeripheral", | ||
| 130 | "params": { | ||
| 131 | "name": device.name, | ||
| 132 | "peripheralId": device.address, | ||
| 133 | "rssi": device.rssi | ||
| 134 | } | ||
| 135 | }) | ||
| 136 | await log_message("下发", discover_response) | ||
| 137 | await websocket.send(discover_response) | ||
| 138 | |||
| 139 | result_response = json.dumps({ | ||
| 140 | "jsonrpc": "2.0", | ||
| 141 | "result": None, | ||
| 142 | "id": request_id | ||
| 143 | }) | ||
| 144 | await log_message("下发", result_response) | ||
| 145 | await websocket.send(result_response) | ||
| 146 | |||
| 147 | elif method == "connect": | ||
| 148 | peripheral_id = params.get("peripheralId") | ||
| 149 | if not peripheral_id: | ||
| 150 | response = json.dumps({ | ||
| 151 | "jsonrpc": "2.0", | ||
| 152 | "result": {"message": "Invalid params"}, | ||
| 153 | "id": request_id | ||
| 154 | }) | ||
| 155 | await log_message("下发", response) | ||
| 156 | await websocket.send(response) | ||
| 157 | continue | ||
| 158 | |||
| 159 | self.client = BleakClient(peripheral_id) | ||
| 160 | self.client.set_disconnected_callback(self.on_disconnect) | ||
| 161 | await self.client.connect() | ||
| 162 | if self.client.is_connected: | ||
| 163 | print(f"已连接至设备: {peripheral_id}") | ||
| 164 | response = json.dumps({ | ||
| 165 | "jsonrpc": "2.0", | ||
| 166 | "result": None, | ||
| 167 | "id": request_id | ||
| 168 | }) | ||
| 169 | await log_message("下发", response) | ||
| 170 | await websocket.send(response) | ||
| 171 | else: | ||
| 172 | response = json.dumps({ | ||
| 173 | "jsonrpc": "2.0", | ||
| 174 | "result": {"message": "Failed to connect"}, | ||
| 175 | "id": request_id | ||
| 176 | }) | ||
| 177 | await log_message("下发", response) | ||
| 178 | await websocket.send(response) | ||
| 179 | |||
| 180 | elif method == "write": | ||
| 181 | service_id = params.get("serviceId") | ||
| 182 | characteristic_id = params.get("characteristicId") | ||
| 183 | message = params.get("message") | ||
| 184 | encoding = params.get("encoding", "utf-8") | ||
| 185 | |||
| 186 | if not all([service_id, characteristic_id, message]): | ||
| 187 | response = json.dumps({ | ||
| 188 | "jsonrpc": "2.0", | ||
| 189 | "result": {"message": "Invalid params"}, | ||
| 190 | "id": request_id | ||
| 191 | }) | ||
| 192 | await log_message("下发", response) | ||
| 193 | await websocket.send(response) | ||
| 194 | continue | ||
| 195 | |||
| 196 | if encoding == "base64": | ||
| 197 | message_bytes = base64.b64decode(message) | ||
| 198 | else: | ||
| 199 | message_bytes = message.encode(encoding) | ||
| 200 | |||
| 201 | await self.client.write_gatt_char(characteristic_id, message_bytes) | ||
| 202 | response = json.dumps({ | ||
| 203 | "jsonrpc": "2.0", | ||
| 204 | "result": None, | ||
| 205 | "id": request_id | ||
| 206 | }) | ||
| 207 | await log_message("下发", response) | ||
| 208 | await websocket.send(response) | ||
| 209 | |||
| 210 | elif method == "read": | ||
| 211 | service_id = params.get("serviceId") | ||
| 212 | characteristic_id = params.get("characteristicId") | ||
| 213 | |||
| 214 | if not all([service_id, characteristic_id]): | ||
| 215 | response = json.dumps({ | ||
| 216 | "jsonrpc": "2.0", | ||
| 217 | "result": {"message": "Invalid params"}, | ||
| 218 | "id": request_id | ||
| 219 | }) | ||
| 220 | await log_message("下发", response) | ||
| 221 | await websocket.send(response) | ||
| 222 | continue | ||
| 223 | |||
| 224 | data = await self.client.read_gatt_char(characteristic_id) | ||
| 225 | response = json.dumps({ | ||
| 226 | "jsonrpc": "2.0", | ||
| 227 | "result": { | ||
| 228 | "serviceId": service_id, | ||
| 229 | "characteristicId": characteristic_id, | ||
| 230 | "message": base64.b64encode(data).decode("utf-8") | ||
| 231 | }, | ||
| 232 | "id": request_id | ||
| 233 | }) | ||
| 234 | await log_message("下发", response) | ||
| 235 | await websocket.send(response) | ||
| 236 | |||
| 237 | elif method == "startNotifications": | ||
| 238 | service_id = params.get("serviceId") | ||
| 239 | characteristic_id = params.get("characteristicId") | ||
| 240 | |||
| 241 | if not all([service_id, characteristic_id]): | ||
| 242 | response = json.dumps({ | ||
| 243 | "jsonrpc": "2.0", | ||
| 244 | "result": {"message": "Invalid params"}, | ||
| 245 | "id": request_id | ||
| 246 | }) | ||
| 247 | await log_message("下发", response) | ||
| 248 | await websocket.send(response) | ||
| 249 | continue | ||
| 250 | |||
| 251 | await self.client.start_notify( | ||
| 252 | characteristic_id, | ||
| 253 | self.notification_handler(websocket, service_id, characteristic_id) | ||
| 254 | ) | ||
| 255 | response = json.dumps({ | ||
| 256 | "jsonrpc": "2.0", | ||
| 257 | "result": None, | ||
| 258 | "id": request_id | ||
| 259 | }) | ||
| 260 | await log_message("下发", response) | ||
| 261 | await websocket.send(response) | ||
| 262 | |||
| 263 | else: | ||
| 264 | response = json.dumps({ | ||
| 265 | "jsonrpc": "2.0", | ||
| 266 | "result": {"message": "Method not found"}, | ||
| 267 | "id": request_id | ||
| 268 | }) | ||
| 269 | await log_message("下发", response) | ||
| 270 | await websocket.send(response) | ||
| 271 | |||
| 272 | except json.JSONDecodeError: | ||
| 273 | response = json.dumps({ | ||
| 274 | "jsonrpc": "2.0", | ||
| 275 | "result": {"message": "Parse error"}, | ||
| 276 | "id": None | ||
| 277 | }) | ||
| 278 | await log_message("下发", response) | ||
| 279 | await websocket.send(response) | ||
| 280 | except Exception as e: | ||
| 281 | response = json.dumps({ | ||
| 282 | "jsonrpc": "2.0", | ||
| 283 | "result": {"message": str(e)}, | ||
| 284 | "id": request_id | ||
| 285 | }) | ||
| 286 | await log_message("下发", response) | ||
| 287 | await websocket.send(response) | ||
| 288 | |||
| 289 | except websockets.exceptions.ConnectionClosedOK: | ||
| 290 | print("WebSocket客户端正常断开") | ||
| 291 | except websockets.exceptions.ConnectionClosedError as e: | ||
| 292 | print(f"WebSocket客户端异常断开: {e.code} - {e.reason}") | ||
| 293 | finally: | ||
| 294 | if self.client and self.client.is_connected: | ||
| 295 | await self.client.disconnect() | ||
| 296 | print("BLE设备已主动断开") | ||
| 297 | self.client = None | ||
| 298 | self.target_device = None | ||
| 299 | |||
| 300 | def notification_handler(self, websocket, service_id, characteristic_id): | ||
| 301 | async def callback(sender, data): | ||
| 302 | response = json.dumps({ | ||
| 303 | "jsonrpc": "2.0", | ||
| 304 | "method": "characteristicDidChange", | ||
| 305 | "params": { | ||
| 306 | "serviceId": service_id, | ||
| 307 | "characteristicId": characteristic_id, | ||
| 308 | "message": base64.b64encode(data).decode("utf-8") | ||
| 309 | } | ||
| 310 | }) | ||
| 311 | await log_message("下发", response) | ||
| 312 | await websocket.send(response) | ||
| 313 | return callback | ||
| 314 | |||
| 315 | async def main(): | ||
| 316 | async with websockets.serve( | ||
| 317 | lambda websocket, path: BLEClient().handle_client(websocket, path), | ||
| 318 | "localhost", 20111 | ||
| 319 | ): | ||
| 320 | print("WebSocket服务已启动: ws://localhost:20111/scratch/ble") | ||
| 321 | print("日志文件路径: ./b.log") | ||
| 322 | await asyncio.Future() # 永久运行 | ||
| 323 | |||
| 324 | if __name__ == "__main__": | ||
| 325 | asyncio.run(main()) | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
s6.9.2.spec
0 → 100644
| 1 | # -*- mode: python ; coding: utf-8 -*- | ||
| 2 | |||
| 3 | |||
| 4 | a = Analysis( | ||
| 5 | ['s6.9.2.py'], | ||
| 6 | pathex=[], | ||
| 7 | binaries=[], | ||
| 8 | datas=[('data', 'data')], | ||
| 9 | hiddenimports=['bleak.backends', 'asyncio'], | ||
| 10 | hookspath=[], | ||
| 11 | hooksconfig={}, | ||
| 12 | runtime_hooks=[], | ||
| 13 | excludes=[], | ||
| 14 | noarchive=False, | ||
| 15 | optimize=0, | ||
| 16 | ) | ||
| 17 | pyz = PYZ(a.pure) | ||
| 18 | |||
| 19 | exe = EXE( | ||
| 20 | pyz, | ||
| 21 | a.scripts, | ||
| 22 | a.binaries, | ||
| 23 | a.datas, | ||
| 24 | [], | ||
| 25 | name='s6.9.2', | ||
| 26 | debug=False, | ||
| 27 | bootloader_ignore_signals=False, | ||
| 28 | strip=False, | ||
| 29 | upx=True, | ||
| 30 | upx_exclude=[], | ||
| 31 | runtime_tmpdir=None, | ||
| 32 | console=True, | ||
| 33 | disable_windowed_traceback=False, | ||
| 34 | argv_emulation=False, | ||
| 35 | target_arch=None, | ||
| 36 | codesign_identity=None, | ||
| 37 | entitlements_file=None, | ||
| 38 | icon=['data\\app.ico'], | ||
| 39 | ) |
| 1 | import pystray | 1 | import pystray |
| 2 | from PIL import Image, ImageDraw | 2 | from PIL import Image |
| 3 | import threading | 3 | import threading |
| 4 | import os | ||
| 5 | import sys | ||
| 4 | 6 | ||
| 5 | def create_image(width, height, color1, color2): | 7 | def load_icon(icon_path): |
| 6 | image = Image.new("RGB", (width, height), color1) | 8 | """加载图标文件""" |
| 7 | dc = ImageDraw.Draw(image) | 9 | try: |
| 8 | dc.rectangle((width // 2, 0, width, height // 2), fill=color2) | 10 | return Image.open(icon_path) |
| 9 | return image | 11 | except FileNotFoundError: |
| 12 | print(f"Icon file not found at {icon_path}") | ||
| 13 | return None | ||
| 14 | |||
| 15 | def on_exit(icon, item): | ||
| 16 | """退出程序的回调函数""" | ||
| 17 | icon.stop() # 停止托盘图标 | ||
| 18 | print("Exiting program...") | ||
| 19 | os._exit(0) # 强制退出程序 | ||
| 10 | 20 | ||
| 11 | def run_icon(icon_path): | 21 | def run_icon(icon_path): |
| 12 | image = Image.open(icon_path) # 加载自定义图标 | 22 | """运行系统托盘图标""" |
| 13 | icon = pystray.Icon("test_icon", image, "My System Tray Icon") | 23 | image = load_icon(icon_path) |
| 14 | icon.run() | 24 | if not image: |
| 25 | print("Failed to load icon. Exiting...") | ||
| 26 | return | ||
| 27 | |||
| 28 | # 创建系统托盘图标 | ||
| 29 | icon = pystray.Icon( | ||
| 30 | name="test_icon", | ||
| 31 | icon=image, | ||
| 32 | title="My System Tray Icon", | ||
| 33 | menu=pystray.Menu( | ||
| 34 | pystray.MenuItem("Exit", on_exit) # 添加右键菜单项 | ||
| 35 | ) | ||
| 36 | ) | ||
| 37 | icon.run() # 运行托盘图标 | ||
| 15 | 38 | ||
| 16 | def start_tray_icon(icon_path): | 39 | def start_tray_icon(icon_path): |
| 40 | """在新线程中启动系统托盘图标""" | ||
| 17 | threading.Thread(target=run_icon, args=(icon_path,), daemon=True).start() | 41 | threading.Thread(target=run_icon, args=(icon_path,), daemon=True).start() |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| 1 | # -*- coding: utf-8 -*-- | 1 | import sys |
| 2 | import asyncio | 2 | import asyncio |
| 3 | import websockets | ||
| 4 | from bleak import BleakScanner, BleakClient | ||
| 5 | import json | 3 | import json |
| 6 | import base64 | 4 | import base64 |
| 7 | import threading | 5 | import threading |
| 8 | from tray_icon import start_tray_icon | 6 | from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QPushButton, QVBoxLayout, QWidget |
| 9 | import os | 7 | from PyQt5.QtCore import QThread, pyqtSignal |
| 10 | import sys | 8 | from bleak import BleakScanner, BleakClient |
| 11 | 9 | import websockets | |
| 12 | def resource_path(relative_path): | ||
| 13 | try: | ||
| 14 | base_path = sys._MEIPASS # 打包后运行时的临时路径 | ||
| 15 | except AttributeError: | ||
| 16 | base_path = os.path.dirname(os.path.abspath(__file__)) # 开发环境下的路径 | ||
| 17 | # 使用 os.path.join 拼接路径,并通过 os.path.abspath 转换为绝对路径 | ||
| 18 | return os.path.abspath(os.path.join(base_path, relative_path)) | ||
| 19 | |||
| 20 | def resource_path(relative_path): | ||
| 21 | """获取资源文件的绝对路径,适用于打包后的程序""" | ||
| 22 | try: | ||
| 23 | base_path = sys._MEIPASS # 打包后运行时的临时路径 | ||
| 24 | except AttributeError: | ||
| 25 | base_path = os.path.abspath(".") # 开发环境下的路径 | ||
| 26 | return os.path.join(base_path, relative_path) | ||
| 27 | |||
| 28 | # 获取图标路径 | ||
| 29 | icon_path = resource_path(resource_path("data/app.ico")) | ||
| 30 | |||
| 31 | # 调用系统托盘图标函数 | ||
| 32 | start_tray_icon(icon_path) | ||
| 33 | 10 | ||
| 34 | # 添加线程锁以确保日志写入的原子性 | 11 | # 添加线程锁以确保日志写入的原子性 |
| 35 | write_lock = threading.Lock() | 12 | write_lock = threading.Lock() |
| 36 | 13 | ||
| 37 | def log_message_sync(direction, message): | 14 | def log_message_sync(direction, message, log_signal=None): |
| 38 | """同步日志记录函数""" | 15 | """同步日志记录函数(支持信号传递)""" |
| 39 | log_entry = f"{direction}: {message}\n" | 16 | log_entry = f"{direction}: {message}\n" |
| 40 | print(log_entry, end='') # 控制台仍然输出 | 17 | print(log_entry, end='') # 控制台输出 |
| 41 | with write_lock: | 18 | with write_lock: |
| 42 | with open('a.log', 'a', encoding='utf-8') as f: | 19 | with open('a.log', 'a', encoding='utf-8') as f: |
| 43 | f.write(log_entry) | 20 | f.write(log_entry) |
| 21 | if log_signal: | ||
| 22 | log_signal.emit(log_entry) # 发射信号到GUI | ||
| 44 | 23 | ||
| 45 | async def log_message(direction, message): | 24 | async def log_message(direction, message, log_signal=None): |
| 46 | """异步封装日志记录""" | 25 | """异步封装日志记录""" |
| 47 | loop = asyncio.get_event_loop() | 26 | loop = asyncio.get_event_loop() |
| 48 | await loop.run_in_executor(None, log_message_sync, direction, message) | 27 | await loop.run_in_executor(None, log_message_sync, direction, message, log_signal) |
| 49 | 28 | ||
| 50 | class BLEClient: | 29 | class BLEClient: |
| 51 | def __init__(self): | 30 | def __init__(self, log_signal=None): |
| 52 | self.target_device = None | 31 | self.target_device = None |
| 53 | self.client = None | 32 | self.client = None |
| 54 | self.services = [] | 33 | self.services = [] |
| 55 | self.optional_services = [] | 34 | self.optional_services = [] |
| 56 | self.websocket = None | 35 | self.websocket = None |
| 36 | self.log_signal = log_signal # 绑定日志信号 | ||
| 57 | 37 | ||
| 58 | def on_disconnect(self, client): | 38 | def on_disconnect(self, client): |
| 59 | print("BLE连接断开,关闭WebSocket") | 39 | print("BLE连接断开,关闭WebSocket") |
| ... | @@ -93,7 +73,7 @@ class BLEClient: | ... | @@ -93,7 +73,7 @@ class BLEClient: |
| 93 | async for message in websocket: | 73 | async for message in websocket: |
| 94 | try: | 74 | try: |
| 95 | # 记录接收到的消息 | 75 | # 记录接收到的消息 |
| 96 | await log_message("接收", message) | 76 | await log_message("接收", message, self.log_signal) |
| 97 | 77 | ||
| 98 | request = json.loads(message) | 78 | request = json.loads(message) |
| 99 | if request["jsonrpc"] != "2.0": | 79 | if request["jsonrpc"] != "2.0": |
| ... | @@ -102,7 +82,7 @@ class BLEClient: | ... | @@ -102,7 +82,7 @@ class BLEClient: |
| 102 | "result": {"message": "Invalid Request"}, | 82 | "result": {"message": "Invalid Request"}, |
| 103 | "id": request.get("id", None) | 83 | "id": request.get("id", None) |
| 104 | }) | 84 | }) |
| 105 | await log_message("下发", response) | 85 | await log_message("下发", response, self.log_signal) |
| 106 | await websocket.send(response) | 86 | await websocket.send(response) |
| 107 | continue | 87 | continue |
| 108 | 88 | ||
| ... | @@ -135,13 +115,6 @@ class BLEClient: | ... | @@ -135,13 +115,6 @@ class BLEClient: |
| 135 | await asyncio.sleep(3) | 115 | await asyncio.sleep(3) |
| 136 | 116 | ||
| 137 | if not found: | 117 | if not found: |
| 138 | # response = json.dumps({ | ||
| 139 | # "jsonrpc": "2.0", | ||
| 140 | # "result": {"message": "Device not found"}, | ||
| 141 | # "id": request_id | ||
| 142 | # }) | ||
| 143 | # await log_message("下发", response) | ||
| 144 | # await websocket.send(response) | ||
| 145 | continue | 118 | continue |
| 146 | 119 | ||
| 147 | device, adv_data = self.target_device | 120 | device, adv_data = self.target_device |
| ... | @@ -154,7 +127,7 @@ class BLEClient: | ... | @@ -154,7 +127,7 @@ class BLEClient: |
| 154 | "rssi": device.rssi | 127 | "rssi": device.rssi |
| 155 | } | 128 | } |
| 156 | }) | 129 | }) |
| 157 | await log_message("下发", discover_response) | 130 | await log_message("下发", discover_response, self.log_signal) |
| 158 | await websocket.send(discover_response) | 131 | await websocket.send(discover_response) |
| 159 | 132 | ||
| 160 | result_response = json.dumps({ | 133 | result_response = json.dumps({ |
| ... | @@ -162,7 +135,7 @@ class BLEClient: | ... | @@ -162,7 +135,7 @@ class BLEClient: |
| 162 | "result": None, | 135 | "result": None, |
| 163 | "id": request_id | 136 | "id": request_id |
| 164 | }) | 137 | }) |
| 165 | await log_message("下发", result_response) | 138 | await log_message("下发", result_response, self.log_signal) |
| 166 | await websocket.send(result_response) | 139 | await websocket.send(result_response) |
| 167 | 140 | ||
| 168 | elif method == "connect": | 141 | elif method == "connect": |
| ... | @@ -173,7 +146,7 @@ class BLEClient: | ... | @@ -173,7 +146,7 @@ class BLEClient: |
| 173 | "result": {"message": "Invalid params"}, | 146 | "result": {"message": "Invalid params"}, |
| 174 | "id": request_id | 147 | "id": request_id |
| 175 | }) | 148 | }) |
| 176 | await log_message("下发", response) | 149 | await log_message("下发", response, self.log_signal) |
| 177 | await websocket.send(response) | 150 | await websocket.send(response) |
| 178 | continue | 151 | continue |
| 179 | 152 | ||
| ... | @@ -187,7 +160,7 @@ class BLEClient: | ... | @@ -187,7 +160,7 @@ class BLEClient: |
| 187 | "result": None, | 160 | "result": None, |
| 188 | "id": request_id | 161 | "id": request_id |
| 189 | }) | 162 | }) |
| 190 | await log_message("下发", response) | 163 | await log_message("下发", response, self.log_signal) |
| 191 | await websocket.send(response) | 164 | await websocket.send(response) |
| 192 | else: | 165 | else: |
| 193 | response = json.dumps({ | 166 | response = json.dumps({ |
| ... | @@ -195,7 +168,7 @@ class BLEClient: | ... | @@ -195,7 +168,7 @@ class BLEClient: |
| 195 | "result": {"message": "Failed to connect"}, | 168 | "result": {"message": "Failed to connect"}, |
| 196 | "id": request_id | 169 | "id": request_id |
| 197 | }) | 170 | }) |
| 198 | await log_message("下发", response) | 171 | await log_message("下发", response, self.log_signal) |
| 199 | await websocket.send(response) | 172 | await websocket.send(response) |
| 200 | 173 | ||
| 201 | elif method == "write": | 174 | elif method == "write": |
| ... | @@ -210,7 +183,7 @@ class BLEClient: | ... | @@ -210,7 +183,7 @@ class BLEClient: |
| 210 | "result": {"message": "Invalid params"}, | 183 | "result": {"message": "Invalid params"}, |
| 211 | "id": request_id | 184 | "id": request_id |
| 212 | }) | 185 | }) |
| 213 | await log_message("下发", response) | 186 | await log_message("下发", response, self.log_signal) |
| 214 | await websocket.send(response) | 187 | await websocket.send(response) |
| 215 | continue | 188 | continue |
| 216 | 189 | ||
| ... | @@ -225,7 +198,7 @@ class BLEClient: | ... | @@ -225,7 +198,7 @@ class BLEClient: |
| 225 | "result": None, | 198 | "result": None, |
| 226 | "id": request_id | 199 | "id": request_id |
| 227 | }) | 200 | }) |
| 228 | await log_message("下发", response) | 201 | await log_message("下发", response, self.log_signal) |
| 229 | await websocket.send(response) | 202 | await websocket.send(response) |
| 230 | 203 | ||
| 231 | elif method == "read": | 204 | elif method == "read": |
| ... | @@ -238,7 +211,7 @@ class BLEClient: | ... | @@ -238,7 +211,7 @@ class BLEClient: |
| 238 | "result": {"message": "Invalid params"}, | 211 | "result": {"message": "Invalid params"}, |
| 239 | "id": request_id | 212 | "id": request_id |
| 240 | }) | 213 | }) |
| 241 | await log_message("下发", response) | 214 | await log_message("下发", response, self.log_signal) |
| 242 | await websocket.send(response) | 215 | await websocket.send(response) |
| 243 | continue | 216 | continue |
| 244 | 217 | ||
| ... | @@ -252,7 +225,7 @@ class BLEClient: | ... | @@ -252,7 +225,7 @@ class BLEClient: |
| 252 | }, | 225 | }, |
| 253 | "id": request_id | 226 | "id": request_id |
| 254 | }) | 227 | }) |
| 255 | await log_message("下发", response) | 228 | await log_message("下发", response, self.log_signal) |
| 256 | await websocket.send(response) | 229 | await websocket.send(response) |
| 257 | 230 | ||
| 258 | elif method == "startNotifications": | 231 | elif method == "startNotifications": |
| ... | @@ -265,7 +238,7 @@ class BLEClient: | ... | @@ -265,7 +238,7 @@ class BLEClient: |
| 265 | "result": {"message": "Invalid params"}, | 238 | "result": {"message": "Invalid params"}, |
| 266 | "id": request_id | 239 | "id": request_id |
| 267 | }) | 240 | }) |
| 268 | await log_message("下发", response) | 241 | await log_message("下发", response, self.log_signal) |
| 269 | await websocket.send(response) | 242 | await websocket.send(response) |
| 270 | continue | 243 | continue |
| 271 | 244 | ||
| ... | @@ -278,7 +251,7 @@ class BLEClient: | ... | @@ -278,7 +251,7 @@ class BLEClient: |
| 278 | "result": None, | 251 | "result": None, |
| 279 | "id": request_id | 252 | "id": request_id |
| 280 | }) | 253 | }) |
| 281 | await log_message("下发", response) | 254 | await log_message("下发", response, self.log_signal) |
| 282 | await websocket.send(response) | 255 | await websocket.send(response) |
| 283 | 256 | ||
| 284 | else: | 257 | else: |
| ... | @@ -287,7 +260,7 @@ class BLEClient: | ... | @@ -287,7 +260,7 @@ class BLEClient: |
| 287 | "result": {"message": "Method not found"}, | 260 | "result": {"message": "Method not found"}, |
| 288 | "id": request_id | 261 | "id": request_id |
| 289 | }) | 262 | }) |
| 290 | await log_message("下发", response) | 263 | await log_message("下发", response, self.log_signal) |
| 291 | await websocket.send(response) | 264 | await websocket.send(response) |
| 292 | 265 | ||
| 293 | except json.JSONDecodeError: | 266 | except json.JSONDecodeError: |
| ... | @@ -296,7 +269,7 @@ class BLEClient: | ... | @@ -296,7 +269,7 @@ class BLEClient: |
| 296 | "result": {"message": "Parse error"}, | 269 | "result": {"message": "Parse error"}, |
| 297 | "id": None | 270 | "id": None |
| 298 | }) | 271 | }) |
| 299 | await log_message("下发", response) | 272 | await log_message("下发", response, self.log_signal) |
| 300 | await websocket.send(response) | 273 | await websocket.send(response) |
| 301 | except Exception as e: | 274 | except Exception as e: |
| 302 | response = json.dumps({ | 275 | response = json.dumps({ |
| ... | @@ -304,7 +277,7 @@ class BLEClient: | ... | @@ -304,7 +277,7 @@ class BLEClient: |
| 304 | "result": {"message": str(e)}, | 277 | "result": {"message": str(e)}, |
| 305 | "id": request_id | 278 | "id": request_id |
| 306 | }) | 279 | }) |
| 307 | await log_message("下发", response) | 280 | await log_message("下发", response, self.log_signal) |
| 308 | await websocket.send(response) | 281 | await websocket.send(response) |
| 309 | 282 | ||
| 310 | except websockets.exceptions.ConnectionClosedOK: | 283 | except websockets.exceptions.ConnectionClosedOK: |
| ... | @@ -329,18 +302,54 @@ class BLEClient: | ... | @@ -329,18 +302,54 @@ class BLEClient: |
| 329 | "message": base64.b64encode(data).decode("utf-8") | 302 | "message": base64.b64encode(data).decode("utf-8") |
| 330 | } | 303 | } |
| 331 | }) | 304 | }) |
| 332 | await log_message("下发", response) | 305 | await log_message("下发", response, self.log_signal) |
| 333 | await websocket.send(response) | 306 | await websocket.send(response) |
| 334 | return callback | 307 | return callback |
| 335 | 308 | ||
| 336 | async def main(): | 309 | class WebSocketThread(QThread): |
| 310 | log_signal = pyqtSignal(str) | ||
| 311 | |||
| 312 | def run(self): | ||
| 313 | async def start_server(): | ||
| 314 | ble_client = BLEClient(self.log_signal) # 绑定日志信号 | ||
| 337 | async with websockets.serve( | 315 | async with websockets.serve( |
| 338 | lambda websocket, path: BLEClient().handle_client(websocket, path), | 316 | lambda websocket, path: ble_client.handle_client(websocket, path), |
| 339 | "localhost", 20111 | 317 | "localhost", 20111 |
| 340 | ): | 318 | ): |
| 341 | print("WebSocket服务已启动: ws://localhost:20111/scratch/ble") | 319 | self.log_signal.emit("WebSocket服务已启动: ws://localhost:20111/scratch/ble") |
| 342 | print("日志文件路径: ./b.log") | ||
| 343 | await asyncio.Future() # 永久运行 | 320 | await asyncio.Future() # 永久运行 |
| 344 | 321 | ||
| 322 | asyncio.run(start_server()) | ||
| 323 | |||
| 324 | class MainWindow(QMainWindow): | ||
| 325 | def __init__(self): | ||
| 326 | super().__init__() | ||
| 327 | |||
| 328 | self.setWindowTitle("BLE WebSocket Server") | ||
| 329 | self.setGeometry(100, 100, 600, 400) | ||
| 330 | |||
| 331 | self.text_edit = QTextEdit(self) | ||
| 332 | self.text_edit.setReadOnly(True) | ||
| 333 | |||
| 334 | self.start_button = QPushButton("启动服务", self) | ||
| 335 | self.start_button.clicked.connect(self.start_server) | ||
| 336 | |||
| 337 | layout = QVBoxLayout() | ||
| 338 | layout.addWidget(self.text_edit) | ||
| 339 | layout.addWidget(self.start_button) | ||
| 340 | |||
| 341 | container = QWidget() | ||
| 342 | container.setLayout(layout) | ||
| 343 | self.setCentralWidget(container) | ||
| 344 | |||
| 345 | self.websocket_thread = WebSocketThread() | ||
| 346 | self.websocket_thread.log_signal.connect(self.text_edit.append) | ||
| 347 | |||
| 348 | def start_server(self): | ||
| 349 | self.websocket_thread.start() | ||
| 350 | |||
| 345 | if __name__ == "__main__": | 351 | if __name__ == "__main__": |
| 346 | asyncio.run(main()) | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 352 | app = QApplication(sys.argv) | ||
| 353 | window = MainWindow() | ||
| 354 | window.show() | ||
| 355 | sys.exit(app.exec_()) | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
we.spec
0 → 100644
| 1 | # -*- mode: python ; coding: utf-8 -*- | ||
| 2 | |||
| 3 | |||
| 4 | a = Analysis( | ||
| 5 | ['we.py'], | ||
| 6 | pathex=[], | ||
| 7 | binaries=[], | ||
| 8 | datas=[('data', 'data')], | ||
| 9 | hiddenimports=[], | ||
| 10 | hookspath=[], | ||
| 11 | hooksconfig={}, | ||
| 12 | runtime_hooks=[], | ||
| 13 | excludes=[], | ||
| 14 | noarchive=False, | ||
| 15 | optimize=0, | ||
| 16 | ) | ||
| 17 | pyz = PYZ(a.pure) | ||
| 18 | |||
| 19 | exe = EXE( | ||
| 20 | pyz, | ||
| 21 | a.scripts, | ||
| 22 | a.binaries, | ||
| 23 | a.datas, | ||
| 24 | [], | ||
| 25 | name='we', | ||
| 26 | debug=False, | ||
| 27 | bootloader_ignore_signals=False, | ||
| 28 | strip=False, | ||
| 29 | upx=True, | ||
| 30 | upx_exclude=[], | ||
| 31 | runtime_tmpdir=None, | ||
| 32 | console=False, | ||
| 33 | disable_windowed_traceback=False, | ||
| 34 | argv_emulation=False, | ||
| 35 | target_arch=None, | ||
| 36 | codesign_identity=None, | ||
| 37 | entitlements_file=None, | ||
| 38 | icon=['data\\app.ico'], | ||
| 39 | ) |
we1.py
0 → 100644
| 1 | import sys | ||
| 2 | import asyncio | ||
| 3 | import websockets | ||
| 4 | from bleak import BleakScanner, BleakClient | ||
| 5 | import json | ||
| 6 | import base64 | ||
| 7 | import threading | ||
| 8 | from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QVBoxLayout, QWidget | ||
| 9 | from PyQt5.QtCore import QThread, pyqtSignal | ||
| 10 | |||
| 11 | # 添加线程锁以确保日志写入的原子性 | ||
| 12 | write_lock = threading.Lock() | ||
| 13 | |||
| 14 | def log_message_sync(direction, message): | ||
| 15 | """同步日志记录函数""" | ||
| 16 | log_entry = f"{direction}: {message}\n" | ||
| 17 | print(log_entry, end='') # 控制台仍然输出 | ||
| 18 | with write_lock: | ||
| 19 | with open('b.log', 'a', encoding='utf-8') as f: | ||
| 20 | f.write(log_entry) | ||
| 21 | |||
| 22 | async def log_message(direction, message): | ||
| 23 | """异步封装日志记录""" | ||
| 24 | loop = asyncio.get_event_loop() | ||
| 25 | await loop.run_in_executor(None, log_message_sync, direction, message) | ||
| 26 | |||
| 27 | class BLEClient: | ||
| 28 | def __init__(self): | ||
| 29 | self.target_device = None | ||
| 30 | self.client = None | ||
| 31 | self.services = [] | ||
| 32 | self.optional_services = [] | ||
| 33 | self.websocket = None | ||
| 34 | |||
| 35 | def on_disconnect(self, client): | ||
| 36 | print("BLE连接断开,关闭WebSocket") | ||
| 37 | if self.websocket and not self.websocket.closed: | ||
| 38 | asyncio.create_task(self.close_websocket()) | ||
| 39 | |||
| 40 | async def close_websocket(self): | ||
| 41 | await self.websocket.close() | ||
| 42 | self.websocket = None | ||
| 43 | |||
| 44 | def detection_callback(self, device, advertisement_data): | ||
| 45 | # 合并所有过滤条件的services进行匹配 | ||
| 46 | if any(service_uuid in advertisement_data.service_uuids for service_uuid in self.services): | ||
| 47 | self.target_device = (device, advertisement_data) | ||
| 48 | if not self.target_device: | ||
| 49 | print("未找到匹配设备") | ||
| 50 | return | ||
| 51 | else: | ||
| 52 | device, adv_data = self.target_device | ||
| 53 | print("\n找到目标设备:") | ||
| 54 | print(f"设备名称: {device.name}") | ||
| 55 | print(f"设备地址: {device.address}") | ||
| 56 | print(f"信号强度: {device.rssi} dBm") | ||
| 57 | print("\n广播信息:") | ||
| 58 | print(f"服务UUID列表: {adv_data.service_uuids}") | ||
| 59 | print(f"制造商数据: {adv_data.manufacturer_data}") | ||
| 60 | print(f"服务数据: {adv_data.service_data}") | ||
| 61 | print(f"本地名称: {adv_data.local_name}") | ||
| 62 | return self.target_device | ||
| 63 | |||
| 64 | async def handle_client(self, websocket, path): | ||
| 65 | self.websocket = websocket | ||
| 66 | if path != "/scratch/ble": | ||
| 67 | await websocket.close(code=1003, reason="Path not allowed") | ||
| 68 | return | ||
| 69 | |||
| 70 | try: | ||
| 71 | async for message in websocket: | ||
| 72 | try: | ||
| 73 | await log_message("接收", message) | ||
| 74 | request = json.loads(message) | ||
| 75 | |||
| 76 | if request["jsonrpc"] != "2.0": | ||
| 77 | continue # 跳过无效协议版本 | ||
| 78 | |||
| 79 | method = request.get("method") | ||
| 80 | params = request.get("params", {}) | ||
| 81 | request_id = request.get("id") | ||
| 82 | |||
| 83 | if method == "discover": | ||
| 84 | # 合并所有过滤条件的services | ||
| 85 | self.services = [] | ||
| 86 | for filt in params.get("filters", [{}]): | ||
| 87 | self.services.extend(filt.get("services", [])) | ||
| 88 | self.optional_services = params.get("optionalServices", []) | ||
| 89 | |||
| 90 | scanner = BleakScanner() | ||
| 91 | scanner.register_detection_callback(self.detection_callback) | ||
| 92 | |||
| 93 | # 带重试的扫描逻辑 | ||
| 94 | max_retries = 3 | ||
| 95 | found = False | ||
| 96 | for attempt in range(max_retries): | ||
| 97 | self.target_device = None | ||
| 98 | await scanner.start() | ||
| 99 | await asyncio.sleep(5) | ||
| 100 | await scanner.stop() | ||
| 101 | |||
| 102 | if self.target_device: | ||
| 103 | found = True | ||
| 104 | break | ||
| 105 | |||
| 106 | if attempt < max_retries - 1: | ||
| 107 | print(f"未找到设备,第{attempt+1}次重试...") | ||
| 108 | await asyncio.sleep(3) | ||
| 109 | |||
| 110 | if found: | ||
| 111 | device, adv_data = self.target_device | ||
| 112 | discover_response = json.dumps({ | ||
| 113 | "jsonrpc": "2.0", | ||
| 114 | "method": "didDiscoverPeripheral", | ||
| 115 | "params": { | ||
| 116 | "name": device.name, | ||
| 117 | "peripheralId": device.address, | ||
| 118 | "rssi": device.rssi | ||
| 119 | } | ||
| 120 | }) | ||
| 121 | await log_message("下发", discover_response) | ||
| 122 | await websocket.send(discover_response) | ||
| 123 | |||
| 124 | result_response = json.dumps({ | ||
| 125 | "jsonrpc": "2.0", | ||
| 126 | "result": None, | ||
| 127 | "id": request_id | ||
| 128 | }) | ||
| 129 | await log_message("下发", result_response) | ||
| 130 | await websocket.send(result_response) | ||
| 131 | |||
| 132 | elif method == "connect": | ||
| 133 | peripheral_id = params.get("peripheralId") | ||
| 134 | if peripheral_id: | ||
| 135 | self.client = BleakClient(peripheral_id) | ||
| 136 | self.client.set_disconnected_callback(self.on_disconnect) | ||
| 137 | await self.client.connect() | ||
| 138 | |||
| 139 | if self.client.is_connected: | ||
| 140 | response = json.dumps({ | ||
| 141 | "jsonrpc": "2.0", | ||
| 142 | "result": None, | ||
| 143 | "id": request_id | ||
| 144 | }) | ||
| 145 | await log_message("下发", response) | ||
| 146 | await websocket.send(response) | ||
| 147 | |||
| 148 | elif method == "write": | ||
| 149 | service_id = params.get("serviceId") | ||
| 150 | characteristic_id = params.get("characteristicId") | ||
| 151 | message = params.get("message") | ||
| 152 | encoding = params.get("encoding", "utf-8") | ||
| 153 | |||
| 154 | if all([service_id, characteristic_id, message]): | ||
| 155 | if encoding == "base64": | ||
| 156 | message_bytes = base64.b64decode(message) | ||
| 157 | else: | ||
| 158 | message_bytes = message.encode(encoding) | ||
| 159 | |||
| 160 | await self.client.write_gatt_char(characteristic_id, message_bytes) | ||
| 161 | response = json.dumps({ | ||
| 162 | "jsonrpc": "2.0", | ||
| 163 | "result": None, | ||
| 164 | "id": request_id | ||
| 165 | }) | ||
| 166 | await log_message("下发", response) | ||
| 167 | await websocket.send(response) | ||
| 168 | |||
| 169 | elif method == "read": | ||
| 170 | service_id = params.get("serviceId") | ||
| 171 | characteristic_id = params.get("characteristicId") | ||
| 172 | |||
| 173 | if all([service_id, characteristic_id]): | ||
| 174 | data = await self.client.read_gatt_char(characteristic_id) | ||
| 175 | response = json.dumps({ | ||
| 176 | "jsonrpc": "2.0", | ||
| 177 | "result": { | ||
| 178 | "serviceId": service_id, | ||
| 179 | "characteristicId": characteristic_id, | ||
| 180 | "message": base64.b64encode(data).decode("utf-8") | ||
| 181 | }, | ||
| 182 | "id": request_id | ||
| 183 | }) | ||
| 184 | await log_message("下发", response) | ||
| 185 | await websocket.send(response) | ||
| 186 | |||
| 187 | elif method == "startNotifications": | ||
| 188 | service_id = params.get("serviceId") | ||
| 189 | characteristic_id = params.get("characteristicId") | ||
| 190 | |||
| 191 | if all([service_id, characteristic_id]): | ||
| 192 | await self.client.start_notify( | ||
| 193 | characteristic_id, | ||
| 194 | self.notification_handler(websocket, service_id, characteristic_id) | ||
| 195 | ) | ||
| 196 | response = json.dumps({ | ||
| 197 | "jsonrpc": "2.0", | ||
| 198 | "result": None, | ||
| 199 | "id": request_id | ||
| 200 | }) | ||
| 201 | await log_message("下发", response) | ||
| 202 | await websocket.send(response) | ||
| 203 | |||
| 204 | except json.JSONDecodeError: | ||
| 205 | error_msg = json.dumps({ | ||
| 206 | "jsonrpc": "2.0", | ||
| 207 | "result": {"message": "Parse error"}, | ||
| 208 | "id": None | ||
| 209 | }) | ||
| 210 | await log_message("下发", error_msg) | ||
| 211 | except Exception as e: | ||
| 212 | error_msg = json.dumps({ | ||
| 213 | "jsonrpc": "2.0", | ||
| 214 | "result": {"message": str(e)}, | ||
| 215 | "id": request.get("id") if request else None | ||
| 216 | }) | ||
| 217 | await log_message("下发", error_msg) | ||
| 218 | |||
| 219 | except websockets.exceptions.ConnectionClosed: | ||
| 220 | print("WebSocket连接关闭") | ||
| 221 | finally: | ||
| 222 | if self.client and self.client.is_connected: | ||
| 223 | await self.client.disconnect() | ||
| 224 | self.client = None | ||
| 225 | self.target_device = None | ||
| 226 | |||
| 227 | def notification_handler(self, websocket, service_id, characteristic_id): | ||
| 228 | async def callback(sender, data): | ||
| 229 | response = json.dumps({ | ||
| 230 | "jsonrpc": "2.0", | ||
| 231 | "method": "characteristicDidChange", | ||
| 232 | "params": { | ||
| 233 | "serviceId": service_id, | ||
| 234 | "characteristicId": characteristic_id, | ||
| 235 | "message": base64.b64encode(data).decode("utf-8") | ||
| 236 | } | ||
| 237 | }) | ||
| 238 | await log_message("下发", response) | ||
| 239 | await websocket.send(response) | ||
| 240 | return callback | ||
| 241 | |||
| 242 | class WebSocketServerThread(QThread): | ||
| 243 | log_signal = pyqtSignal(str) | ||
| 244 | |||
| 245 | def run(self): | ||
| 246 | async def main(): | ||
| 247 | async with websockets.serve( | ||
| 248 | lambda websocket, path: BLEClient().handle_client(websocket, path), | ||
| 249 | "localhost", 20111 | ||
| 250 | ): | ||
| 251 | self.log_signal.emit("WebSocket服务已启动: ws://localhost:20111/scratch/ble") | ||
| 252 | self.log_signal.emit("日志文件路径: ./b.log") | ||
| 253 | await asyncio.Future() # 永久运行 | ||
| 254 | |||
| 255 | asyncio.run(main()) | ||
| 256 | |||
| 257 | class MainWindow(QMainWindow): | ||
| 258 | def __init__(self): | ||
| 259 | super().__init__() | ||
| 260 | self.initUI() | ||
| 261 | |||
| 262 | def initUI(self): | ||
| 263 | self.setWindowTitle("BLE WebSocket Server") | ||
| 264 | self.setGeometry(100, 100, 800, 600) | ||
| 265 | |||
| 266 | self.text_edit = QTextEdit(self) | ||
| 267 | self.text_edit.setReadOnly(True) | ||
| 268 | |||
| 269 | layout = QVBoxLayout() | ||
| 270 | layout.addWidget(self.text_edit) | ||
| 271 | |||
| 272 | container = QWidget() | ||
| 273 | container.setLayout(layout) | ||
| 274 | self.setCentralWidget(container) | ||
| 275 | |||
| 276 | self.websocket_thread = WebSocketServerThread() | ||
| 277 | self.websocket_thread.log_signal.connect(self.update_log) | ||
| 278 | self.websocket_thread.start() | ||
| 279 | |||
| 280 | def update_log(self, message): | ||
| 281 | self.text_edit.append(message) | ||
| 282 | |||
| 283 | if __name__ == "__main__": | ||
| 284 | app = QApplication(sys.argv) | ||
| 285 | window = MainWindow() | ||
| 286 | window.show() | ||
| 287 | sys.exit(app.exec_()) | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
we1.spec
0 → 100644
| 1 | # -*- mode: python ; coding: utf-8 -*- | ||
| 2 | |||
| 3 | |||
| 4 | a = Analysis( | ||
| 5 | ['we1.py'], | ||
| 6 | pathex=[], | ||
| 7 | binaries=[], | ||
| 8 | datas=[('data', 'data')], | ||
| 9 | hiddenimports=[], | ||
| 10 | hookspath=[], | ||
| 11 | hooksconfig={}, | ||
| 12 | runtime_hooks=[], | ||
| 13 | excludes=[], | ||
| 14 | noarchive=False, | ||
| 15 | optimize=0, | ||
| 16 | ) | ||
| 17 | pyz = PYZ(a.pure) | ||
| 18 | |||
| 19 | exe = EXE( | ||
| 20 | pyz, | ||
| 21 | a.scripts, | ||
| 22 | a.binaries, | ||
| 23 | a.datas, | ||
| 24 | [], | ||
| 25 | name='we1', | ||
| 26 | debug=False, | ||
| 27 | bootloader_ignore_signals=False, | ||
| 28 | strip=False, | ||
| 29 | upx=True, | ||
| 30 | upx_exclude=[], | ||
| 31 | runtime_tmpdir=None, | ||
| 32 | console=True, | ||
| 33 | disable_windowed_traceback=False, | ||
| 34 | argv_emulation=False, | ||
| 35 | target_arch=None, | ||
| 36 | codesign_identity=None, | ||
| 37 | entitlements_file=None, | ||
| 38 | icon=['data\\app.ico'], | ||
| 39 | ) |
-
Please register or sign in to post a comment