├── .expo-shared └── assets.json ├── .gitignore ├── App.js ├── App ├── Add.js ├── Browser.js └── Devices.js ├── README.md ├── app.json ├── assets ├── android-icon-foreground.png ├── gplay-feature-graphic.png ├── icon.png ├── qr-code.svg └── splash.png ├── babel.config.js ├── package-lock.json ├── package.json └── screenshots ├── S10-Add.jpg ├── S10-Devices.jpg ├── S10-Lights.jpg ├── SP-S10-Add.png ├── SP-S10-Devices.png ├── SP-S10-Lights.png ├── SP-iPhone-Add.png ├── SP-iPhone-Devices.png ├── SP-iPhone-Lights.png ├── iPad-Add.png ├── iPad-Devices.png ├── iPad-Lights.png ├── iPhone-Add.JPG ├── iPhone-Devices.JPG └── iPhone-Lights.JPG /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | .expo/* 3 | npm-debug.* 4 | *.jks 5 | *.p8 6 | *.p12 7 | *.key 8 | *.mobileprovision 9 | *.orig.* 10 | web-build/ 11 | 12 | # macOS 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { StatusBar } from 'expo-status-bar' 3 | import { getStatusBarHeight } from 'react-native-status-bar-height'; 4 | import { ThemeProvider } from 'react-native-elements' 5 | import { View, Platform, StyleSheet } from 'react-native'; 6 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs' 7 | import { NavigationContainer } from '@react-navigation/native'; 8 | import { SafeAreaProvider } from 'react-native-safe-area-context' 9 | import { MaterialCommunityIcons, FontAwesome5 } from '@expo/vector-icons'; 10 | import AsyncStorage from '@react-native-community/async-storage' 11 | import Browser from './App/Browser' 12 | import Devices from './App/Devices' 13 | import Add from './App/Add' 14 | 15 | global.theme = { 16 | colors: { 17 | primary: '#009688', 18 | secondary: '#333333', 19 | success: '#3CB479', 20 | error: '#F44336', 21 | warning: '#BAA441', 22 | info: '#949494', 23 | lightGray: '#d1d1d1', 24 | background: '#FAFAFA' 25 | } 26 | } 27 | 28 | global.styles = StyleSheet.create({ 29 | button: { 30 | borderRadius: 4, 31 | paddingVertical: 6, 32 | paddingHorizontal: 16 33 | }, 34 | text: { 35 | fontFamily: "Roboto", 36 | }, 37 | }) 38 | 39 | //This ensures that there is a single color status bar on both IOS and Android and that the content starts below it. 40 | const ColoredStatusBar = ({ backgroundColor, ...props }) => ( 41 | 42 | 43 | 44 | ); 45 | 46 | const Tab = createBottomTabNavigator(); 47 | 48 | export default function App() { 49 | const [selectedDevice, setSelectedDevice] = useState(-1) 50 | const [devices, setDevices] = useState([]) 51 | 52 | const saveData = async () => { 53 | try { 54 | await AsyncStorage.setItem('devices', JSON.stringify(devices)) 55 | await AsyncStorage.setItem('selectedDevice', JSON.stringify(selectedDevice)) 56 | } catch (e) { 57 | console.warn('Failed to save the data to the storage. Error: ' + e) 58 | } 59 | } 60 | 61 | const readData = async () => { 62 | try { 63 | const devicesRead = await AsyncStorage.getItem('devices') 64 | const selectedDeviceRead = await AsyncStorage.getItem('selectedDevice') 65 | 66 | if (devicesRead !== null) { 67 | setDevices(JSON.parse(devicesRead)); 68 | } 69 | else { 70 | console.warn("Error reading devices") 71 | } 72 | 73 | if (selectedDeviceRead !== null) { 74 | setSelectedDevice(JSON.parse(selectedDeviceRead)); 75 | } 76 | else { 77 | console.warn("Error reading selected device") 78 | } 79 | } catch (e) { 80 | console.warn('Failed to fetch the data from storage') 81 | } 82 | } 83 | 84 | useEffect(() => { 85 | readData(); 86 | }, []) 87 | 88 | useEffect(() => { 89 | saveData(); 90 | }, [devices, selectedDevice]) 91 | 92 | return ( 93 | 94 | 95 | 96 | 97 | 118 | ( 123 | 124 | ), 125 | }}> 126 | {() => { return }} 127 | 128 | ( 133 | 134 | ), 135 | }}> 136 | {() => { return }} 137 | 138 | ( 143 | 144 | ), 145 | }}> 146 | {() => { return }} 147 | 148 | 149 | 150 | 151 | 152 | ); 153 | } 154 | 155 | -------------------------------------------------------------------------------- /App/Add.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { View, Text, ScrollView } from 'react-native' 3 | import { Input, Button } from 'react-native-elements' 4 | import { SafeAreaView } from 'react-native-safe-area-context' 5 | import DropDownPicker from 'react-native-dropdown-picker'; 6 | import produce from 'immer' 7 | import { useNavigation } from '@react-navigation/native' 8 | import { Platform } from 'react-native'; 9 | 10 | export default function Add({ setDevices }) { 11 | const navigation = useNavigation(); 12 | const [device, setDevice] = useState({ 13 | name: "", 14 | protocol: "http", 15 | ip: "", 16 | port: "8888" 17 | }) 18 | 19 | const addDevice = () => { 20 | console.log(device); 21 | if(device.name !== "" && device.ip !== "") { 22 | setDevices(devices => produce(devices, devicesDraft => { devicesDraft.push({ name: device.name, protocol: device.protocol, ip: device.ip, port: device.port }) })) 23 | setDevice({ 24 | name: "", 25 | protocol: "http", 26 | ip: "", 27 | port: "8888" 28 | }) 29 | navigation.navigate("Devices"); 30 | } 31 | } 32 | 33 | return ( 34 | 35 | 36 | 37 | Add Device: 38 | 39 | Name: 40 | setDevice({ ...device, name: value })} placeholder="Name" /> 41 | 42 | 43 | IP Address: 44 | setDevice({ ...device, ip: value })} placeholder="192.168.1.67" /> 45 | 46 | 47 | Port: 48 | setDevice({ ...device, port: value })} placeholder="8888" /> 49 | 50 | 51 | Protocol: 52 | 53 | { setDevice({ ...device, protocol: protocol.value }) }} 68 | /> 69 | 70 | 71 |