You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

198 lines
6.6 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import asyncio
from bleak import BleakClient, BleakScanner
from bleak.backends.characteristic import BleakGATTCharacteristic
from binascii import unhexlify
from crcmod import crcmod
import threading
from mqtt_client import sub_run
from queue import LifoQueue
import ast
def crc16Add(str_data):
crc16 = crcmod.mkCrcFun(0x18005, rev=True, initCrc=0xFFFF, xorOut=0x0000)
data = str_data.replace(" ", "")
readcrcout = hex(crc16(unhexlify(data))).upper()
str_list = list(readcrcout)
if len(str_list) < 6:
str_list.insert(2, '0'*(6-len(str_list))) # 位数不足补0
crc_data = "".join(str_list)
return crc_data[2:4]+' '+crc_data[4:]
def ten2sixteen(num, length):
"""
十进制转十六进制
:param num: 十进制数字
:param length: 字节长度
:return:
"""
data = str(hex(eval(str(num))))[2:]
data_len = len(data)
if data_len % 2 == 1:
data = '0' + data
data_len += 1
sixteen_str = "00 " * (length - data_len//2) + data[0:2] + ' ' + data[2:]
return sixteen_str.strip()
def cmd2bytearray(cmd_str: str):
verify = crc16Add(cmd_str)
cmd = FRAME_HEAD + ' ' + cmd_str + ' ' + verify + ' ' + FRAME_TAIL
print(cmd)
return bytearray.fromhex(cmd)
def device_capluse():
"""
获取设备气路胶囊信息
:return:
"""
cmd_data = '00 00 00 01 0E 01 06 00 00'
return cmd2bytearray(cmd_data)
def start_play(scent: int, playtime: int):
play_cmd = '00 00 00 01 02 05'
scent_channel = ten2sixteen(scent, 1)
if playtime == 0: # 一直播放
playtime16 = 'FF FF FF FF'
else:
playtime16 = ten2sixteen(playtime, 4)
cmd_data = play_cmd + ' ' + scent_channel + ' ' + playtime16
return cmd2bytearray(cmd_data)
def multiple_play(scent_channels, play_times):
data = ["00"] * 6
for i in range(len(scent_channels)):
data[scent_channels[i] - 1] = "FF" if int(play_times[i]/100) > 255 else ten2sixteen(int(play_times[i]/100), 1)
play_cmd = "00 00 00 01 13 06 " + " ".join(data)
return cmd2bytearray(play_cmd)
def stop_play():
"""
停止播放
:return:
"""
stop_cmd = '00 00 00 01 00 01 00'
return cmd2bytearray(stop_cmd)
def status_check():
"""
检查工作状态
:return:
"""
status_cmd = '00 00 00 01 11 01 00 00 00'
return cmd2bytearray(status_cmd)
def msg_decoder(msg):
list_part = msg[msg.find('['):msg.find(']')+1]
# 使用 ast.literal_eval 安全地解析字符串
data = ast.literal_eval(list_part)
scent_channels = [item.get("channelId") for item in data]
play_times = [item.get("time") for item in data]
return scent_channels, play_times
async def get_services(mac_address: str):
async with BleakClient(mac_address) as client:
svcs = client.services
return svcs
async def find_and_get_services(device_name: str):
# 发现所有设备
devices = await BleakScanner.discover()
# 遍历设备列表,查找目标设备
for device in devices:
if device.name == device_name:
print(f"Found device: {device.name}, MAC Address: {device.address}")
# 获取服务
services = await get_services(device.address)
if services:
last_service = services.services[40]
return {
"device_name": device.name,
"mac_address": device.address,
"last_service_uuid": last_service.uuid,
}
else:
print("No services found for the device.")
return None
# 如果没有找到设备
print(f"Device with name '{device_name}' not found.")
return None
# 监听回调函数,此处为打印消息
def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray):
# print("rev data:", data)
print("rev data bytes2hex:", ' '.join(['%02x' % b for b in data]))
async def main():
print("starting scan...")
# 基于MAC地址查找设备
information = await find_and_get_services(device_name)
par_device_addr, svc_uuid = information["mac_address"], information["last_service_uuid"]
# 设备的Characteristic UUID
uuid_head = ["6e400003"]
# 设备的Characteristic UUID具备写属性Write
uuid_head_write = ["6e400002"]
uuid = svc_uuid.split('-')[1:]
par_notification_characteristic = "-".join(uuid_head+uuid)
par_write_characteristic = "-".join(uuid_head_write+uuid)
device = await BleakScanner.find_device_by_address(
par_device_addr, cb=dict(use_bdaddr=False)
)
if device is None:
print("could not find device with address '%s'" % par_device_addr)
return
# 事件定义
disconnected_event = asyncio.Event()
# 断开连接事件回调
def disconnected_callback(client):
print("Disconnected callback called!")
disconnected_event.set()
print("connecting to device...")
async with BleakClient(device, disconnected_callback=disconnected_callback) as client:
print("Connected")
await client.start_notify(par_notification_characteristic, notification_handler)
await client.write_gatt_char(par_write_characteristic, device_capluse()) # 获取设备气路胶囊信息
await asyncio.sleep(2.0)
while True:
msg = queue.get()
if msg == "0":
break
scent_channels, play_times = msg_decoder(msg)
print(scent_channels, play_times)
if len(scent_channels) == 1:
await client.write_gatt_char(par_write_characteristic, start_play(scent_channels[0], play_times[0])) # 发送开始播放指令
await asyncio.sleep(play_times[0] / 1000)
else:
await client.write_gatt_char(par_write_characteristic, multiple_play(scent_channels, play_times))
await asyncio.sleep(max(play_times) / 1000)
await client.write_gatt_char(par_write_characteristic, stop_play()) # 发送停止播放指令
await client.write_gatt_char(par_write_characteristic, status_check()) # 检查设备工作状态
await client.stop_notify(par_notification_characteristic)
await client.disconnect()
if __name__ == "__main__":
username = "stark"
password = "12345678"
broker = "47.120.70.16"
port = 1883
sub_topic = "/smell/cmd"
FRAME_HEAD = 'F5'
FRAME_TAIL = '55'
device_name = "scent08d1f90efa9a"
queue = LifoQueue()
t = threading.Thread(target=sub_run, args=(queue, username, password, broker, port, sub_topic),daemon=True)
t.start()
asyncio.run(main())