Perda de dados após conectar o módulo bluetooth
Objetivo
Estou tentando retornar dados do dispositivo BlueTooth depois de conectado porque para usar a função de leitura e gravação, preciso de alguns dados. Dados de exemplo name, overflowServiceUUIDs, solicitedServiceUUIDs, mtu, rssi...e muitos outros. Porque se quero ler ou escrever preciso de alguns atributos. Estou usando a biblioteca react-native-ble-plx.
O que está acontecendo?
Depois que o dispositivo foi conectado perdi alguns valores.
Importante
type DeviceState = {
connected: boolean;
services: Service[];
device: Device | null;
characteristics: Record<string, Characteristic[]>;
};
const INITIAL_DEVICE_STATE = {
connected: false,
services: [],
device: null,
characteristics: {},
};
const [adapterState, setAdapterState] = useState(false);
const [bleDevices, setBleDevices] = useState<Device[]>([]);
const [isScanning, setIsScanning] = useState(false);
const [connectedDevice, setConnectedDevice] = useState<DeviceState>(
INITIAL_DEVICE_STATE,
);
# The state isScaning is used to be if we are scanning devices.
# The connectedDevice state will be the connected device.
As funções de sequência
toggleScanDevices ()
Colocará todos os dispositivos no bleDevicesestado.
const toggleScanDevices = () => {
setIsScanning(true);
setBleDevices([]);
bleManager.startDeviceScan(null, {}, (bleError, device) => {
if (device && _.findIndex(bleDevices, { id: device.id }) < 0) {
bleDevices.push(device);
setBleDevices(bleDevices);
}
});
setTimeout(() => {
setIsScanning(false);
bleManager.stopDeviceScan();
}, 5000);
};
toggleConnectDevice (device.name)
const toggleConnectDevice = (name: string) => async () => {
if (!connectedDevice.device) {
await connectDevice(name);
} else {
const { device } = connectedDevice;
if (!device) return;
await device.cancelConnection();
if (!(await device.isConnected())) {
setConnectedDevice(INITIAL_DEVICE_STATE);
}
}
};
connectDevice (nome)
const connectDevice = async (name: string) => {
let device = findDeviceWhereNameContains(name);
if (device === null) {
setConnectedDevice(INITIAL_DEVICE_STATE);
return false;
}
let isConnected = await device.isConnected();
if (!isConnected) {
/* Testar aqui */
device = await bleManager.connectToDevice(device.id);
isConnected = await device.isConnected();
}
device = await device.discoverAllServicesAndCharacteristics();
device.onDisconnected((error, device) => {
setConnectedDevice(INITIAL_DEVICE_STATE);
});
const services = await device.services();
const characteristics: Record<string, Characteristic[]> = {};
const descriptors = {};
_.forEach(services, async service => {
const deviceCharacteristics = await device?.characteristicsForService(
service.uuid,
);
characteristics[service.uuid] = deviceCharacteristics || [];
});
setConnectedDevice(state => ({
...state,
services,
characteristics,
device,
}));
const newDevice = { ...connectedDevice, device };
setConnectedDevice(newDevice);
console.log('não atualizado', connectedDevice);
console.log('novo valor', newDevice);
};
findDeviceWhereNameContains (nome)
const findDeviceWhereNameContains = (name: string) => {
const device = bleDevices.find(item => String(item.name).includes(name));
if (device !== undefined) {
return device;
}
return null;
};
Dentro da connectDevicefunção tenho um let deviceque recebo o valor sobre o findDeviceWhereNameContains, se logar essa variável devicerecebo muitos dados muito importantes, mas ainda não estou conectado. Então quando eu verificar sobre o if (!isConnected)aqui vou conectar, e depois disso, do avesso se ao logar devicenovamente perdi alguns valores.
O log antes de conectar
O log após conectar
Device {overflowServiceUUIDs: null, solicitedServiceUUIDs: null, localName: null, isConnectable: null, txPowerLevel: null, …}
overflowServiceUUIDs: null
solicitedServiceUUIDs: null
localName: null
isConnectable: null
txPowerLevel: null
serviceUUIDs: null
serviceData: null
mtu: null
name: "MLT-BT05"
manufacturerData: null
rssi: null
id: "88:25:83:F0:30:BC"
Respostas
Verificando a lib que você está usando, ele cria outro objeto de dispositivo a partir da resposta que obtém ao chamar o módulo nativo, pode ser o caso de esse novo objeto vir com valores nulos nesses campos e substituir os valores que você precisa. Como está exatamente como funciona agora, você pode copiar esses valores para outro objeto antes que eles sejam apagados
import pick from 'lodash/pick';
const connectDevice = async (name: string) => {
let device = findDeviceWhereNameContains(name);
// according to the screenshot, the object still have the
// information you want at this point
// taking advantage taht you have lodash already you can use pick
const valuesYouNeed = pick(device, ['serviceUUIDs', ...]);
...
// here you merge them back with other device's attr that are present at this point
const newDevice = { ...pick(device, ['connected', ...]), ...valuesYouNeed };
setConnectedDevice(newDevice);
console.log('não atualizado', connectedDevice);
console.log('novo valor', newDevice);
};
Mas você deve ter cuidado para que eles não sejam substituídos novamente.
Se você acha que isso é um comportamento falho de react-native-ble-plxvocê poderia abrir um PR com as alterações no Deviceconstrutor para evitar que isso aconteça.
Já que você está usando o texto datilografado, você terá algumas reclamações sobre tipificações, você pode criar um tipo a partir do tipo de react-native-ble-plxdispositivo e escolher apenas os atributos de parte dele para omitir métodos e evitar armazenar objetos complexos em seu estado.
import { Device } from 'react-native-ble-plx`;
type DeviceState = Pick<
Device,
| 'id'
| 'name'
| 'rssi'
| 'mtu'
| 'manufacturerData'
| 'serviceData'
| 'serviceUUIDs'
| 'localName'
| 'txPowerLevel'
| 'solicitedServiceUUIDs'
| 'isConnectable'
| 'overflowServiceUUIDs'
>