flutter_sdk/example/lib/services/cat_eye_monitor_service.dart

152 lines
4.6 KiB
Dart
Raw Permalink Normal View History

2026-08-20 09:25:05 +08:00
import 'dart:convert';
import 'dart:io';
import 'package:crypto/crypto.dart' as crypto;
import 'package:flutter_sdk/starcloud_flutter_sdk.dart';
import 'package:starcloud/blue/blue_features.dart';
import 'package:starcloud/blue/blue_read_write.dart';
import 'package:starcloud/blue/entity/blue_message.dart';
import 'package:starcloud/device/ble/base/base_sender.dart';
import 'package:starcloud/device/ble/ble_reply.dart';
import 'package:starcloud/device/ble/business/ble_type.dart';
import 'package:starcloud/device/ble/tool/ble_tool.dart';
import 'package:starcloud/entity/star_cloud_lock_list.dart';
import 'package:starcloud/tool/sm4_encipher/sm4.dart';
/// 主动监控前将猫眼从省电模式切换到实时监控模式。
///
/// 实时模式由 BLE feature bit 64 写入锁端。
/// 省电模式(catEyeMode=0)下锁固件会忽略主动 TalkRequest。
///
/// 公开 Flutter SDK 文档未定义 cat-eye 云端设置 API;因此这里不能调用
/// 非公开的 `updateCatEyeConfig` 并把其服务端权限错误当作 BLE 操作失败。
class CatEyeMonitorService {
static const int _catEyeFeatureBit = 64;
static const int _realtimeMode = 2;
Future<void> enableRealtimeMode({
required StarCloudLock lock,
}) async {
final context = await StarCloudLockContext.fromLock(lock);
final featureData = <int>[
_realtimeMode,
0, 0, 0, 0, 0, 0, // 非自定义模式的录像参数
0, 0, // detectionDistance / realTimePicture
1, 10, 1, 1, // 自动亮屏、亮屏时长、逗留/异常告警
];
final blue = BlueReadWrite.shareManager();
if (blue == null) {
throw const StarCloudException('Bluetooth service is unavailable');
}
blue.deviceName = lock.bluetooth.bluetoothDeviceName;
if (!Platform.isAndroid) blue.mac = lock.mac;
blue.type = BlueFeaturesType.lock;
blue.clearCallback();
final reply = await blue.writeAndReturn(
_SetCatEyeModeCommand(
keyId: context.keyId,
userId: context.userId,
featureData: featureData,
token: context.token,
needAuthor: context.needAuthor,
publicKey: context.publicKey,
privateKey: context.privateKey,
),
getPrivateKey: () => context.privateKey,
timeout: const Duration(seconds: 20),
);
if (reply.status != 0) {
throw StarCloudException(
'Failed to enable cat-eye realtime mode over BLE',
raw: 'status=0x${reply.status.toRadixString(16)}',
);
}
}
}
class _SetCatEyeModeCommand extends SenderProtocol {
_SetCatEyeModeCommand({
required this.keyId,
required this.userId,
required this.featureData,
required List<int> token,
required this.needAuthor,
required this.publicKey,
required this.privateKey,
}) : token = List<int>.from(token),
super(CommandType.generalExtendedCommond);
final String keyId;
final String userId;
final List<int> featureData;
final int needAuthor;
final List<int> publicKey;
final List<int> privateKey;
@override
List<int>? token;
@override
List<int> messageDetail() {
final data = <int>[0x30, 0x30, 72];
var subData = <int>[];
final keyBytes = utf8.encode(keyId);
subData.addAll(keyBytes);
subData = BLETool.getFixedLengthList(subData, 40 - keyBytes.length);
final userBytes = utf8.encode(userId);
subData.addAll(userBytes);
subData = BLETool.getFixedLengthList(subData, 20 - userBytes.length);
subData
..add(CatEyeMonitorService._catEyeFeatureBit)
..add(featureData.length)
..addAll(featureData);
if (needAuthor == 0) {
subData.add(0);
} else {
final authData = <int>[
...userBytes,
...keyBytes,
...?token,
...publicKey,
];
final authCode = crypto.md5.convert(authData).bytes;
subData
..add(authCode.length)
..addAll(authCode);
}
data
..add(subData.length)
..addAll(subData);
while (data.length % 16 != 0) {
data.add(0);
}
return SM4.encrypt(data, key: privateKey, mode: SM4CryptoMode.ECB);
}
@override
BLEReply blueMessageToReply(BlueMessage message) {
return _SetCatEyeModeReply.parseData(commandType, message.data);
}
}
class _SetCatEyeModeReply extends BLEReply {
_SetCatEyeModeReply.parseData(
CommandType commandType,
List<int> data,
) : super.parseData(commandType, data) {
// 通用扩展应答:[0x30, 0x30, mainStatus, subCommand, subStatus, ...]
// 原 App 以 mainStatus 为本次设置结果。
status = data.length > 2 ? data[2] : 0xff;
errorWithStatus(status);
if (status == 0x06 && data.length >= 9) {
token = data.sublist(5, 9);
}
}
}