@ledvance/group-ui-biz-bundle 1.0.129 → 1.0.131
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/package.json +1 -1
- package/src/modules/mood/RecommendMoodItem.tsx +1 -1
- package/src/modules/mood_new/RecommendMoodItem.tsx +1 -1
- package/src/modules/{swithInching → switchInching}/Router.ts +1 -1
- package/src/modules/switchInching/SwitchInching.tsx +203 -0
- package/src/modules/switchInching/SwitchInchingAction.ts +60 -0
- package/src/modules/timeSchedule/Interface.ts +9 -11
- package/src/modules/timeSchedule/TimeScheduleActions.ts +7 -1
- package/src/modules/timeSchedule/TimeScheduleDetailPage.tsx +5 -4
- package/src/modules/timeSchedule/TimeSchedulePage.tsx +1 -0
- package/src/modules/timeSchedule/components/ManuaSettings.tsx +28 -1
- package/src/modules/timer/TimerAction.ts +4 -1
- package/src/modules/swithInching/SwithInching.tsx +0 -172
- package/src/modules/swithInching/SwithInchingAction.ts +0 -32
package/package.json
CHANGED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import React, {useEffect, useMemo, useRef, useState} from "react";
|
|
2
|
+
import {ScrollView, StyleSheet, Text, View} from "react-native";
|
|
3
|
+
import Page from "@ledvance/base/src/components/Page";
|
|
4
|
+
import {useUAGroupInfo} from "@ledvance/base/src/models/modules/NativePropsSlice";
|
|
5
|
+
import {useNavigation} from '@react-navigation/native'
|
|
6
|
+
import {SwitchButton, Utils} from "tuya-panel-kit";
|
|
7
|
+
import LdvPickerView from "@ledvance/base/src/components/ldvPickerView";
|
|
8
|
+
import I18n from '@ledvance/base/src/i18n'
|
|
9
|
+
import {defSwitchInching, SwitchInchingItem, SwitchInchingPageParams, useSwitchInching} from "./SwitchInchingAction";
|
|
10
|
+
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
11
|
+
import Segmented from "@ledvance/base/src/components/Segmented";
|
|
12
|
+
import Spacer from "@ledvance/base/src/components/Spacer";
|
|
13
|
+
import { useParams } from "@ledvance/base/src/hooks/Hooks";
|
|
14
|
+
|
|
15
|
+
const { convertX: cx } = Utils.RatioUtils
|
|
16
|
+
const { withTheme } = Utils.ThemeUtils
|
|
17
|
+
|
|
18
|
+
const SwitchInching = (props: { theme?: ThemeType }) => {
|
|
19
|
+
const params = useParams<SwitchInchingPageParams>()
|
|
20
|
+
const uaGroupInfo = useUAGroupInfo()
|
|
21
|
+
const navigation = useNavigation()
|
|
22
|
+
const [switchInching, setSwitchInching] = useSwitchInching()
|
|
23
|
+
const timeRef = useRef({
|
|
24
|
+
minute: '00',
|
|
25
|
+
second: '00',
|
|
26
|
+
})
|
|
27
|
+
const [state, setState] = useState({
|
|
28
|
+
loading: false,
|
|
29
|
+
channel: 0,
|
|
30
|
+
switchInchingItem: defSwitchInching[0]
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const updateState = (newState: Partial<typeof state>) => {
|
|
34
|
+
setState(prev => ({ ...prev, ...newState }));
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const updateTime = (minute?: string, second?: string) => {
|
|
38
|
+
if (minute !== undefined) timeRef.current.minute = minute;
|
|
39
|
+
if (second !== undefined) timeRef.current.second = second;
|
|
40
|
+
|
|
41
|
+
if (!Number(timeRef.current.minute) && Number(timeRef.current.second) < 1) {
|
|
42
|
+
timeRef.current.second = '01';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (Number(timeRef.current.minute) === 60) {
|
|
46
|
+
timeRef.current.second = '00';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const time = Number(timeRef.current.minute) * 60 + Number(timeRef.current.second);
|
|
50
|
+
const newSwitchInchingItem = {
|
|
51
|
+
...state.switchInchingItem,
|
|
52
|
+
time
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
updateState({ switchInchingItem: newSwitchInchingItem });
|
|
56
|
+
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const switchInchingItem = useMemo(() => {
|
|
60
|
+
return switchInching.find(item => item.channel === state.channel) ?? { ...defSwitchInching[0], channel: state.channel }
|
|
61
|
+
}, [state.channel, JSON.stringify(switchInching)])
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const { minute, second } = formateValue(switchInchingItem)
|
|
65
|
+
timeRef.current = { minute, second }
|
|
66
|
+
updateState({ switchInchingItem })
|
|
67
|
+
}, [switchInchingItem])
|
|
68
|
+
|
|
69
|
+
const saveInchingConfig = async (item: SwitchInchingItem) => {
|
|
70
|
+
const updatedSwitchInching = [...switchInching];
|
|
71
|
+
const existingItemIndex = updatedSwitchInching.findIndex(item => item.channel === state.channel);
|
|
72
|
+
if (existingItemIndex === -1) {
|
|
73
|
+
updatedSwitchInching.push(item);
|
|
74
|
+
} else {
|
|
75
|
+
updatedSwitchInching[existingItemIndex] = item;
|
|
76
|
+
}
|
|
77
|
+
await setSwitchInching(updatedSwitchInching)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const formateValue = (switchInchingItem: SwitchInchingItem) => {
|
|
81
|
+
const minute = Math.floor((switchInchingItem.time || 0) / 60).toString().padStart(2, '0')
|
|
82
|
+
const second = ((switchInchingItem.time || 0) % 60).toString().padStart(2, '0')
|
|
83
|
+
return { minute, second }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const styles = StyleSheet.create({
|
|
87
|
+
switchContainer: {
|
|
88
|
+
backgroundColor: props.theme?.container.background,
|
|
89
|
+
marginHorizontal: cx(24),
|
|
90
|
+
padding: cx(10),
|
|
91
|
+
borderRadius: cx(6),
|
|
92
|
+
flexDirection: 'column',
|
|
93
|
+
},
|
|
94
|
+
switchCardContainer: {
|
|
95
|
+
paddingVertical: cx(4),
|
|
96
|
+
paddingHorizontal: cx(10),
|
|
97
|
+
backgroundColor: props.theme?.global.background,
|
|
98
|
+
flexDirection: 'row',
|
|
99
|
+
justifyContent: 'center',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
borderRadius: cx(6),
|
|
102
|
+
},
|
|
103
|
+
switchCardTitle: {
|
|
104
|
+
color: props.theme?.global.fontColor,
|
|
105
|
+
fontSize: cx(14),
|
|
106
|
+
flex: 1,
|
|
107
|
+
fontWeight: '400',
|
|
108
|
+
},
|
|
109
|
+
switchDescription: {
|
|
110
|
+
color: props.theme?.global.fontColor,
|
|
111
|
+
flexWrap: 'wrap',
|
|
112
|
+
fontSize: cx(12),
|
|
113
|
+
marginTop: cx(4),
|
|
114
|
+
},
|
|
115
|
+
secondTopic: {
|
|
116
|
+
color: props.theme?.global.fontColor,
|
|
117
|
+
flexWrap: 'wrap',
|
|
118
|
+
fontSize: cx(14),
|
|
119
|
+
marginTop: cx(30),
|
|
120
|
+
marginHorizontal: cx(24),
|
|
121
|
+
},
|
|
122
|
+
pickContainer: {
|
|
123
|
+
position: 'relative',
|
|
124
|
+
marginVertical: cx(24),
|
|
125
|
+
marginHorizontal: cx(24),
|
|
126
|
+
},
|
|
127
|
+
disabledCover: {
|
|
128
|
+
position: 'absolute',
|
|
129
|
+
width: '100%',
|
|
130
|
+
height: '100%',
|
|
131
|
+
left: 0,
|
|
132
|
+
top: 0,
|
|
133
|
+
zIndex: 999,
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<Page
|
|
139
|
+
backText={uaGroupInfo.name}
|
|
140
|
+
onBackClick={navigation.goBack}
|
|
141
|
+
headlineText={I18n.getLang('sockets_specific_settings_switch_inching')}
|
|
142
|
+
>
|
|
143
|
+
<ScrollView nestedScrollEnabled={true}>
|
|
144
|
+
{params.channelConfig && params.channelConfig.length > 1 && <>
|
|
145
|
+
<Segmented
|
|
146
|
+
style={{ marginHorizontal: cx(24) }}
|
|
147
|
+
options={params.channelConfig.map(item => ({
|
|
148
|
+
label: item.channelTitle,
|
|
149
|
+
value: item.channel
|
|
150
|
+
}))}
|
|
151
|
+
value={state.channel}
|
|
152
|
+
onChange={v => {
|
|
153
|
+
updateState({ channel: Number(v) })
|
|
154
|
+
}}
|
|
155
|
+
/>
|
|
156
|
+
<Spacer />
|
|
157
|
+
</>}
|
|
158
|
+
<View style={styles.switchContainer}>
|
|
159
|
+
<View style={styles.switchCardContainer}>
|
|
160
|
+
<Text style={styles.switchCardTitle}>
|
|
161
|
+
{I18n.getLang('socket_settings_switch_off_firstbox_text')}
|
|
162
|
+
</Text>
|
|
163
|
+
<SwitchButton
|
|
164
|
+
value={state.switchInchingItem.enable}
|
|
165
|
+
onValueChange={async v => {
|
|
166
|
+
const time = Number(timeRef.current.minute) * 60 + Number(timeRef.current.second)
|
|
167
|
+
const newSwitchInchingItem = {
|
|
168
|
+
...state.switchInchingItem,
|
|
169
|
+
enable: v,
|
|
170
|
+
time
|
|
171
|
+
}
|
|
172
|
+
updateState({ switchInchingItem: newSwitchInchingItem })
|
|
173
|
+
await saveInchingConfig(newSwitchInchingItem)
|
|
174
|
+
}}
|
|
175
|
+
/>
|
|
176
|
+
</View>
|
|
177
|
+
<Text style={styles.switchDescription}>
|
|
178
|
+
{I18n.getLang('switchinching_overview_description_text')}
|
|
179
|
+
</Text>
|
|
180
|
+
</View>
|
|
181
|
+
<Text style={styles.secondTopic}>
|
|
182
|
+
{I18n.getLang('socket_settings_switch_off_secondtopic')}
|
|
183
|
+
</Text>
|
|
184
|
+
<View style={styles.pickContainer}>
|
|
185
|
+
{state.switchInchingItem.enable && <View style={styles.disabledCover} />}
|
|
186
|
+
<LdvPickerView
|
|
187
|
+
hour={timeRef.current.minute}
|
|
188
|
+
minute={timeRef.current.second}
|
|
189
|
+
unit={[
|
|
190
|
+
I18n.getLang('socket_settings_switch_off_min'),
|
|
191
|
+
I18n.getLang('socket_settings_switch_off_s'),
|
|
192
|
+
]}
|
|
193
|
+
setHour={m => updateTime(m, undefined)}
|
|
194
|
+
setMinute={s => updateTime(undefined, s)}
|
|
195
|
+
maxHour={61}
|
|
196
|
+
/>
|
|
197
|
+
</View>
|
|
198
|
+
</ScrollView>
|
|
199
|
+
</Page>
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export default withTheme(SwitchInching)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {Buffer} from 'buffer'
|
|
2
|
+
import {Result} from "@ledvance/base/src/models/modules/Result"
|
|
3
|
+
import {useFeatureHook} from "@ledvance/base/src/models/modules/NativePropsSlice";
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
|
|
6
|
+
interface SwitchInchingConfig {
|
|
7
|
+
switchInching: SwitchInchingItem[]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SwitchInchingItem {
|
|
11
|
+
channel: number,
|
|
12
|
+
enable: boolean,
|
|
13
|
+
time: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SwitchInchingPageParams {
|
|
17
|
+
channelConfig?: {
|
|
18
|
+
channelTitle: string
|
|
19
|
+
channel: number
|
|
20
|
+
}[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const defSwitchInching = [{ enable: false, channel: 0, time: 1 }]
|
|
24
|
+
|
|
25
|
+
export const useSwitchInching = (): [SwitchInchingItem[], (v: SwitchInchingItem[]) => Promise<Result<any>>] => {
|
|
26
|
+
const [switchInching, setSwitchInching] = useFeatureHook<SwitchInchingConfig, SwitchInchingItem[]>('switchInching', defSwitchInching, modes => {
|
|
27
|
+
const buffers = modes.map(mode => {
|
|
28
|
+
const buffer = new Uint8Array(3)
|
|
29
|
+
// 第1个字节:开/关+通道号
|
|
30
|
+
buffer[0] = packOnOffChannel(mode.enable, mode.channel)
|
|
31
|
+
// 第2、3个字节:点动时间
|
|
32
|
+
buffer[1] = (mode.time >> 8) & 0xff
|
|
33
|
+
buffer[2] = mode.time & 0xff
|
|
34
|
+
return buffer
|
|
35
|
+
})
|
|
36
|
+
const combinedBuffer = new Uint8Array(buffers.length * 3)
|
|
37
|
+
buffers.forEach((buffer, index) => {
|
|
38
|
+
combinedBuffer.set(buffer, index * 3)
|
|
39
|
+
})
|
|
40
|
+
return Buffer.from(combinedBuffer).toString('base64')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const newSwitchInching = useMemo(() => {
|
|
45
|
+
if (typeof switchInching === 'object' && !Array.isArray(switchInching)) {
|
|
46
|
+
const item = JSON.parse(JSON.stringify(switchInching))
|
|
47
|
+
return [{ ...item, channel: 0 }]
|
|
48
|
+
}
|
|
49
|
+
return switchInching
|
|
50
|
+
}, [switchInching])
|
|
51
|
+
|
|
52
|
+
return [newSwitchInching, setSwitchInching]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 工具函数:将开/关和通道号打包为一个字节
|
|
56
|
+
function packOnOffChannel(enable: boolean, channel: number = 0): number {
|
|
57
|
+
const enableBit = enable ? 1 : 0;
|
|
58
|
+
const channelBits = (channel << 1) & 0b11111110;
|
|
59
|
+
return enableBit | channelBits;
|
|
60
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import I18n from "@ledvance/base/src/i18n";
|
|
2
|
-
import { AdjustType, DiySceneInfo } from "@ledvance/base/src/utils/interface";
|
|
2
|
+
import { AdjustType, ApplyForItem, DiySceneInfo } from "@ledvance/base/src/utils/interface";
|
|
3
3
|
import { MoodInfo, MoodUIInfo } from "../mood_new/Interface";
|
|
4
4
|
|
|
5
5
|
export interface IAddSingleTime {
|
|
@@ -62,16 +62,6 @@ export interface HSV {
|
|
|
62
62
|
v: number;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
export type Category = 'light' | 'socket' | 'fan' | 'mainLight' | 'secondaryLight'
|
|
66
|
-
|
|
67
|
-
export interface ApplyForItem {
|
|
68
|
-
name?: string
|
|
69
|
-
key: string;
|
|
70
|
-
dp: string;
|
|
71
|
-
type: Category;
|
|
72
|
-
enable: boolean;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
65
|
interface judgmentSupport {
|
|
76
66
|
isSupportColor: boolean;
|
|
77
67
|
isSupportBrightness: boolean;
|
|
@@ -102,6 +92,7 @@ export enum DeviceType {
|
|
|
102
92
|
FanLight = 'fanLight',
|
|
103
93
|
MoodStrip = 'moodStrip',
|
|
104
94
|
Shutter = 'shutter',
|
|
95
|
+
OsramFanLight = 'osramFanLight',
|
|
105
96
|
}
|
|
106
97
|
// export type DeviceType = 'LightSource' | 'CeilingLight' | 'StringLight' | 'StripLight' | 'MixLight';
|
|
107
98
|
|
|
@@ -144,6 +135,11 @@ export interface ShutterData extends DeviceData {
|
|
|
144
135
|
percentControl: number
|
|
145
136
|
}
|
|
146
137
|
|
|
138
|
+
export interface OsramFanLightData extends DeviceData {
|
|
139
|
+
fanMode: 'fresh' | 'nature'
|
|
140
|
+
fanSpeed: number
|
|
141
|
+
}
|
|
142
|
+
|
|
147
143
|
export type ComponentConfig =
|
|
148
144
|
| { type: DeviceType.LightSource; deviceData: DeviceData }
|
|
149
145
|
| { type: DeviceType.MixLight; deviceData: MixLightData }
|
|
@@ -152,6 +148,8 @@ export type ComponentConfig =
|
|
|
152
148
|
| { type: DeviceType.FanLight; deviceData: FanLightData }
|
|
153
149
|
| { type: DeviceType.MoodStrip; deviceData: MoodStripData}
|
|
154
150
|
| { type: DeviceType.Shutter; deviceData: ShutterData}
|
|
151
|
+
| { type: DeviceType.OsramFanLight; deviceData: OsramFanLightData}
|
|
152
|
+
|
|
155
153
|
export interface TimeScheduleDetailState {
|
|
156
154
|
timeSchedule: Timer;
|
|
157
155
|
dps: Record<string, any>;
|
|
@@ -47,7 +47,13 @@ export const defMoodStripDeviceData = {
|
|
|
47
47
|
|
|
48
48
|
export const defShutterDeviceData = {
|
|
49
49
|
percentControl: 100
|
|
50
|
-
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const defOsramFanLightDeviceData = {
|
|
53
|
+
...defDeviceData,
|
|
54
|
+
fanSpeed: 1,
|
|
55
|
+
fanMode: 'fresh',
|
|
56
|
+
}
|
|
51
57
|
|
|
52
58
|
export const addTimeSchedule = async (props: IAddSingleTime) => {
|
|
53
59
|
try {
|
|
@@ -19,7 +19,6 @@ import Spacer from '@ledvance/base/src/components/Spacer';
|
|
|
19
19
|
import LdvWeekView from '@ledvance/base/src/components/weekSelect';
|
|
20
20
|
import { convertTo12HourFormat, loopText, showDialog } from '@ledvance/base/src/utils/common';
|
|
21
21
|
import {
|
|
22
|
-
ApplyForItem,
|
|
23
22
|
ComponentConfig,
|
|
24
23
|
DeviceType,
|
|
25
24
|
TimeScheduleDetailState,
|
|
@@ -37,7 +36,7 @@ import DeleteButton from '@ledvance/base/src/components/DeleteButton';
|
|
|
37
36
|
import SegmentControl from '@ledvance/base/src/components/segmentControl';
|
|
38
37
|
import { useParams } from '@ledvance/base/src/hooks/Hooks';
|
|
39
38
|
import ManualSettings from './components/ManuaSettings';
|
|
40
|
-
import { defDeviceData, defMixDeviceData, defStripDeviceData, defFanLightDeviceData, defMoodStripDeviceData, defShutterDeviceData } from './TimeScheduleActions';
|
|
39
|
+
import { defDeviceData, defMixDeviceData, defStripDeviceData, defFanLightDeviceData, defMoodStripDeviceData, defShutterDeviceData, defOsramFanLightDeviceData } from './TimeScheduleActions';
|
|
41
40
|
import MoodItem from '../mood_new/MoodItem';
|
|
42
41
|
import Summary from './components/Summary'
|
|
43
42
|
import { getRemoteMoodList } from '../mood_new/MoodActions'
|
|
@@ -45,7 +44,7 @@ import { MoodInfo, MoodUIInfo } from '../mood_new/Interface';
|
|
|
45
44
|
import InfoText from '@ledvance/base/src/components/InfoText';
|
|
46
45
|
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
47
46
|
import Tag from '@ledvance/base/src/components/Tag';
|
|
48
|
-
import { DiySceneInfo } from '@ledvance/base/src/utils/interface';
|
|
47
|
+
import { ApplyForItem, DiySceneInfo } from '@ledvance/base/src/utils/interface';
|
|
49
48
|
import DiySceneItem from '@ledvance/base/src/components/DiySceneItem';
|
|
50
49
|
|
|
51
50
|
const { convertX: cx } = Utils.RatioUtils;
|
|
@@ -679,7 +678,8 @@ const getDefaultManual = (props: TimeScheduleDetailPageParams): ComponentConfig
|
|
|
679
678
|
isFanLight: DeviceType.FanLight,
|
|
680
679
|
isCeilingLight: DeviceType.CeilingLight,
|
|
681
680
|
isMoodStrip: DeviceType.MoodStrip,
|
|
682
|
-
isShutter: DeviceType.Shutter
|
|
681
|
+
isShutter: DeviceType.Shutter,
|
|
682
|
+
isOsramFanLight: DeviceType.OsramFanLight,
|
|
683
683
|
};
|
|
684
684
|
|
|
685
685
|
const deviceType = Object.entries(deviceTypeMap)
|
|
@@ -693,6 +693,7 @@ const getDefaultManual = (props: TimeScheduleDetailPageParams): ComponentConfig
|
|
|
693
693
|
[DeviceType.FanLight]: defFanLightDeviceData,
|
|
694
694
|
[DeviceType.MoodStrip]: defMoodStripDeviceData,
|
|
695
695
|
[DeviceType.Shutter]: defShutterDeviceData,
|
|
696
|
+
[DeviceType.OsramFanLight]: defOsramFanLightDeviceData,
|
|
696
697
|
[DeviceType.LightSource]: defDeviceData,
|
|
697
698
|
};
|
|
698
699
|
|
|
@@ -37,6 +37,7 @@ export interface TimeSchedulePageParams {
|
|
|
37
37
|
isUVCFan?: boolean;
|
|
38
38
|
isMoodStrip?: boolean
|
|
39
39
|
isShutter?: boolean
|
|
40
|
+
isOsramFan?: boolean
|
|
40
41
|
applyForList: ApplyForItem[];
|
|
41
42
|
applyForDisabled?: boolean; // 是否可以选择apply for
|
|
42
43
|
manualDataDp2Obj: (dps: Record<string, any>) => DeviceStateType;
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
directOptions,
|
|
9
9
|
modeOptions,
|
|
10
10
|
ShutterData,
|
|
11
|
+
OsramFanLightData,
|
|
11
12
|
} from '../Interface';
|
|
12
13
|
import { View } from 'react-native';
|
|
13
14
|
import Card from '@ledvance/base/src/components/Card';
|
|
@@ -28,6 +29,8 @@ import { cctToColor } from '@ledvance/base/src/utils/cctUtils';
|
|
|
28
29
|
import {hsv2Hex, mapFloatToRange } from '@ledvance/base/src/utils';
|
|
29
30
|
import { AdjustType, ApplyForItem } from '@ledvance/base/src/utils/interface';
|
|
30
31
|
import LdvSlider from '@ledvance/base/src/components/ldvSlider';
|
|
32
|
+
import OsramFanAdjustView from '@ledvance/base/src/components/OsramFanAdjustView'
|
|
33
|
+
|
|
31
34
|
const { convertX: cx } = Utils.RatioUtils;
|
|
32
35
|
const { withTheme } = Utils.ThemeUtils
|
|
33
36
|
|
|
@@ -112,7 +115,7 @@ function ManualSettings(props: ManualSettingProps) {
|
|
|
112
115
|
state.applyFlag = Symbol()
|
|
113
116
|
}}
|
|
114
117
|
/>
|
|
115
|
-
{item.enable &&
|
|
118
|
+
{item.enable && !['socket', 'fan', 'osramFan'].includes(item.type) && (
|
|
116
119
|
<LampAdjustView
|
|
117
120
|
isSupportColor={props.isSupportColor}
|
|
118
121
|
isSupportBrightness={props.isSupportBrightness}
|
|
@@ -206,6 +209,30 @@ function ManualSettings(props: ManualSettingProps) {
|
|
|
206
209
|
/>
|
|
207
210
|
)}
|
|
208
211
|
|
|
212
|
+
{item.enable && item.type === 'osramFan' && (
|
|
213
|
+
<OsramFanAdjustView
|
|
214
|
+
hideFanSwitch={true}
|
|
215
|
+
fanSwitch={item.enable}
|
|
216
|
+
setFanSwitch={() => {}}
|
|
217
|
+
fanSpeed={(state.deviceData as OsramFanLightData).fanSpeed}
|
|
218
|
+
setFanSpeed={(v: number) => {
|
|
219
|
+
state.deviceData = {
|
|
220
|
+
...state.deviceData,
|
|
221
|
+
fanSpeed: v
|
|
222
|
+
}
|
|
223
|
+
state.manualFlag = Symbol()
|
|
224
|
+
}}
|
|
225
|
+
fanMode={(state.deviceData as OsramFanLightData).fanMode}
|
|
226
|
+
setFanMode={(v: 'fresh' | 'nature') => {
|
|
227
|
+
state.deviceData = {
|
|
228
|
+
...state.deviceData,
|
|
229
|
+
fanMode: v
|
|
230
|
+
}
|
|
231
|
+
state.manualFlag = Symbol()
|
|
232
|
+
}}
|
|
233
|
+
/>
|
|
234
|
+
)}
|
|
235
|
+
|
|
209
236
|
<ApplyForDeviceList
|
|
210
237
|
devices={cloneDeep(groupDevices)}
|
|
211
238
|
/>
|
|
@@ -22,6 +22,7 @@ export interface TimerTask {
|
|
|
22
22
|
stringOnMin: I18nKey
|
|
23
23
|
stringOffMin: I18nKey
|
|
24
24
|
cloudKey: 'lightingInfo' | 'socketInfo' | 'fanInfo' | 'plugCountDownInfo'
|
|
25
|
+
displayToDpValue?: (value: number) => number
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export enum TaskStatus {
|
|
@@ -65,6 +66,7 @@ export function useTimerTasks(timerSettableDps: TimerSettableDp[], devIdGroup: s
|
|
|
65
66
|
stringOnMin: timerSettableDp.stringOnMin,
|
|
66
67
|
stringOffMin: timerSettableDp.stringOffMin,
|
|
67
68
|
cloudKey: timerSettableDp.cloudKey,
|
|
69
|
+
displayToDpValue: timerSettableDp.displayToDpValue,
|
|
68
70
|
}))
|
|
69
71
|
}, [JSON.stringify(timerSettableDps)])
|
|
70
72
|
|
|
@@ -75,7 +77,7 @@ export function useTimerTasks(timerSettableDps: TimerSettableDp[], devIdGroup: s
|
|
|
75
77
|
timerTasks => {
|
|
76
78
|
const dps = {}
|
|
77
79
|
timerTasks.forEach(task => {
|
|
78
|
-
dps[task.dp.code] = task.duration
|
|
80
|
+
dps[task.dp.code] = task.displayToDpValue ? task.displayToDpValue(task.duration) : task.duration
|
|
79
81
|
const featureKey = timerSettableDps.find(timerSettableDp => timerSettableDp.dp.code === task.dp.code)?.cloudKey
|
|
80
82
|
devIdGroup.forEach(devId => {
|
|
81
83
|
NativeApi.putJson(devId, task.cloudKey ?? featureKey, JSON.stringify({ [getKey('Status', task.dp.code)]: !!task.duration, [getKey('StartTime', task.dp.code)]: dayjs(task.startTime * 1000).format('YYYY-MM-DD HH:mm:ss'), progressAllNumber: task.duration }))
|
|
@@ -176,6 +178,7 @@ export interface TimerSettableDp {
|
|
|
176
178
|
stringOnMin: I18nKey
|
|
177
179
|
stringOffMin: I18nKey
|
|
178
180
|
cloudKey: 'lightingInfo' | 'socketInfo' | 'fanInfo' | 'plugCountDownInfo'
|
|
181
|
+
displayToDpValue?: (value: number) => number
|
|
179
182
|
}
|
|
180
183
|
|
|
181
184
|
export interface TimerPageParams {
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import React, {useEffect} from "react";
|
|
2
|
-
import {StyleSheet, Text, View} from "react-native";
|
|
3
|
-
import Page from "@ledvance/base/src/components/Page";
|
|
4
|
-
import {useUAGroupInfo} from "@ledvance/base/src/models/modules/NativePropsSlice";
|
|
5
|
-
import {useNavigation} from '@react-navigation/native'
|
|
6
|
-
import {SwitchButton, Utils} from "tuya-panel-kit";
|
|
7
|
-
import LdvPickerView from "@ledvance/base/src/components/ldvPickerView";
|
|
8
|
-
import I18n from '@ledvance/base/src/i18n'
|
|
9
|
-
import {useReactive, useUpdateEffect} from "ahooks";
|
|
10
|
-
import {useSwitchInching} from "./SwithInchingAction";
|
|
11
|
-
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
12
|
-
|
|
13
|
-
const {convertX: cx} = Utils.RatioUtils
|
|
14
|
-
const { withTheme } = Utils.ThemeUtils
|
|
15
|
-
|
|
16
|
-
interface SwitchInchingState {
|
|
17
|
-
enable: boolean,
|
|
18
|
-
minute: string,
|
|
19
|
-
second: string,
|
|
20
|
-
flag: Symbol | number
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const SwitchInching = (props: { theme?: ThemeType }) => {
|
|
24
|
-
const uaGroupInfo = useUAGroupInfo()
|
|
25
|
-
const navigation = useNavigation()
|
|
26
|
-
const [switchInching, setSwitchInching] = useSwitchInching()
|
|
27
|
-
|
|
28
|
-
const state = useReactive<SwitchInchingState>({
|
|
29
|
-
enable: false,
|
|
30
|
-
minute: '00',
|
|
31
|
-
second: '00',
|
|
32
|
-
flag: 1
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
state.enable = switchInching.enable
|
|
37
|
-
state.minute = formatValue('minute')
|
|
38
|
-
state.second = formatValue('second')
|
|
39
|
-
}, [JSON.stringify(switchInching)])
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const requestSwitchInching = () => {
|
|
43
|
-
const enable = state.enable
|
|
44
|
-
const time = Number(state.minute) * 60 + Number(state.second)
|
|
45
|
-
setSwitchInching({enable, time}).then(_r => {})
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (state.flag !== 1) {
|
|
50
|
-
requestSwitchInching()
|
|
51
|
-
}
|
|
52
|
-
}, [state.flag])
|
|
53
|
-
|
|
54
|
-
useUpdateEffect(() => {
|
|
55
|
-
if (!Number(state.minute) && Number(state.second) < 2) {
|
|
56
|
-
state.second = '02'
|
|
57
|
-
}
|
|
58
|
-
if (Number(state.minute) === 60) {
|
|
59
|
-
state.second = '00'
|
|
60
|
-
}
|
|
61
|
-
}, [state.minute, state.second])
|
|
62
|
-
|
|
63
|
-
const formatValue = (type: string) => {
|
|
64
|
-
const m = parseInt(String((switchInching?.time || 0) / 60))
|
|
65
|
-
if (type === 'minute') {
|
|
66
|
-
return m.toString().padStart(2, '0')
|
|
67
|
-
} else {
|
|
68
|
-
const s = switchInching.time - 60 * m
|
|
69
|
-
return s === 0 ? '00' : s < 10 ? '0' + s : String(s)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const styles = StyleSheet.create({
|
|
74
|
-
switchContainer: {
|
|
75
|
-
backgroundColor: props.theme?.container.background,
|
|
76
|
-
marginHorizontal: cx(24),
|
|
77
|
-
padding: cx(10),
|
|
78
|
-
borderRadius: cx(6),
|
|
79
|
-
flexDirection: 'column',
|
|
80
|
-
},
|
|
81
|
-
switchCardContainer: {
|
|
82
|
-
paddingVertical: cx(4),
|
|
83
|
-
paddingHorizontal: cx(10),
|
|
84
|
-
backgroundColor: props.theme?.global.background,
|
|
85
|
-
flexDirection: 'row',
|
|
86
|
-
justifyContent: 'center',
|
|
87
|
-
alignItems: 'center',
|
|
88
|
-
borderRadius: cx(6),
|
|
89
|
-
},
|
|
90
|
-
switchCardTitle: {
|
|
91
|
-
color: props.theme?.global.fontColor,
|
|
92
|
-
fontSize: cx(14),
|
|
93
|
-
flex: 1,
|
|
94
|
-
fontWeight: '400',
|
|
95
|
-
},
|
|
96
|
-
switchDescription: {
|
|
97
|
-
color: props.theme?.global.fontColor,
|
|
98
|
-
flexWrap: 'wrap',
|
|
99
|
-
fontSize: cx(12),
|
|
100
|
-
marginTop: cx(4),
|
|
101
|
-
},
|
|
102
|
-
secondTopic: {
|
|
103
|
-
color: props.theme?.global.fontColor,
|
|
104
|
-
flexWrap: 'wrap',
|
|
105
|
-
fontSize: cx(14),
|
|
106
|
-
marginTop: cx(30),
|
|
107
|
-
marginHorizontal: cx(24),
|
|
108
|
-
},
|
|
109
|
-
pickContainer: {
|
|
110
|
-
position: 'relative',
|
|
111
|
-
marginVertical: cx(24),
|
|
112
|
-
marginHorizontal: cx(24),
|
|
113
|
-
},
|
|
114
|
-
disabledCover: {
|
|
115
|
-
position: 'absolute',
|
|
116
|
-
width: '100%',
|
|
117
|
-
height: '100%',
|
|
118
|
-
left: 0,
|
|
119
|
-
top: 0,
|
|
120
|
-
zIndex: 999,
|
|
121
|
-
},
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<Page
|
|
126
|
-
backText={uaGroupInfo.name}
|
|
127
|
-
onBackClick={navigation.goBack}
|
|
128
|
-
headlineText={I18n.getLang('socket_settings_switch_off_firstbox_text')}
|
|
129
|
-
>
|
|
130
|
-
<View style={styles.switchContainer}>
|
|
131
|
-
<View style={styles.switchCardContainer}>
|
|
132
|
-
<Text style={styles.switchCardTitle}>
|
|
133
|
-
{I18n.getLang('socket_settings_switch_off_firstbox_text')}
|
|
134
|
-
</Text>
|
|
135
|
-
<SwitchButton
|
|
136
|
-
value={switchInching.enable}
|
|
137
|
-
onValueChange={v => {
|
|
138
|
-
state.enable = v
|
|
139
|
-
state.flag = Symbol()
|
|
140
|
-
}}
|
|
141
|
-
/>
|
|
142
|
-
</View>
|
|
143
|
-
<Text style={styles.switchDescription}>
|
|
144
|
-
{I18n.getLang('switchinching_overview_description_text')}
|
|
145
|
-
</Text>
|
|
146
|
-
</View>
|
|
147
|
-
<Text style={styles.secondTopic}>
|
|
148
|
-
{I18n.getLang('socket_settings_switch_off_secondtopic')}
|
|
149
|
-
</Text>
|
|
150
|
-
<View style={styles.pickContainer}>
|
|
151
|
-
{switchInching.enable && <View style={styles.disabledCover}/>}
|
|
152
|
-
<LdvPickerView
|
|
153
|
-
maxHour={61}
|
|
154
|
-
hour={state.minute}
|
|
155
|
-
minute={state.second}
|
|
156
|
-
unit={[
|
|
157
|
-
I18n.getLang('socket_settings_switch_off_min'),
|
|
158
|
-
I18n.getLang('socket_settings_switch_off_s'),
|
|
159
|
-
]}
|
|
160
|
-
setHour={m => {
|
|
161
|
-
state.minute = m
|
|
162
|
-
}}
|
|
163
|
-
setMinute={s => {
|
|
164
|
-
state.second = s
|
|
165
|
-
}}
|
|
166
|
-
/>
|
|
167
|
-
</View>
|
|
168
|
-
</Page>
|
|
169
|
-
)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export default withTheme(SwitchInching)
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import {Buffer} from 'buffer'
|
|
2
|
-
import {Result} from "@ledvance/base/src/models/modules/Result"
|
|
3
|
-
import {useFeatureHook} from "@ledvance/base/src/models/modules/NativePropsSlice";
|
|
4
|
-
|
|
5
|
-
interface SwitchInchingConfig {
|
|
6
|
-
switchInching: SwitchInchingModel
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface SwitchInchingModel {
|
|
10
|
-
enable: boolean
|
|
11
|
-
channel?: number
|
|
12
|
-
time: number
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const useSwitchInching = (): [SwitchInchingModel, (v: SwitchInchingModel) => Promise<Result<any>>] => {
|
|
16
|
-
return useFeatureHook<SwitchInchingConfig, SwitchInchingModel>('switchInching', {enable: false, channel: 0, time: 1}, mode => {
|
|
17
|
-
const buffer = new Uint8Array(3)
|
|
18
|
-
// 第1个字节:开/关+通道号
|
|
19
|
-
buffer[0] = packOnOffChannel(mode.enable, mode.channel)
|
|
20
|
-
// 第2、3个字节:点动时间
|
|
21
|
-
buffer[1] = (mode.time >> 8) & 0xff
|
|
22
|
-
buffer[2] = mode.time & 0xff
|
|
23
|
-
return Buffer.from(buffer).toString('base64')
|
|
24
|
-
})
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// 工具函数:将开/关和通道号打包为一个字节
|
|
28
|
-
function packOnOffChannel(enable: boolean, channel: number = 0): number {
|
|
29
|
-
const enableBit = enable ? 1 : 0;
|
|
30
|
-
const channelBits = (channel << 1) & 0b11111110;
|
|
31
|
-
return enableBit | channelBits;
|
|
32
|
-
}
|