React Native - Is there a way to use bluetooth in peripheral mode? - react-native

I am developing an app in ReactNative offline.
One of the functionalities is to use bluetooth to synchronize the data (that the app was collecting) with other devices that use the same app.
I started to develop this task with the react-native-ble-manager library, i can connect from device A to device B, but I don't understand how to listen to the incoming connection in device B. I need to know this to show a certain view.
can anybody help me?
I am using the correct library?
Thanks!

You can't use only the react-native-ble-manager for this project. The library states in its readme that it is based on cordova-plugin-ble-central, which can only act as a central. For a BLE connection you need a central and a peripheral.
Take a look at react-native-peripheral. It allows you to act as a perihperal, create a characteristic with some data, add it to a service and register it so other devices can find it. This is their usage example:
import Peripheral, { Service, Characteristic } from 'react-native-peripheral'
Peripheral.onStateChanged(state => {
// wait until Bluetooth is ready
if (state === 'poweredOn') {
// first, define a characteristic with a value
const ch = new Characteristic({
uuid: '...',
value: '...', // Base64-encoded string
properties: ['read', 'write'],
permissions: ['readable', 'writeable'],
})
// add the characteristic to a service
const service = new Service({
uuid: '...',
characteristics: [ch],
})
// register GATT services that your device provides
Peripheral.addService(service).then(() => {
// start advertising to make your device discoverable
Peripheral.startAdvertising({
name: 'My BLE device',
serviceUuids: ['...'],
})
})
}
})
There is a section about dynamic values where they explain how you can use the onReadRequest and onWriteRequest callbacks to listen to read and write operations on the peripheral and even return dynamic values on each read request:
new Characteristic({
uuid: '...',
properties: ['read', 'write'],
permissions: ['readable', 'writeable'],
onReadRequest: async (offset?: number) => {
const value = '...' // calculate the value
return value // you can also return a promise
},
onWriteRequest: async (value: string, offset?: number) => {
// store or do something with the value
this.value = value
},
})

Related

How to update state machine

Is it possible to replace the currently initialized state machine in React?
For example, I initialize a state machine via Provider with a dummy configuration. Then, upon entering a specific page in the app, I update it via context.
Background of what I want to achieve: Consumer can either load the configuration globally or page-specific. Let's say for example that a required data for your config is unknown until you reach a specific portion of your page. So, only then you can properly create a state machine. Or, if consumer wants to keep the configuration per module/page. Bec doing this at the top level would mean, you have to collate all configurations which can get lengthy and hard to debug assuming you know the configuration in advance.
In provider
const dummyConfig = {
id: 'init',
initial: 'init_state',
states: {
init_state: {
on: {},
},
},
};
const initMachine = createMachine(dummyConfig);
const [stateMachine, setStateMachine] = useState(initMachine);
// stateMachine does not get updated with the new machine from the child component below
// passed via setMachine callback in the context
const [current, send, service] = useMachine(stateMachine);
const updateMachine = useCallback((machine: StateMachine) => {
setStateMachine(machine);
}, []);
return (
<MachineContext.Provider value={[current, send, service, updateMachine]}>
{children}
</StateMachineContext.Provider>
);
In child component somewhere in the app
const machine = createMachine(newConfig);
// Context hook
const { current, send, servicee, updateMachine } = useStateMachine();
// I can receive the new machine up in the provider (checked via console log),
// but it does not update the machine
// The value of current remains that from the first created machine
updateMachine(machine);

electron.js and sql - correct way to set it up?

I am new to electron.js - been reading the documentation and some similar post here:
How do I make a database call from an Electron front end?
Secure Database Connection in ElectronJS Production App?
Electron require() is not defined
How to use preload.js properly in Electron
But it's still not super clear how to properly implement a secure SQL integration. Basically, I want to create a desktop database client. The app will connect to the remote db and users can run all kind of predefined queries and the results will show up in the app.
The documentation says that if you are working with a remote connection you shouldn't run node in the renderer. Should I then require the SQL module in the main process and use IPC to send data back and forth and preload IPCremote?
Thanks for the help
Short answer: yes
Long answer:
Allowing node on your renderer poses a big security risk for your app. It is best practices in this case to create pass a function to your preloader. There are a few options you can use to do this:
Pass a ipcRenderer.invoke function wrapped in another function to your renderer in your preload. You can then invoke a call to your main process which can either send info back via the same function or via sending it via the window.webContents.send command and listening for it on the window api on your renderer. EG:
Preload.js:
const invoke = (channel, args, cb = () => {return}) => {
ipcRenderer.invoke(channel, args).then((res) => {
cb(res);
});
};
const handle = (channel, cb) => {
ipcRenderer.on(channel, function (Event, message) {
cb(Event, message);
});
};
contextBridge.exposeInMainWorld("GlobalApi", {
invoke: invoke,
handle:handle
});
Renderer:
let users
window.GlobalApi.handle("users", (data)=>{users=data})
window.GlobalApi.invoke("get", "users")
or:
let users;
window.GlobalApi.invoke("get", "users", (data)=>{users=data})
Main:
ipcMain.handle("get", async (path) => {
let data = dbFunctions.get(path)
window.webContents.send(
path,
data
);
}
Create a DB interface in your preload script that passes certain invocations to your renderer that when called will return the value that you need from your db. E.G.
Renderer:
let users = window.myCoolApi.get("users");
Preload.js:
let get = function(path){
let data = dbFuncions.readSomeDatafromDB("path");
return data; // Returning the function itself is a no-no shown below
// return dbFuncions.readSomeDatafromDB("path"); Don't do this
}
contextBridge.exposeInMainWorld("myCoolApi", {
get:get
});
There are more options, but these should generally ensure security as far as my knowledge goes.

Reading GPS Receiver using WebUSB

I am trying to use WebUSB to read GPS coordinates from a USB-connected GPS receiver from within javascript.
I have been able to connect to the receiver; however, I am unsure how to use WebUSB to access the NMEA messages.
So far, I have the following proof-of-concept code:
<html>
<head>
<title>WebUSB Serial Sample Application</title>
</head>
<body>
Connect<br>
Send Data<br>
Read Data<br>
<script>
let y;
let device;
var connectButton = document.getElementById('click')
connectButton.addEventListener('click', function() {
navigator.usb.requestDevice({
filters: [{}]
}).then((selectedDevice) => {
device = selectedDevice;
return device.open()
.then(() => device.selectConfiguration(1))
.then(() => device.claimInterface(device.configuration.interfaces[0].interfaceNumber))
.then(() => device.selectAlternateInterface(device.configuration.interfaces[0].interfaceNumber, 0))
.then(() => {
y = device;
})
});
})
var sendButton = document.getElementById('send')
var sendDecoder = new TextDecoder()
sendButton.addEventListener('click', async () => {
y.controlTransferOut({
requestType: 'class',
recipient: 'interface',
request: 0x22,
value: 0x01,
index: 0x00});
y.controlTransferIn({
requestType: 'standard',
recipient: 'device',
request: 0x06,
value: 0x0302,
index: 0x409
}, 255)
.then(result => {
let decoder = new TextDecoder()
console.log(sendDecoder.decode(result.data));
console.log('sent req');
}).catch(error => {
console.log(error);
});
});
</script>
</body>
</html>
The transferOut statement allows me to read the Vendor name from the device. So, I know I'm connected and can communicate with it. I just don't know what specific command(s) are needed to access the lat/lon from the device.
UPDATE:
Below is a snapshot of the device config info
There are a couple of layers you'll need to understand in order to accomplish your goal. The first is the USB interface implemented by the device.
In the example code you've posted there are two calls which send commands to the device. The first is a controlTransferOut() which sends the standard USB CDC-ACM SET_CONTROL_LINE_STATE request (0x22) to the device to enable the DTR signal (0x01). If your device implements the USB CDC-ACM protocol then this is the write thing to do as it signals to the device that the host software is ready to receive data. If your device doesn't implement this protocol then the command will be ignored or fail. You're already claiming the first interface, to understand USB protocol that interface implements you should check device.configuration.interfaces[0].alternates[0].interfaceClass and device.configuration.interfaces[1].alternates[0].interfaceClass. A USB CDC-ACM device will have one interface with class 2 and one with class 10. Class 2 is for the control interface while class 10 is the data interface. If your device doesn't have these two interfaces then it probably isn't implementing the USB CDC-ACM protocol and you'll have to figure out what protocol it uses first.
The second call you make is a controlTransferIn() which sends the standard USB GET_DESCRIPTOR request (0x06) to read a string descriptor from the device. Passing 0x0302 asks it to read the string descriptor (type 3) at index 2. This will work for any USB device (assuming the index is right) and so doesn't tell you whether you've figured out what protocol the interface supports.
Assuming this is a USB CDC-ACM interface then you'll want to look at device.configuration.interfaces[1].alternates[0].endpoints and figure out the endpoint numbers for the IN and OUT endpoints. These are what you'll pass to transferIn() and transferOut() to send and receive data from the device.
Once you have all that figured out you'll need to figure out how to get the device to start sending NMEA messages. If you are lucky then in its default mode it will automatically send them and you can just start calling transferIn() to receive them. Otherwise you will have to figure out what command to send to the device to put it in a the right mode. If you have and documentation for the device or example code written in other languages that supports this device then that will be very helpful for figuring this out.
I have finally solved this problem. The answer (for me, anyway) was to purchase a different GPS receiver that implemented the CDC-ACM interface since there seems to be more examples and better docs for this protocol.
The following proof-of-concept code is working:
<html>
<head>
<title>WebUSB Serial Sample Application</title>
</head>
<body>
Connect<br>
Read Data<br>
<script>
let y;
let device;
var connectButton = document.getElementById('click')
connectButton.addEventListener('click', function() {
navigator.usb.requestDevice({
filters: [{}]
})
.then((selectedDevice) => {
device = selectedDevice;
return device.open();
})
.then(() => device.selectConfiguration(1))
.then(() => device.claimInterface(device.configuration.interfaces[0].interfaceNumber))
.then(() => device.claimInterface(device.configuration.interfaces[1].interfaceNumber))
.then(() => device.selectAlternateInterface(device.configuration.interfaces[0].interfaceNumber, 0))
.then(() => device.selectAlternateInterface(device.configuration.interfaces[1].interfaceNumber, 0))
.then(() => {
y = device;
})
})
var readButton = document.getElementById('read')
var readDecoder = new TextDecoder()
var readLoop = () => {
y.transferIn(2,64)
.then(result => {
let decoder = new TextDecoder()
console.log(readDecoder.decode(result.data.buffer));
readLoop();
}).catch(error => {
console.log(error);
});
};
readButton.addEventListener('click', async () => {
readLoop();
});
</script>
</body>
</html>

Multiple realms in React Native don't query realm object server correctly on first launch of app after install

I am having an issue dealing with multiple realms in React Native. I'm working on an app that allows users to use the app without having a subscription (using a local realm) and then at any point in their app journey they have the option of upgrading to a subscription with syncing (which uses sync to a realm object server).
When I start the app I check to see if they are using sync and if so I initialize a synced realm with their user and everything works great. I get all the data I expect.
However, when the app starts on first launch after install (that part about first launch after install is crucial) and I see that they don't use sync I initialize a local realm which I save data to until they decide to log in to their sync account (if they have one). At this point I attempt to pull information from the synced realm but it does not have the information that I saw when I only initialized the synced realm (in the case that on app startup I detect they use sync).
I am able to log in as the sync user but the data isn't there if I've previously initialized a local realm AND this logic gets run on the first launch of the app after install. The data only shows up from the realm object server when I initialize a local and synced realm on a secondary launch of the app (no reinstall before launching).
Here's a simple test script with dummy data in it with which I've been able to replicate the observed behavior:
const username = 'testuser2';
const password = 'supersecret';
const tld = 'REALM_OBJECT_SERVER_TLD';
class Test extends Realm.Object {}
Test.schema = {
name: 'Test',
properties: {
id: {
type: 'string',
},
}
};
function initLocalRealm() {
return new Realm({
path: 'local.realm',
schema: [Test],
});
}
function initSyncedRealmWithUser(user) {
return new Realm({
path: 'synced.realm',
sync: {
user,
url: `realm://${tld}:9080/~/data`,
},
schema: [Test],
});
}
function writeTestObjectWithId(realm, id) {
realm.write(() => {
realm.create('Test', {
id,
});
alert(`Test object with id: ${id}`);
});
}
initLocalRealm();
// setup
// uncomment this and comment out the login section to setup user on first run
// Realm.Sync.User.register(`http://${tld}:9080`, username, password, (error, user) => {
// if (error) {
// return;
// }
// const syncedRealm = initSyncedRealmWithUser(user);
// writeTestObjectWithId(syncedRealm, '1');
// });
// login
Realm.Sync.User.login(`http://${tld}:9080`, username, password, (error, user) => {
if (error) {
return;
}
const syncedRealm = initSyncedRealmWithUser(user);
alert(`Synced realm test objects: ${syncedRealm.objects('Test').length}`);
});
If you create a react native app and then add this code to the main components componentDidMount function you should see that on the first run of the app (after you've uncommented the register code once) you will see the Test collection length at 0, but then when you refresh you will see the Test collection length at 1.
Any help on this would be awesome.
Thanks!
running your code snippet, I get a length of 1 immediately as soon as I uncomment the login section. Could you try observing your synchronized realm with the Realm Browser and see if it seems to have the data you are expecting after registering the user?

difference between udid and client_identification_sequence

I cannot understand somethings about the push notification with quickblox
I have a chat (a webchat) in a webview in a Xamarin apps (i know isn't very clever approach)
i try to create a subscrition via javascript
but i cannot undestand the way for calculate
uuid and client_identification_sequence
var params = {
notification_channels: 'gcm',
device: {
platform: 'android',
udid: '538a068a-d66a-44d4-86c8-18ffed7f20d8'
},
push_token: {
environment: 'development',
client_identification_sequence: ''
}
}; 
QB.pushnotifications.subscriptions.create(params, function (err, res) {
debugger;
if (err) {
debugger;
// error
} else {
debugger;
// success
}
});
i've tried to calculate the uuid with "Xam.Plugin.DeviceInfo"
but what is the way for client_identification_sequence?
should I take this value from the "apns" (for apple push notification) but where?
I have the same roblem with the xamarin project
var d = await wbWrapper.SubscribeForPushNotificationAsync([pushtoken], CrossDeviceInfo.Current.GenerateAppId());
thankyou
uuid - it's your device unique identifier. It's actually can be anything that uniquely identify current particular device.
client_identification_sequence - it's your push token.
For Android - it's registration id (or registration token).
For iOS - it's device token