Skip to content

Commit deb15da

Browse files
authored
Merge pull request #471 from PlayerData/reboot
Reboot
2 parents 3d5677c + 77090b3 commit deb15da

File tree

16 files changed

+1047
-187
lines changed

16 files changed

+1047
-187
lines changed

example/app.config.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import 'ts-node/register';
44
const config: ExpoConfig = {
55
name: "react-native-mcu-manager-example",
66
slug: "react-native-mcu-manager-example",
7-
version: "1.0.0",
8-
orientation: "portrait",
97
assetBundlePatterns: ["**/*"],
8+
orientation: "portrait",
9+
platforms: ["ios", "android"],
10+
scheme: "rnmcumgr",
11+
version: "1.0.0",
1012
splash: {
1113
image: ".assets/images/pd.png",
1214
backgroundColor: "#FFFFFF",
@@ -27,6 +29,7 @@ const config: ExpoConfig = {
2729
},
2830
plugins: [
2931
["expo-document-picker"],
32+
["expo-router"],
3033
["./gradlePlugin.ts"]
3134
]
3235
};

example/index.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

example/package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"name": "react-native-mcu-manager-example",
33
"description": "Example app for react-native-mcu-manager",
4+
"main": "expo-router/entry",
45
"version": "0.0.1",
5-
"private": true,
6+
"license": "MIT",
67
"scripts": {
78
"android": "expo run:android",
89
"ios": "expo run:ios",
@@ -13,12 +14,19 @@
1314
"dependencies": {
1415
"@playerdata/react-native-mcu-manager": "workspace:*",
1516
"expo": "52.0.25",
17+
"expo-constants": "~17.0.4",
1618
"expo-document-picker": "13.0.2",
19+
"expo-linking": "~7.0.4",
20+
"expo-router": "~4.0.16",
1721
"expo-splash-screen": "0.29.20",
22+
"expo-status-bar": "~2.0.1",
1823
"lodash": "4.17.21",
1924
"react": "18.3.1",
2025
"react-native": "0.76.3",
2126
"react-native-ble-plx": "3.4.0",
27+
"react-native-reanimated": "~3.16.1",
28+
"react-native-safe-area-context": "4.12.0",
29+
"react-native-screens": "~4.4.0",
2230
"react-native-toast-message": "2.2.1"
2331
},
2432
"devDependencies": {
@@ -27,7 +35,6 @@
2735
"@types/lodash": "4.17.14",
2836
"@types/react": "19.0.7",
2937
"metro-react-native-babel-preset": "0.77.0",
30-
"pod-install": "0.3.4",
3138
"ts-node": "^10.9.2",
3239
"typescript": "5.7.3"
3340
}

example/src/app/(home)/_layout.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Stack } from 'expo-router';
2+
3+
const HomeLayout = () => {
4+
return (
5+
<Stack>
6+
<Stack.Screen name="index" options={{ headerShown: false }} />
7+
<Stack.Screen
8+
name="select_device"
9+
options={{ title: 'Select Device', presentation: 'modal' }}
10+
/>
11+
</Stack>
12+
);
13+
};
14+
15+
export default HomeLayout;

example/src/app/(home)/index.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React, { useState } from 'react';
2+
import { Button, StyleSheet, Text, View } from 'react-native';
3+
4+
import { Link } from 'expo-router';
5+
6+
import { resetDevice } from '@playerdata/react-native-mcu-manager';
7+
8+
import { useSelectedDevice } from '../../context/selectedDevice';
9+
10+
const styles = StyleSheet.create({
11+
root: {
12+
padding: 16,
13+
},
14+
15+
block: {
16+
marginBottom: 16,
17+
},
18+
});
19+
20+
const Home = () => {
21+
const { selectedDevice } = useSelectedDevice();
22+
const [resetState, setResetState] = useState('');
23+
24+
return (
25+
<View style={styles.root}>
26+
<View style={styles.block}>
27+
<Text>
28+
Select a device, then use the tabs below to choose which function to
29+
test
30+
</Text>
31+
</View>
32+
33+
<Link asChild href="/select_device">
34+
<Button title="Select Device" />
35+
</Link>
36+
37+
<View style={styles.block}>
38+
<Text>Selected:</Text>
39+
40+
{selectedDevice?.deviceId && <Text>{selectedDevice.deviceName}</Text>}
41+
</View>
42+
43+
<View style={styles.block}>
44+
<Text>{resetState}</Text>
45+
46+
<Button
47+
title="Reset Device"
48+
disabled={!selectedDevice?.deviceId}
49+
onPress={() => {
50+
setResetState('Resetting...');
51+
52+
resetDevice(selectedDevice?.deviceId || '')
53+
.then(() => setResetState('Reset complete'))
54+
.catch((error) => setResetState(error.message));
55+
}}
56+
/>
57+
</View>
58+
</View>
59+
);
60+
};
61+
62+
export default Home;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react';
2+
import { Button, FlatList, StyleSheet, Text, View } from 'react-native';
3+
import { useNavigation } from 'expo-router';
4+
5+
import { useSelectedDevice } from '../../context/selectedDevice';
6+
import useBluetoothDevices from '../../hooks/useBluetoothDevices';
7+
8+
const styles = StyleSheet.create({
9+
list: {
10+
padding: 16,
11+
},
12+
});
13+
14+
const SelectDevice = () => {
15+
const navigation = useNavigation();
16+
const { setSelectedDevice } = useSelectedDevice();
17+
const { devices, error: scanError } = useBluetoothDevices();
18+
19+
return (
20+
<FlatList
21+
contentContainerStyle={styles.list}
22+
data={devices}
23+
keyExtractor={({ id }) => id}
24+
renderItem={({ item }) => (
25+
<View>
26+
<Text>{item.name || item.id}</Text>
27+
28+
<Button
29+
title="Select"
30+
onPress={() => {
31+
setSelectedDevice({ deviceId: item.id, deviceName: item.name });
32+
navigation.goBack();
33+
}}
34+
/>
35+
</View>
36+
)}
37+
ListHeaderComponent={() => <Text>{scanError}</Text>}
38+
/>
39+
);
40+
};
41+
42+
export default SelectDevice;

example/src/app/_layout.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useState } from 'react';
2+
import { Tabs } from 'expo-router';
3+
import 'react-native-reanimated';
4+
5+
import {
6+
SelectedDeviceProvider,
7+
SelectedDevice,
8+
} from '../context/selectedDevice';
9+
10+
const RootLayout = () => {
11+
const [selectedDevice, setSelectedDevice] = useState<SelectedDevice | null>(
12+
null
13+
);
14+
15+
return (
16+
<SelectedDeviceProvider value={{ selectedDevice, setSelectedDevice }}>
17+
<Tabs>
18+
<Tabs.Screen
19+
name="(home)"
20+
options={{ title: 'Home' }}
21+
/>
22+
<Tabs.Screen name="update" options={{ title: 'Update' }} />
23+
</Tabs>
24+
</SelectedDeviceProvider>
25+
);
26+
};
27+
28+
export default RootLayout;

example/src/App.tsx renamed to example/src/app/update.tsx

Lines changed: 13 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,16 @@ import {
66
import React, { useState } from 'react';
77
import {
88
Button,
9-
FlatList,
10-
Modal,
119
SafeAreaView,
1210
ScrollView,
1311
StyleSheet,
1412
Text,
1513
View,
1614
} from 'react-native';
1715

18-
import useBluetoothDevices from './useBluetoothDevices';
19-
import useFilePicker from './useFilePicker';
20-
import useFirmwareUpdate from './useFirmwareUpdate';
16+
import useFilePicker from '../hooks/useFilePicker';
17+
import useFirmwareUpdate from '../hooks/useFirmwareUpdate';
18+
import { useSelectedDevice } from '../context/selectedDevice';
2119

2220
const styles = StyleSheet.create({
2321
root: {
@@ -27,29 +25,21 @@ const styles = StyleSheet.create({
2725
block: {
2826
marginBottom: 16,
2927
},
30-
31-
list: {
32-
padding: 16,
33-
},
3428
});
3529

36-
export default function App() {
37-
const [devicesListVisible, setDevicesListVisible] = useState(false);
38-
const [selectedDeviceId, setSelectedDeviceId] = useState<string | null>(null);
39-
const [selectedDeviceName, setSelectedDeviceName] = useState<string | null>(
40-
null
41-
);
30+
const Update = () => {
31+
const { selectedDevice } = useSelectedDevice();
32+
4233
const [fileType, setFileType] = useState<UpgradeFileType>(
4334
UpgradeFileType.BIN
4435
);
4536
const [upgradeMode, setUpgradeMode] = useState<UpgradeMode | undefined>(
4637
undefined
4738
);
4839

49-
const { devices, error: scanError } = useBluetoothDevices();
5040
const { selectedFile, filePickerError, pickFile } = useFilePicker();
5141
const { cancelUpdate, runUpdate, progress, state } = useFirmwareUpdate(
52-
selectedDeviceId,
42+
selectedDevice?.deviceId || null,
5343
selectedFile?.uri || null,
5444
fileType,
5545
upgradeMode
@@ -61,41 +51,14 @@ export default function App() {
6151
<Text style={styles.block}>Step 1 - Select Device to Update</Text>
6252

6353
<View style={styles.block}>
64-
{selectedDeviceId && (
54+
{selectedDevice?.deviceId && (
6555
<>
6656
<Text>Selected:</Text>
67-
<Text>{selectedDeviceName}</Text>
57+
<Text>{selectedDevice.deviceName}</Text>
6858
</>
6959
)}
70-
<Button
71-
onPress={() => setDevicesListVisible(true)}
72-
title="Select Device"
73-
/>
7460
</View>
7561

76-
<Modal visible={devicesListVisible}>
77-
<FlatList
78-
contentContainerStyle={styles.list}
79-
data={devices}
80-
keyExtractor={({ id }) => id}
81-
renderItem={({ item }) => (
82-
<View>
83-
<Text>{item.name || item.id}</Text>
84-
85-
<Button
86-
title="Select"
87-
onPress={() => {
88-
setSelectedDeviceId(item.id);
89-
setSelectedDeviceName(item.name);
90-
setDevicesListVisible(false);
91-
}}
92-
/>
93-
</View>
94-
)}
95-
ListHeaderComponent={() => <Text>{scanError}</Text>}
96-
/>
97-
</Modal>
98-
9962
<Text style={styles.block}>Step 2 - Select Update File</Text>
10063

10164
<View style={styles.block}>
@@ -154,13 +117,13 @@ export default function App() {
154117
</Text>
155118

156119
<Button
157-
disabled={!selectedFile || !selectedDeviceId}
120+
disabled={!selectedFile || !selectedDevice?.deviceId}
158121
onPress={() => selectedFile && runUpdate()}
159122
title="Start Update"
160123
/>
161124

162125
<Button
163-
disabled={!selectedFile || !selectedDeviceId}
126+
disabled={!selectedFile || !selectedDevice?.deviceId}
164127
onPress={() => cancelUpdate()}
165128
title="Cancel Update"
166129
/>
@@ -169,3 +132,5 @@ export default function App() {
169132
</SafeAreaView>
170133
);
171134
}
135+
136+
export default Update;

example/src/context/selectedDevice.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { createContext, useContext } from 'react';
2+
3+
export interface SelectedDevice {
4+
deviceId: string;
5+
deviceName: string | null;
6+
}
7+
8+
const SelectedDeviceContext = createContext<{
9+
selectedDevice: SelectedDevice | null;
10+
setSelectedDevice: (device: SelectedDevice) => void;
11+
}>({
12+
selectedDevice: null,
13+
setSelectedDevice: () => {},
14+
});
15+
16+
export const SelectedDeviceProvider = SelectedDeviceContext.Provider;
17+
18+
export const useSelectedDevice = () => {
19+
const context = useContext(SelectedDeviceContext);
20+
21+
if (!context) {
22+
throw new Error(
23+
'useSelectedDevice must be used within a SelectedDeviceProvider'
24+
);
25+
}
26+
27+
return context;
28+
};

example/src/useBluetoothDevices.ts renamed to example/src/hooks/useBluetoothDevices.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { sortBy, uniqBy } from 'lodash';
22
import { useEffect, useRef, useState } from 'react';
33
import { Device } from 'react-native-ble-plx';
44

5-
import { BLEService } from './BLEService';
5+
import { BLEService } from '../BLEService';
66

77
const useBluetoothDevices = () => {
88
const [bleManager] = useState(() => BLEService.manager);
@@ -15,7 +15,7 @@ const useBluetoothDevices = () => {
1515
BLEService.initializeBLE().then(() =>
1616
bleManager.startDeviceScan(
1717
[],
18-
{ allowDuplicates: false },
18+
{ allowDuplicates: false, legacyScan: false },
1919
(e, scannedDevice) => {
2020
if (e) {
2121
setError(`${e.message} - ${e.reason}`);

0 commit comments

Comments
 (0)