@ray-js/robot-data-stream 0.0.15-beta.8 → 0.0.16-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/constant.d.ts +1 -0
- package/lib/constant.js +11 -0
- package/lib/mqtt/hooks/useStructuredMessage.d.ts +0 -3
- package/lib/mqtt/hooks/useStructuredMessage.js +150 -75
- package/lib/mqtt/mqttProvider.d.ts +10 -1
- package/lib/mqtt/mqttProvider.js +5 -3
- package/lib/mqtt/type/fun.d.ts +24 -9
- package/lib/mqtt/type/fun.js +2 -1
- package/lib/mqtt/type/index.d.ts +4 -1
- package/lib/mqtt/type/protocols/base.d.ts +4 -1
- package/lib/mqtt/type/protocols/base.js +9 -3
- package/lib/mqtt/type/protocols/carpetCleanProtocol.d.ts +2 -2
- package/lib/mqtt/type/protocols/carpetCleanProtocol.js +2 -2
- package/lib/mqtt/type/protocols/carpetProtocol.d.ts +5 -5
- package/lib/mqtt/type/protocols/carpetProtocol.js +7 -7
- package/lib/mqtt/type/protocols/roomPropertyProtocol.d.ts +8 -2
- package/lib/mqtt/type/protocols/roomPropertyProtocol.js +26 -5
- package/lib/mqtt/type/protocols/scheduleProtocol.d.ts +12 -3
- package/lib/mqtt/type/protocols/scheduleProtocol.js +17 -0
- package/lib/mqtt/type/protocols/selectRoomCleanProtocol.d.ts +6 -2
- package/lib/mqtt/type/protocols/selectRoomCleanProtocol.js +12 -4
- package/lib/mqtt/type/protocols/spotCleanProtocol.d.ts +8 -2
- package/lib/mqtt/type/protocols/spotCleanProtocol.js +24 -2
- package/lib/mqtt/type/protocols/wifiMapProtocol.d.ts +1 -0
- package/lib/mqtt/type/protocols/wifiMapProtocol.js +2 -1
- package/lib/mqtt/type/protocols/zoneCleanProtocol.d.ts +7 -2
- package/lib/mqtt/type/protocols/zoneCleanProtocol.js +31 -4
- package/lib/mqtt/useRoomProperty.js +8 -5
- package/lib/mqtt/useSchedule.js +20 -7
- package/lib/mqtt/useSelectRoomClean.js +37 -14
- package/lib/mqtt/useSpotClean.js +22 -12
- package/lib/mqtt/useWifiMap.js +4 -2
- package/lib/mqtt/useZoneClean.js +38 -46
- package/lib/mqtt/waterPreference.d.ts +20 -0
- package/lib/mqtt/waterPreference.js +70 -0
- package/package.json +7 -4
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as z from '../../../myLib/zod/mini';
|
|
2
|
+
import { MqttError } from '../../promise';
|
|
2
3
|
import { BaseResponseSchema, Point } from './base';
|
|
4
|
+
const VALIDATION_ERROR_CODE = -2;
|
|
3
5
|
const zoneSchema = z.object({
|
|
4
6
|
// 区域点集
|
|
5
7
|
points: z.array(Point),
|
|
@@ -20,10 +22,12 @@ export const setZoneCleanSchema = z.object({
|
|
|
20
22
|
zones: z.array(zoneSchema),
|
|
21
23
|
origin: Point,
|
|
22
24
|
suctions: z.optional(z.array(z.string())),
|
|
23
|
-
cisterns: z.optional(z.array(z.string())),
|
|
25
|
+
cisterns: z.optional(z.array(z.union([z.string(), z.number()]))),
|
|
26
|
+
waterValues: z.optional(z.array(z.union([z.string(), z.number()]))),
|
|
24
27
|
cleanCounts: z.optional(z.array(z.int())),
|
|
25
28
|
yMops: z.optional(z.array(z.int())),
|
|
26
|
-
sweepMopModes: z.optional(z.array(z.string()))
|
|
29
|
+
sweepMopModes: z.optional(z.array(z.string())),
|
|
30
|
+
routePreferences: z.optional(z.array(z.string()))
|
|
27
31
|
});
|
|
28
32
|
|
|
29
33
|
// 划区清扫响应 Schema
|
|
@@ -35,15 +39,38 @@ const setZoneCleanResponseSchema = z.extend(BaseResponseSchema, {
|
|
|
35
39
|
// 吸力, closed - 关闭 gentle - 安静 normal - 正常 strong - 强劲 max - 超强 ,默认值为''
|
|
36
40
|
suctions: z.optional(z.array(z.string())),
|
|
37
41
|
// 拖地水量 "closed"- 关闭,"low"-低,"middle"-中,"high"-高,默认值为''
|
|
38
|
-
cisterns: z.optional(z.array(z.string())),
|
|
42
|
+
cisterns: z.optional(z.array(z.union([z.string(), z.number()]))),
|
|
43
|
+
// 自定义拖地水量
|
|
44
|
+
waterValues: z.optional(z.array(z.union([z.string(), z.number()]))),
|
|
39
45
|
// 清扫次数 默认值1
|
|
40
46
|
cleanCounts: z.optional(z.array(z.int())),
|
|
41
47
|
// 拖地模式 1:开启Y型拖地 0:关闭Y型拖地 -1 未设置 默认值-1
|
|
42
48
|
yMops: z.optional(z.array(z.int())),
|
|
43
49
|
// 扫拖模式 "only_sweep":仅扫,"only_mop":仅拖,"both_work":扫拖同时,"clean_before_mop":先扫后拖,默认值为'only_sweep'
|
|
44
|
-
sweepMopModes: z.optional(z.array(z.string()))
|
|
50
|
+
sweepMopModes: z.optional(z.array(z.string())),
|
|
51
|
+
// 路线偏好
|
|
52
|
+
routePreferences: z.optional(z.array(z.string()))
|
|
45
53
|
});
|
|
46
54
|
|
|
47
55
|
// 类型导出
|
|
48
56
|
|
|
57
|
+
export function validateSetZoneClean(data) {
|
|
58
|
+
const num = data.zones.length;
|
|
59
|
+
const validateLength = (name, value) => {
|
|
60
|
+
if (value && value.length !== num) {
|
|
61
|
+
throw new MqttError(`${name} length must be ${num}, actual is ${value.length}`, {
|
|
62
|
+
errCode: VALIDATION_ERROR_CODE,
|
|
63
|
+
reqType: 'set_zone_clean'
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
validateLength('suctions', data.suctions);
|
|
68
|
+
validateLength('cisterns', data.cisterns);
|
|
69
|
+
validateLength('waterValues', data.waterValues);
|
|
70
|
+
validateLength('cleanCounts', data.cleanCounts);
|
|
71
|
+
validateLength('yMops', data.yMops);
|
|
72
|
+
validateLength('sweepMopModes', data.sweepMopModes);
|
|
73
|
+
validateLength('routePreferences', data.routePreferences);
|
|
74
|
+
}
|
|
75
|
+
|
|
49
76
|
// 函数类型定义
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
1
2
|
import { useContext } from 'react';
|
|
2
3
|
import { handleMqttError, useStructuredMessage } from './hooks/useStructuredMessage';
|
|
3
4
|
import { SingletonContext } from './mqttProvider';
|
|
4
5
|
import { RoomPropertyEnum } from './type';
|
|
5
6
|
import { validator } from './type/fun';
|
|
7
|
+
import { normalizeWaterPreferencePayload } from './waterPreference';
|
|
6
8
|
/**
|
|
7
9
|
* 自定义 Hook,用于房间属性管理
|
|
8
10
|
* @param devId 设备ID(可选,如果不提供则从 MqttProvider Context 获取)
|
|
@@ -45,7 +47,7 @@ export const useRoomProperty = () => {
|
|
|
45
47
|
setRoomProperty: data => {
|
|
46
48
|
return new Promise((resolve, reject) => {
|
|
47
49
|
try {
|
|
48
|
-
var _data$num, _data$suctions, _data$
|
|
50
|
+
var _data$num, _data$suctions, _data$cleanCounts, _data$yMops, _data$sweepMopModes, _data$names, _data$routePreference;
|
|
49
51
|
if (!useMqtt) {
|
|
50
52
|
reject(new Error('useMqtt is not used'));
|
|
51
53
|
return;
|
|
@@ -56,18 +58,19 @@ export const useRoomProperty = () => {
|
|
|
56
58
|
ids
|
|
57
59
|
} = data;
|
|
58
60
|
const len = ids.length;
|
|
59
|
-
const sendData = {
|
|
61
|
+
const sendData = _objectSpread({
|
|
60
62
|
num: (_data$num = data.num) !== null && _data$num !== void 0 ? _data$num : len,
|
|
61
63
|
ids,
|
|
62
64
|
suctions: (_data$suctions = data.suctions) !== null && _data$suctions !== void 0 ? _data$suctions : new Array(len).fill('closed'),
|
|
63
|
-
cisterns: (_data$cisterns = data.cisterns) !== null && _data$cisterns !== void 0 ? _data$cisterns : new Array(len).fill('closed'),
|
|
64
65
|
cleanCounts: (_data$cleanCounts = data.cleanCounts) !== null && _data$cleanCounts !== void 0 ? _data$cleanCounts : new Array(len).fill(1),
|
|
65
66
|
yMops: (_data$yMops = data.yMops) !== null && _data$yMops !== void 0 ? _data$yMops : new Array(len).fill(-1),
|
|
66
67
|
sweepMopModes: (_data$sweepMopModes = data.sweepMopModes) !== null && _data$sweepMopModes !== void 0 ? _data$sweepMopModes : new Array(len).fill('only_sweep'),
|
|
67
68
|
names: (_data$names = data.names) !== null && _data$names !== void 0 ? _data$names : new Array(len).fill(''),
|
|
69
|
+
nameLabels: data.nameLabels,
|
|
68
70
|
floorTypes: data.floorTypes,
|
|
69
|
-
orders: data.orders
|
|
70
|
-
|
|
71
|
+
orders: data.orders,
|
|
72
|
+
routePreferences: (_data$routePreference = data.routePreferences) !== null && _data$routePreference !== void 0 ? _data$routePreference : new Array(len).fill('standard')
|
|
73
|
+
}, normalizeWaterPreferencePayload(data.cisterns, data.waterValues));
|
|
71
74
|
|
|
72
75
|
// 使用 TypeBox Schema 进行参数验证(包含自定义验证逻辑)
|
|
73
76
|
const validatedData = validator.validate('set_room_property', sendData);
|
package/lib/mqtt/useSchedule.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
1
2
|
import "core-js/modules/esnext.iterator.map.js";
|
|
2
3
|
// 定时
|
|
3
4
|
import { encodeDeviceTimer0x30 } from '@ray-js/robot-protocol';
|
|
4
5
|
import { useContext } from 'react';
|
|
5
|
-
import {
|
|
6
|
+
import { CLEAN_MODE_MAP, SUCTION_MAP, ROUTE_PREFERENCE_MAP } from '../constant';
|
|
7
|
+
import { normalizeWaterPreferencePayload, resolveWaterLevel } from './waterPreference';
|
|
6
8
|
import { handleMqttError, useStructuredMessage } from './hooks/useStructuredMessage';
|
|
7
9
|
import { SingletonContext } from './mqttProvider';
|
|
8
10
|
import { ScheduleEnum } from './type';
|
|
@@ -53,8 +55,16 @@ export const useSchedule = () => {
|
|
|
53
55
|
try {
|
|
54
56
|
// 使用 TypeBox Schema 进行参数验证
|
|
55
57
|
const validatedData = validator.validate('set_schedule', message);
|
|
58
|
+
const list = validatedData.list.map(item => {
|
|
59
|
+
var _item$routePreference, _item$ids;
|
|
60
|
+
return _objectSpread(_objectSpread(_objectSpread({}, item), normalizeWaterPreferencePayload(item.cisterns, item.waterValues)), {}, {
|
|
61
|
+
routePreferences: (_item$routePreference = item.routePreferences) !== null && _item$routePreference !== void 0 ? _item$routePreference : new Array(((_item$ids = item.ids) === null || _item$ids === void 0 ? void 0 : _item$ids.length) || 1).fill('standard')
|
|
62
|
+
});
|
|
63
|
+
});
|
|
56
64
|
if (useMqtt) {
|
|
57
|
-
sendStructuredMessage(ScheduleEnum.set,
|
|
65
|
+
sendStructuredMessage(ScheduleEnum.set, _objectSpread(_objectSpread({}, validatedData), {}, {
|
|
66
|
+
list
|
|
67
|
+
}), resolve, error => reject(handleMqttError(error, 'Failed to set schedule')), ScheduleEnum.query // 响应类型是 query,不是 set
|
|
58
68
|
);
|
|
59
69
|
return;
|
|
60
70
|
}
|
|
@@ -63,7 +73,7 @@ export const useSchedule = () => {
|
|
|
63
73
|
const command = encodeDeviceTimer0x30({
|
|
64
74
|
version: commandVersion,
|
|
65
75
|
number: validatedData.num,
|
|
66
|
-
list:
|
|
76
|
+
list: list.map(_ref => {
|
|
67
77
|
let {
|
|
68
78
|
active,
|
|
69
79
|
suctions,
|
|
@@ -72,13 +82,15 @@ export const useSchedule = () => {
|
|
|
72
82
|
cleanCounts,
|
|
73
83
|
cycle,
|
|
74
84
|
cisterns,
|
|
75
|
-
|
|
85
|
+
waterValues,
|
|
86
|
+
time,
|
|
87
|
+
routePreferences
|
|
76
88
|
} = _ref;
|
|
77
89
|
return {
|
|
78
90
|
effectiveness: active,
|
|
79
91
|
cleanMode: CLEAN_MODE_MAP[(sweepMopModes === null || sweepMopModes === void 0 ? void 0 : sweepMopModes[0]) || ''],
|
|
80
92
|
fanLevel: SUCTION_MAP[(suctions === null || suctions === void 0 ? void 0 : suctions[0]) || ''],
|
|
81
|
-
waterLevel:
|
|
93
|
+
waterLevel: resolveWaterLevel(waterValues === null || waterValues === void 0 ? void 0 : waterValues[0], cisterns === null || cisterns === void 0 ? void 0 : cisterns[0]),
|
|
82
94
|
time: {
|
|
83
95
|
hour: time[0],
|
|
84
96
|
minute: time[1]
|
|
@@ -86,7 +98,8 @@ export const useSchedule = () => {
|
|
|
86
98
|
week: cycle,
|
|
87
99
|
sweepCount: (cleanCounts === null || cleanCounts === void 0 ? void 0 : cleanCounts[0]) || 1,
|
|
88
100
|
roomIds: ids || [],
|
|
89
|
-
roomNum: (ids === null || ids === void 0 ? void 0 : ids.length) || 0
|
|
101
|
+
roomNum: (ids === null || ids === void 0 ? void 0 : ids.length) || 0,
|
|
102
|
+
routePreference: ROUTE_PREFERENCE_MAP[(routePreferences === null || routePreferences === void 0 ? void 0 : routePreferences[0]) || 'standard']
|
|
90
103
|
};
|
|
91
104
|
})
|
|
92
105
|
});
|
|
@@ -97,7 +110,7 @@ export const useSchedule = () => {
|
|
|
97
110
|
reqType: ScheduleEnum.query,
|
|
98
111
|
version: commandVersion,
|
|
99
112
|
taskId: `${Date.now()}`,
|
|
100
|
-
list
|
|
113
|
+
list,
|
|
101
114
|
num: validatedData.num
|
|
102
115
|
});
|
|
103
116
|
} catch (error) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
1
2
|
import "core-js/modules/esnext.iterator.constructor.js";
|
|
2
3
|
import "core-js/modules/esnext.iterator.for-each.js";
|
|
3
4
|
import "core-js/modules/esnext.iterator.map.js";
|
|
@@ -8,6 +9,7 @@ import { handleMqttError, useStructuredMessage } from './hooks/useStructuredMess
|
|
|
8
9
|
import { SingletonContext } from './mqttProvider';
|
|
9
10
|
import { RoomCleanSetEnum } from './type';
|
|
10
11
|
import { validator } from './type/fun';
|
|
12
|
+
import { normalizeWaterPreferencePayload } from './waterPreference';
|
|
11
13
|
/**
|
|
12
14
|
* 自定义 Hook,用于选区清扫
|
|
13
15
|
* @returns 包含 requestSelectRoomClean 和 setRoomClean 函数的对象
|
|
@@ -59,9 +61,11 @@ export const useSelectRoomClean = () => {
|
|
|
59
61
|
ids: [],
|
|
60
62
|
suctions: [],
|
|
61
63
|
cisterns: [],
|
|
64
|
+
waterValues: [],
|
|
62
65
|
cleanCounts: [],
|
|
63
66
|
yMops: [],
|
|
64
|
-
sweepMopModes: []
|
|
67
|
+
sweepMopModes: [],
|
|
68
|
+
routePreferences: []
|
|
65
69
|
});
|
|
66
70
|
} catch (error) {
|
|
67
71
|
reject(handleMqttError(error, 'Failed to request select room clean'));
|
|
@@ -77,28 +81,36 @@ export const useSelectRoomClean = () => {
|
|
|
77
81
|
if (useMqtt) {
|
|
78
82
|
const ids = [];
|
|
79
83
|
const suctions = [];
|
|
80
|
-
const cisterns = [];
|
|
81
84
|
const cleanCounts = [];
|
|
82
85
|
const yMops = [];
|
|
83
86
|
const sweepMopModes = [];
|
|
87
|
+
const routePreferences = [];
|
|
84
88
|
rooms.forEach(room => {
|
|
85
|
-
var _room$suction, _room$cleanTimes, _room$yMop, _room$sweepMopMode;
|
|
89
|
+
var _room$suction, _room$cleanTimes, _room$yMop, _room$sweepMopMode, _room$routePreference;
|
|
86
90
|
ids.push(room.roomId);
|
|
87
91
|
suctions.push((_room$suction = room.suction) !== null && _room$suction !== void 0 ? _room$suction : '');
|
|
88
|
-
cisterns.push(room.cistern);
|
|
89
92
|
cleanCounts.push((_room$cleanTimes = room.cleanTimes) !== null && _room$cleanTimes !== void 0 ? _room$cleanTimes : 1);
|
|
90
93
|
yMops.push((_room$yMop = room.yMop) !== null && _room$yMop !== void 0 ? _room$yMop : -1);
|
|
91
94
|
sweepMopModes.push((_room$sweepMopMode = room.sweepMopMode) !== null && _room$sweepMopMode !== void 0 ? _room$sweepMopMode : 'only_sweep');
|
|
95
|
+
routePreferences.push((_room$routePreference = room.routePreference) !== null && _room$routePreference !== void 0 ? _room$routePreference : 'standard');
|
|
92
96
|
});
|
|
93
|
-
|
|
97
|
+
const waterPayload = normalizeWaterPreferencePayload(rooms.map(room => {
|
|
98
|
+
var _room$cistern;
|
|
99
|
+
return (_room$cistern = room.cistern) !== null && _room$cistern !== void 0 ? _room$cistern : '';
|
|
100
|
+
}), rooms.map(room => {
|
|
101
|
+
var _room$waterValue;
|
|
102
|
+
return (_room$waterValue = room.waterValue) !== null && _room$waterValue !== void 0 ? _room$waterValue : '';
|
|
103
|
+
}));
|
|
104
|
+
sendStructuredMessage(RoomCleanSetEnum.set, _objectSpread(_objectSpread({
|
|
94
105
|
ids,
|
|
95
|
-
suctions
|
|
96
|
-
|
|
106
|
+
suctions
|
|
107
|
+
}, waterPayload), {}, {
|
|
97
108
|
cleanCounts,
|
|
98
109
|
yMops,
|
|
99
110
|
sweepMopModes,
|
|
111
|
+
routePreferences,
|
|
100
112
|
num: rooms.length
|
|
101
|
-
}, resolve, error => reject(handleMqttError(error, 'Failed to set room clean')), RoomCleanSetEnum.query // 响应类型是 query,不是 set
|
|
113
|
+
}), resolve, error => reject(handleMqttError(error, 'Failed to set room clean')), RoomCleanSetEnum.query // 响应类型是 query,不是 set
|
|
102
114
|
);
|
|
103
115
|
return;
|
|
104
116
|
}
|
|
@@ -115,7 +127,13 @@ export const useSelectRoomClean = () => {
|
|
|
115
127
|
var _room$suction2;
|
|
116
128
|
return (_room$suction2 = room.suction) !== null && _room$suction2 !== void 0 ? _room$suction2 : '';
|
|
117
129
|
});
|
|
118
|
-
const
|
|
130
|
+
const waterPayload = normalizeWaterPreferencePayload(rooms.map(room => {
|
|
131
|
+
var _room$cistern2;
|
|
132
|
+
return (_room$cistern2 = room.cistern) !== null && _room$cistern2 !== void 0 ? _room$cistern2 : '';
|
|
133
|
+
}), rooms.map(room => {
|
|
134
|
+
var _room$waterValue2;
|
|
135
|
+
return (_room$waterValue2 = room.waterValue) !== null && _room$waterValue2 !== void 0 ? _room$waterValue2 : '';
|
|
136
|
+
}));
|
|
119
137
|
const cleanCounts = rooms.map(room => {
|
|
120
138
|
var _room$cleanTimes2;
|
|
121
139
|
return (_room$cleanTimes2 = room.cleanTimes) !== null && _room$cleanTimes2 !== void 0 ? _room$cleanTimes2 : 1;
|
|
@@ -128,19 +146,24 @@ export const useSelectRoomClean = () => {
|
|
|
128
146
|
var _room$sweepMopMode2;
|
|
129
147
|
return (_room$sweepMopMode2 = room.sweepMopMode) !== null && _room$sweepMopMode2 !== void 0 ? _room$sweepMopMode2 : 'only_sweep';
|
|
130
148
|
});
|
|
131
|
-
|
|
149
|
+
const routePreferences = rooms.map(room => {
|
|
150
|
+
var _room$routePreference2;
|
|
151
|
+
return (_room$routePreference2 = room.routePreference) !== null && _room$routePreference2 !== void 0 ? _room$routePreference2 : 'standard';
|
|
152
|
+
});
|
|
153
|
+
resolve(_objectSpread(_objectSpread({
|
|
132
154
|
success: true,
|
|
133
155
|
errCode: 0,
|
|
134
156
|
reqType: RoomCleanSetEnum.query,
|
|
135
157
|
version: commandVersion,
|
|
136
158
|
taskId: `${Date.now()}`,
|
|
137
159
|
ids,
|
|
138
|
-
suctions
|
|
139
|
-
|
|
160
|
+
suctions
|
|
161
|
+
}, waterPayload), {}, {
|
|
140
162
|
cleanCounts,
|
|
141
163
|
yMops,
|
|
142
|
-
sweepMopModes
|
|
143
|
-
|
|
164
|
+
sweepMopModes,
|
|
165
|
+
routePreferences
|
|
166
|
+
}));
|
|
144
167
|
} catch (error) {
|
|
145
168
|
reject(handleMqttError(error, 'Failed to set room clean'));
|
|
146
169
|
}
|
package/lib/mqtt/useSpotClean.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
1
2
|
import "core-js/modules/esnext.iterator.map.js";
|
|
2
3
|
// 定点清扫
|
|
3
4
|
|
|
@@ -8,6 +9,7 @@ import { encodeSpotClean0x16 } from '@ray-js/robot-protocol';
|
|
|
8
9
|
import { pointsToString } from '../utils';
|
|
9
10
|
import { useStructuredMessage, handleMqttError } from './hooks/useStructuredMessage';
|
|
10
11
|
import { validator } from './type/fun';
|
|
12
|
+
import { normalizeWaterPreferencePayload } from './waterPreference';
|
|
11
13
|
/**
|
|
12
14
|
* 自定义 Hook,用于定点清扫
|
|
13
15
|
* @returns 包含 requestSpotClean 和 setSpotClean 函数的对象
|
|
@@ -57,25 +59,30 @@ export const useSpotClean = () => {
|
|
|
57
59
|
origin
|
|
58
60
|
} = validatedData;
|
|
59
61
|
const num = spots.length;
|
|
62
|
+
const waterPayload = normalizeWaterPreferencePayload(validatedData.cisterns, validatedData.waterValues);
|
|
60
63
|
|
|
61
64
|
// 为可选字段设置默认值
|
|
62
65
|
const {
|
|
63
66
|
suctions = new Array(num).fill(''),
|
|
64
|
-
cisterns = new Array(num).fill(''),
|
|
65
67
|
cleanCounts = new Array(num).fill(1),
|
|
66
68
|
yMops = new Array(num).fill(-1),
|
|
67
|
-
sweepMopModes = new Array(num).fill('only_sweep')
|
|
69
|
+
sweepMopModes = new Array(num).fill('only_sweep'),
|
|
70
|
+
routePreferences = new Array(num).fill('standard'),
|
|
71
|
+
names
|
|
68
72
|
} = validatedData;
|
|
69
73
|
if (useMqtt) {
|
|
70
|
-
sendStructuredMessage(SpotCleanEnum.set, {
|
|
74
|
+
sendStructuredMessage(SpotCleanEnum.set, _objectSpread(_objectSpread({
|
|
71
75
|
num,
|
|
72
76
|
polygons: spots.map(spot => pointsToString([spot], origin)),
|
|
73
|
-
suctions
|
|
74
|
-
|
|
77
|
+
suctions
|
|
78
|
+
}, waterPayload), {}, {
|
|
75
79
|
cleanCounts,
|
|
76
80
|
yMops,
|
|
77
|
-
sweepMopModes
|
|
78
|
-
|
|
81
|
+
sweepMopModes,
|
|
82
|
+
routePreferences
|
|
83
|
+
}, names && {
|
|
84
|
+
names
|
|
85
|
+
}), resolve, error => reject(handleMqttError(error, 'Failed to set spot clean')), SpotCleanEnum.query // 响应类型是 query,不是 set
|
|
79
86
|
);
|
|
80
87
|
return;
|
|
81
88
|
}
|
|
@@ -87,19 +94,22 @@ export const useSpotClean = () => {
|
|
|
87
94
|
point: spots[0]
|
|
88
95
|
});
|
|
89
96
|
devices.common.model.actions.command_trans.set(command);
|
|
90
|
-
resolve({
|
|
97
|
+
resolve(_objectSpread(_objectSpread({
|
|
91
98
|
success: true,
|
|
92
99
|
errCode: 0,
|
|
93
100
|
reqType: SpotCleanEnum.query,
|
|
94
101
|
version: commandVersion,
|
|
95
102
|
taskId: `${Date.now()}`,
|
|
96
103
|
polygons: spots.map(spot => pointsToString([spot], origin)),
|
|
97
|
-
suctions
|
|
98
|
-
|
|
104
|
+
suctions
|
|
105
|
+
}, waterPayload), {}, {
|
|
99
106
|
cleanCounts,
|
|
100
107
|
yMops,
|
|
101
|
-
sweepMopModes
|
|
102
|
-
|
|
108
|
+
sweepMopModes,
|
|
109
|
+
routePreferences
|
|
110
|
+
}, names && {
|
|
111
|
+
names
|
|
112
|
+
}));
|
|
103
113
|
} catch (error) {
|
|
104
114
|
reject(handleMqttError(error, 'Failed to set spot clean'));
|
|
105
115
|
}
|
package/lib/mqtt/useWifiMap.js
CHANGED
|
@@ -18,7 +18,7 @@ export const useWifiMap = () => {
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* 设置 WiFi 地图
|
|
21
|
-
* @param data 包含 taskId 的参数对象(taskId
|
|
21
|
+
* @param data 包含 taskId 的参数对象(taskId 可选,不提供时自动生成,switch 可选,默认)
|
|
22
22
|
* @returns Promise<WifiMapResponse> 响应结果
|
|
23
23
|
*/
|
|
24
24
|
|
|
@@ -36,9 +36,11 @@ export const useWifiMap = () => {
|
|
|
36
36
|
let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
37
37
|
return new Promise((resolve, reject) => {
|
|
38
38
|
try {
|
|
39
|
+
var _data$switch;
|
|
39
40
|
// 如果没有提供 taskId,自动生成
|
|
40
41
|
const wifiMapData = {
|
|
41
|
-
taskId: data.taskId || String(Date.now())
|
|
42
|
+
taskId: data.taskId || String(Date.now()),
|
|
43
|
+
switch: (_data$switch = data.switch) !== null && _data$switch !== void 0 ? _data$switch : true
|
|
42
44
|
};
|
|
43
45
|
|
|
44
46
|
// 使用 TypeBox Schema 进行参数验证
|
package/lib/mqtt/useZoneClean.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
1
2
|
import "core-js/modules/esnext.iterator.map.js";
|
|
2
3
|
// 划区清扫
|
|
3
4
|
import { encodeZoneClean0x3a, requestZoneClean0x3b } from '@ray-js/robot-protocol';
|
|
@@ -7,6 +8,7 @@ import { handleMqttError, useStructuredMessage } from './hooks/useStructuredMess
|
|
|
7
8
|
import { SingletonContext } from './mqttProvider';
|
|
8
9
|
import { ZoneCleanEnum } from './type';
|
|
9
10
|
import { validator } from './type/fun';
|
|
11
|
+
import { normalizeWaterPreferencePayload } from './waterPreference';
|
|
10
12
|
/**
|
|
11
13
|
* 自定义 Hook,用于划区清扫
|
|
12
14
|
* @returns 包含 requestZoneClean 和 setZoneClean 函数的对象
|
|
@@ -73,16 +75,15 @@ export const useZoneClean = () => {
|
|
|
73
75
|
|
|
74
76
|
// 将业务参数转换为协议数据格式
|
|
75
77
|
const num = zones.length;
|
|
78
|
+
const waterPayload = normalizeWaterPreferencePayload(params.cisterns, params.waterValues);
|
|
76
79
|
const {
|
|
77
80
|
suctions = new Array(num).fill(''),
|
|
78
|
-
cisterns = new Array(num).fill(''),
|
|
79
81
|
cleanCounts = new Array(num).fill(1),
|
|
80
82
|
yMops = new Array(num).fill(-1),
|
|
81
|
-
sweepMopModes = new Array(num).fill('only_sweep')
|
|
83
|
+
sweepMopModes = new Array(num).fill('only_sweep'),
|
|
84
|
+
routePreferences = new Array(num).fill('standard')
|
|
82
85
|
} = params;
|
|
83
|
-
|
|
84
|
-
// 构建符合 SetZoneCleanData 格式的数据
|
|
85
|
-
const zoneCleanData = {
|
|
86
|
+
const zoneCleanData = _objectSpread(_objectSpread({
|
|
86
87
|
zones: zones.map(_ref => {
|
|
87
88
|
let {
|
|
88
89
|
points,
|
|
@@ -96,41 +97,44 @@ export const useZoneClean = () => {
|
|
|
96
97
|
};
|
|
97
98
|
}),
|
|
98
99
|
origin,
|
|
99
|
-
suctions
|
|
100
|
-
|
|
100
|
+
suctions
|
|
101
|
+
}, waterPayload), {}, {
|
|
101
102
|
cleanCounts,
|
|
102
103
|
yMops,
|
|
103
|
-
sweepMopModes
|
|
104
|
-
|
|
104
|
+
sweepMopModes,
|
|
105
|
+
routePreferences
|
|
106
|
+
});
|
|
105
107
|
|
|
106
108
|
// 使用 TypeBox Schema 进行参数验证
|
|
107
109
|
const validatedData = validator.validate('set_zone_clean', zoneCleanData);
|
|
110
|
+
const polygons = zones.map(_ref2 => {
|
|
111
|
+
let {
|
|
112
|
+
points
|
|
113
|
+
} = _ref2;
|
|
114
|
+
return pointsToString(points, origin);
|
|
115
|
+
});
|
|
116
|
+
const names = zones.map(_ref3 => {
|
|
117
|
+
let {
|
|
118
|
+
name
|
|
119
|
+
} = _ref3;
|
|
120
|
+
return name || '';
|
|
121
|
+
});
|
|
122
|
+
const commonFields = _objectSpread(_objectSpread({
|
|
123
|
+
suctions: validatedData.suctions
|
|
124
|
+
}, waterPayload), {}, {
|
|
125
|
+
cleanCounts: validatedData.cleanCounts,
|
|
126
|
+
yMops: validatedData.yMops,
|
|
127
|
+
sweepMopModes: validatedData.sweepMopModes,
|
|
128
|
+
routePreferences: validatedData.routePreferences
|
|
129
|
+
});
|
|
108
130
|
if (useMqtt) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
let {
|
|
112
|
-
points
|
|
113
|
-
} = _ref2;
|
|
114
|
-
return pointsToString(points, origin);
|
|
115
|
-
});
|
|
116
|
-
const names = zones.map(_ref3 => {
|
|
117
|
-
let {
|
|
118
|
-
name
|
|
119
|
-
} = _ref3;
|
|
120
|
-
return name || '';
|
|
121
|
-
});
|
|
122
|
-
sendStructuredMessage(ZoneCleanEnum.set, {
|
|
123
|
-
num: zones.length,
|
|
131
|
+
sendStructuredMessage(ZoneCleanEnum.set, _objectSpread(_objectSpread({
|
|
132
|
+
num,
|
|
124
133
|
switchGo: true,
|
|
125
|
-
polygons
|
|
126
|
-
|
|
127
|
-
cisterns: validatedData.cisterns,
|
|
128
|
-
cleanCounts: validatedData.cleanCounts,
|
|
129
|
-
yMops: validatedData.yMops,
|
|
130
|
-
sweepMopModes: validatedData.sweepMopModes,
|
|
134
|
+
polygons
|
|
135
|
+
}, commonFields), {}, {
|
|
131
136
|
names
|
|
132
|
-
}, resolve, error => reject(handleMqttError(error, 'Failed to set zone clean')), ZoneCleanEnum.query
|
|
133
|
-
);
|
|
137
|
+
}), resolve, error => reject(handleMqttError(error, 'Failed to set zone clean')), ZoneCleanEnum.query);
|
|
134
138
|
return;
|
|
135
139
|
}
|
|
136
140
|
|
|
@@ -146,19 +150,7 @@ export const useZoneClean = () => {
|
|
|
146
150
|
}))
|
|
147
151
|
});
|
|
148
152
|
devices.common.model.actions.command_trans.set(command);
|
|
149
|
-
|
|
150
|
-
let {
|
|
151
|
-
points
|
|
152
|
-
} = _ref4;
|
|
153
|
-
return pointsToString(points, origin);
|
|
154
|
-
});
|
|
155
|
-
const names = zones.map(_ref5 => {
|
|
156
|
-
let {
|
|
157
|
-
name
|
|
158
|
-
} = _ref5;
|
|
159
|
-
return name || '';
|
|
160
|
-
});
|
|
161
|
-
resolve({
|
|
153
|
+
resolve(_objectSpread({
|
|
162
154
|
success: true,
|
|
163
155
|
errCode: 0,
|
|
164
156
|
reqType: ZoneCleanEnum.query,
|
|
@@ -166,7 +158,7 @@ export const useZoneClean = () => {
|
|
|
166
158
|
taskId: `${Date.now()}`,
|
|
167
159
|
polygons,
|
|
168
160
|
names
|
|
169
|
-
});
|
|
161
|
+
}, commonFields));
|
|
170
162
|
} catch (error) {
|
|
171
163
|
reject(handleMqttError(error, 'Failed to set zone clean'));
|
|
172
164
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type WaterPreferenceValue = string | number;
|
|
2
|
+
/**
|
|
3
|
+
* 将自定义水量 / 标准水量统一归一化为设备命令需要的数值。
|
|
4
|
+
* 优先使用 waterValue,其次回退到 cistern。
|
|
5
|
+
*/
|
|
6
|
+
export declare const resolveWaterLevel: (waterValue?: WaterPreferenceValue, cistern?: WaterPreferenceValue, fallback?: number) => number;
|
|
7
|
+
export declare const hasPreferenceValue: (value?: WaterPreferenceValue | null) => boolean;
|
|
8
|
+
/**
|
|
9
|
+
* MQTT 水量相关字段是可选的。
|
|
10
|
+
* 当数组里全是空字符串时,认为该字段未设置,避免把空 cisterns/waterValues 发给设备。
|
|
11
|
+
*/
|
|
12
|
+
export declare const normalizeOptionalPreferenceArray: <T extends WaterPreferenceValue>(values?: T[] | undefined) => T[] | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* 自定义水量优先于旧 cistern 水量。
|
|
15
|
+
* 当某一项存在自定义水量时,同位置的 cistern 会被清空;最终空数组字段会被省略。
|
|
16
|
+
*/
|
|
17
|
+
export declare const normalizeWaterPreferencePayload: (cisterns?: WaterPreferenceValue[], waterValues?: WaterPreferenceValue[]) => {
|
|
18
|
+
cisterns?: WaterPreferenceValue[] | undefined;
|
|
19
|
+
waterValues?: WaterPreferenceValue[] | undefined;
|
|
20
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
|
+
import "core-js/modules/esnext.iterator.constructor.js";
|
|
3
|
+
import "core-js/modules/esnext.iterator.some.js";
|
|
4
|
+
import { CISTERN_MAP } from '../constant';
|
|
5
|
+
/**
|
|
6
|
+
* 将自定义水量 / 标准水量统一归一化为设备命令需要的数值。
|
|
7
|
+
* 优先使用 waterValue,其次回退到 cistern。
|
|
8
|
+
*/
|
|
9
|
+
export const resolveWaterLevel = function (waterValue, cistern) {
|
|
10
|
+
var _ref, _resolveValue;
|
|
11
|
+
let fallback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : CISTERN_MAP.closed;
|
|
12
|
+
const resolveValue = value => {
|
|
13
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
if (typeof value !== 'string') {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
if (value in CISTERN_MAP) {
|
|
20
|
+
return CISTERN_MAP[value];
|
|
21
|
+
}
|
|
22
|
+
const parsed = Number(value);
|
|
23
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
24
|
+
};
|
|
25
|
+
return (_ref = (_resolveValue = resolveValue(waterValue)) !== null && _resolveValue !== void 0 ? _resolveValue : resolveValue(cistern)) !== null && _ref !== void 0 ? _ref : fallback;
|
|
26
|
+
};
|
|
27
|
+
export const hasPreferenceValue = value => value !== '' && value !== null && value !== undefined;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* MQTT 水量相关字段是可选的。
|
|
31
|
+
* 当数组里全是空字符串时,认为该字段未设置,避免把空 cisterns/waterValues 发给设备。
|
|
32
|
+
*/
|
|
33
|
+
export const normalizeOptionalPreferenceArray = values => {
|
|
34
|
+
if (!values) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return values.some(hasPreferenceValue) ? values : undefined;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 自定义水量优先于旧 cistern 水量。
|
|
42
|
+
* 当某一项存在自定义水量时,同位置的 cistern 会被清空;最终空数组字段会被省略。
|
|
43
|
+
*/
|
|
44
|
+
export const normalizeWaterPreferencePayload = (cisterns, waterValues) => {
|
|
45
|
+
var _cisterns$length, _waterValues$length;
|
|
46
|
+
const maxLength = Math.max((_cisterns$length = cisterns === null || cisterns === void 0 ? void 0 : cisterns.length) !== null && _cisterns$length !== void 0 ? _cisterns$length : 0, (_waterValues$length = waterValues === null || waterValues === void 0 ? void 0 : waterValues.length) !== null && _waterValues$length !== void 0 ? _waterValues$length : 0);
|
|
47
|
+
if (maxLength === 0) {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
const normalizedCisterns = normalizeOptionalPreferenceArray(Array.from({
|
|
51
|
+
length: maxLength
|
|
52
|
+
}, (_, index) => {
|
|
53
|
+
var _cisterns$index;
|
|
54
|
+
if (hasPreferenceValue(waterValues === null || waterValues === void 0 ? void 0 : waterValues[index])) {
|
|
55
|
+
return '';
|
|
56
|
+
}
|
|
57
|
+
return (_cisterns$index = cisterns === null || cisterns === void 0 ? void 0 : cisterns[index]) !== null && _cisterns$index !== void 0 ? _cisterns$index : '';
|
|
58
|
+
}));
|
|
59
|
+
const normalizedWaterValues = normalizeOptionalPreferenceArray(Array.from({
|
|
60
|
+
length: maxLength
|
|
61
|
+
}, (_, index) => {
|
|
62
|
+
var _waterValues$index;
|
|
63
|
+
return (_waterValues$index = waterValues === null || waterValues === void 0 ? void 0 : waterValues[index]) !== null && _waterValues$index !== void 0 ? _waterValues$index : '';
|
|
64
|
+
}));
|
|
65
|
+
return _objectSpread(_objectSpread({}, normalizedCisterns ? {
|
|
66
|
+
cisterns: normalizedCisterns
|
|
67
|
+
} : {}), normalizedWaterValues ? {
|
|
68
|
+
waterValues: normalizedWaterValues
|
|
69
|
+
} : {});
|
|
70
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ray-js/robot-data-stream",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16-beta.1",
|
|
4
4
|
"description": "扫地机P2P数据流标准化组件",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"files": [
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
"start:wechat": "ray start ./example -t wechat --verbose",
|
|
29
29
|
"start:web": "ray start ./example -t web",
|
|
30
30
|
"prepublishOnly": "yarn build",
|
|
31
|
-
"release-it": "standard-version"
|
|
31
|
+
"release-it": "standard-version",
|
|
32
|
+
"docs:dev": "vitepress dev docs",
|
|
33
|
+
"docs:build": "vitepress build docs"
|
|
32
34
|
},
|
|
33
35
|
"peerDependencies": {
|
|
34
36
|
"@ray-js/log4js": "^0.0.4",
|
|
@@ -53,14 +55,15 @@
|
|
|
53
55
|
"@ray-js/log4js": "^0.0.4",
|
|
54
56
|
"@ray-js/ray": "1.8.0-beta.14",
|
|
55
57
|
"@ray-js/robot-protocol": "^1.0.1",
|
|
58
|
+
"@types/lodash-es": "^4.17.12",
|
|
56
59
|
"core-js": "^3.19.1",
|
|
57
60
|
"eslint-config-tuya-panel": "^0.4.2",
|
|
58
61
|
"husky": "^1.2.0",
|
|
59
62
|
"lint-staged": "^10.2.11",
|
|
60
63
|
"patch-package": "^8.0.0",
|
|
61
|
-
"@types/lodash-es": "^4.17.12",
|
|
62
64
|
"prettier": "^3.8.0",
|
|
63
|
-
"standard-version": "9.3.2"
|
|
65
|
+
"standard-version": "9.3.2",
|
|
66
|
+
"vitepress": "^1.6.4"
|
|
64
67
|
},
|
|
65
68
|
"husky": {
|
|
66
69
|
"hooks": {
|