I am trying to pair a gateway to React native app using the react-native-ble-plx.
The below source code works fine in Android wheras in iOS, the bleManager.startDeviceScan() is not triggered. Nothing happens after this step.
Any help is much appreciated!
Source code:
const connectBLE = () => {
const subscription = bleManager.onStateChange(async (state) => {
if (state === 'PoweredOn') {
subscription.remove();
scanAndConnect();
}
};
}
const scanAndConnect = () => {
bleManager.startDeviceScan(null, null, async (error, device) => {
if (error) {
showToast(error, 'error');
console.log('Handle error - scanning will be stopped automatically');
return;
}
console.log('Devices');
console.log(device.name);
// Check if it is a device you are looking for based on device name
if (device.name === "BLE_0006") {
// Stop scanning as we have found the device.
bleManager.stopDeviceScan();
// Establish Device connection.
device
.connect()
.then((deviceData) => {
/** Show Toast on Device disconnect */
bleManager.onDeviceDisconnected(
deviceData.id,
(connectionError, connectionData) => {
if (connectionError) {
console.log(connectionError);
}
console.log('Device is disconnected');
console.log(connectionData);
},
);
/** Discover All Services and Characteristics */
return device.discoverAllServicesAndCharacteristics();
})
.then(async (deviceObject) => {
console.log('deviceObject');
console.log(deviceObject);
/** Subscribe for the Readable service */
device.monitorCharacteristicForService(
Enum.bleConnectionInfo.customServiceUUID,
Enum.bleConnectionInfo.readCharacteristicUUID,
(error, characteristic) => {
if (error) {
console.log('Error in monitorCharacteristicForService');
console.log(error.message);
return;
}
console.log(characteristic.uuid, characteristic.value);
]);
},
);
})
.catch((error) => {
console.warn(error);
showToast(error, 'error');
});
}
});
}
This might be helpful for someone!
The Issue was resolved by moving the bleManager() initialization outside the functional component.
Related
Hello I have a streaming service, (it is an online radio) that I need to stream in my app, the url it is the following
https://cast.uncuartocomunicacion.com:8020/live
I have been using react native sound player to stream it but I had issues with android's performance. So I switched to react native track player, but I haven't been able of playing the streaming service.
the following it is the code I have been using.
const start = async () => {
await TrackPlayer.setupPlayer();
await TrackPlayer.add({
id: 'trackI1',
url: 'https://cast.uncuartocomunicacion.com:8020/live',
title: 'Area deportiva',
artist: 'Area deportiva',
artwork: {
uri:
'https://pbs.twimg.com/profile_images/1480935488232075270/STi9FaUo_400x400.jpg',
},
});
TrackPlayer.updateOptions({
stopWithApp: false,
});
setLoading(false);
};
useEffect(() => {
//let isMounted = true;
//Alert.alert(audioUrl);
//getUrl();
//SoundPlayer.loadUrl('https://cast.uncuartocomunicacion.com:8020/live');
/*TrackPlayer.setupPlayer()
.then(() => {
setLoading(false);
})
.catch((e) => {
setLoading(false);
});*/
start()
.then()
.catch((e) => {
Alert.alert('e ' + JSON.stringify(e));
});
}, []);
const handlePlayPause = () => {
console.warn('asa is playing ', isPlaying);
/* */
try {
/* !isPlaying ? SoundPlayer.resume() : SoundPlayer.pause();*/
if (!isPlaying) {
Alert.alert('enre aquiu ');
TrackPlayer.play()
.then((r) => {
Alert.alert('then play' + JSON.stringify(r));
})
.catch((e) => {
Alert.alert('e ' + JSON.stringify(e));
});
} else {
TrackPlayer.pause().then((r) => console.log(r));
}
} catch (e) {}
setIsPlaying(!isPlaying);
};
I hope someone can help me!!!
For some reason when you play audio with SSL and use another port (8020) it doesn't work, but you can try using http and add
android:usesCleartextTraffic="true"
in AndroidManifest
Don't know what's the issue here.
If anyone know what's the issue here then please help me out.
I am trying to make anyonymous login.
Don't know what's the issue here.
If anyone know what's the issue here then please help me out.
I am trying to make anyonymous login.
const LoginManager = () => {
// Set an initializing state whilst Firebase connects
const [initializing, setInitializing] = useState(true);
const [user, setUser] = useGlobal('user');
// Handle user state changes
function onAuthStateChanged(user) {
setUser(user);
if (initializing) {
setInitializing(false);
}
}
useEffect(() => {
const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
return subscriber; // unsubscribe on unmount
// eslint-disable-next-line react-hooks/exhaustive-deps
if (initializing) {
return null;
}
if (!user) {
return auth()
.signInAnonymously()
.then(() => {
console.log('User signed in anonymously');
})
.catch((error) => {
if (error.code === 'auth/operation-not-allowed') {
console.log('Enable anonymous in your firebase console.');
}
console.log(error);
});
} else {
console.log('User already signed in.');
}
}, []);
return null;
};
I want to get user's current location and set it into AsyncStorage a array. I will do it in the useEffect hook. But the problem is my functions are not working that according to given order. Here are my code
useEffect(() => {
getUserLocation();
setUserLocation();
check();
}, []);
/*Get User's Currunt Location*/
const getUserLocation = () => {
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 15000,
})
.then((location) => {
var lt = location.latitude;
var lg = location.longitude;
setlatitude(lt);
setlongitude(lg);
console.log("getUserLocation", lt, lg);
})
.catch((error) => {
const { code, message } = error;
console.warn(code, message);
});
};
/*Set User's Currunt Location to AsyncStorage*/
const setUserLocation = async () => {
try {
await AsyncStorage.setItem("user_location", JSON.stringify(userLocation));
console.log("setUserLocation", userLocation);
} catch (error) {
console.log("error setting user location");
}
};
const check = () => {
AsyncStorage.getItem("user_location", (err, result) => {
if (result !== null) {
console.log("check", result);
setlatitude(result.latitude);
setlongitude(result.longitude);
} else {
console.log("Data Not Found");
}
});
};
Whenever you use .then you are scheduling your code to run at some point in the future, when the promise has completed. So setUserLocation runs before the then of getUserLocation.
Also, it looks like your getUserLocation set react state, which won't be available until the next render. We use effects to manage this.
// Get the location on mount
useEffect(getUserLocation, []);
// Whenever the location updates, set it into storage
useEffect(() => setUserLocation().then(check), [latitude, longitude]);
geolocation.getCurrentPosition() is not working in Android mobiles, is this an Ionic bug? Or is there any better solution to achieve this?
ngOnInit() {
this.geolocation.getCurrentPosition().then((resp) => {
}).catch((error) => {
console.log('Error getting location', error);
});
let watch = this.geolocation.watchPosition();
watch.subscribe((data) => {
this.data = data;
this.currentLat = data.coords.latitude;
this.currentLng = data.coords.longitude;
this.accuracy = data.coords.accuracy;
});
}
I had this issue once and was able to solve it by enabling High Accuracy GPS on the mobile device.
You can use the Location Accuracy Plugin
https://ionicframework.com/docs/native/location-accuracy
to activate high accuracy mode when loading the application
import { LocationAccuracy } from '#ionic-native/location-accuracy/ngx';
constructor(private locationAccuracy: LocationAccuracy) { }
...
this.locationAccuracy.canRequest().then((canRequest: boolean) => {
if(canRequest) {
this.locationAccuracy.request(this.locationAccuracy.REQUEST_PRIORITY_HIGH_ACCURACY).then(
() => console.log('Request successful'),
error => console.log('Error requesting location permissions', error)
);
}
});
or you can simply pass
this.geolocation.getCurrentPosition({ enableHighAccuracy : true, timeout: 10000 } )
Try setting the next values in the variable options
import { Geolocation, Geoposition } from '../../node_modules/#ionic-native/geolocation';
constructor(private geolocation: Geolocation)
...
var options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
this.geolocation.getCurrentPosition(options)
.then((result: Geoposition) => {
console.log(result)
})
.catch(error => {
console.error(error)
})
});
I would like connect 2 devices with WebRTC on localhost. All devices have no internet access. They are connected to a same local wifi.
I try this on React Native App.
In this context offline, do I need to trickle ICE candidates and addIceCandidate ? If I understund correctly, ICE candidates is for iceServer. But my case, iceServer is null (because i'm offline only, connected on same localhost wifi) :
const configuration = { iceServers: [{ urls: [] }] };
So actualty i exchange offer and answer, but after setRemoteDescription the answer, the connectionState stay on checking.
You can see my React Component :
constructor(props) {
super(props);
this.pc = new RTCPeerConnection(configuration);
}
state = initialState;
componentDidMount() {
const { pc } = this;
if (pc) {
this.setState({
peerCreated: true
});
}
this.setConnectionState();
pc.oniceconnectionstatechange = () => this.setConnectionState();
pc.onaddstream = ({ stream }) => {
if (stream) {
this.setState({
receiverVideoURL: stream.toURL()
});
}
};
pc.onnegotiationneeded = () => {
if (this.state.initiator) {
this.createOffer();
}
};
pc.onicecandidate = ({ candidate }) => {
if (candidate === null) {
const { offer } = this.state;
const field = !offer ? 'offer' : 'data';
setTimeout(() => {
alert('setTimeout started');
this.setState({
[field]: JSON.stringify(pc.localDescription)
});
}, 2000);
}
};
}
#autobind
setConnectionState() {
this.setState({
connectionState: this.pc.iceConnectionState
});
}
getUserMedia() {
MediaStreamTrack.getSources(() => {
getUserMedia(
{
audio: false,
video: true
},
this.getUserMediaSuccess,
this.getUserMediaError
);
});
}
#autobind
async getUserMediaSuccess(stream) {
const { pc } = this;
pc.addStream(stream);
await this.setState({ videoURL: stream.toURL() });
if (this.state.initiator) {
return this.createOffer();
}
return this.createAnswer();
}
getUserMediaError(error) {
console.log(error);
}
#autobind
logError(error) {
const errorArray = [...this.state.error, error];
return this.setState({
error: errorArray
});
}
/**
* Create offer
*
* #memberof HomeScreen
*/
#autobind
createOffer() {
const { pc } = this;
pc.createOffer()
.then(offer => pc.setLocalDescription(offer))
.then(() => {
this.setState({
offerCreated: true
});
})
.catch(this.logError);
}
/**
* Create anwser
*
* #memberof HomeScreen
*/
#autobind
async createAnswer() {
const { pc } = this;
const { data } = this.state;
if (data) {
const sd = new RTCSessionDescription(JSON.parse(data));
await this.setState({
offerImported: true
});
pc.setRemoteDescription(sd)
.then(() => pc.createAnswer())
.then(answer => pc.setLocalDescription(answer))
.then(() => {
this.setState({
answerCreated: true
});
})
.catch(this.logError);
}
}
#autobind
receiveAnswer() {
const { pc } = this;
const { data } = this.state;
const sd = new RTCSessionDescription(JSON.parse(data));
return pc
.setRemoteDescription(sd)
.then(() => {
this.setState({
answerImported: true
});
})
.catch(this.logError);
}
/**
* Start communication
*
* #param {boolean} [initiator=true]
* #returns
* #memberof HomeScreen
*/
#autobind
async start(initiator = true) {
if (!initiator) {
await this.setState({
initiator: false
});
}
return this.getUserMedia();
}
Anyone can help me?
No iceServers is fine on a LAN, but peers must still exchange at least one candidate: their host candidate (based on their machine's LAN IP address).
Either:
Trickle candidates using onicecandidate -> signaling -> addIceCandidate as usual, or
Out-wait the ICE process (a few seconds) before exchanging pc.localDescription.
It looks like you're attempting the latter. This approach works because...
Trickle ICE is an optimization.
The signaling (trickling) of individual ice candidates using onicecandidate, is an optimization meant to speed up negotiation. Once setLocalDescription succeeds, the browser's internal ICE Agent starts, inserting ICE candidates, as they're discovered, into the localDescription itself. Wait a few seconds to negotiate, and trickling isn't necessary at all: all ICE candidates will be in the offer and answer transmitted.
Your code
From your onicecandidate code it looks like you're already attempting to gather the localDescription after ICE completion (and you've written it to work from both ends):
pc.onicecandidate = ({ candidate }) => {
if (!candidate) {
const { offer } = this.state;
const field = !offer ? 'offer' : 'data';
this.setState({
[field]: JSON.stringify(pc.localDescription)
});
}
};
In the offerer side you've correctly commented out the equivalent code in createOffer:
pc.createOffer()
.then(offer => pc.setLocalDescription(offer))
.catch(this.logError);
// .then(() => {
// this.setState({
// offer: JSON.stringify(pc.localDescription)
// });
// });
But on the answerer side, you have not, and that's likely the problem:
createAnswer() {
const { pc } = this;
const { data } = this.state;
if (data) {
const sd = new RTCSessionDescription(JSON.parse(data));
pc.setRemoteDescription(sd)
.then(() => pc.createAnswer())
.then(answer => pc.setLocalDescription(answer))
.then(() => {
this.setState({
offer: JSON.stringify(pc.localDescription)
});
})
.catch(this.logError);
}
}
This means it sends an answer back immediately, before the answerer's ICE agent has had time to insert any candidates into the answer. This is probably why it fails.
On a side-note: Nothing appears to wait for getUserMedia to finish either, so answers likely won't contain any video either, depending on the timing of your getUserMediaSuccess function, which fails to add any tracks or streams to the connection. But assuming you're just doing data channels, this should work with my recommended fixes.