[ISSUE-14] Multiple clients write support (#16)
* [ISSUE-14] Multiple clients write support
This commit is contained in:
parent
eb047847f2
commit
f5562128fb
|
@ -5,6 +5,9 @@ tmp.js
|
||||||
# Compiled
|
# Compiled
|
||||||
build
|
build
|
||||||
|
|
||||||
|
# Dist
|
||||||
|
dist
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
|
|
@ -5,4 +5,3 @@ docs
|
||||||
yarn.lock
|
yarn.lock
|
||||||
*.test.js
|
*.test.js
|
||||||
README.md
|
README.md
|
||||||
dist
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "redux-bluetooth",
|
"name": "redux-bluetooth",
|
||||||
"version": "0.1.17",
|
"version": "0.1.18",
|
||||||
"description": "Redux middleware to dispatch actions via bluetooth to a peripheral store",
|
"description": "Redux middleware to dispatch actions via bluetooth to a peripheral store",
|
||||||
"main": "build/index.js",
|
"main": "build/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -2,9 +2,11 @@ export default function Characteristic(uuid, Parent, util, descriptor, { encode,
|
||||||
function ReduxCharacteristic() {
|
function ReduxCharacteristic() {
|
||||||
Parent.call(this, {
|
Parent.call(this, {
|
||||||
uuid,
|
uuid,
|
||||||
properties: ['write', 'notify'],
|
properties: ['writeWithoutResponse', 'read', 'notify'],
|
||||||
descriptors: [descriptor],
|
descriptors: [descriptor],
|
||||||
});
|
});
|
||||||
|
this.messages = {};
|
||||||
|
this.maxValueSize = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
util.inherits(ReduxCharacteristic, Parent);
|
util.inherits(ReduxCharacteristic, Parent);
|
||||||
|
@ -13,18 +15,42 @@ export default function Characteristic(uuid, Parent, util, descriptor, { encode,
|
||||||
function (data, offset, withoutResponse, callback) {
|
function (data, offset, withoutResponse, callback) {
|
||||||
if (offset) {
|
if (offset) {
|
||||||
callback(this.RESULT_ATTR_NOT_LONG);
|
callback(this.RESULT_ATTR_NOT_LONG);
|
||||||
return;
|
return this.RESULT_ATTR_NOT_LONG;
|
||||||
}
|
}
|
||||||
|
|
||||||
const action = decode(data);
|
const buffer = decode(data);
|
||||||
this.onAction(JSON.parse(action));
|
const id = buffer.slice(0, 9);
|
||||||
|
const chunk = buffer.slice(10, this.maxValueSize);
|
||||||
|
const message = ((this.messages[id] || '') + chunk);
|
||||||
|
if (message.startsWith('[[[') && message.endsWith(']]]')) {
|
||||||
|
const action = JSON.parse(message.slice(3, message.length - 3));
|
||||||
|
this.onAction(action);
|
||||||
|
this.messages[id] = '';
|
||||||
|
} else {
|
||||||
|
this.messages[id] = message;
|
||||||
|
}
|
||||||
|
|
||||||
callback(this.RESULT_SUCCESS);
|
callback(this.RESULT_SUCCESS);
|
||||||
|
return this.RESULT_SUCCESS;
|
||||||
|
};
|
||||||
|
|
||||||
|
ReduxCharacteristic.prototype.onReadRequest = function (offset, callback) {
|
||||||
|
if (offset) {
|
||||||
|
callback(this.RESULT_ATTR_NOT_LONG, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const configuration = encode(
|
||||||
|
`|||${JSON.stringify(this.configuration)}|||`,
|
||||||
|
);
|
||||||
|
callback(this.RESULT_SUCCESS, configuration);
|
||||||
};
|
};
|
||||||
|
|
||||||
ReduxCharacteristic.prototype.onSubscribe = function (maxValueSize, updateValueCallback) {
|
ReduxCharacteristic.prototype.onSubscribe = function (maxValueSize, updateValueCallback) {
|
||||||
this.maxValueSize = maxValueSize;
|
this.maxValueSize = maxValueSize;
|
||||||
this.updateValueCallback = updateValueCallback;
|
this.updateValueCallback = updateValueCallback;
|
||||||
|
this.configuration = {
|
||||||
|
limit: maxValueSize,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
ReduxCharacteristic.prototype.onUnsubscribe = function () {
|
ReduxCharacteristic.prototype.onUnsubscribe = function () {
|
||||||
|
|
|
@ -16,7 +16,7 @@ let decode = null;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
encode = jest.fn().mockReturnValue('mockEncode');
|
encode = jest.fn().mockReturnValue('mockEncode');
|
||||||
decode = jest.fn().mockReturnValue('{"mockDecode":"mockDecode"}');
|
decode = jest.fn().mockReturnValue('123456789:]]]');
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -25,15 +25,21 @@ afterEach(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('new Characteristic', () => {
|
test('new Characteristic', () => {
|
||||||
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', {
|
||||||
|
encode,
|
||||||
|
decode,
|
||||||
|
});
|
||||||
|
|
||||||
expect(characteristic.uuid).toBe('mockUUID');
|
expect(characteristic.uuid).toBe('mockUUID');
|
||||||
expect(characteristic.properties).toEqual(['write', 'notify']);
|
expect(characteristic.properties).toEqual(['writeWithoutResponse', 'read', 'notify']);
|
||||||
expect(characteristic.descriptors).toEqual(['mockDescriptor']);
|
expect(characteristic.descriptors).toEqual(['mockDescriptor']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Characteristic.onWriteRequest: RESULT_ATTR_NOT_LONG', () => {
|
test('Characteristic.onWriteRequest: RESULT_ATTR_NOT_LONG', () => {
|
||||||
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', {
|
||||||
|
encode,
|
||||||
|
decode,
|
||||||
|
});
|
||||||
|
|
||||||
const callback = jest.fn();
|
const callback = jest.fn();
|
||||||
characteristic.onWriteRequest(null, true, false, callback);
|
characteristic.onWriteRequest(null, true, false, callback);
|
||||||
|
@ -41,28 +47,68 @@ test('Characteristic.onWriteRequest: RESULT_ATTR_NOT_LONG', () => {
|
||||||
expect(callback).toBeCalledWith('RESULT_ATTR_NOT_LONG');
|
expect(callback).toBeCalledWith('RESULT_ATTR_NOT_LONG');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Characteristic.onWriteRequest: RESULT_SUCCESS / Chunk', () => {
|
||||||
test('Characteristic.onWriteRequest: RESULT_SUCCESS', () => {
|
|
||||||
let callback = null;
|
let callback = null;
|
||||||
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', {
|
||||||
|
encode,
|
||||||
|
decode,
|
||||||
|
});
|
||||||
|
|
||||||
callback = jest.fn();
|
callback = jest.fn();
|
||||||
const spyOnAction = jest.spyOn(characteristic, 'onAction');
|
const spyOnAction = jest.spyOn(characteristic, 'onAction');
|
||||||
characteristic.onWriteRequest(null, false, false, callback);
|
characteristic.onWriteRequest(null, false, false, callback);
|
||||||
|
|
||||||
expect(spyOnAction).toBeCalledWith({ mockDecode: 'mockDecode' });
|
expect(spyOnAction).not.toBeCalled();
|
||||||
expect(callback).toBeCalledWith('RESULT_SUCCESS');
|
expect(characteristic.messages[123456789]).toEqual(']]]');
|
||||||
|
|
||||||
callback = jest.fn();
|
|
||||||
characteristic.onAction = jest.fn();
|
|
||||||
characteristic.onWriteRequest(null, false, false, callback);
|
|
||||||
|
|
||||||
expect(characteristic.onAction).toBeCalledWith({ mockDecode: 'mockDecode' });
|
|
||||||
expect(callback).toBeCalledWith('RESULT_SUCCESS');
|
expect(callback).toBeCalledWith('RESULT_SUCCESS');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Characteristic.onWriteRequest: RESULT_SUCCESS / Message', () => {
|
||||||
|
let callback = null;
|
||||||
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', {
|
||||||
|
encode,
|
||||||
|
decode,
|
||||||
|
});
|
||||||
|
|
||||||
|
callback = jest.fn();
|
||||||
|
const spyOnAction = jest.spyOn(characteristic, 'onAction');
|
||||||
|
characteristic.messages[123456789] = '[[[{"mockDecode":"mockDecode"}';
|
||||||
|
characteristic.onWriteRequest(null, false, false, callback);
|
||||||
|
|
||||||
|
expect(spyOnAction).toBeCalledWith({ mockDecode: 'mockDecode' });
|
||||||
|
expect(characteristic.messages[123456789]).toEqual('');
|
||||||
|
expect(callback).toBeCalledWith('RESULT_SUCCESS');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Characteristic.onReadRequest: RESULT_ATTR_NOT_LONG', () => {
|
||||||
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', {
|
||||||
|
encode,
|
||||||
|
decode,
|
||||||
|
});
|
||||||
|
|
||||||
|
const callback = jest.fn();
|
||||||
|
characteristic.onReadRequest(true, callback);
|
||||||
|
|
||||||
|
expect(callback).toBeCalledWith('RESULT_ATTR_NOT_LONG', null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Characteristic.onReadRequest: RESULT_SUCCESS', () => {
|
||||||
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', {
|
||||||
|
encode,
|
||||||
|
decode,
|
||||||
|
});
|
||||||
|
|
||||||
|
const callback = jest.fn();
|
||||||
|
characteristic.onReadRequest(false, callback);
|
||||||
|
|
||||||
|
expect(callback).toBeCalledWith('RESULT_SUCCESS', 'mockEncode');
|
||||||
|
});
|
||||||
|
|
||||||
test('Characteristic.onSubscribe', () => {
|
test('Characteristic.onSubscribe', () => {
|
||||||
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', {
|
||||||
|
encode,
|
||||||
|
decode,
|
||||||
|
});
|
||||||
|
|
||||||
characteristic.onSubscribe(null, 'mockUpdateValueCallback');
|
characteristic.onSubscribe(null, 'mockUpdateValueCallback');
|
||||||
|
|
||||||
|
@ -70,7 +116,10 @@ test('Characteristic.onSubscribe', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Characteristic.onUnsubscribe', () => {
|
test('Characteristic.onUnsubscribe', () => {
|
||||||
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', {
|
||||||
|
encode,
|
||||||
|
decode,
|
||||||
|
});
|
||||||
|
|
||||||
characteristic.onSubscribe(null, 'mockUpdateValueCallback');
|
characteristic.onSubscribe(null, 'mockUpdateValueCallback');
|
||||||
characteristic.onUnsubscribe();
|
characteristic.onUnsubscribe();
|
||||||
|
@ -79,7 +128,10 @@ test('Characteristic.onUnsubscribe', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Characteristic.updateState', () => {
|
test('Characteristic.updateState', () => {
|
||||||
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', { encode, decode });
|
const characteristic = Characteristic('mockUUID', Parent, util, 'mockDescriptor', {
|
||||||
|
encode,
|
||||||
|
decode,
|
||||||
|
});
|
||||||
|
|
||||||
characteristic.updateState({ mockState: 'mockState' });
|
characteristic.updateState({ mockState: 'mockState' });
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export default function Central(
|
export default function Central(
|
||||||
|
id,
|
||||||
bluetooth,
|
bluetooth,
|
||||||
{ encode, decode },
|
{ encode, decode },
|
||||||
{ SERVICE_UUID, CHARACTERISTIC_UUID }) {
|
{ SERVICE_UUID, CHARACTERISTIC_UUID }) {
|
||||||
|
@ -6,6 +7,10 @@ export default function Central(
|
||||||
server: null,
|
server: null,
|
||||||
characteristic: null,
|
characteristic: null,
|
||||||
message: '',
|
message: '',
|
||||||
|
configuration: {
|
||||||
|
limit: 20,
|
||||||
|
},
|
||||||
|
id,
|
||||||
};
|
};
|
||||||
|
|
||||||
const connect = name => bluetooth
|
const connect = name => bluetooth
|
||||||
|
@ -22,29 +27,58 @@ export default function Central(
|
||||||
state.characteristic = characteristic;
|
state.characteristic = characteristic;
|
||||||
});
|
});
|
||||||
|
|
||||||
const handler = callback => state.characteristic.startNotifications().then(() => {
|
const listener = callback => (event) => {
|
||||||
const listerner = (event) => {
|
const chunk = decode(event.target.value);
|
||||||
const chunk = decode(event.target.value);
|
const message = `${state.message}${chunk}`;
|
||||||
const message = `${state.message}${chunk}`;
|
if (message.startsWith('[[[') && message.endsWith(']]]')) {
|
||||||
|
const json = JSON.parse(message.slice(3, message.length - 3));
|
||||||
|
callback(json);
|
||||||
|
state.message = '';
|
||||||
|
} else if (message.startsWith('|||') && message.endsWith('|||')) {
|
||||||
|
state.message = '';
|
||||||
|
} else {
|
||||||
|
state.message = message;
|
||||||
|
}
|
||||||
|
return state.message;
|
||||||
|
};
|
||||||
|
|
||||||
if (message.startsWith('[[[') && message.endsWith(']]]')) {
|
const handler = callback => state.characteristic.startNotifications()
|
||||||
const json = JSON.parse(message.slice(3, message.length - 3));
|
.then(() => {
|
||||||
callback(json);
|
const eventListener = listener(callback);
|
||||||
state.message = '';
|
state.characteristic.addEventListener('characteristicvaluechanged', eventListener);
|
||||||
} else {
|
return state.characteristic.readValue();
|
||||||
state.message = message;
|
}).then((data) => {
|
||||||
}
|
const configuration = decode(data);
|
||||||
};
|
state.configuration = JSON.parse(configuration.slice(3, configuration.length - 3));
|
||||||
state.characteristic.addEventListener('characteristicvaluechanged', listerner);
|
return state.configuration;
|
||||||
return listerner;
|
});
|
||||||
});
|
|
||||||
|
|
||||||
const write = (action) => {
|
const write = (action) => {
|
||||||
if (!state.server || !state.server.connected || !state.characteristic) return null;
|
if (!state.server || !state.server.connected || !state.characteristic) return Promise.reject();
|
||||||
const stringify = JSON.stringify(action);
|
const stringify = `[[[${JSON.stringify(action)}]]]`;
|
||||||
const serialized = encode(stringify);
|
const message = encode(stringify);
|
||||||
|
const key = encode(`${state.id}:`);
|
||||||
|
const dataSize = state.configuration.limit - key.length;
|
||||||
|
const writes = [];
|
||||||
|
|
||||||
return state.characteristic.writeValue(serialized);
|
|
||||||
|
let i = 0;
|
||||||
|
do {
|
||||||
|
const next = i + dataSize;
|
||||||
|
const end = Math.min(next, message.length);
|
||||||
|
const data = message.slice(i, end);
|
||||||
|
const buffer = new Uint8Array(key.length + data.length);
|
||||||
|
buffer.set(key, 0);
|
||||||
|
buffer.set(data, key.length);
|
||||||
|
writes.push(buffer);
|
||||||
|
|
||||||
|
i = next;
|
||||||
|
} while (i < message.length);
|
||||||
|
|
||||||
|
// Serialize Promises
|
||||||
|
return writes.reduce((promise, chunk) =>
|
||||||
|
promise.then(() => state.characteristic.writeValue(chunk)),
|
||||||
|
Promise.resolve());
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -52,5 +86,6 @@ export default function Central(
|
||||||
connect,
|
connect,
|
||||||
handler,
|
handler,
|
||||||
write,
|
write,
|
||||||
|
listener,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ const bluetooth = {
|
||||||
let central = null;
|
let central = null;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
central = new Central(bluetooth, encoder, CENTRAL_CONFIG);
|
central = new Central(123, bluetooth, encoder, CENTRAL_CONFIG);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
@ -60,44 +60,87 @@ test('Central: connect', () => {
|
||||||
|
|
||||||
test('Central: handler', () => {
|
test('Central: handler', () => {
|
||||||
const callback = jest.fn();
|
const callback = jest.fn();
|
||||||
expect.assertions(3);
|
expect.assertions(4);
|
||||||
|
|
||||||
const promise = central.connect('mockName').then(() => central.handler(callback))
|
const promise = central.connect('mockName').then(() => central.handler(callback))
|
||||||
.then((listerner) => {
|
.then((configuration) => {
|
||||||
|
expect(configuration).toEqual({ mockDecode: 'mockDecode' });
|
||||||
expect(characteristic.startNotifications).toBeCalled();
|
expect(characteristic.startNotifications).toBeCalled();
|
||||||
listerner({ target: { value: 'mockEvent' } });
|
expect(characteristic.readValue).toBeCalled();
|
||||||
expect(callback).toBeCalledWith({ mockDecode: 'mockDecode' });
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
return expect(promise).resolves.toBe(true);
|
return expect(promise).resolves.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Central: handler chunk', () => {
|
test('Central: listener - chunk message', () => {
|
||||||
const callback = jest.fn();
|
const callback = jest.fn();
|
||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
|
|
||||||
encoder.decode = jest.fn().mockReturnValue('{"mockDecode":"mockDecode"}');
|
encoder.decode = jest.fn().mockReturnValue('{"mockDecode":"mockDecode"}');
|
||||||
central = new Central(bluetooth, encoder, CENTRAL_CONFIG);
|
central = new Central(123, bluetooth, encoder, CENTRAL_CONFIG);
|
||||||
|
|
||||||
const promise = central.connect('mockName').then(() => central.handler(callback))
|
const promise = central.connect('mockName')
|
||||||
.then((listerner) => {
|
.then(() => {
|
||||||
expect(characteristic.startNotifications).toBeCalled();
|
const listener = central.listener(callback);
|
||||||
listerner({ target: { value: 'mockEvent' } });
|
const message = listener({ target: { value: 'mockEvent' } });
|
||||||
expect(callback.mock.calls.length).toEqual(0);
|
expect(callback).not.toBeCalled();
|
||||||
|
expect(message).toEqual('{"mockDecode":"mockDecode"}');
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
return expect(promise).resolves.toBe(true);
|
return expect(promise).resolves.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Central: write', () => {
|
test('Central: listener - complete message', () => {
|
||||||
|
const callback = jest.fn();
|
||||||
|
expect.assertions(3);
|
||||||
|
|
||||||
|
encoder.decode = jest.fn().mockReturnValue('[[[{"mockDecode":"mockDecode"}]]]');
|
||||||
|
central = new Central(123, bluetooth, encoder, CENTRAL_CONFIG);
|
||||||
|
|
||||||
|
const promise = central.connect('mockName')
|
||||||
|
.then(() => {
|
||||||
|
const listener = central.listener(callback);
|
||||||
|
const message = listener({ target: { value: 'mockEvent' } });
|
||||||
|
expect(callback).toBeCalledWith({ mockDecode: 'mockDecode' });
|
||||||
|
expect(message).toEqual('');
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return expect(promise).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Central: listener - internal message', () => {
|
||||||
|
const callback = jest.fn();
|
||||||
|
expect.assertions(3);
|
||||||
|
|
||||||
|
encoder.decode = jest.fn().mockReturnValue('|||{"mockDecode":"mockDecode"}|||');
|
||||||
|
central = new Central(123, bluetooth, encoder, CENTRAL_CONFIG);
|
||||||
|
|
||||||
|
const promise = central.connect('mockName')
|
||||||
|
.then(() => {
|
||||||
|
const listener = central.listener(callback);
|
||||||
|
const message = listener({ target: { value: 'mockEvent' } });
|
||||||
|
expect(callback).not.toBeCalled();
|
||||||
|
expect(message).toEqual('');
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return expect(promise).resolves.toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Central: write - empty message', () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
|
|
||||||
|
encoder.encode = jest.fn().mockReturnValue([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
|
||||||
|
central = new Central(123, bluetooth, encoder, CENTRAL_CONFIG);
|
||||||
|
|
||||||
const promise = central.connect('mockName')
|
const promise = central.connect('mockName')
|
||||||
.then(() => central.write({ type: 'ACTION' }))
|
.then(() => central.write({ type: 'ACTION' }))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(characteristic.writeValue).toBeCalledWith('mockEncode');
|
expect(characteristic.writeValue)
|
||||||
|
.toBeCalledWith(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import Central from './central';
|
||||||
const { navigator, TextDecoder, TextEncoder } = window;
|
const { navigator, TextDecoder, TextEncoder } = window;
|
||||||
|
|
||||||
export default new Central(
|
export default new Central(
|
||||||
|
`${Date.now()}`.slice(4, 13), // Client ID
|
||||||
navigator.bluetooth,
|
navigator.bluetooth,
|
||||||
Encoder({ TextEncoder, TextDecoder }),
|
Encoder({ TextEncoder, TextDecoder }),
|
||||||
CENTRAL_CONFIG,
|
CENTRAL_CONFIG,
|
||||||
|
|
|
@ -11,6 +11,7 @@ global.navigator = { bluetooth: null };
|
||||||
|
|
||||||
test('central', () => {
|
test('central', () => {
|
||||||
const result = new Central(
|
const result = new Central(
|
||||||
|
123,
|
||||||
null,
|
null,
|
||||||
true,
|
true,
|
||||||
CENTRAL_CONFIG,
|
CENTRAL_CONFIG,
|
||||||
|
|
Loading…
Reference in New Issue