searching for new devices
parent
c8d9e1bdd3
commit
22d4a8bae7
|
@ -16,4 +16,10 @@ https://aboutreact.com/switch-screen-out-of-the-navigation-drawer-in-react-nativ
|
|||
|
||||
https://stackoverflow.com/questions/66804691/include-android-permission-in-expo-react-native
|
||||
|
||||
# mDNS
|
||||
|
||||
https://expo.canny.io/feature-requests/p/zeroconf-protocol-support
|
||||
|
||||
https://docs.expo.dev/develop/development-builds/development-workflows/#build-locally-with-android-studio-and-xcode
|
||||
|
||||
https://forum.arduino.cc/t/help-understanding-mdns-on-esp32/1026655/5
|
||||
|
|
|
@ -178,7 +178,23 @@
|
|||
"expertModeDescription": "Durch das Einschalten werden zusätzliche Funktionen in der App freigeschaltet, wie beispielsweise die Möglichkeit, benutzerdefinierte Farbcodes anzugeben."
|
||||
},
|
||||
"modalSearchForNewDevices": {
|
||||
"pageTitle": "Suche nach Geräten"
|
||||
"pageTitle": "Suche nach Geräten",
|
||||
"searchButtonText": {
|
||||
"scanning": "Suche abbrechen",
|
||||
"afterScan": "Erneut suchen"
|
||||
},
|
||||
"searchDescription": {
|
||||
"scanning": "Es wird nach neuen Geräten gesucht. Dies kann einen Moment dauern. Bitte schalten Sie das neue Gerät in der Zwischenzeit nicht aus.",
|
||||
"afterScanDevicesFound": "Neue Geräte wurden gefunden. Bitte wählen Sie ein Gerät aus, um es mit der App zu verbinden.",
|
||||
"afterScanNoDevicesFound": "Keine Geräte gefunden. Bitte stellen Sie sicher, dass sich das Gerät im selben Netzwerk befindet."
|
||||
},
|
||||
"textFoundDevices": "Gefundene Geräte",
|
||||
"textButtonBackToOverview": "Zurück zur Übersicht",
|
||||
"foundDeviceConnectButton": {
|
||||
"connecting": "Verbinde",
|
||||
"connected": "Verbunden",
|
||||
"connect": "Verbinden"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,7 +178,23 @@
|
|||
"expertModeDescription": "Turning it on unlocks additional features in the app, such as the ability to specify custom color codes."
|
||||
},
|
||||
"modalSearchForNewDevices": {
|
||||
"pageTitle": "Search for devices"
|
||||
"pageTitle": "Search for devices",
|
||||
"searchButtonText": {
|
||||
"scanning": "Cancel Search",
|
||||
"afterScan": "Search Again"
|
||||
},
|
||||
"searchDescription": {
|
||||
"scanning": "Searching for new devices. This might take a moment. Please do not turn off the new device during this time.",
|
||||
"afterScanDevicesFound": "New devices have been found. Please select a device to connect it with the app.",
|
||||
"afterScanNoDevicesFound": "No devices found. Please make sure the device is on the same network."
|
||||
},
|
||||
"textFoundDevices": "Found Devices",
|
||||
"textButtonBackToOverview": "Back to Overview",
|
||||
"foundDeviceConnectButton": {
|
||||
"connecting": "Connecting",
|
||||
"connected": "Connected",
|
||||
"connect": "Connect"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
import { useContext } from "react";
|
||||
import { Text, TouchableHighlight, TouchableOpacity, View } from "react-native";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { AppContext, AppStyles } from "../../utils";
|
||||
import MyIcon from "../Icon";
|
||||
|
||||
export default function MyButton({ title, style, disabled, onPress }) {
|
||||
export default function MyButton({
|
||||
title,
|
||||
style,
|
||||
disabled,
|
||||
onPress,
|
||||
buttonLeftComponent,
|
||||
}) {
|
||||
const appContext = useContext(AppContext);
|
||||
|
||||
return (
|
||||
|
@ -21,8 +33,10 @@ export default function MyButton({ title, style, disabled, onPress }) {
|
|||
},
|
||||
AppStyles.Shadow,
|
||||
disabled && { opacity: 0.6 },
|
||||
buttonLeftComponent && { flexDirection: "row", alignItems: "center" },
|
||||
]}
|
||||
>
|
||||
{buttonLeftComponent}
|
||||
<Text style={{ textAlign: "center", color: appContext.appTheme.text }}>
|
||||
{title}
|
||||
</Text>
|
||||
|
|
|
@ -2,7 +2,12 @@ import { useContext } from "react";
|
|||
import { Text, View } from "react-native";
|
||||
import { AppContext, AppStyles } from "../../utils";
|
||||
|
||||
export default function Card({ children, cardTopicText, cardBackgroundColor }) {
|
||||
export default function Card({
|
||||
children,
|
||||
cardTopicText,
|
||||
cardBackgroundColor,
|
||||
disablePaddingBottom,
|
||||
}) {
|
||||
const appContext = useContext(AppContext);
|
||||
|
||||
const backgroundColor =
|
||||
|
@ -12,12 +17,14 @@ export default function Card({ children, cardTopicText, cardBackgroundColor }) {
|
|||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingTop: 10,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
paddingBottom: 10,
|
||||
}}
|
||||
style={[
|
||||
{
|
||||
paddingTop: 10,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
},
|
||||
!disablePaddingBottom && { paddingBottom: 10 },
|
||||
]}
|
||||
>
|
||||
{cardTopicText !== undefined && (
|
||||
<Text
|
||||
|
|
|
@ -146,6 +146,7 @@ export default function Sidebar(props) {
|
|||
<MyIconButton
|
||||
iconName="plus"
|
||||
iconSize={24}
|
||||
style={{ padding: 2 }}
|
||||
onPress={() => props.navigation.navigate("modalSearchForNewDevices")}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
@ -1,17 +1,102 @@
|
|||
import { useContext, useEffect } from "react";
|
||||
import { AppContext } from "../../utils";
|
||||
import { View } from "react-native";
|
||||
import MyButton from "../../Components/Button";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
AddNewDevice,
|
||||
AppContext,
|
||||
AppStyles,
|
||||
ModalContainer,
|
||||
} from "../../utils";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
FlatList,
|
||||
ScrollView,
|
||||
Text,
|
||||
View,
|
||||
} from "react-native";
|
||||
import MyButton, { MyTextButton } from "../../Components/Button";
|
||||
import Zeroconf from "react-native-zeroconf";
|
||||
import Card from "../../Components/Card";
|
||||
import MyIcon from "../../Components/Icon";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const zeroconf = new Zeroconf();
|
||||
|
||||
export default function SearchForNewDevicesModalContent() {
|
||||
// TODO: stop scanning on unfocs
|
||||
// TODO: manual input for ip address
|
||||
// TODO: link to faq?
|
||||
|
||||
const devFoundDevices = [
|
||||
{
|
||||
deviceModel: "Aurora",
|
||||
deviceIp: "127.0.0.1",
|
||||
},
|
||||
{
|
||||
deviceModel: "Aurora",
|
||||
deviceIp: "127.0.0.1",
|
||||
},
|
||||
{
|
||||
deviceModel: "Aurora",
|
||||
deviceIp: "127.0.0.1",
|
||||
},
|
||||
{
|
||||
deviceModel: "Aurora",
|
||||
deviceIp: "127.0.0.1",
|
||||
},
|
||||
{
|
||||
deviceModel: "Aurora",
|
||||
deviceIp: "127.0.0.1",
|
||||
},
|
||||
{
|
||||
deviceModel: "Aurora",
|
||||
deviceIp: "127.0.0.1",
|
||||
},
|
||||
{
|
||||
deviceModel: "Aurora",
|
||||
deviceIp: "127.0.0.1",
|
||||
},
|
||||
];
|
||||
|
||||
const AppSearchState = {
|
||||
init: 0, // appears when user opens the screen
|
||||
scanning: 1,
|
||||
afterScan: 2,
|
||||
};
|
||||
|
||||
export default function SearchForNewDevicesModalContent({ navigation }) {
|
||||
const appContext = useContext(AppContext);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [searchState, setSearchState] = useState(AppSearchState.init);
|
||||
const [foundDevices, setFoundDevices] = useState([]);
|
||||
|
||||
const startScanning = () => {
|
||||
//zeroconf.scan();
|
||||
|
||||
setSearchState(AppSearchState.scanning);
|
||||
setFoundDevices([]);
|
||||
|
||||
let chance = Math.random() < 0.5;
|
||||
|
||||
if (chance) {
|
||||
// only for testing
|
||||
setTimeout(() => {
|
||||
setFoundDevices((prev) => [...prev, devFoundDevices[0]]);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
setTimeout(() => stopScanning(), 4000);
|
||||
};
|
||||
|
||||
const stopScanning = () => {
|
||||
console.log("stop scanning");
|
||||
setSearchState(AppSearchState.afterScan);
|
||||
// zeroconf.stop();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log("useEffect");
|
||||
|
||||
startScanning();
|
||||
|
||||
/*
|
||||
zeroconf.on("start", () => {
|
||||
console.log("start");
|
||||
|
@ -28,19 +113,220 @@ export default function SearchForNewDevicesModalContent() {
|
|||
zeroconf.on("error", (err) => {
|
||||
console.log("err", err);
|
||||
}); */
|
||||
|
||||
setSearchState(AppSearchState.scanning);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View style={{ marginTop: 40, alignItems: "center" }}>
|
||||
<MyButton
|
||||
style={{ width: 200 }}
|
||||
title={"Gerätesuche starten"}
|
||||
onPress={() => {
|
||||
console.log("start device search");
|
||||
let texts = { searchDescription: "", searchButton: "" };
|
||||
|
||||
zeroconf.scan("http", "tcp", "local.");
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
switch (searchState) {
|
||||
case AppSearchState.init:
|
||||
break;
|
||||
case AppSearchState.scanning:
|
||||
texts.searchDescription = t(
|
||||
"screens.modalSearchForNewDevices.searchDescription.scanning"
|
||||
);
|
||||
texts.searchButton = t(
|
||||
"screens.modalSearchForNewDevices.searchButtonText.scanning"
|
||||
);
|
||||
break;
|
||||
case AppSearchState.afterScan:
|
||||
if (foundDevices.length > 0) {
|
||||
texts.searchDescription = t(
|
||||
"screens.modalSearchForNewDevices.searchDescription.afterScanDevicesFound"
|
||||
);
|
||||
} else {
|
||||
texts.searchDescription = t(
|
||||
"screens.modalSearchForNewDevices.searchDescription.afterScanNoDevicesFound"
|
||||
);
|
||||
}
|
||||
|
||||
texts.searchButton = t(
|
||||
"screens.modalSearchForNewDevices.searchButtonText.afterScan"
|
||||
);
|
||||
break;
|
||||
default:
|
||||
console.log(`Search state ${searchState} not found`);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={{ flex: 1, marginTop: 40, alignItems: "center" }}>
|
||||
<View style={{ margin: 40 }}>
|
||||
{searchState === AppSearchState.scanning && (
|
||||
<ActivityIndicator
|
||||
style={{ marginBottom: 20 }}
|
||||
size="large"
|
||||
color={appContext.appTheme.colors.secondary}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Text
|
||||
style={[
|
||||
{ textAlign: "center", color: appContext.appTheme.text },
|
||||
AppStyles.typography14,
|
||||
]}
|
||||
>
|
||||
{texts.searchDescription}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<MyButton
|
||||
style={{ width: 200 }}
|
||||
title={texts.searchButton}
|
||||
onPress={() => {
|
||||
if (searchState !== AppSearchState.scanning) {
|
||||
startScanning();
|
||||
} else if (searchState === AppSearchState.scanning) {
|
||||
stopScanning();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<View style={{ width: "100%" }}>
|
||||
<FlatList
|
||||
ListHeaderComponent={
|
||||
<>
|
||||
{foundDevices.length > 0 && (
|
||||
<View style={{ marginLeft: 20 }}>
|
||||
<Text
|
||||
style={[
|
||||
AppStyles.typography16,
|
||||
{
|
||||
fontWeight: "bold",
|
||||
marginTop: 20,
|
||||
color: appContext.appTheme.text,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t("screens.modalSearchForNewDevices.textFoundDevices")}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
ListFooterComponent={
|
||||
<View style={{ paddingBottom: 10 }}>
|
||||
{foundDevices.length > 0 && (
|
||||
<MyTextButton
|
||||
style={{ marginTop: 20 }}
|
||||
title={t(
|
||||
"screens.modalSearchForNewDevices.textButtonBackToOverview"
|
||||
)}
|
||||
onPress={() => navigation.goBack()}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
}
|
||||
scrollEnabled={false}
|
||||
data={foundDevices}
|
||||
extraData={foundDevices}
|
||||
renderItem={({ item }) => (
|
||||
<FoundDevice item={item} appContext={appContext} t={t} />
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const FoundDeviceState = {
|
||||
notConnected: 0,
|
||||
connecting: 1,
|
||||
connected: 2,
|
||||
};
|
||||
|
||||
function FoundDevice({ item, appContext, t }) {
|
||||
const [connectingState, setConnectingState] = useState(
|
||||
FoundDeviceState.notConnected
|
||||
);
|
||||
// TODO: loading the image of the new found device
|
||||
|
||||
return (
|
||||
<Card disablePaddingBottom>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<View style={{ flexDirection: "row", gap: 10 }}>
|
||||
<View style={{ height: 60, width: 60, backgroundColor: "gray" }} />
|
||||
|
||||
<View>
|
||||
<Text
|
||||
style={{
|
||||
fontWeight: "bold",
|
||||
color: appContext.appTheme.text,
|
||||
}}
|
||||
>
|
||||
New Device
|
||||
</Text>
|
||||
<Text style={{ color: appContext.appTheme.textSecondary }}>
|
||||
{item.deviceModel}
|
||||
</Text>
|
||||
<Text style={{ color: appContext.appTheme.textSecondary }}>
|
||||
{item.deviceIp}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View>
|
||||
<MyButton
|
||||
title={
|
||||
connectingState === FoundDeviceState.connecting
|
||||
? t(
|
||||
"screens.modalSearchForNewDevices.foundDeviceConnectButton.connecting"
|
||||
)
|
||||
: connectingState === FoundDeviceState.connected
|
||||
? t(
|
||||
"screens.modalSearchForNewDevices.foundDeviceConnectButton.connected"
|
||||
)
|
||||
: t(
|
||||
"screens.modalSearchForNewDevices.foundDeviceConnectButton.connect"
|
||||
)
|
||||
}
|
||||
disabled={connectingState !== FoundDeviceState.notConnected}
|
||||
onPress={() => {
|
||||
console.log("connect");
|
||||
// TODO: connect with esp api
|
||||
setConnectingState(FoundDeviceState.connecting);
|
||||
|
||||
setTimeout(() => {
|
||||
setConnectingState(FoundDeviceState.connected);
|
||||
|
||||
// generate random two digit number code
|
||||
let code = Math.floor(Math.random() * 100);
|
||||
|
||||
// TODO: add device and fill up with information provided by the esp
|
||||
appContext.setDevices((prev) => [
|
||||
...prev,
|
||||
AddNewDevice(
|
||||
"New Device " + code,
|
||||
item.deviceModel,
|
||||
item.deviceIp
|
||||
),
|
||||
]);
|
||||
}, 2000);
|
||||
}}
|
||||
buttonLeftComponent={
|
||||
connectingState === FoundDeviceState.connecting ? (
|
||||
<ActivityIndicator
|
||||
size="small"
|
||||
color={appContext.appTheme.colors.gray}
|
||||
style={{ marginRight: 6 }}
|
||||
/>
|
||||
) : connectingState === FoundDeviceState.connected ? (
|
||||
<MyIcon name="check" size={12} style={{ marginRight: 6 }} />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
14
src/utils.js
14
src/utils.js
|
@ -535,6 +535,20 @@ const devDevices = [
|
|||
},
|
||||
];
|
||||
|
||||
export function AddNewDevice(displayName, deviceModel, deviceIp) {
|
||||
return {
|
||||
id: GetUuid(),
|
||||
displayName: displayName,
|
||||
deviceModel: deviceModel,
|
||||
deviceIp: deviceIp,
|
||||
firmware: {
|
||||
version: "1.0.1",
|
||||
lastUpdated: "11.07.2023 um 20:33 Uhr",
|
||||
},
|
||||
selectedScene: "",
|
||||
};
|
||||
}
|
||||
|
||||
// preview
|
||||
const devDeviceScenes = [
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue