feat: init duooomi BLE SDK with brownfield support

This commit is contained in:
km2023
2026-04-02 15:20:10 +08:00
parent 0c6e858f11
commit 9e27189799
14 changed files with 2009 additions and 1466 deletions

123
ble/protocol/types.ts Normal file
View File

@@ -0,0 +1,123 @@
import { z } from 'zod'
export const DeviceInfoZod = z.object({
allspace: z.bigint(),
freespace: z.bigint(),
name: z.string().max(128, 'Device Name too long'),
size: z.string().describe('设备分辨率, 以x作为分隔符, 宽x高, 例如 360x360'),
brand: z.string().max(128, 'Brand name too long'),
powerlevel: z
.number()
.min(0, 'Power level could not be less then 0')
.max(100, 'Power level could not be more then 100'),
})
export type DeviceInfo = z.infer<typeof DeviceInfoZod>
export const ProtocolFrameZod = z.object({
head: z.number().describe('固定表示传输方向 APP -> BLE = 0xb1, BLE -> APP = 0xb0'),
type: z.number().describe('表示传输数据对应哪个Event, 与EVENT_TYPES.xxx.code对应'),
subpageTotal: z.number().describe('代表总的分包大小'),
curPage: z.number().describe('代表当前分包序号'),
dataLen: z.number().describe('代表当前数据包字节长度 bytes'),
data: z.instanceof(ArrayBuffer).describe('数据包内实际二进制字节内容'),
checksum: z.number().describe('数据包校验字节, 整个数据包中除该字节以外的所有字节数据的单字节和 = (0 - 该字节)'),
})
export type ProtocolFrame = z.infer<typeof ProtocolFrameZod>
export const BondingResponseZod = z.object({
type: z.number().describe('绑定命令类型 0x0f'),
sn: z
.string()
.length(14)
.describe(
'设备sn码, 第1位A量产D售后E试产F测试环境后六位000000-999999234568位不变第7位可能根据配置不同变化',
),
success: z
.number()
.describe('绑定成功/失败 0 = 失败1 = 成功如传入的userId与已绑定的userId一致直接返回成功代表已绑定'),
contents: z
.array(z.string())
.describe('绑定成功时才返回设备内已存在的原始内容绑定失败时返回空数组数组按照id排序'),
})
export type BindingResponse = z.infer<typeof BondingResponseZod>
export const UnBindResponseZod = z.object({
success: z.number().describe('解绑成功 = 1, 失败 = 0'),
})
export type UnBindResponse = z.infer<typeof UnBindResponseZod>
export const VersionInfoZod = z.object({
version: z.string().max(128, 'Version string too long').describe('设备版本号'),
type: z.string().describe('方法type 0x07'), // e.g., "duooomi_v1"
})
export type VersionInfo = z.infer<typeof VersionInfoZod>
export const FileTransferDataZod = z.object({
data: z.instanceof(ArrayBuffer),
})
export type FileTransferData = z.infer<typeof FileTransferDataZod>
export const BindingRequestZod = z.object({
type: z.number().describe('绑定命令类型 0x0f'),
userId: z.string().length(32).describe('32位长度的user Id'),
})
export type BindingRequest = z.infer<typeof BindingRequestZod>
// 删除文件协议响应状态
export const DELETE_FILE_STATUS = {
SUCCESS: 0, // 删除成功
FAILED: 1, // 删除失败
NOT_EXISTS: 2, // 文件不存在
} as const
export type DeleteFileStatus = (typeof DELETE_FILE_STATUS)[keyof typeof DELETE_FILE_STATUS]
// 删除文件请求接口
export const DeleteFileRequestZod = z.object({
type: z.number().describe('删除文件命令类型 0x13'),
key: z.string().describe('要删除内容对应的key'),
})
export type DeleteFileRequest = z.infer<typeof DeleteFileRequestZod>
// 删除文件响应接口
export const DeleteFileResponseZod = z.object({
type: z.number().describe('删除文件命令类型 0x13'),
success: z.number().min(0).max(2).describe('删除结果状态: 0=成功, 1=失败, 2=文件不存在'),
})
export type DeleteFileResponse = z.infer<typeof DeleteFileResponseZod>
// 预先发送协议状态
export const PREPARE_TRANSFER_STATUS = {
READY: 'ready', // 准备好接收
NO_SPACE: 'no_space', // 存储空间不足
DUPLICATED: 'duplicated', // 已存在相同key的资源
} as const
export type PrepareTransferStatus = (typeof PREPARE_TRANSFER_STATUS)[keyof typeof PREPARE_TRANSFER_STATUS]
// 预先发送请求接口
export const PrepareTransferRequestZod = z.object({
type: z.number().describe('预先发送命令类型 0x14'),
key: z.string().describe('要传输内容对应的key'),
size: z.number().describe('ani文件大小信息单位为bytes'),
})
export type PrepareTransferRequest = z.infer<typeof PrepareTransferRequestZod>
// 预先发送响应接口
export const PrepareTransferResponseZod = z.object({
type: z.number().describe('预先发送命令类型 0x14'),
key: z.string().describe('要传输内容对应的key'),
status: z.enum(['ready', 'no_space', 'duplicated']).describe('告知APP是否可继续发送'),
})
export type PrepareTransferResponse = z.infer<typeof PrepareTransferResponseZod>