@ray-js/lamp-schedule-core 1.0.4-beta-15 → 1.0.5-beta.10
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/config/dpCodes.d.ts +18 -1
- package/lib/config/dpCodes.js +34 -1
- package/lib/conflict/index.d.ts +13 -3
- package/lib/conflict/index.js +27 -1
- package/lib/conflict/scheduleDataManager.js +7 -3
- package/lib/conflict/transform.js +2 -1
- package/lib/conflict/type.d.ts +4 -0
- package/lib/dpParser/__test__/rhythms.test.js +7 -2
- package/lib/dpParser/__test__/rhythmsSigmesh.test.d.ts +1 -0
- package/lib/dpParser/__test__/rhythmsSigmesh.test.js +263 -0
- package/lib/dpParser/__test__/sleepSigmesh.test.d.ts +1 -0
- package/lib/dpParser/__test__/sleepSigmesh.test.js +185 -0
- package/lib/dpParser/__test__/wakeupSigmesh.test.js +4 -2
- package/lib/dpParser/brightAdjustData.d.ts +10 -0
- package/lib/dpParser/brightAdjustData.js +43 -0
- package/lib/dpParser/colourDataRaw.d.ts +14 -0
- package/lib/dpParser/colourDataRaw.js +56 -0
- package/lib/dpParser/index.d.ts +4 -1
- package/lib/dpParser/index.js +14 -1
- package/lib/dpParser/rhythms/index.d.ts +9 -0
- package/lib/dpParser/rhythms/index.js +27 -0
- package/lib/dpParser/{rhythms.d.ts → rhythms/rhythmsCommon.d.ts} +2 -3
- package/lib/dpParser/{rhythms.js → rhythms/rhythmsCommon.js} +6 -9
- package/lib/dpParser/rhythms/rhythmsSigmesh.d.ts +17 -0
- package/lib/dpParser/rhythms/rhythmsSigmesh.js +143 -0
- package/lib/dpParser/sleep/index.d.ts +1 -2
- package/lib/dpParser/sleep/index.js +13 -12
- package/lib/dpParser/sleep/sleepSigmesh.d.ts +20 -0
- package/lib/dpParser/sleep/sleepSigmesh.js +88 -97
- package/lib/dpParser/stripLocalTimer.d.ts +41 -0
- package/lib/dpParser/stripLocalTimer.js +275 -0
- package/lib/dpParser/wakeup/index.d.ts +0 -2
- package/lib/dpParser/wakeup/index.js +12 -16
- package/lib/dpParser/wakeup/wakeupSigmesh.js +10 -1
- package/lib/hooks/__test__/useBaseLightDp.test.js +10 -2
- package/lib/hooks/__test__/useTimerDp.test.js +5 -5
- package/lib/hooks/useBaseLightDp.d.ts +7 -2
- package/lib/hooks/useBaseLightDp.js +34 -16
- package/lib/hooks/useCommonSupport.js +19 -0
- package/lib/hooks/useCycleDp.js +2 -0
- package/lib/hooks/useDPByProtocol.d.ts +11 -0
- package/lib/hooks/useRandomDp.js +2 -0
- package/lib/hooks/useRhythmsDp.d.ts +3 -3
- package/lib/hooks/useRhythmsDp.js +2 -0
- package/lib/hooks/useSleepDp.js +2 -0
- package/lib/hooks/useTimerDp.d.ts +4 -3
- package/lib/hooks/useTimerDp.js +21 -4
- package/lib/hooks/useTimerReportDp.js +1 -0
- package/lib/hooks/useWakeUpDp.js +2 -0
- package/lib/hooks/useWakeupDp.js +2 -0
- package/lib/types/rhythms.d.ts +16 -0
- package/lib/types/rhythms.js +3 -1
- package/lib/types/timer.d.ts +11 -0
- package/lib/utils/ScheduleDataSync.js +1 -0
- package/lib/utils/ScheduleSupport.d.ts +4 -0
- package/lib/utils/ScheduleSupport.js +23 -3
- package/lib/utils/__test__/ScheduleDataSync.test.d.ts +1 -0
- package/lib/utils/__test__/ScheduleDataSync.test.js +2 -0
- package/lib/utils/__test__/objectToId.test.d.ts +1 -0
- package/lib/utils/__test__/objectToId.test.js +98 -0
- package/lib/utils/getDPByProtocol.d.ts +22 -0
- package/lib/utils/matterDeviceUtils.d.ts +22 -0
- package/package.json +1 -1
package/lib/config/dpCodes.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export declare const scheduleDpCodes: {
|
|
|
19
19
|
readonly CYCLE_TIMING: "cycle_timing";
|
|
20
20
|
/** 通用本地定时 */
|
|
21
21
|
readonly LOCAL_TIMER: "local_timer";
|
|
22
|
-
/** 灯带本地定时 */
|
|
22
|
+
/** 灯带本地定时 Beacon */
|
|
23
23
|
readonly STRIP_LOCAL_TIMER: "strip_local_timer";
|
|
24
24
|
/** 定时同步 一般用于 Beacon */
|
|
25
25
|
readonly TIMER_SYNC: "timer_sync";
|
|
@@ -33,6 +33,23 @@ export declare const scheduleDpCodes: {
|
|
|
33
33
|
readonly BRIGHTNESS: "bright_value";
|
|
34
34
|
/** 彩光 */
|
|
35
35
|
readonly COLOUR_DATA: "colour_data";
|
|
36
|
+
/** 彩光原始数据 Beacon */
|
|
37
|
+
readonly COLOUR_DATA_RAW: "colour_data_raw";
|
|
38
|
+
/** 亮度调节 Beacon */
|
|
39
|
+
readonly BRIGHT_ADJUST_DATA: "bright_adjust_data";
|
|
40
|
+
/** ------ 风扇灯 Beacon DP -------- */
|
|
41
|
+
/** 风扇模式 Beacon */
|
|
42
|
+
readonly FAN_MODE: "fan_mode";
|
|
43
|
+
/** 风向 Beacon */
|
|
44
|
+
readonly FAN_DIRECTION: "fan_direction";
|
|
45
|
+
/** 摇头 Beacon */
|
|
46
|
+
readonly SHAKE: "shake";
|
|
47
|
+
readonly WHITE_SWITCH: "white_switch";
|
|
48
|
+
readonly COLOUR_SWITCH: "colour_switch";
|
|
49
|
+
readonly FAN_SWITCH: "fan_switch";
|
|
50
|
+
readonly FAN_SPEED: "fan_speed";
|
|
51
|
+
readonly FAN_BEEP: "fan_beep";
|
|
52
|
+
readonly STRIP_SCENE: "strip_scene";
|
|
36
53
|
/** 模式 */
|
|
37
54
|
readonly WORK_MODE: "work_mode";
|
|
38
55
|
};
|
package/lib/config/dpCodes.js
CHANGED
|
@@ -22,7 +22,7 @@ export const scheduleDpCodes = {
|
|
|
22
22
|
[EScheduleType.CYCLE_TIMING]: 'cycle_timing',
|
|
23
23
|
/** 通用本地定时 */
|
|
24
24
|
[EScheduleType.LOCAL_TIMER]: 'local_timer',
|
|
25
|
-
/** 灯带本地定时 */
|
|
25
|
+
/** 灯带本地定时 Beacon */
|
|
26
26
|
[EScheduleType.STRIP_LOCAL_TIMER]: 'strip_local_timer',
|
|
27
27
|
/** 定时同步 一般用于 Beacon */
|
|
28
28
|
[EScheduleType.TIMER_SYNC]: 'timer_sync',
|
|
@@ -36,6 +36,39 @@ export const scheduleDpCodes = {
|
|
|
36
36
|
BRIGHTNESS: 'bright_value',
|
|
37
37
|
/** 彩光 */
|
|
38
38
|
COLOUR_DATA: 'colour_data',
|
|
39
|
+
/** 彩光原始数据 Beacon */
|
|
40
|
+
COLOUR_DATA_RAW: 'colour_data_raw',
|
|
41
|
+
/** 亮度调节 Beacon */
|
|
42
|
+
BRIGHT_ADJUST_DATA: 'bright_adjust_data',
|
|
43
|
+
/** ------ 风扇灯 Beacon DP -------- */
|
|
44
|
+
/** 风扇模式 Beacon */
|
|
45
|
+
FAN_MODE: 'fan_mode',
|
|
46
|
+
// 枚举值: fresh, nature
|
|
47
|
+
/** 风向 Beacon */
|
|
48
|
+
FAN_DIRECTION: 'fan_direction',
|
|
49
|
+
// 枚举值: forward, reverse
|
|
50
|
+
/** 摇头 Beacon */
|
|
51
|
+
SHAKE: 'shake',
|
|
52
|
+
// boolean
|
|
53
|
+
// 主灯开关
|
|
54
|
+
WHITE_SWITCH: 'white_switch',
|
|
55
|
+
// boolean
|
|
56
|
+
// 氛围灯开关
|
|
57
|
+
COLOUR_SWITCH: 'colour_switch',
|
|
58
|
+
// boolean
|
|
59
|
+
// 风扇开关
|
|
60
|
+
FAN_SWITCH: 'fan_switch',
|
|
61
|
+
// boolean
|
|
62
|
+
// 风扇速度
|
|
63
|
+
FAN_SPEED: 'fan_speed',
|
|
64
|
+
// number 1-100
|
|
65
|
+
// 声音
|
|
66
|
+
FAN_BEEP: 'fan_beep',
|
|
67
|
+
// boolean
|
|
68
|
+
// 情景
|
|
69
|
+
STRIP_SCENE: 'strip_scene',
|
|
70
|
+
// beacon mesh情景功能,支持速度,亮度编辑
|
|
71
|
+
|
|
39
72
|
/** 模式 */
|
|
40
73
|
WORK_MODE: 'work_mode'
|
|
41
74
|
};
|
package/lib/conflict/index.d.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { TConflictRes, ConflictRule, TFoldScheduleList, TSchedule } from './type';
|
|
1
|
+
import { TConflictRes, ConflictRule, TFoldScheduleList, TSchedule, PartialSchedule } from './type';
|
|
2
|
+
import { EScheduleFunctionType } from '../types';
|
|
2
3
|
/**
|
|
3
4
|
* 冲突检测
|
|
4
5
|
*/
|
|
5
6
|
export declare class Conflict {
|
|
6
7
|
static rule: ConflictRule;
|
|
7
8
|
static isInit: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* 获取冲突检测数据
|
|
11
|
+
*/
|
|
12
|
+
static getData(): Record<string, any>[];
|
|
8
13
|
/**
|
|
9
14
|
* 添加冲突规则
|
|
10
15
|
*/
|
|
@@ -18,13 +23,18 @@ export declare class Conflict {
|
|
|
18
23
|
* conflictList - 具体的冲突检测结果数组
|
|
19
24
|
*/
|
|
20
25
|
static add(current: TSchedule): TConflictRes;
|
|
26
|
+
/**
|
|
27
|
+
* 删除指定类型的日程
|
|
28
|
+
* @param type - 日程类型
|
|
29
|
+
*/
|
|
30
|
+
static removeByType(type: EScheduleFunctionType): void;
|
|
21
31
|
/**
|
|
22
32
|
* 删除日程
|
|
23
33
|
*
|
|
24
|
-
* @param current - 待添加的日程对象,类型为 Schedule
|
|
34
|
+
* @param current - 待添加的日程对象,类型为 Schedule, 如果没有 detail 则认为是删除指定类型的日程
|
|
25
35
|
* @returns void
|
|
26
36
|
*/
|
|
27
|
-
static remove(current: TSchedule): void;
|
|
37
|
+
static remove(current: TSchedule | PartialSchedule): void;
|
|
28
38
|
/**
|
|
29
39
|
* 更新新日程时检测冲突
|
|
30
40
|
*
|
package/lib/conflict/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
2
|
import "core-js/modules/es.json.stringify.js";
|
|
3
3
|
import "core-js/modules/esnext.iterator.constructor.js";
|
|
4
|
+
import "core-js/modules/esnext.iterator.filter.js";
|
|
4
5
|
import "core-js/modules/esnext.iterator.find.js";
|
|
5
6
|
import { scheduleLogger } from '../utils/ScheduleLogger';
|
|
6
7
|
import { checkConflicts } from './ConflictResolver';
|
|
@@ -12,6 +13,13 @@ import { EScheduleFunctionType } from '../types';
|
|
|
12
13
|
* 冲突检测
|
|
13
14
|
*/
|
|
14
15
|
export class Conflict {
|
|
16
|
+
/**
|
|
17
|
+
* 获取冲突检测数据
|
|
18
|
+
*/
|
|
19
|
+
static getData() {
|
|
20
|
+
return ScheduleDataManager.getInstance().getData();
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
/**
|
|
16
24
|
* 添加冲突规则
|
|
17
25
|
*/
|
|
@@ -50,13 +58,31 @@ export class Conflict {
|
|
|
50
58
|
};
|
|
51
59
|
}
|
|
52
60
|
|
|
61
|
+
/**
|
|
62
|
+
* 删除指定类型的日程
|
|
63
|
+
* @param type - 日程类型
|
|
64
|
+
*/
|
|
65
|
+
static removeByType(type) {
|
|
66
|
+
const instance = ScheduleDataManager.getInstance();
|
|
67
|
+
const list = instance.getData() || [];
|
|
68
|
+
const filteredList = list.filter(item => item.type !== type);
|
|
69
|
+
ScheduleDataManager.list = filteredList;
|
|
70
|
+
}
|
|
71
|
+
|
|
53
72
|
/**
|
|
54
73
|
* 删除日程
|
|
55
74
|
*
|
|
56
|
-
* @param current - 待添加的日程对象,类型为 Schedule
|
|
75
|
+
* @param current - 待添加的日程对象,类型为 Schedule, 如果没有 detail 则认为是删除指定类型的日程
|
|
57
76
|
* @returns void
|
|
58
77
|
*/
|
|
59
78
|
static remove(current) {
|
|
79
|
+
if (!current) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (current.type && !current.detail) {
|
|
83
|
+
this.removeByType(current.type);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
60
86
|
const [cur] = transScheduleListToConflictList([current]);
|
|
61
87
|
scheduleLogger.debug('Conflict.remove cur, current:', cur, current);
|
|
62
88
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "core-js/modules/es.json.stringify.js";
|
|
1
2
|
import "core-js/modules/esnext.iterator.constructor.js";
|
|
2
3
|
import "core-js/modules/esnext.iterator.filter.js";
|
|
3
4
|
import "core-js/modules/esnext.iterator.for-each.js";
|
|
@@ -63,10 +64,11 @@ export class ScheduleDataManager {
|
|
|
63
64
|
}
|
|
64
65
|
const idsToAdd = new Set(itemsMap.keys());
|
|
65
66
|
const filteredList = ScheduleDataManager.list.filter(item => !idsToAdd.has(item.id));
|
|
66
|
-
|
|
67
|
+
const newList = [...filteredList, ...itemsMap.values()].filter(item => {
|
|
67
68
|
var _item$data;
|
|
68
69
|
return (item === null || item === void 0 || (_item$data = item.data) === null || _item$data === void 0 ? void 0 : _item$data.status) === true;
|
|
69
70
|
});
|
|
71
|
+
ScheduleDataManager.list = newList;
|
|
70
72
|
}
|
|
71
73
|
clearData() {
|
|
72
74
|
scheduleLogger.debug('ScheduleDataManager.clearData pre', ScheduleDataManager.list);
|
|
@@ -76,12 +78,14 @@ export class ScheduleDataManager {
|
|
|
76
78
|
if (!current) {
|
|
77
79
|
return;
|
|
78
80
|
}
|
|
79
|
-
scheduleLogger.debug('ScheduleDataManager.deleteData pre', current);
|
|
81
|
+
scheduleLogger.debug('ScheduleDataManager.deleteData pre', current, JSON.stringify(ScheduleDataManager.list));
|
|
80
82
|
const index = ScheduleDataManager.list.findIndex(item => item.id === current.id);
|
|
81
83
|
if (index !== -1) {
|
|
82
84
|
ScheduleDataManager.list.splice(index, 1);
|
|
85
|
+
scheduleLogger.debug('ScheduleDataManager.deleteData post', JSON.stringify(ScheduleDataManager.list));
|
|
86
|
+
} else {
|
|
87
|
+
scheduleLogger.debug('ScheduleDataManager.deleteData post not found', JSON.stringify(ScheduleDataManager.list));
|
|
83
88
|
}
|
|
84
|
-
scheduleLogger.debug('ScheduleDataManager.deleteData post', ScheduleDataManager.list);
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
// 添加remove方法作为deleteData的别名,用于测试
|
|
@@ -18,6 +18,7 @@ import { scheduleLogger } from '../utils/ScheduleLogger';
|
|
|
18
18
|
export const timerDataToSchedule = data => {
|
|
19
19
|
var _data$status;
|
|
20
20
|
scheduleLogger.debug('timerDataToSchedule:', data);
|
|
21
|
+
const stableTimerId = data.timerId || (data === null || data === void 0 ? void 0 : data.id);
|
|
21
22
|
const _data = {
|
|
22
23
|
status: (_data$status = data.status) !== null && _data$status !== void 0 ? _data$status : false,
|
|
23
24
|
weeks: data.loops.split('').map(item => Number(item)),
|
|
@@ -26,7 +27,7 @@ export const timerDataToSchedule = data => {
|
|
|
26
27
|
dps: data.dps,
|
|
27
28
|
aliasName: data.aliasName || '',
|
|
28
29
|
isAppPush: data.isAppPush || false,
|
|
29
|
-
id:
|
|
30
|
+
id: stableTimerId
|
|
30
31
|
};
|
|
31
32
|
const timerId = `${EScheduleFunctionType.TIMER}_${_data.id}`;
|
|
32
33
|
return {
|
package/lib/conflict/type.d.ts
CHANGED
|
@@ -52,5 +52,9 @@ export type TSchedule = {
|
|
|
52
52
|
type: EScheduleFunctionType;
|
|
53
53
|
detail: ScheduleNodeType[];
|
|
54
54
|
};
|
|
55
|
+
export type PartialSchedule = {
|
|
56
|
+
type: EScheduleFunctionType;
|
|
57
|
+
detail?: ScheduleNodeType[];
|
|
58
|
+
};
|
|
55
59
|
export type TFoldScheduleList = TSchedule[];
|
|
56
60
|
export {};
|
|
@@ -71,7 +71,9 @@ describe('RhythmParser', () => {
|
|
|
71
71
|
expect(result).toBe('');
|
|
72
72
|
});
|
|
73
73
|
it('should return empty string when support instance is missing', () => {
|
|
74
|
-
getSupportIns
|
|
74
|
+
// getSupportIns is called in both the dispatcher and the common formatter,
|
|
75
|
+
// so we need to mock it for all calls within this test
|
|
76
|
+
getSupportIns.mockReturnValue(null);
|
|
75
77
|
const result = rhythmParser.formatter({
|
|
76
78
|
version: 1,
|
|
77
79
|
power: true,
|
|
@@ -91,9 +93,12 @@ describe('RhythmParser', () => {
|
|
|
91
93
|
});
|
|
92
94
|
expect(result).toBe('');
|
|
93
95
|
expect(scheduleLogger.error).toHaveBeenCalledWith('协议解析 rhythms formatter =====', '数据为空');
|
|
96
|
+
// Restore default mock
|
|
97
|
+
getSupportIns.mockReturnValue(createSupport(true));
|
|
94
98
|
});
|
|
95
99
|
it('should force temperature to 100 when device does not support temperature DP', () => {
|
|
96
|
-
getSupportIns
|
|
100
|
+
// getSupportIns is called in both the dispatcher and the common formatter
|
|
101
|
+
getSupportIns.mockReturnValue(createSupport(false));
|
|
97
102
|
const result = rhythmParser.formatter({
|
|
98
103
|
version: 0,
|
|
99
104
|
power: true,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import "core-js/modules/esnext.iterator.constructor.js";
|
|
2
|
+
import "core-js/modules/esnext.iterator.map.js";
|
|
3
|
+
jest.mock('@ray-js/panel-sdk/lib/utils', () => ({
|
|
4
|
+
generateDpStrStep: jest.fn(str => {
|
|
5
|
+
let index = 0;
|
|
6
|
+
return len => {
|
|
7
|
+
const segment = str.slice(index, index + len);
|
|
8
|
+
index += len;
|
|
9
|
+
return {
|
|
10
|
+
value: parseInt(segment, 16)
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
}),
|
|
14
|
+
numToHexString: jest.fn(num => num.toString(16).padStart(2, '0'))
|
|
15
|
+
}));
|
|
16
|
+
jest.mock('../../utils/ScheduleLogger', () => {
|
|
17
|
+
const logger = {
|
|
18
|
+
debug: jest.fn(),
|
|
19
|
+
warn: jest.fn(),
|
|
20
|
+
error: jest.fn(),
|
|
21
|
+
info: jest.fn()
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
scheduleLogger: logger
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
jest.mock('../../hooks/useCommonSupport', () => ({
|
|
28
|
+
getSupportIns: jest.fn()
|
|
29
|
+
}));
|
|
30
|
+
jest.mock('../../utils/getDPByProtocol', () => ({
|
|
31
|
+
getDPByProtocol: jest.fn().mockReturnValue({
|
|
32
|
+
TEMPERATURE: 'temp_value'
|
|
33
|
+
})
|
|
34
|
+
}));
|
|
35
|
+
import { rhythmParserSigmesh, RhythmSigmesh } from '../rhythms/rhythmsSigmesh';
|
|
36
|
+
const {
|
|
37
|
+
getSupportIns
|
|
38
|
+
} = require('../../hooks/useCommonSupport');
|
|
39
|
+
const createSupport = function () {
|
|
40
|
+
let isSupportTemp = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
41
|
+
return {
|
|
42
|
+
isSigMeshDevice: jest.fn().mockReturnValue(true),
|
|
43
|
+
isSupportDp: jest.fn().mockImplementation(dp => {
|
|
44
|
+
if (dp === 'temp_value') {
|
|
45
|
+
return isSupportTemp;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
})
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 构建 sigmesh rhythms DP 字符串
|
|
54
|
+
* 格式: version(2) + power(2) + mode(2) + weeks(2) + paramMode(2) + number(2)
|
|
55
|
+
* + nodes[valid(2) + hour(2) + minute(2) + brightness(2) + temperature(2)]
|
|
56
|
+
*/
|
|
57
|
+
const buildDpStr = function (nodes) {
|
|
58
|
+
let version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
|
|
59
|
+
let power = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
|
|
60
|
+
let mode = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
|
|
61
|
+
let weeks = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0x7f;
|
|
62
|
+
let paramMode = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
|
|
63
|
+
const header = [version, power, mode, weeks, paramMode, nodes.length].map(v => v.toString(16).padStart(2, '0')).join('');
|
|
64
|
+
const body = nodes.map(node => [node.valid, node.hour, node.minute, node.brightness, node.temperature].map(v => v.toString(16).padStart(2, '0')).join('')).join('');
|
|
65
|
+
return `${header}${body}`;
|
|
66
|
+
};
|
|
67
|
+
describe('RhythmSigmesh parser & formatter', () => {
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
jest.clearAllMocks();
|
|
70
|
+
getSupportIns.mockReturnValue(createSupport(true));
|
|
71
|
+
});
|
|
72
|
+
describe('parser', () => {
|
|
73
|
+
it('returns default value when dp string is empty', () => {
|
|
74
|
+
const result = rhythmParserSigmesh.parser('');
|
|
75
|
+
expect(result).toEqual(new RhythmSigmesh().defaultValue);
|
|
76
|
+
});
|
|
77
|
+
it('returns default value when dp string length is invalid', () => {
|
|
78
|
+
// Too short
|
|
79
|
+
const result = rhythmParserSigmesh.parser('010100');
|
|
80
|
+
expect(result).toEqual(new RhythmSigmesh().defaultValue);
|
|
81
|
+
});
|
|
82
|
+
it('returns default value when node data is not aligned', () => {
|
|
83
|
+
// Header (12 chars) + partial node data (not multiple of 10)
|
|
84
|
+
// 14 chars header + 2 chars body (not 10)
|
|
85
|
+
const result = rhythmParserSigmesh.parser('01010000007f0104' + '01');
|
|
86
|
+
expect(result).toEqual(new RhythmSigmesh().defaultValue);
|
|
87
|
+
});
|
|
88
|
+
it('parses valid dp string with 4 nodes', () => {
|
|
89
|
+
const dpStr = buildDpStr([{
|
|
90
|
+
valid: 1,
|
|
91
|
+
hour: 6,
|
|
92
|
+
minute: 30,
|
|
93
|
+
brightness: 100,
|
|
94
|
+
temperature: 50
|
|
95
|
+
}, {
|
|
96
|
+
valid: 1,
|
|
97
|
+
hour: 11,
|
|
98
|
+
minute: 30,
|
|
99
|
+
brightness: 100,
|
|
100
|
+
temperature: 100
|
|
101
|
+
}, {
|
|
102
|
+
valid: 1,
|
|
103
|
+
hour: 17,
|
|
104
|
+
minute: 0,
|
|
105
|
+
brightness: 70,
|
|
106
|
+
temperature: 50
|
|
107
|
+
}, {
|
|
108
|
+
valid: 1,
|
|
109
|
+
hour: 20,
|
|
110
|
+
minute: 30,
|
|
111
|
+
brightness: 70,
|
|
112
|
+
temperature: 50
|
|
113
|
+
}]);
|
|
114
|
+
const result = rhythmParserSigmesh.parser(dpStr);
|
|
115
|
+
expect(result.version).toBe(1);
|
|
116
|
+
expect(result.power).toBe(true);
|
|
117
|
+
expect(result.mode).toBe(0);
|
|
118
|
+
expect(result.paramMode).toBe(1);
|
|
119
|
+
expect(result.number).toBe(4);
|
|
120
|
+
expect(result.rhythms).toHaveLength(4);
|
|
121
|
+
expect(result.rhythms[0]).toMatchObject({
|
|
122
|
+
power: true,
|
|
123
|
+
hour: 6,
|
|
124
|
+
minute: 30,
|
|
125
|
+
brightness: 100,
|
|
126
|
+
temperature: 50
|
|
127
|
+
});
|
|
128
|
+
expect(result.rhythms[3]).toMatchObject({
|
|
129
|
+
power: true,
|
|
130
|
+
hour: 20,
|
|
131
|
+
minute: 30,
|
|
132
|
+
brightness: 70,
|
|
133
|
+
temperature: 50
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
it('parses power=false correctly', () => {
|
|
137
|
+
const dpStr = buildDpStr([{
|
|
138
|
+
valid: 0,
|
|
139
|
+
hour: 8,
|
|
140
|
+
minute: 0,
|
|
141
|
+
brightness: 50,
|
|
142
|
+
temperature: 30
|
|
143
|
+
}], 1, 0);
|
|
144
|
+
const result = rhythmParserSigmesh.parser(dpStr);
|
|
145
|
+
expect(result.power).toBe(false);
|
|
146
|
+
expect(result.rhythms[0].power).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('formatter', () => {
|
|
150
|
+
it('returns empty string when data is null', () => {
|
|
151
|
+
const result = rhythmParserSigmesh.formatter(null);
|
|
152
|
+
expect(result).toBe('');
|
|
153
|
+
});
|
|
154
|
+
it('returns empty string when support is null', () => {
|
|
155
|
+
getSupportIns.mockReturnValueOnce(null);
|
|
156
|
+
const result = rhythmParserSigmesh.formatter({
|
|
157
|
+
version: 1,
|
|
158
|
+
power: true,
|
|
159
|
+
mode: 0,
|
|
160
|
+
weeks: [1, 1, 1, 1, 1, 1, 1, 0],
|
|
161
|
+
paramMode: 1,
|
|
162
|
+
number: 1,
|
|
163
|
+
rhythms: [{
|
|
164
|
+
power: true,
|
|
165
|
+
hour: 6,
|
|
166
|
+
minute: 30,
|
|
167
|
+
brightness: 100,
|
|
168
|
+
temperature: 50
|
|
169
|
+
}]
|
|
170
|
+
});
|
|
171
|
+
expect(result).toBe('');
|
|
172
|
+
});
|
|
173
|
+
it('formats valid data to dp string', () => {
|
|
174
|
+
const result = rhythmParserSigmesh.formatter({
|
|
175
|
+
version: 1,
|
|
176
|
+
power: true,
|
|
177
|
+
mode: 0,
|
|
178
|
+
weeks: [1, 1, 1, 1, 1, 1, 1, 0],
|
|
179
|
+
paramMode: 1,
|
|
180
|
+
number: 2,
|
|
181
|
+
rhythms: [{
|
|
182
|
+
power: true,
|
|
183
|
+
hour: 6,
|
|
184
|
+
minute: 30,
|
|
185
|
+
brightness: 100,
|
|
186
|
+
temperature: 50
|
|
187
|
+
}, {
|
|
188
|
+
power: false,
|
|
189
|
+
hour: 20,
|
|
190
|
+
minute: 0,
|
|
191
|
+
brightness: 70,
|
|
192
|
+
temperature: 30
|
|
193
|
+
}]
|
|
194
|
+
});
|
|
195
|
+
const expected = buildDpStr([{
|
|
196
|
+
valid: 1,
|
|
197
|
+
hour: 6,
|
|
198
|
+
minute: 30,
|
|
199
|
+
brightness: 100,
|
|
200
|
+
temperature: 50
|
|
201
|
+
}, {
|
|
202
|
+
valid: 0,
|
|
203
|
+
hour: 20,
|
|
204
|
+
minute: 0,
|
|
205
|
+
brightness: 70,
|
|
206
|
+
temperature: 30
|
|
207
|
+
}], 1, 1, 0, 0x7f, 1);
|
|
208
|
+
expect(result).toBe(expected);
|
|
209
|
+
});
|
|
210
|
+
it('forces temperature to 100 when device does not support temperature DP', () => {
|
|
211
|
+
getSupportIns.mockReturnValueOnce(createSupport(false));
|
|
212
|
+
const result = rhythmParserSigmesh.formatter({
|
|
213
|
+
version: 1,
|
|
214
|
+
power: true,
|
|
215
|
+
mode: 0,
|
|
216
|
+
weeks: [1, 1, 1, 1, 1, 1, 1, 0],
|
|
217
|
+
paramMode: 1,
|
|
218
|
+
number: 1,
|
|
219
|
+
rhythms: [{
|
|
220
|
+
power: true,
|
|
221
|
+
hour: 6,
|
|
222
|
+
minute: 0,
|
|
223
|
+
brightness: 50,
|
|
224
|
+
temperature: 30
|
|
225
|
+
}]
|
|
226
|
+
});
|
|
227
|
+
// temperature should be 100 (0x64), not 30 (0x1e)
|
|
228
|
+
expect(result.slice(-2)).toBe('64');
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
describe('round-trip', () => {
|
|
232
|
+
it('round-trips dp string through parser and formatter', () => {
|
|
233
|
+
const dpStr = buildDpStr([{
|
|
234
|
+
valid: 1,
|
|
235
|
+
hour: 6,
|
|
236
|
+
minute: 30,
|
|
237
|
+
brightness: 100,
|
|
238
|
+
temperature: 50
|
|
239
|
+
}, {
|
|
240
|
+
valid: 1,
|
|
241
|
+
hour: 11,
|
|
242
|
+
minute: 30,
|
|
243
|
+
brightness: 100,
|
|
244
|
+
temperature: 100
|
|
245
|
+
}, {
|
|
246
|
+
valid: 1,
|
|
247
|
+
hour: 17,
|
|
248
|
+
minute: 0,
|
|
249
|
+
brightness: 70,
|
|
250
|
+
temperature: 50
|
|
251
|
+
}, {
|
|
252
|
+
valid: 1,
|
|
253
|
+
hour: 20,
|
|
254
|
+
minute: 30,
|
|
255
|
+
brightness: 70,
|
|
256
|
+
temperature: 50
|
|
257
|
+
}]);
|
|
258
|
+
const parsed = rhythmParserSigmesh.parser(dpStr);
|
|
259
|
+
const formatted = rhythmParserSigmesh.formatter(parsed);
|
|
260
|
+
expect(formatted).toBe(dpStr);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|