diff --git a/README.md b/README.md
index 3e8ecc1..79404ee 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/locales/de.json b/locales/de.json
index b537a9b..86c2228 100644
--- a/locales/de.json
+++ b/locales/de.json
@@ -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"
+ }
}
}
}
diff --git a/locales/en.json b/locales/en.json
index 12b629a..3ea8a61 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -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"
+ }
}
}
}
diff --git a/src/Components/Button/index.js b/src/Components/Button/index.js
index bf19190..5f9996d 100644
--- a/src/Components/Button/index.js
+++ b/src/Components/Button/index.js
@@ -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}
{title}
diff --git a/src/Components/Card/index.js b/src/Components/Card/index.js
index 5dc5448..a79474c 100644
--- a/src/Components/Card/index.js
+++ b/src/Components/Card/index.js
@@ -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 (
{cardTopicText !== undefined && (
props.navigation.navigate("modalSearchForNewDevices")}
/>
diff --git a/src/Screens/SearchForNewDevices/index.js b/src/Screens/SearchForNewDevices/index.js
index e000fff..aacbdf7 100644
--- a/src/Screens/SearchForNewDevices/index.js
+++ b/src/Screens/SearchForNewDevices/index.js
@@ -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 (
-
- {
- console.log("start device search");
+ let texts = { searchDescription: "", searchButton: "" };
- zeroconf.scan("http", "tcp", "local.");
- }}
- />
-
+ 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 (
+
+
+
+ {searchState === AppSearchState.scanning && (
+
+ )}
+
+
+ {texts.searchDescription}
+
+
+
+ {
+ if (searchState !== AppSearchState.scanning) {
+ startScanning();
+ } else if (searchState === AppSearchState.scanning) {
+ stopScanning();
+ }
+ }}
+ />
+
+
+
+ {foundDevices.length > 0 && (
+
+
+ {t("screens.modalSearchForNewDevices.textFoundDevices")}
+
+
+ )}
+ >
+ }
+ ListFooterComponent={
+
+ {foundDevices.length > 0 && (
+ navigation.goBack()}
+ />
+ )}
+
+ }
+ scrollEnabled={false}
+ data={foundDevices}
+ extraData={foundDevices}
+ renderItem={({ item }) => (
+
+ )}
+ />
+
+
+
+ );
+}
+
+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 (
+
+
+
+
+
+
+
+ New Device
+
+
+ {item.deviceModel}
+
+
+ {item.deviceIp}
+
+
+
+
+
+ {
+ 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 ? (
+
+ ) : connectingState === FoundDeviceState.connected ? (
+
+ ) : null
+ }
+ />
+
+
+
);
}
diff --git a/src/utils.js b/src/utils.js
index 97a8078..c3eda97 100644
--- a/src/utils.js
+++ b/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 = [
{