Various fixes

This commit is contained in:
Martin Bauer 2021-07-22 18:39:02 +02:00
parent 0bb0e2f121
commit a307e2a4ea
10 changed files with 19971 additions and 53 deletions

4
App.js
View File

@ -53,7 +53,6 @@ export default class App extends React.Component {
this.setState({ isReady: true }); this.setState({ isReady: true });
this.device = new DeviceReduxCoupling(store); this.device = new DeviceReduxCoupling(store);
console.log("subscribing");
let theApp = this; let theApp = this;
this.unsubscribe = store.subscribe(() => { this.unsubscribe = store.subscribe(() => {
const state = store.getState(); const state = store.getState();
@ -66,8 +65,8 @@ export default class App extends React.Component {
componentWillUnmount() { componentWillUnmount() {
if (this.unsubscribe) { if (this.unsubscribe) {
console.log("Unsubscribe");
this.unsubscribe(); this.unsubscribe();
console.log("unsubscribe");
} }
} }
@ -130,6 +129,7 @@ export default class App extends React.Component {
); );
let activeView; let activeView;
if (this.state.disconnected) if (this.state.disconnected)
activeView = disconnectedView; activeView = disconnectedView;
else if (this.state.isProvisioning) else if (this.state.isProvisioning)

View File

@ -5,14 +5,14 @@ import AntDesignIcon from "react-native-vector-icons/AntDesign";
import Fa5Icon from "react-native-vector-icons/FontAwesome5"; import Fa5Icon from "react-native-vector-icons/FontAwesome5";
const IconCard = props => { const IconCard = props => {
let iconClass; let IconClass;
if (props.iconType === "AntDesign") { if (props.iconType === "AntDesign") {
iconClass = AntDesignIcon; IconClass = AntDesignIcon;
} }
else if (props.iconType === "FontAwesome5") { else if (props.iconType === "FontAwesome5") {
iconClass = Fa5Icon; IconClass = Fa5Icon;
} else if (props.iconType === "Entypo") { } else if (props.iconType === "Entypo") {
iconClass = EntypoIcon; IconClass = EntypoIcon;
} }
return ( return (
@ -22,7 +22,7 @@ const IconCard = props => {
<Text style={{ color: 'white', fontSize: props.fontSize, textAlign: "center" }}> {props.value}</Text> <Text style={{ color: 'white', fontSize: props.fontSize, textAlign: "center" }}> {props.value}</Text>
</View> </View>
<View style={{ alignItems: 'center', justifyContent: 'center', paddingLeft: 20 }}> <View style={{ alignItems: 'center', justifyContent: 'center', paddingLeft: 20 }}>
<iconClass style={{ color: 'white', fontSize: 40 }} name={props.iconName} /> <IconClass style={{ color: 'white', fontSize: 40 }} name={props.iconName} />
<Text style={{ color: 'white', marginTop: 5 }}> {props.label}</Text> <Text style={{ color: 'white', marginTop: 5 }}> {props.label}</Text>
</View> </View>
</View> </View>

View File

@ -30,7 +30,7 @@ const OpCodes = {
export default class SwimTrackerWebsocketConnection { export default class SwimTrackerWebsocketConnection {
constructor(swimTrackerHost, onData, onStarted, onStopped, onWifiStateInfo, onConnect, onDisconnect) { constructor(swimTrackerHost, onData, onStarted, onStopped, onWifiStateInfo, onConnect, onDisconnect) {
this.swimTrackerHost = swimTrackerHost; this.swimTrackerHost = swimTrackerHost;
this.onData = onData; this.onData = onData;
this.onStarted = onStarted; this.onStarted = onStarted;
this.onStopped = onStopped; this.onStopped = onStopped;
@ -41,6 +41,7 @@ export default class SwimTrackerWebsocketConnection {
const wsOptions = { const wsOptions = {
maxReconnectionDelay: 4000 maxReconnectionDelay: 4000
}; };
this.ws = new ReconnectingWebSocket(`ws://${swimTrackerHost}:81`, [], wsOptions); this.ws = new ReconnectingWebSocket(`ws://${swimTrackerHost}:81`, [], wsOptions);
this.ws.onmessage = this._onMessage; this.ws.onmessage = this._onMessage;
this.ws.onopen = this.onConnect; this.ws.onopen = this.onConnect;
@ -58,6 +59,15 @@ export default class SwimTrackerWebsocketConnection {
this._wifiScanPromises = []; this._wifiScanPromises = [];
} }
close() {
this.ws.onmessage = null;
this.ws.onopen = null;
this.ws.onclose = null;
this.ws.onerror = null;
this.ws.close();
this.ws = null;
}
sendStartCommand() { sendStartCommand() {
this._sendMsg(OpCodes.START_SESSION); this._sendMsg(OpCodes.START_SESSION);
} }
@ -95,6 +105,7 @@ export default class SwimTrackerWebsocketConnection {
} }
wifiSetModeSTA(ssid, password) { wifiSetModeSTA(ssid, password) {
console.log("Setting sta mode", ssid, password);
this._sendMsg(OpCodes.WIFI_STATE_SET, { this._sendMsg(OpCodes.WIFI_STATE_SET, {
"sta_ssid": ssid, "sta_ssid": ssid,
"sta_password": password, "sta_password": password,
@ -108,7 +119,7 @@ export default class SwimTrackerWebsocketConnection {
msg = new Uint8Array([code, ...serializedData]); msg = new Uint8Array([code, ...serializedData]);
} else { } else {
msg = new Uint8Array(1); msg = new Uint8Array(1);
msg[0] = OpCodes.WIFI_TRIGGER_SCAN; msg[0] = code;
} }
this.ws.send(msg); this.ws.send(msg);
} }

19878
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -83,6 +83,11 @@ export class DeviceReduxCoupling {
const state = this.reduxStore.getState(); const state = this.reduxStore.getState();
if (this.conn === null || (state.settings.swimTrackerHost != this.conn.swimTrackerHost)) { if (this.conn === null || (state.settings.swimTrackerHost != this.conn.swimTrackerHost)) {
console.log(" ---- starting websocket connection to ", state.settings.swimTrackerHost);
if( this.conn !== null) {
this.conn.close();
}
this.conn = new SwimTrackerWebsocketConnection(state.settings.swimTrackerHost, this.conn = new SwimTrackerWebsocketConnection(state.settings.swimTrackerHost,
this._onNewData, this._onNewData,
(sessionId) => this.reduxStore.dispatch(reportSessionStarted(sessionId)), (sessionId) => this.reduxStore.dispatch(reportSessionStarted(sessionId)),
@ -137,27 +142,27 @@ export const deviceStateReducer = (state = INITIAL_DEVICE_STATE, action) => {
}; };
return res; return res;
case DEVICE_CONNECT: case DEVICE_CONNECT:
return { ...INITIAL_DEVICE_STATE, connState: ConnState.CONNECTED_STOPPED }; return { ...INITIAL_DEVICE_STATE, wifiState: state.wifiState, connState: ConnState.CONNECTED_STOPPED };
case DEVICE_DISCONNECT: case DEVICE_DISCONNECT:
return { ...INITIAL_DEVICE_STATE, connState: ConnState.DISCONNECTED }; return { ...INITIAL_DEVICE_STATE, wifiState: state.wifiState, connState: ConnState.DISCONNECTED };
case SESSION_STARTED: case SESSION_STARTED:
return { ...INITIAL_DEVICE_STATE, connState: ConnState.CONNECTED_RUNNING, sessionId: action.sessionId }; return { ...INITIAL_DEVICE_STATE, wifiState: state.wifiState, connState: ConnState.CONNECTED_RUNNING, sessionId: action.sessionId };
case SESSION_STOPPED: case SESSION_STOPPED:
return { ...INITIAL_DEVICE_STATE, connState: ConnState.CONNECTED_STOPPED }; return { ...INITIAL_DEVICE_STATE, wifiState: state.wifiState, connState: ConnState.CONNECTED_STOPPED };
case START_SESSION: case START_SESSION:
if (state.connState === ConnState.SESSION_STARTED) if (state.connState === ConnState.SESSION_STARTED)
return state; return state;
return { ...INITIAL_DEVICE_STATE, connState: ConnState.CONNECTED_STARTING }; return { ...INITIAL_DEVICE_STATE, wifiState: state.wifiState, connState: ConnState.CONNECTED_STARTING };
case STOP_SESSION: case STOP_SESSION:
if (state.connState === ConnState.SESSION_STOPPED) if (state.connState === ConnState.SESSION_STOPPED)
return state; return state;
return { ...INITIAL_DEVICE_STATE, connState: ConnState.CONNECTED_STOPPING }; return { ...INITIAL_DEVICE_STATE, wifiState: state.wifiState, connState: ConnState.CONNECTED_STOPPING };
case WIFI_SET_STATE: case WIFI_SET_STATE:
let wifState = WifiState.UNKNOWN; let wifiState = WifiState.UNKNOWN;
if (action.newStateStr === "STATION_MODE") { wifState = WifiState.STA; } if (action.newStateStr === "STATION_MODE") { wifiState = WifiState.STA; }
else if (action.newStateStr === "AP_PROVISIONING") { wifState = WifiState.AP_PROVISIONING; } else if (action.newStateStr === "AP_PROVISIONING") { wifiState = WifiState.AP_PROVISIONING; }
else if (action.newStateStr === "AP_SECURE") { wifState = WifiState.AP_SECURE; } else if (action.newStateStr === "AP_SECURE") { wifiState = WifiState.AP_SECURE; }
return { ...state, wifiState: wifState }; return { ...state, wifiState: wifiState };
default: default:
//console.log("Unhandled state in deviceStateReducer", action, action.type, "state", state); //console.log("Unhandled state in deviceStateReducer", action, action.type, "state", state);
return state; return state;

View File

@ -3,13 +3,18 @@ import { deviceStateReducer } from "./DeviceReduxCoupling";
export const CHANGE_USER_NAME = "SET_USERNAME"; export const CHANGE_USER_NAME = "SET_USERNAME";
export const RESET_DEVICE_DATA = "RESET_DEVICE_DATA"; export const RESET_DEVICE_DATA = "RESET_DEVICE_DATA";
export const CHANGE_SWIMTRACKER_HOSTNAME = "CHANGE_SWIMTRACKER_HOSTNAME";
export const changeUsername = newUsername => ({ export const changeUsername = newUsername => ({
type: CHANGE_USER_NAME, type: CHANGE_USER_NAME,
newUserName: newUsername, newUserName: newUsername,
}); });
export const changeSwimTrackerHostname = newSwimTrackerHost => ( {
type: CHANGE_SWIMTRACKER_HOSTNAME,
newSwimTrackerHost: newSwimTrackerHost,
});
export const startSession = () => ({ export const startSession = () => ({
type: START_SESSION type: START_SESSION
}); });
@ -48,8 +53,10 @@ const settingsReducer = (state = INITIAL_SETTINGS, action) => {
switch (action.type) { switch (action.type) {
case CHANGE_USER_NAME: case CHANGE_USER_NAME:
return { ...state, username: action.newUsername }; return { ...state, username: action.newUsername };
case CHANGE_SWIMTRACKER_HOSTNAME:
return {... state, swimTrackerHost: action.newSwimTrackerHost};
default: default:
return state return state;
} }
}; };

View File

@ -2,13 +2,23 @@ import React from "react";
import { import {
StyleSheet, StyleSheet,
Text, Text,
View,
TextInput,
ActivityIndicator, ActivityIndicator,
} from "react-native"; } from "react-native";
import SetupView from '../components/SetupView'; import SetupView from '../components/SetupView';
import EvilIcon from "react-native-vector-icons/EvilIcons";
import { connect } from 'react-redux';
import { changeSwimTrackerHostname } from '../state/Reducer';
function ConnectingView(props) { function ConnectingView(props) {
let onHostnameChange = newHostName => {
props.dispatch(changeSwimTrackerHostname(newHostName));
};
return ( return (
<SetupView <SetupView
headerText="Connecting..." headerText="Connecting..."
@ -18,8 +28,17 @@ function ConnectingView(props) {
<ActivityIndicator size="large" color="#ffffff" /> <ActivityIndicator size="large" color="#ffffff" />
<Text style={styles.subtext}> <Text style={styles.subtext}>
Please connect your phone to the WiFi of your SwimTracker Please connect your phone to the WiFi of your SwimTracker
</Text> </Text>
<View style={styles.row}>
<EvilIcon name="archive" style={styles.hostIcon}></EvilIcon>
<TextInput
onChangeText={onHostnameChange}
value={props.swimTrackerHost}
style={styles.hostnameInput}
placeholder="Hostname/IP"
placeholderTextColor="rgba(255,255,255,0.5)"
></TextInput>
</View>
</SetupView> </SetupView>
) )
} }
@ -27,11 +46,38 @@ function ConnectingView(props) {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
subtext: { subtext: {
color: "rgba(255,255,255,1)", color: "rgba(255,255,255,1)",
textAlign: "left", textAlign: "center",
fontSize: 18, fontSize: 18,
lineHeight: 25, lineHeight: 25,
width: "80%", width: "80%",
},
row: {
backgroundColor: "rgba(255,255,255,0.4)",
borderRadius: 5,
width: "80%",
height: 50,
flexDirection: "row",
alignItems: "center",
marginTop: 60,
marginBottom: 5,
},
hostIcon: {
fontSize: 25,
color: "rgba(255,255,255,1)",
marginLeft: 15,
marginRight: 15,
},
hostnameInput: {
height: 30,
color: "rgba(255,255,255,1)",
width: "80%",
fontSize: 18,
} }
}); });
export default ConnectingView;
const mapStateToProps = (state) => {
return { swimTrackerHost: state.settings.swimTrackerHost };
};
export default connect(mapStateToProps)(ConnectingView);

View File

@ -8,12 +8,13 @@ import {
TouchableOpacity, TouchableOpacity,
} from "react-native"; } from "react-native";
import themeColors from '../components/themeColors'; import themeColors from '../components/themeColors';
import MaterialIcon from "react-native-vector-icons/MaterialCommunityIcons"; import MaterialIcon from "react-native-vector-icons/MaterialIcons";
import MaterialCommIcon from "react-native-vector-icons/MaterialCommunityIcons";
import EntypoIcon from "react-native-vector-icons/Entypo"; import EntypoIcon from "react-native-vector-icons/Entypo";
import FeatherIcon from "react-native-vector-icons/Feather"; import FeatherIcon from "react-native-vector-icons/Feather";
import { MaterialCommunityIcons } from '@expo/vector-icons'; //import { MaterialCommunityIcons } from '@expo/vector-icons';
import { ConnState, startSession } from '../state/DeviceReduxCoupling'; import { ConnState, startSession } from '../state/DeviceReduxCoupling';
@ -70,7 +71,7 @@ function ButtonGrid(props) {
style={[{ backgroundColor: themeColors["GREEN SEA"] }, buttonGridStyles.button]} style={[{ backgroundColor: themeColors["GREEN SEA"] }, buttonGridStyles.button]}
activeOpacity={0.6} activeOpacity={0.6}
> >
<MaterialIcon name="swim" style={buttonGridStyles.icon}></MaterialIcon> <MaterialCommIcon name="swim" style={buttonGridStyles.icon}></MaterialCommIcon>
<Text style={buttonGridStyles.buttonText}>{"LETZTE\nSESSIONS"}</Text> <Text style={buttonGridStyles.buttonText}>{"LETZTE\nSESSIONS"}</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
@ -80,7 +81,7 @@ function ButtonGrid(props) {
style={[{ backgroundColor: themeColors["MIDNIGHT BLUE"] }, buttonGridStyles.button]} style={[{ backgroundColor: themeColors["MIDNIGHT BLUE"] }, buttonGridStyles.button]}
activeOpacity={0.6} activeOpacity={0.6}
> >
<MaterialIcon name="account-group" style={buttonGridStyles.icon}></MaterialIcon> <MaterialCommIcon name="account-group" style={buttonGridStyles.icon}></MaterialCommIcon>
<Text style={buttonGridStyles.buttonText}>SOCIAL</Text> <Text style={buttonGridStyles.buttonText}>SOCIAL</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity
@ -88,7 +89,7 @@ function ButtonGrid(props) {
style={[{ backgroundColor: themeColors["MIDNIGHT BLUE"] }, buttonGridStyles.button]} style={[{ backgroundColor: themeColors["MIDNIGHT BLUE"] }, buttonGridStyles.button]}
activeOpacity={0.6} activeOpacity={0.6}
> >
<MaterialCommunityIcons name="settings" style={buttonGridStyles.icon}></MaterialCommunityIcons> <MaterialIcon name="settings" style={buttonGridStyles.icon}></MaterialIcon>
<Text style={buttonGridStyles.buttonText}>EINSTELLUNGEN</Text> <Text style={buttonGridStyles.buttonText}>EINSTELLUNGEN</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>

View File

@ -47,8 +47,10 @@ function WifiPasswordView(props) {
setErrorMsg("Password has to be at least 8 characters long") setErrorMsg("Password has to be at least 8 characters long")
else if (password1.length > 128) else if (password1.length > 128)
setErrorMsg("Password too long"); setErrorMsg("Password too long");
else else {
props.onSubmit(props.ssid, password1);
setErrorMsg(""); setErrorMsg("");
}
}; };
return ( return (

View File

@ -113,7 +113,7 @@ class WifiSelectionView extends React.Component {
if (this.state.wifiInfo.length > 0) { if (this.state.wifiInfo.length > 0) {
inner = ( inner = (
<View style={styles.listContainer}> <View style={styles.listContainer}>
<ScrollView style={{backgroundColor: "red", centerContent: true, paddingTop: 20}}> <ScrollView style={{centerContent: true, paddingTop: 20}}>
{this.state.wifiInfo.map(e => ( {this.state.wifiInfo.map(e => (
<WifiListElement <WifiListElement
text={e.ssid} text={e.ssid}
@ -128,6 +128,9 @@ class WifiSelectionView extends React.Component {
confirmPwInput: false, confirmPwInput: false,
buttonText: "OK", buttonText: "OK",
subText: "Please enter the password for your home WiFi", subText: "Please enter the password for your home WiFi",
onSubmit: (ssid, pw) => {
this.props.device.conn.wifiSetModeSTA(ssid, pw);
},
}); });
}}> }}>
</WifiListElement>) </WifiListElement>)
@ -157,6 +160,9 @@ class WifiSelectionView extends React.Component {
confirmPwInput: true, confirmPwInput: true,
buttonText: "Set Password", buttonText: "Set Password",
subText: "Use this option only if you're home WiFi doesn't reach the SwimTracker. The SwimTracker creates its own WiFi with the password you set here.", subText: "Use this option only if you're home WiFi doesn't reach the SwimTracker. The SwimTracker creates its own WiFi with the password you set here.",
onSubmit: (ssid, pw) => {
this.props.device.conn.wifiSetModeAP(pw);
},
}); });
}} }}
lowerRightButtonText="Need help?" lowerRightButtonText="Need help?"