fbpixel
Étiquettes : ,

Nous allons créer une application React Native qui servira de client Websockets et pourra communiquer avec un serveur distant. WebSockets est un protocole populaire de communication web simple et robuste permettant la communication en temps réel entre client et serveur.

Matériel

  • Appareil Android
  • Ordinateur pour la programmation
  • Un câble USB pour connecter l’appareil Android au PC

Configuration du projet React Native

Pour communiquer via Websocket nous allons utiliser la librairie Websocket, react-use-websocket

npm install --save react-use-websocket

Utilisation de la librairie

A partir de la librairie, nous importons les objets et fonctions qui nous intéresse

import useWebSocket, { ReadyState } from 'react-use-websocket';
  • ReadyState état de la connexion avec le serveur
  • useWebScoket permet d’initialiser une connexion websockets

Nous créons ensuite un composant fonctionnel avec les états désirés

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const [ipAddress, setIpAddress] = useState('');
const [ipServer, setIpServer] = useState('ws://192.168.1.52:8765');
const [messageText, setMessageText] = useState("");
const [messageHistory, setMessageHistory] = useState([]);
const { sendMessage, sendJsonMessage, lastMessage, lastJsonMessage, readyState } = useWebSocket(ipServer);
const connectionStatus = {
[ReadyState.CONNECTING]: 'Connecting',
[ReadyState.OPEN]: 'Open',
[ReadyState.CLOSING]: 'Closing',
[ReadyState.CLOSED]: 'Closed',
[ReadyState.UNINSTANTIATED]: 'Uninstantiated',
}[readyState];
const paramCmd = {
type: 'setParam',
param1: 30,
param2: 2.3,
}
const [ipAddress, setIpAddress] = useState(''); const [ipServer, setIpServer] = useState('ws://192.168.1.52:8765'); const [messageText, setMessageText] = useState(""); const [messageHistory, setMessageHistory] = useState([]); const { sendMessage, sendJsonMessage, lastMessage, lastJsonMessage, readyState } = useWebSocket(ipServer); const connectionStatus = { [ReadyState.CONNECTING]: 'Connecting', [ReadyState.OPEN]: 'Open', [ReadyState.CLOSING]: 'Closing', [ReadyState.CLOSED]: 'Closed', [ReadyState.UNINSTANTIATED]: 'Uninstantiated', }[readyState]; const paramCmd = { type: 'setParam', param1: 30, param2: 2.3, }
  const [ipAddress, setIpAddress] = useState('');
  const [ipServer, setIpServer] = useState('ws://192.168.1.52:8765');
  const [messageText, setMessageText] = useState("");
  const [messageHistory, setMessageHistory] = useState([]);
  const { sendMessage, sendJsonMessage, lastMessage, lastJsonMessage, readyState } = useWebSocket(ipServer);

  const connectionStatus = {
    [ReadyState.CONNECTING]: 'Connecting',
    [ReadyState.OPEN]: 'Open',
    [ReadyState.CLOSING]: 'Closing',
    [ReadyState.CLOSED]: 'Closed',
    [ReadyState.UNINSTANTIATED]: 'Uninstantiated',
  }[readyState];

  const paramCmd = {
    type: 'setParam',
    param1: 30,
    param2: 2.3,
  }

Dans les états nous récupérons les fonctions et états de useWebSokcet

  • sendMessage la fonction pour envoyer des messages au format String
  • sendJsonMessage pour pour envoyer des messages au format JSON
  • lastMessage contient la réponse du serveur au format String
  • lastJsonMessage contient le dernier message du serveur au format JSON
  • readyState contient l’état de la connexion

Nous définissons aussi une constante au format Json paramCmd.

Nous utilisons un hook useEffect dans lequel nous récupérons l’adresse IP de l’appareil et nous gérons les messages reçus du serveur.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
useEffect(() => {
const fetchIpAddress = async () => {
const ip = await NetworkInfo.getIPV4Address();
setIpAddress(ip);
console.log("ip adresses ; ", ip)
};
fetchIpAddress();
if (lastMessage !== null) {
setMessageHistory((prev) => prev.concat(lastMessage));
}
if (lastJsonMessage !== null) {
console.log(JSON.stringify(lastJsonMessage));
}
return () => {
};
}, [lastMessage, setMessageHistory,lastJsonMessage]);
useEffect(() => { const fetchIpAddress = async () => { const ip = await NetworkInfo.getIPV4Address(); setIpAddress(ip); console.log("ip adresses ; ", ip) }; fetchIpAddress(); if (lastMessage !== null) { setMessageHistory((prev) => prev.concat(lastMessage)); } if (lastJsonMessage !== null) { console.log(JSON.stringify(lastJsonMessage)); } return () => { }; }, [lastMessage, setMessageHistory,lastJsonMessage]);
  useEffect(() => {
    const fetchIpAddress = async () => {
      const ip = await NetworkInfo.getIPV4Address();
      setIpAddress(ip);
      console.log("ip adresses ; ", ip)
    };

    fetchIpAddress();
	
    if (lastMessage !== null) {
      setMessageHistory((prev) => prev.concat(lastMessage));
    }
    if (lastJsonMessage !== null) {
      console.log(JSON.stringify(lastJsonMessage));
    }
    return () => {
    };
  }, [lastMessage, setMessageHistory,lastJsonMessage]);

Enfin, nous créons le rendu de l’application avec les éléments suivant

  • un texte pour afficher l’adresse IP de l’appareil
  • un bouton pour envoyer un message JSON
  • zone de texte pour le texte à écrire pour écrire le message au format texte
  • bouton Send pour envoyer le message
  • zone de texte pour entrer l’adresse du serveur
  • zone de texte pour afficher les réponses du serveur
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
return (
<View style={styles.mainBody}>
<Text
style={styles.mainTitle}>
AC Websocket Terminal
</Text>
<ScrollView>
<View style={styles.deviceItem}>
<View style={{flex:2}}>
<Text style={styles.deviceName}>{ipAddress}</Text>
<Text style={styles.deviceInfo}>{connectionStatus}</Text>
</View>
<TouchableOpacity
onPress={() => sendJSON()}
disabled={readyState !== ReadyState.OPEN}
style={styles.deviceButton}>
<Text
style={styles.buttonText}>
Send JSON
</Text>
</TouchableOpacity>
</View>
<View
style={styles.inputBar}>
<TextInput
style={styles.textInput}
placeholder="Enter a message"
value={messageText}
onChangeText={(text) => setMessageText(text)
}
/>
<TouchableOpacity
onPress={() => sendMessage(messageText)}
disabled={readyState !== ReadyState.OPEN}
style={[styles.sendButton]}>
<Text
style={styles.buttonText}>
SEND
</Text>
</TouchableOpacity>
</View>
<TextInput
placeholder="Server IP"
onChangeText={setIpServer}
value={ipServer}
/>
<View style={{flex:1,minHeight:200}}>
<Text>Received Message:</Text>
<ScrollView style={styles.textOutput}>
{lastMessage ? <Text>last message : {lastMessage.data}</Text> : null}
{messageHistory.map((message, idx) => (
<Text key={idx}>{message ? message.data : null}</Text>
))}
</ScrollView>
</View>
</ScrollView>
</View>
);
return ( <View style={styles.mainBody}> <Text style={styles.mainTitle}> AC Websocket Terminal </Text> <ScrollView> <View style={styles.deviceItem}> <View style={{flex:2}}> <Text style={styles.deviceName}>{ipAddress}</Text> <Text style={styles.deviceInfo}>{connectionStatus}</Text> </View> <TouchableOpacity onPress={() => sendJSON()} disabled={readyState !== ReadyState.OPEN} style={styles.deviceButton}> <Text style={styles.buttonText}> Send JSON </Text> </TouchableOpacity> </View> <View style={styles.inputBar}> <TextInput style={styles.textInput} placeholder="Enter a message" value={messageText} onChangeText={(text) => setMessageText(text) } /> <TouchableOpacity onPress={() => sendMessage(messageText)} disabled={readyState !== ReadyState.OPEN} style={[styles.sendButton]}> <Text style={styles.buttonText}> SEND </Text> </TouchableOpacity> </View> <TextInput placeholder="Server IP" onChangeText={setIpServer} value={ipServer} /> <View style={{flex:1,minHeight:200}}> <Text>Received Message:</Text> <ScrollView style={styles.textOutput}> {lastMessage ? <Text>last message : {lastMessage.data}</Text> : null} {messageHistory.map((message, idx) => ( <Text key={idx}>{message ? message.data : null}</Text> ))} </ScrollView> </View> </ScrollView> </View> );
  return (
    <View style={styles.mainBody}>
      <Text
        style={styles.mainTitle}>
        AC Websocket Terminal
      </Text>
      <ScrollView>

      <View style={styles.deviceItem}>
                      <View style={{flex:2}}>
                        <Text style={styles.deviceName}>{ipAddress}</Text>
                        <Text style={styles.deviceInfo}>{connectionStatus}</Text>
                      </View>
                      <TouchableOpacity
                        onPress={() => sendJSON()}
                        disabled={readyState !== ReadyState.OPEN}
                        style={styles.deviceButton}>
                        <Text
                          style={styles.buttonText}>
                          Send JSON
                        </Text>
                      </TouchableOpacity>
                    </View>


      <View
              style={styles.inputBar}>        
              <TextInput
                style={styles.textInput}
                placeholder="Enter a message"
                value={messageText}
                onChangeText={(text) =>    setMessageText(text)
                }
              />
              <TouchableOpacity
                        onPress={() => sendMessage(messageText)}
                        disabled={readyState !== ReadyState.OPEN}
                        style={[styles.sendButton]}>
                        <Text
                          style={styles.buttonText}>
                          SEND
                        </Text>
                      </TouchableOpacity>
        </View>

      <TextInput
        placeholder="Server IP"
        onChangeText={setIpServer}
        value={ipServer}
      />
      <View style={{flex:1,minHeight:200}}>
      <Text>Received Message:</Text>
              <ScrollView style={styles.textOutput}>
                {lastMessage ? <Text>last message : {lastMessage.data}</Text> : null}
                
                {messageHistory.map((message, idx) => (
                  <Text key={idx}>{message ? message.data : null}</Text>
                ))}
              </ScrollView>
              </View>
      </ScrollView>
    </View>
  );

Création d’un serveur WebSockets avec Python

Pour tester notre application, nous créons un serveur websockets sur le PC

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#!/usr/bin/env python
# python3 -m pip install websockets
import json
import asyncio
from websockets.server import serve
def getIpAddress():
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
ipaddress = s.getsockname()[0]
s.close()
return ipaddress
ipaddress = getIpAddress()
port = 8765
async def echo(websocket):
async for message in websocket:
print("received from {}:{} : ".format(websocket.remote_address[0],websocket.remote_address[1]) + message)
if('{' not in message):
await websocket.send(message)
else:
request = json.loads(message)
answer = {}
if(request['type'] == 'setParam'):
answer['type'] = request['type']
if(request['param1']<100 and request['param2']>1.2):
answer['valid'] = True
for key, val in request.items():
print("\t"+key+": ", val)
else:
answer['valid'] = False
else:
answer['type'] = 'unknown'
answer['valid'] = False
await websocket.send(json.dumps(answer))
async def main():
print("Server is activated on ws://{}:{}".format(ipaddress,port))
#async with serve(echo, "localhost", 8765):
async with serve(echo, "0.0.0.0", port):
await asyncio.Future() # run forever
asyncio.run(main())
#!/usr/bin/env python # python3 -m pip install websockets import json import asyncio from websockets.server import serve def getIpAddress(): import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ipaddress = s.getsockname()[0] s.close() return ipaddress ipaddress = getIpAddress() port = 8765 async def echo(websocket): async for message in websocket: print("received from {}:{} : ".format(websocket.remote_address[0],websocket.remote_address[1]) + message) if('{' not in message): await websocket.send(message) else: request = json.loads(message) answer = {} if(request['type'] == 'setParam'): answer['type'] = request['type'] if(request['param1']<100 and request['param2']>1.2): answer['valid'] = True for key, val in request.items(): print("\t"+key+": ", val) else: answer['valid'] = False else: answer['type'] = 'unknown' answer['valid'] = False await websocket.send(json.dumps(answer)) async def main(): print("Server is activated on ws://{}:{}".format(ipaddress,port)) #async with serve(echo, "localhost", 8765): async with serve(echo, "0.0.0.0", port): await asyncio.Future() # run forever asyncio.run(main())
#!/usr/bin/env python
# python3 -m pip install websockets

import json
import asyncio
from websockets.server import serve

def getIpAddress():
    import socket
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("8.8.8.8", 80))
    ipaddress = s.getsockname()[0]
    s.close()
    return ipaddress

ipaddress = getIpAddress()
port = 8765

async def echo(websocket):
    async for message in websocket:
        print("received from {}:{} : ".format(websocket.remote_address[0],websocket.remote_address[1]) + message)
        
        if('{' not in message):
            await websocket.send(message)
        else:
            request = json.loads(message)
            answer = {}
            if(request['type'] == 'setParam'):
                answer['type'] = request['type']
                if(request['param1']<100 and request['param2']>1.2):
                    answer['valid'] = True
                    for key, val in request.items():
                        print("\t"+key+": ", val)
                else:
                    answer['valid'] = False
            else:
                answer['type'] = 'unknown'
                answer['valid'] = False
                
            await websocket.send(json.dumps(answer))

async def main():
    print("Server is activated on ws://{}:{}".format(ipaddress,port))
    #async with serve(echo, "localhost", 8765):
    async with serve(echo, "0.0.0.0", port):
        await asyncio.Future()  # run forever

asyncio.run(main())

Résultat

Grâce à cette application, nous pouvons envoyer des String et JSON au serveur et afficher ses réponses

react-native-websockets-client-app Utilisation des WebSockets avec React Native

Code complet de communication WebSockets avec React Native

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
/**
* https://reactnative.dev/docs/network
* https://www.npmjs.com/package/react-use-websocket
* https://github.com/robtaussig/react-use-websocket
* test on python
* https://websockets.readthedocs.io/en/stable/
*/
import React, {useState, useEffect, useCallback} from 'react';
import {
View,
ScrollView,
Text,
TextInput,
TouchableOpacity,
StyleSheet} from 'react-native';
import { NetworkInfo } from 'react-native-network-info'
import useWebSocket, { ReadyState } from 'react-use-websocket';
const WebsocketTerminal = () => {
const [ipAddress, setIpAddress] = useState('');
const [ipServer, setIpServer] = useState('ws://192.168.1.52:8765');
const [messageText, setMessageText] = useState("");
const [messageHistory, setMessageHistory] = useState([]);
const { sendMessage, sendJsonMessage, lastMessage, lastJsonMessage, readyState } = useWebSocket(ipServer);
useEffect(() => {
const fetchIpAddress = async () => {
const ip = await NetworkInfo.getIPV4Address();
setIpAddress(ip);
console.log("ip adresses ; ", ip)
};
fetchIpAddress();
if (lastMessage !== null) {
setMessageHistory((prev) => prev.concat(lastMessage));
}
if (lastJsonMessage !== null) {
console.log(JSON.stringify(lastJsonMessage));
}
return () => {
};
}, [lastMessage, setMessageHistory,lastJsonMessage]);
const connectionStatus = {
[ReadyState.CONNECTING]: 'Connecting',
[ReadyState.OPEN]: 'Open',
[ReadyState.CLOSING]: 'Closing',
[ReadyState.CLOSED]: 'Closed',
[ReadyState.UNINSTANTIATED]: 'Uninstantiated',
}[readyState];
const sendJSON = () => {
const paramCmd = {
type: 'setParam',
param1: 30,
param2: 2.3,
}
sendJsonMessage(paramCmd);
}
return (
<View style={styles.mainBody}>
<Text
style={styles.mainTitle}>
AC Websocket Terminal
</Text>
<ScrollView>
<View style={styles.deviceItem}>
<View style={{flex:2}}>
<Text style={styles.deviceName}>{ipAddress}</Text>
<Text style={styles.deviceInfo}>{connectionStatus}</Text>
</View>
<TouchableOpacity
onPress={() => sendJSON()}
disabled={readyState !== ReadyState.OPEN}
style={styles.deviceButton}>
<Text
style={styles.buttonText}>
Send JSON
</Text>
</TouchableOpacity>
</View>
<View
style={styles.inputBar}>
<TextInput
style={styles.textInput}
placeholder="Enter a message"
value={messageText}
onChangeText={(text) => setMessageText(text)
}
/>
<TouchableOpacity
onPress={() => sendMessage(messageText)}
disabled={readyState !== ReadyState.OPEN}
style={[styles.sendButton]}>
<Text
style={styles.buttonText}>
SEND
</Text>
</TouchableOpacity>
</View>
<TextInput
placeholder="Server IP"
onChangeText={setIpServer}
value={ipServer}
/>
<View style={{flex:1,minHeight:200}}>
<Text>Received Message:</Text>
<ScrollView style={styles.textOutput}>
{lastMessage ? <Text>last message : {lastMessage.data}</Text> : null}
{messageHistory.map((message, idx) => (
<Text key={idx}>{message ? message.data : null}</Text>
))}
</ScrollView>
</View>
</ScrollView>
</View>
);
}
export default WebsocketTerminal;
let BACKGROUND_COLOR = "#161616"; //191A19
let BUTTON_COLOR = "#346751"; //1E5128
let ERROR_COLOR = "#C84B31"; //4E9F3D
let TEXT_COLOR = "#ECDBBA"; //D8E9A8
var styles = StyleSheet.create({
mainBody: { flex: 1, justifyContent: 'center', backgroundColor: BACKGROUND_COLOR},
mainTitle:{
color: TEXT_COLOR,
fontSize: 30,
textAlign: 'center',
borderBottomWidth: 2,
borderBottomColor: ERROR_COLOR,
},
backgroundVideo: {
borderWidth: 2,
borderColor: 'red',
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
webVideo: {
borderWidth: 2,
borderColor: 'green',
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
buttonText: {
color: TEXT_COLOR,
fontWeight: 'bold',
fontSize: 12,
textAlign: 'center',
textAlignVertical: 'center',
},
sendButton: {
backgroundColor: BUTTON_COLOR,
padding: 15,
borderRadius: 15,
margin: 2,
paddingHorizontal: 20,
},
deviceItem: {
flexDirection: 'row',
flex: 3,
marginBottom: 2,
},
deviceName: {
fontSize: 14,
fontWeight: 'bold',
},
deviceInfo: {
fontSize: 8,
},
deviceButton: {
backgroundColor: '#2196F3',
padding: 10,
borderRadius: 10,
margin: 2,
paddingHorizontal: 20,
},
inputBar:{
flexDirection: 'row',
justifyContent: 'space-between',
margin: 5,
},
textInput:{
backgroundColor: '#888888',
margin: 2,
borderRadius: 15,
flex:3,
},
textOutput:{
backgroundColor: '#333333',
margin: 10,
borderRadius: 2,
borderWidth: 1,
borderColor: '#EEEEEE',
textAlignVertical: 'top',
}
});
/** * https://reactnative.dev/docs/network * https://www.npmjs.com/package/react-use-websocket * https://github.com/robtaussig/react-use-websocket * test on python * https://websockets.readthedocs.io/en/stable/ */ import React, {useState, useEffect, useCallback} from 'react'; import { View, ScrollView, Text, TextInput, TouchableOpacity, StyleSheet} from 'react-native'; import { NetworkInfo } from 'react-native-network-info' import useWebSocket, { ReadyState } from 'react-use-websocket'; const WebsocketTerminal = () => { const [ipAddress, setIpAddress] = useState(''); const [ipServer, setIpServer] = useState('ws://192.168.1.52:8765'); const [messageText, setMessageText] = useState(""); const [messageHistory, setMessageHistory] = useState([]); const { sendMessage, sendJsonMessage, lastMessage, lastJsonMessage, readyState } = useWebSocket(ipServer); useEffect(() => { const fetchIpAddress = async () => { const ip = await NetworkInfo.getIPV4Address(); setIpAddress(ip); console.log("ip adresses ; ", ip) }; fetchIpAddress(); if (lastMessage !== null) { setMessageHistory((prev) => prev.concat(lastMessage)); } if (lastJsonMessage !== null) { console.log(JSON.stringify(lastJsonMessage)); } return () => { }; }, [lastMessage, setMessageHistory,lastJsonMessage]); const connectionStatus = { [ReadyState.CONNECTING]: 'Connecting', [ReadyState.OPEN]: 'Open', [ReadyState.CLOSING]: 'Closing', [ReadyState.CLOSED]: 'Closed', [ReadyState.UNINSTANTIATED]: 'Uninstantiated', }[readyState]; const sendJSON = () => { const paramCmd = { type: 'setParam', param1: 30, param2: 2.3, } sendJsonMessage(paramCmd); } return ( <View style={styles.mainBody}> <Text style={styles.mainTitle}> AC Websocket Terminal </Text> <ScrollView> <View style={styles.deviceItem}> <View style={{flex:2}}> <Text style={styles.deviceName}>{ipAddress}</Text> <Text style={styles.deviceInfo}>{connectionStatus}</Text> </View> <TouchableOpacity onPress={() => sendJSON()} disabled={readyState !== ReadyState.OPEN} style={styles.deviceButton}> <Text style={styles.buttonText}> Send JSON </Text> </TouchableOpacity> </View> <View style={styles.inputBar}> <TextInput style={styles.textInput} placeholder="Enter a message" value={messageText} onChangeText={(text) => setMessageText(text) } /> <TouchableOpacity onPress={() => sendMessage(messageText)} disabled={readyState !== ReadyState.OPEN} style={[styles.sendButton]}> <Text style={styles.buttonText}> SEND </Text> </TouchableOpacity> </View> <TextInput placeholder="Server IP" onChangeText={setIpServer} value={ipServer} /> <View style={{flex:1,minHeight:200}}> <Text>Received Message:</Text> <ScrollView style={styles.textOutput}> {lastMessage ? <Text>last message : {lastMessage.data}</Text> : null} {messageHistory.map((message, idx) => ( <Text key={idx}>{message ? message.data : null}</Text> ))} </ScrollView> </View> </ScrollView> </View> ); } export default WebsocketTerminal; let BACKGROUND_COLOR = "#161616"; //191A19 let BUTTON_COLOR = "#346751"; //1E5128 let ERROR_COLOR = "#C84B31"; //4E9F3D let TEXT_COLOR = "#ECDBBA"; //D8E9A8 var styles = StyleSheet.create({ mainBody: { flex: 1, justifyContent: 'center', backgroundColor: BACKGROUND_COLOR}, mainTitle:{ color: TEXT_COLOR, fontSize: 30, textAlign: 'center', borderBottomWidth: 2, borderBottomColor: ERROR_COLOR, }, backgroundVideo: { borderWidth: 2, borderColor: 'red', position: 'absolute', top: 0, left: 0, bottom: 0, right: 0, }, webVideo: { borderWidth: 2, borderColor: 'green', position: 'absolute', top: 0, left: 0, bottom: 0, right: 0, }, buttonText: { color: TEXT_COLOR, fontWeight: 'bold', fontSize: 12, textAlign: 'center', textAlignVertical: 'center', }, sendButton: { backgroundColor: BUTTON_COLOR, padding: 15, borderRadius: 15, margin: 2, paddingHorizontal: 20, }, deviceItem: { flexDirection: 'row', flex: 3, marginBottom: 2, }, deviceName: { fontSize: 14, fontWeight: 'bold', }, deviceInfo: { fontSize: 8, }, deviceButton: { backgroundColor: '#2196F3', padding: 10, borderRadius: 10, margin: 2, paddingHorizontal: 20, }, inputBar:{ flexDirection: 'row', justifyContent: 'space-between', margin: 5, }, textInput:{ backgroundColor: '#888888', margin: 2, borderRadius: 15, flex:3, }, textOutput:{ backgroundColor: '#333333', margin: 10, borderRadius: 2, borderWidth: 1, borderColor: '#EEEEEE', textAlignVertical: 'top', } });
/**
 * https://reactnative.dev/docs/network
 * https://www.npmjs.com/package/react-use-websocket
 * https://github.com/robtaussig/react-use-websocket
 * test on python
 * https://websockets.readthedocs.io/en/stable/
 */

import React, {useState, useEffect, useCallback} from 'react';
import {   
  View, 
  ScrollView, 
  Text,
  TextInput,
  TouchableOpacity, 
  StyleSheet} from 'react-native';
import { NetworkInfo } from 'react-native-network-info'
import useWebSocket, { ReadyState } from 'react-use-websocket';



const WebsocketTerminal = () =>  {

  const [ipAddress, setIpAddress] = useState('');
  const [ipServer, setIpServer] = useState('ws://192.168.1.52:8765');
  const [messageText, setMessageText] = useState("");
  const [messageHistory, setMessageHistory] = useState([]);
  const { sendMessage, sendJsonMessage, lastMessage, lastJsonMessage, readyState } = useWebSocket(ipServer);


  useEffect(() => {
    const fetchIpAddress = async () => {
      const ip = await NetworkInfo.getIPV4Address();
      setIpAddress(ip);
      console.log("ip adresses ; ", ip)
    };

    fetchIpAddress();
	
    if (lastMessage !== null) {
      setMessageHistory((prev) => prev.concat(lastMessage));
    }
    if (lastJsonMessage !== null) {
      console.log(JSON.stringify(lastJsonMessage));
    }
    return () => {
    };
  }, [lastMessage, setMessageHistory,lastJsonMessage]);

  const connectionStatus = {
    [ReadyState.CONNECTING]: 'Connecting',
    [ReadyState.OPEN]: 'Open',
    [ReadyState.CLOSING]: 'Closing',
    [ReadyState.CLOSED]: 'Closed',
    [ReadyState.UNINSTANTIATED]: 'Uninstantiated',
  }[readyState];

  const sendJSON = () => {
    const paramCmd = {
      type: 'setParam',
      param1: 30,
      param2: 2.3,
    }

    sendJsonMessage(paramCmd);

  }
  
  return (
    <View style={styles.mainBody}>
      <Text
        style={styles.mainTitle}>
        AC Websocket Terminal
      </Text>
      <ScrollView>

      <View style={styles.deviceItem}>
                      <View style={{flex:2}}>
                        <Text style={styles.deviceName}>{ipAddress}</Text>
                        <Text style={styles.deviceInfo}>{connectionStatus}</Text>
                      </View>
                      <TouchableOpacity
                        onPress={() => sendJSON()}
                        disabled={readyState !== ReadyState.OPEN}
                        style={styles.deviceButton}>
                        <Text
                          style={styles.buttonText}>
                          Send JSON
                        </Text>
                      </TouchableOpacity>
                    </View>


      <View
              style={styles.inputBar}>        
              <TextInput
                style={styles.textInput}
                placeholder="Enter a message"
                value={messageText}
                onChangeText={(text) =>    setMessageText(text)
                }
              />
              <TouchableOpacity
                        onPress={() => sendMessage(messageText)}
                        disabled={readyState !== ReadyState.OPEN}
                        style={[styles.sendButton]}>
                        <Text
                          style={styles.buttonText}>
                          SEND
                        </Text>
                      </TouchableOpacity>
        </View>

      <TextInput
        placeholder="Server IP"
        onChangeText={setIpServer}
        value={ipServer}
      />
      <View style={{flex:1,minHeight:200}}>
      <Text>Received Message:</Text>
              <ScrollView style={styles.textOutput}>
                {lastMessage ? <Text>last message : {lastMessage.data}</Text> : null}
                
                {messageHistory.map((message, idx) => (
                  <Text key={idx}>{message ? message.data : null}</Text>
                ))}
              </ScrollView>
              </View>
      </ScrollView>
    </View>
  );
}

export default WebsocketTerminal;


let BACKGROUND_COLOR = "#161616"; //191A19
let BUTTON_COLOR = "#346751"; //1E5128
let ERROR_COLOR = "#C84B31"; //4E9F3D
let TEXT_COLOR = "#ECDBBA"; //D8E9A8
var styles = StyleSheet.create({

  mainBody: { flex: 1, justifyContent: 'center',  backgroundColor: BACKGROUND_COLOR},

  mainTitle:{
    color: TEXT_COLOR,
    fontSize: 30,
    textAlign: 'center',
    borderBottomWidth: 2,
    borderBottomColor: ERROR_COLOR,
  },

  backgroundVideo: {
    borderWidth: 2,
    borderColor: 'red',
    position: 'absolute',
    top: 0,
    left: 0,
    bottom: 0,
    right: 0,
  },

  webVideo: {
    borderWidth: 2,
    borderColor: 'green',
    position: 'absolute',
    top: 0,
    left: 0,
    bottom: 0,
    right: 0,
  },

  buttonText: {
    color: TEXT_COLOR,
    fontWeight: 'bold',
    fontSize: 12,
	  textAlign: 'center',
    textAlignVertical: 'center',
  },

  sendButton: {
    backgroundColor: BUTTON_COLOR,
    padding: 15,
    borderRadius: 15,
    margin: 2,
    paddingHorizontal: 20,
    },

    deviceItem: {
      flexDirection: 'row',
      flex: 3,
      marginBottom: 2,
    },
    deviceName: {
      fontSize: 14,
      fontWeight: 'bold',
    },
    deviceInfo: {
      fontSize: 8,
    },
    deviceButton: {
      backgroundColor: '#2196F3',
      padding: 10,
      borderRadius: 10,
      margin: 2,
      paddingHorizontal: 20,
    },

  inputBar:{
    flexDirection: 'row',
    justifyContent: 'space-between',
    margin: 5,
  },  

  textInput:{
    backgroundColor: '#888888',
    margin: 2,
    borderRadius: 15,
    flex:3,
  },

  textOutput:{
    backgroundColor: '#333333',
    margin: 10,
    borderRadius: 2,
    borderWidth: 1,
    borderColor: '#EEEEEE',
    textAlignVertical: 'top',
  }

});

Sources