Can't figure out how to send commands to bluetooth device - react-native

So i've been struggling to implement a bluetooth fingerprint reader that I got from aliexpress. I have looked for 2 days now on how to do it and I am at the stage that I can connect and send stuff. But I have no clue how to do that without having the proper UUID and service. Here is the peripheral Info
{
"characteristics":[
{
"properties":{
"Read":"Read"
},
"characteristic":"2a00",
"service":"1800"
},
{
"properties":{
"Read":"Read"
},
"characteristic":"2a01",
"service":"1800"
},
{
"properties":{
"Read":"Read"
},
"characteristic":"2a04",
"service":"1800"
},
{
"properties":{
"Read":"Read"
},
"characteristic":"2a29",
"service":"180a"
},
{
"properties":{
"Read":"Read"
},
"characteristic":"2a24",
"service":"180a"
},
{
"properties":{
"Read":"Read"
},
"characteristic":"2a25",
"service":"180a"
},
{
"properties":{
"Read":"Read"
},
"characteristic":"2a27",
"service":"180a"
},
{
"properties":{
"Read":"Read"
},
"characteristic":"2a26",
"service":"180a"
},
{
"properties":{
"Read":"Read"
},
"characteristic":"2a28",
"service":"180a"
},
{
"properties":{
"Read":"Read"
},
"characteristic":"2a23",
"service":"180a"
},
{
"properties":{
"Read":"Read"
},
"characteristic":"2a2a",
"service":"180a"
},
{
"properties":{
"Write":"Write",
"Read":"Read"
},
"characteristic":"49535343-6daa-4d02-abf6-19569aca69fe",
"service":"49535343-fe7d-4ae5-8fa9-9fafd205e455"
},
{
"descriptors":[
{
"value":null,
"uuid":"2902"
}
],
"properties":{
"Notify":"Notify",
"Write":"Write"
},
"characteristic":"49535343-aca3-481c-91ec-d85e28a60318",
"service":"49535343-fe7d-4ae5-8fa9-9fafd205e455"
},
{
"descriptors":[
{
"value":null,
"uuid":"2902"
}
],
"properties":{
"Indicate":"Indicate",
"Notify":"Notify"
},
"characteristic":"fff1",
"service":"fff0"
},
{
"properties":{
"Write":"Write",
"WriteWithoutResponse":"WriteWithoutResponse"
},
"characteristic":"fff2",
"service":"fff0"
}
],
"services":[
{
"uuid":"1800"
},
{
"uuid":"180a"
},
{
"uuid":"49535343-fe7d-4ae5-8fa9-9fafd205e455"
},
{
"uuid":"fff0"
}
],
"advertising":{
"txPowerLevel":2,
"serviceData":{
},
"serviceUUIDs":[
"fff0"
],
"localName":"FGT19100003",
"isConnectable":true,
"manufacturerData":{
"bytes":[
2,
1,
2,
12,
9,
70,
71,
84,
49,
57,
49,
48,
48,
48,
48,
51,
3,
3,
240,
255,
2,
10,
2,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
"data":"AgECDAlGR1QxOTEwMDAwMwMD8P8CCgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"CDVType":"ArrayBuffer"
}
},
"rssi":-48,
"id":"88:1B:99:25:AD:54",
"name":"FGT19100003"
But I see characteristic with just 4 chars. Now to my knowledge they have a format of XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. I have gotten some source code in JAVA that I can upload if anyone wants to have a look at it (it works I installed it and it can scan the fingerprint and gives me the BitMatrix of it back.) (src java project: https://filebin.net/7jeo1o0lkgd8x2oy)
Here is the code for the function that I use in RN.
test(peripheral: { connected: any; id: string; }) {
if (peripheral) {
if (peripheral.connected) {
BleManager.disconnect(peripheral.id);
} else {
BleManager.connect(peripheral.id).then(() => {
let peripherals = this.state.peripherals;
let p = peripherals.get(peripheral.id);
if (p) {
p.connected = true;
peripherals.set(peripheral.id, p);
this.setState({ peripherals });
}
console.log('Connected to ' + peripheral.id);
setTimeout(() => {
BleManager.retrieveServices(peripheral.id).then((peripheralInfo) => {
console.log(JSON.stringify(peripheralInfo))
var service = '49535343-6daa-4d02-abf6-19569aca69fe';
var crustCharacteristic = '49535343-fe7d-4ae5-8fa9-9fafd205e455';
setTimeout(() => {
BleManager.startNotification(peripheral.id, service, crustCharacteristic).then(() => {
console.log('Started notification on ' + peripheral.id);
setTimeout(() => {
BleManager.write(peripheral.id, service, crustCharacteristic, [0]).then(() => {
console.log('write succes');
});
}, 500);
}).catch((error) => {
console.log('Notification error', error);
});
}, 200);
});
}, 900);
}).catch((error) => {
console.log('Connection error', error);
});
}
}
}
Does anyone have experiance with it? I also got a table that states which I think contains all possible commands (https://i.imgur.com/0TP0etn.png).

BLE Services & Characteristics that only have 16-bit UUIDs are the standards ones set by the Bluetooth SIG. They can be found here and here respectively. Most of the characteristics in that list are the standard ones, and I would bet the only one with a 128-bit UUID (0x49535343-fe7d-4ae5-8fa9-9fafd205e455) is the custom service for the finger print data, that or the 0xFFF0 service. They each have some characteristics with indicate/notify, so those are the ones that I imagine would have the data. 0xFFF0 is not a standard UUID, but it is often used in sample codes and implementation examples, but typically shouldn't be in a shipping product, but it has as much chance as being the right characteristic as the other one. I would try making an extra large data buffer, and setting the characteristics to notify/indicate in both services, then do a fingerprint test, and see what you get.
Alternatively, you can use one of the mobile phone BLE debug apps from Nordic (NRFconnect) or Cypress (CySmart) to do the same test and get an idea of the right characteristic by using your phone to perform the same test.

Related

Getting Only one Screen while recording Agora

I m getting only one screen while recording the screen
My Config
Start Recording
https://api.agora.io/v1/apps/${config.agora.appID}/cloud_recording/resourceid/${resource}/mode/${mode}/start,
{
cname: ',
uid: String(uid),
clientRequest: {
token,
recordingConfig: {
// subscribeVideoUids: ['#allstream#'],
// subscribeAudioUids: ['#allstream#'],
subscribeUidGroup: 0,
maxIdleTime: 30,
streamTypes: 2,
channelType: 0,
videoStreamType: 0,
audioProfile: 1,
transcodingConfig: {
height: 640,
width: 480,
bitrate: 400,
fps: 15,
mixedVideoLayout: 1,
backgroundColor: '#FFFFFF'
}
},
recordingFileConfig: {
avFileType: ['hls', 'mp4']
},
storageConfig: {
vendor: 1,
region: 14,
bucket: '',
accessKey: '',
secretKey: '*****',
fileNamePrefix: ['media', 'recordings']
}
}
},
{ headers: { Authorization } }
Stop Recording
`http://api.agora.io/v1/apps/${config.agora.appID}/cloud_recording/resourceid/${resource}/sid/${sid}/mode/${mode}/stop`, {
cname: eventId,
uid: String(uid),
clientRequest: {
async_stop: false
}
},
{ headers: { Authorization } }
You should use "mix" as the value for your mode if you wish to have all the screens in the recording. If you are using "individual" as mode you will get multiple dumps for each users audio and video.

Axios get response is not carried over into state [Native React]

first question here.
when i try to run this, it does fetch data in the response.data, but that data is not set into state to be passed through as prop to another page and it always stays [Object object] or [undefined] & i have no clue what's going wrong
state = {
APiData: {},
userInput: "cheese",
onCall: false
}
findFood = () => {
let self = this;
let userInput = this.state.userInput.toLowerCase();
let url = "https://api.spoonacular.com/recipes/search?query=" + userInput + "&number=1&apiKey="+apiKey;
axios.get(url)
.then(function (response) {
console.log(response.data); //get's data
self.setState({APIdata: response.data});
})
.catch(function (error) {
console.log(error)
});
}
renderbody = () => {
console.log(this.state.APIdata) //this thing, is undefined
// return (<SearchBody data={this.state.APIdata} key={apiKey}/>)
}
this is the data in response.data
"baseUri": "https://spoonacular.com/recipeImages/",
"expires": 1585843318293,
"isStale": false,
"number": 1,
"offset": 0,
"processingTimeMs": 437,
"results": Array [
Object {
"id": 215435,
"image": "three-cheese-pizza-for-cheese-lovers-215435.jpg",
"imageUrls": Array [
"three-cheese-pizza-for-cheese-lovers-215435.jpg",
],
"readyInMinutes": 45,
"servings": 8,
"title": "Three-Cheese Pizza (For Cheese Lovers)",
},
],
"totalResults": 855,
}
Object {
"baseUri": "https://spoonacular.com/recipeImages/",
"expires": 1585843318293,
"isStale": false,
"number": 1,
"offset": 0,
"processingTimeMs": 437,
"results": Array [
Object {
"id": 215435,
"image": "three-cheese-pizza-for-cheese-lovers-215435.jpg",
"imageUrls": Array [
"three-cheese-pizza-for-cheese-lovers-215435.jpg",
],
"readyInMinutes": 45,
"servings": 8,
"title": "Three-Cheese Pizza (For Cheese Lovers)",
},
],
"totalResults": 855,
}
You are not using state. Your state needs to be inside your constructor
constructor(props) {
super(props);
this.state = {
APiData: {},
userInput: "cheese",
onCall: false
}
}
Then, you just need to call this.setState({ APIdata: response.data });
I would recommend you to use react-redux to get data using this.props.

Get Latitude and Longitude from Google Places Autocomplete in React Native

I've implemented autocomplete for my address field, but the json returned from the Google Maps Places Autocomplete doesn't include the geocoded coords for the places.
There are some answers out there that don't seem to fit. For instance, this one refers to things like google.maps.places.Autocomplete(input, options); which I don't think is a thing in React Native.
Other answers appear to be based on react-native-google-places-autocomplete, but I've implemented this myself and I'd love to not do it again using that module.
Here's my method where I call the API.
async handleAddressChange() {
const url = `https://maps.googleapis.com/maps/api/place/autocomplete/json?key=${GoogleAPIKey}&input=${this.state.address}`;
try {
const result = await fetch(url);
const json = await result.json();
this.setState({ addressPredictions: json.predictions });
} catch (err) {
console.error(err);
}
}
setAddress(prediction) {
this.setState({ address: prediction.description, showPredictions: false });
}
The API response doesn't have any place or geometry property on it:
Object {
"description": "1234 Some Avenue Northeast, Washington, DC, USA",
"id": "4c79fba1b3a5ad33478b79b54896a75a4d56ca53",
"matched_substrings": Array [
Object {
"length": 4,
"offset": 0,
},
],
"place_id": "ChIJneQ1fBO5t4kRf8mTw4ieb4Q",
"reference": "ChIJneQ1fBO5t4kRf8mTw4ieb4Q",
"structured_formatting": Object {
"main_text": "1234 Some Avenue Northeast",
"main_text_matched_substrings": Array [
Object {
"length": 4,
"offset": 0,
},
],
"secondary_text": "Washington, DC, USA",
},
"terms": Array [
Object {
"offset": 0,
"value": "1600",
},
Object {
"offset": 5,
"value": "Maryland Avenue Northeast",
},
Object {
"offset": 32,
"value": "Washington",
},
Object {
"offset": 44,
"value": "DC",
},
Object {
"offset": 48,
"value": "USA",
},
],
"types": Array [
"street_address",
"geocode",
],
}
I have found a solution for the same-
Just use like this-
<GooglePlacesAutocomplete
GooglePlacesDetailsQuery={{ fields: "geometry" }}
fetchDetails={true} // you need this to fetch the details object onPress
placeholder="Search"
query={{
key: "API_KEY_GOES_HERE",
language: "en", // language of the results
}}
onPress={(data: any, details: any = null) => {
console.log("data", data);
console.log("details", details);
console.log(JSON.stringify(details?.geometry?.location));
}}
onFail={(error) => console.error(error)} />
Once you have the place id (ChIJneQ1fBO5t4kRf8mTw4ieb4Q for the example in your question), you can do a place details request.
Make sure you include the Places library in your API call: https://maps.googleapis.com/maps/api/js?libraries=places and a valid API key.
function initialize() {
var map = new google.maps.Map(document.getElementById('map-canvas'), {
center: new google.maps.LatLng(0, 0),
zoom: 15
});
var service = new google.maps.places.PlacesService(map);
service.getDetails({
placeId: 'ChIJneQ1fBO5t4kRf8mTw4ieb4Q'
}, function(place, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
// Create marker
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location
});
// Center map on place location
map.setCenter(place.geometry.location);
}
});
}
initialize();
#map-canvas {
height: 160px;
}
<div id="map-canvas"></div>
<!-- Replace the value of the key parameter with your own API key. -->
<script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&libraries=places&callback=initialize">
</script>

How to populate an array BEFORE mounted lifecycle hook

I have a simple API call in getFeedingsAgain. I am calling it inside "beforeMount" yet I can see via my console.logs that my data value for "catFeedingsAgain" stays empty once "mounted" is called. I am trying to populate the catFeedingsAgain array BEFORE the component's "mounted()" lifecycle hook is called so a function I have inside the "mounted()" hook can use that array data. How can I make this work?
Thank you for your time.
UPDATED: including entire component code now.
NOTE: I am basically trying to replace the initial array within Amcharts...Line Chart #2...."dataProvider" with the resultant array from the API call in getFeedingsAgain.
```
<template>
<!-- second chart group -->
<div class="chart-block" style="padding-top:50px">
{{ message }}
<div ref="line" style="vertical-align: middle; display: inline-block; width: 50%; height: 30px;"></div>
<div ref="column" style="vertical-align: middle;display: inline-block; width: 50%; height: 30px;"></div>
</div>
</template>
<script>
import axios from 'axios';
export default {
props: ['message'],
name: 'app',
computed:{
},
created(){
this.getFeedingsAgain(this.message);
},
data() {
return {
chartCatID: this.message,
catFeedingsAgain: [],
catMedicationsAgain: [],
}
},
mounted () {
/**
* Line Chart #2
*/
// this.getFeedingsAgain(this.message);
console.log("mounted");
console.log(this.catFeedingsAgain);
// TODO: line = weight(waf) / day(created?)
AmCharts.makeChart( this.$refs.line, {
"type": "serial",
"dataProvider": [ {
"day": 1,
"weight_after_food": 120
}, {
"day": 2,
"weight_after_food": 54
}, {
"day": 3,
"weight_after_food": -18
}, {
"day": 4,
"weight_after_food": -12
}, {
"day": 5,
"weight_after_food": -51
}, {
"day": 6,
"weight_after_food": 12
}, {
"day": 7,
"weight_after_food": 56
}, {
"day": 8,
"weight_after_food": 113
}, {
"day": 9,
"weight_after_food": 142
}, {
"day": 10,
"weight_after_food": 125
} ],
"categoryField": "day",
"autoMargins": false,
"marginLeft": 0,
"marginRight": 5,
"marginTop": 0,
"marginBottom": 0,
"graphs": [ {
"valueField": "weight_after_food",
"showBalloon": false,
"lineColor": "#ffbf63",
"negativeLineColor": "#289eaf"
} ],
"valueAxes": [ {
"gridAlpha": 0,
"axisAlpha": 0,
"guides": [ {
"weight_after_food": 0,
"lineAlpha": 0.1
} ]
} ],
"categoryAxis": {
"gridAlpha": 0,
"axisAlpha": 0,
"startOnAxis": true
}
} );
/**
* Column Chart #2
*/
// TODO: column = dose(dosage) / day(created?)
AmCharts.makeChart( this.$refs.column, {
"type": "serial",
"dataProvider": [ {
"day": 1,
"value": -5
}, {
"day": 2,
"value": 3
}, {
"day": 3,
"value": 7
}, {
"day": 4,
"value": -3
}, {
"day": 5,
"value": 3
}, {
"day": 6,
"value": 4
}, {
"day": 7,
"value": 6
}, {
"day": 8,
"value": -3
}, {
"day": 9,
"value": -2
}, {
"day": 10,
"value": 6
} ],
"categoryField": "day",
"autoMargins": false,
"marginLeft": 0,
"marginRight": 0,
"marginTop": 0,
"marginBottom": 0,
"graphs": [ {
"valueField": "value",
"type": "column",
"fillAlphas": 1,
"showBalloon": false,
"lineColor": "#ffbf63",
"negativeFillColors": "#289eaf",
"negativeLineColor": "#289eaf"
} ],
"valueAxes": [ {
"gridAlpha": 0,
"axisAlpha": 0
} ],
"categoryAxis": {
"gridAlpha": 0,
"axisAlpha": 0
}
} );
},
methods:{
getFeedingsAgain(value) {
axios.get(`${process.env.KITTY_URL}/api/v1/feedings/?cat__slug&cat__name=${value}`)
.then(response => {
console.log("getFeedingsAgain: ");
console.log(response.data.results);
this.catFeedingsAgain = response.data.results
})
.catch(error => console.log(error));
},
getMedicationsAgain(value) {
axios.get(`${process.env.KITTY_URL}/api/v1/medications/?cat__slug=&cat__name=${value}`)
.then(response => {console.log("catMedications: ");console.log(response.data.results); this.catMedications = response.data.results})
.catch(error => console.log(error));
},
}
}
</script>
<style>
.amcharts-chart-div a{
text-indent: -9999px;
outline: none;
}
</style>
```
You could also create a watcher, to perform an action when the variable changes.
something like this
watch: {
catFeedingsAgain: function() {
AmCharts.makeChart( this.$refs.line, {
"type": "serial",
"dataProvider": this.catFeedingsAgain,
...
});
}
},
you can find the documentation here; https://v2.vuejs.org/v2/guide/computed.html#Watchers
First, you should not use beforeMount() method. Avoid it as much as you can. Instead use created() life-cycle method:
created() {
this.getFeedingsAgain(this.message);
}
Second, you want to populate an array before mounted is called. But since your data is being loaded from API asynchronously, you cannot do that. You cannot stop/pause mounted event from happening. Vue.js doesn't know when API response will be available. It could take 2 seconds, 3 seconds or simply fail.
The only option you have is to use v-if in your template. As long as your array length is zero, you can hide DOM elements or show loading progress bar.
First, you should not use beforeMount() method. Avoid it as much as you can. Instead use created() life-cycle method:
created() {
this.getFeedingsAgain(this.message);
}
Second, you want to populate an array before mounted is called. But since your data is being loaded from API asynchronously, you cannot do that. You cannot stop/pause mounted event from happening. Vue.js doesn't know when API response will be available. It could take 2 seconds, 3 seconds or simply fail.
The only option you have is to use v-if in your template. As long as your array length is zero, you can hide DOM elements or show loading progress bar.
Finally, you may want to initialize some sort of Chart, then you can use Vue.js watcher or initialize it within the then() block of the API.
EDIT
I have to do this, then I will use watcher if I wish to initialize Charting component on every change like this:
watch: {
catFeedingsAgain() {
AmCharts.makeChart(this.$refs.line, {
"type": "serial",
"dataProvider": this.catFeedingsAgain,
});
}
},
If I have to do this only once, then I would something like this:
methods: {
getFeedingsAgain(value) {
axios.get(`${process.env.KITTY_URL}/api/v1/feedings/?cat__slug&cat__name=${value}`)
.then(response => {
this.catFeedingsAgain = response.data.results;
AmCharts.makeChart(this.$refs.line, {
"type": "serial",
"dataProvider": this.catFeedingsAgain,
});
})
.catch(error => console.log(error));
},
}
Both are right approaches. It all depends upon the context.
You should fetch the data when the view is created and the data is already being observed and this is happens at created hook.
// rename beforeMount -> created
created(){
this.getFeedingsAgain(this.message);
}
From Vue created docks
At this stage, the instance has finished processing the options which
means the following have been set up: data observation, computed
properties, methods, watch/event callbacks. However, the mounting
phase has not been started
Seems like this is what you are looking for

Cannot read property 'dataSets' of undefined

I always get this error when I update the line chart data. I know that setState is wrong, but the specific modification method cannot be found, and ask for help. Thank you.
I think it's the problem, but there's no solution.
this.setState({
data: {
...this.state.data.$set,
dataSets:[{
...this.state.data.$set.dataSets[0],
values
}]
},
xAxis: {
...this.state.xAxis.$set.valueFormatter,
valueFormatter,
axisMaximum,
axisMaximum
},
})
Chart github address:
https://github.com/wuxudong/react-native-charts-wrapper
This is the source code, just uploaded the picture, the feeling is not suitable for reading.
componentDidMount() {
this.setState(
update(this.state, {
data: {
$set: {
dataSets: [{
values: [{y: 10}, {y: 20}, {y: 30.11}, {y: 25.77}],
label: 'LineChart',
config: {
lineWidth: 0.5,
drawCircles: false,
highlightColor: processColor('red'),
color: processColor('red'),
drawFilled: true,
fillColor: processColor('red'),
fillAlpha: 60,
valueTextSize: 15,
valueFormatter: "##.000",
}
}],
}
},
xAxis: {
$set: {
fontFamily:"HelveticaNeue-Medium",
fontWeight:"bold",
fontStyle:"italic",
valueFormatter: ['2017-01-01 10:00:000','2017-02-01 10:00:000','2017-03-01 10:00:000'],
position: 'BOTTOM' ,
drawGridLines: false
}
},
yAxis: {
$set: {
left: {
drawLabels: true,
drawAxisLine: true,
drawGridLines: false,
drawValueAboveBar:false,
zeroLine: {
enabled: true,
lineWidth: 1
}
},
right: {
enabled: false
}
}
},
})
);
}
componentWillReceiveProps(nextProps) {
debugger;
if (nextProps.zxData !== this.props.zxData) {
let values=[];
let valueFormatter=[];
let axisMaximum=nextProps.zxData.length;
nextProps.zxData.map((item,key)=>{
values.push({y:item.YValue});
valueFormatter.push(item.XValue);
})
this.setState({
data: {
...this.state.data.$set,
dataSets:[{
...this.state.data.$set.dataSets[0],
values
}]
},
xAxis: {
...this.state.xAxis.$set.valueFormatter,
valueFormatter,
axisMaximum,
axisMaximum
},
})
}
}