Skip to content

Commit

Permalink
Merge pull request #405 from afedotov/fix-qiyi-cube
Browse files Browse the repository at this point in the history
Fix QiYi smart cube driver issues
  • Loading branch information
cs0x7f authored Jul 16, 2024
2 parents 5d2ff53 + 133dd95 commit 6e1dcc8
Showing 1 changed file with 81 additions and 16 deletions.
97 changes: 81 additions & 16 deletions src/js/hardware/bluetooth.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@ var GiikerCube = execMain(function() {
var cube = undefined;
var _device = null;

function toUuid128(uuid) {
if (/^[0-9A-Fa-f]{4}$/.exec(uuid)) {
uuid = "0000" + uuid + "-0000-1000-8000-00805F9B34FB";
}
return uuid.toUpperCase();
}

function matchUUID(uuid1, uuid2) {
return uuid1.toUpperCase() == uuid2.toUpperCase();
return toUuid128(uuid1) == toUuid128(uuid2);
}

var GiikerCube = (function() {
Expand Down Expand Up @@ -1394,6 +1401,8 @@ var GiikerCube = execMain(function() {
var SERVICE_UUID = '0000fff0' + UUID_SUFFIX;
var CHRCT_UUID_CUBE = '0000fff6' + UUID_SUFFIX;

var QIYI_CIC_LIST = [0x0504];

var decoder = null;
var deviceMac = null;
var KEYS = ['NoDg7ANAjGkEwBYCc0xQnADAVgkzGAzHNAGyRTanQi5QIFyHrjQMQgsC6QA'];
Expand All @@ -1409,11 +1418,13 @@ var GiikerCube = execMain(function() {
savedMacMap[_deviceName] = deviceMac;
kernel.setProp('giiMacMap', JSON.stringify(savedMacMap));
}
}
if (!deviceMac || forcePrompt) {
} else {
var savedMacMap = JSON.parse(kernel.getProp('giiMacMap', '{}'));
var mac = savedMacMap[_deviceName];
if (!mac || forcePrompt) {
if (!mac && /^QY-QYSC-.-[0-9A-F]{4}$/.exec(_deviceName)) {
mac = 'CC:A3:00:00:' + _deviceName.slice(10, 12) + ':' + _deviceName.slice(12, 14);
}
mac = prompt((isWrongKey ? 'The MAC provided might be wrong!\n' : '') + GIIKER_REQMACMSG, mac || 'xx:xx:xx:xx:xx:xx');
}
var m = /^([0-9a-f]{2}[:-]){5}[0-9a-f]{2}$/i.exec(mac);
Expand Down Expand Up @@ -1480,14 +1491,61 @@ var GiikerCube = execMain(function() {
return sendMessage(content);
}

function init(device) {
_deviceName = device.name;
if (/^QY-QYSC-.-[0-9A-F]{4}$/.exec(_deviceName)) {
deviceMac = 'cc:a3:00:00:' + _deviceName.slice(10, 12) + ':' + _deviceName.slice(12, 14);
function getManufacturerDataBytes(mfData) {
if (mfData instanceof DataView) { // this is workaround for Bluefy browser
return new DataView(mfData.buffer.slice(2));
}
for (var id of QIYI_CIC_LIST) {
if (mfData.has(id)) {
DEBUG && console.log('[qiyicube] found Manufacturer Data under CIC = 0x' + id.toString(16).padStart(4, '0'));
return mfData.get(id);
}
}
initMac(true);
DEBUG && console.log('[qiyicube] Looks like this cube has new unknown CIC');
}

function waitForAdvs() {
if (!_device || !_device.watchAdvertisements) {
return Promise.reject(-1);
}
var abortController = new AbortController();
return new Promise(function(resolve, reject) {
var onAdvEvent = function(event) {
DEBUG && console.log('[qiyicube] receive adv event', event);
var mfData = event.manufacturerData;
var dataView = getManufacturerDataBytes(mfData);
if (dataView && dataView.byteLength >= 6) {
var mac = [];
for (var i = 5; i >= 0; i--) {
mac.push((dataView.getUint8(i) + 0x100).toString(16).slice(1));
}
_device && _device.removeEventListener('advertisementreceived', onAdvEvent);
abortController.abort();
resolve(mac.join(':'));
}
};
_device.addEventListener('advertisementreceived', onAdvEvent);
_device.watchAdvertisements({ signal: abortController.signal });
setTimeout(function() { // reject if no mac found
_device && _device.removeEventListener('advertisementreceived', onAdvEvent);
abortController.abort();
reject(-2);
}, 10000);
});
}

function init(device) {
clear();
_deviceName = device.name.trim();
DEBUG && console.log('[qiyicube]', 'start init device');
return device.gatt.connect().then(function(gatt) {
return waitForAdvs().then(function (mac) {
DEBUG && console.log('[qiyicube] init, found cube bluetooth hardware MAC = ' + mac);
deviceMac = mac;
}, function(err) {
DEBUG && console.log('[qiyicube] init, unable to automatically determine cube MAC, error code = ' + err);
}).then(function() {
return device.gatt.connect();
}).then(function(gatt) {
_gatt = gatt;
return gatt.getPrimaryService(SERVICE_UUID);
}).then(function(service) {
Expand All @@ -1496,7 +1554,7 @@ var GiikerCube = execMain(function() {
return _service.getCharacteristics();
}).then(function(chrcts) {
for (var i = 0; i < chrcts.length; i++) {
var chrct = chrcts[i]
var chrct = chrcts[i];
DEBUG && console.log('[qiyicube]', 'init find chrct', chrct.uuid);
if (matchUUID(chrct.uuid, CHRCT_UUID_CUBE)) {
_chrct_cube = chrct;
Expand All @@ -1506,6 +1564,7 @@ var GiikerCube = execMain(function() {
_chrct_cube.addEventListener('characteristicvaluechanged', onCubeEvent);
return _chrct_cube.startNotifications();
}).then(function() {
initMac(true);
return sendHello(deviceMac);
});
}
Expand Down Expand Up @@ -1552,7 +1611,7 @@ var GiikerCube = execMain(function() {
if (opcode == 0x2) { // cube hello
sendMessage(msg.slice(2, 7));
var newFacelet = parseFacelet(msg.slice(7, 34));
callback(newFacelet, [], [ts / 1.6, locTime], _deviceName);
callback(newFacelet, [], [Math.trunc(ts / 1.6), locTime], _deviceName);
prevCubie.fromFacelet(newFacelet);
batteryLevel = msg[35];
giikerutil.updateBattery([batteryLevel, _deviceName]);
Expand Down Expand Up @@ -1588,7 +1647,7 @@ var GiikerCube = execMain(function() {
prevMoves.unshift("URFDLB".charAt(axis) + " 2'".charAt(power));
prevMoves = prevMoves.slice(0, 8);
curFacelet = curCubie.toFaceCube();
toCallback.push([curFacelet, prevMoves.slice(), [todoMoves[i][1], locTime], _deviceName]);
toCallback.push([curFacelet, prevMoves.slice(), [Math.trunc(todoMoves[i][1] / 1.6), locTime], _deviceName]);
var tmp = curCubie;
curCubie = prevCubie;
prevCubie = tmp;
Expand All @@ -1597,7 +1656,7 @@ var GiikerCube = execMain(function() {
if (newFacelet != curFacelet) {
DEBUG && console.log('[qiyicube]', 'facelet', newFacelet);
curCubie.fromFacelet(newFacelet);
callback(newFacelet, prevMoves, [ts, locTime], _deviceName);
callback(newFacelet, prevMoves, [Math.trunc(ts / 1.6), locTime], _deviceName);
var tmp = curCubie;
curCubie = prevCubie;
prevCubie = tmp;
Expand Down Expand Up @@ -1638,13 +1697,19 @@ var GiikerCube = execMain(function() {
_gatt = null;
_deviceName = null;
deviceMac = null;
curCubie = new mathlib.CubieCube();
prevCubie = new mathlib.CubieCube();
prevMoves = [];
lastTs = 0;
batteryLevel = 100;
return result;
}

return {
init: init,
opservs: [SERVICE_UUID],
getBatteryLevel: function() { return Promise.resolve(batteryLevel); },
cics: QIYI_CIC_LIST,
getBatteryLevel: function() { return Promise.resolve([batteryLevel, _deviceName]); },
clear: clear
}
})();
Expand Down Expand Up @@ -1697,8 +1762,8 @@ var GiikerCube = execMain(function() {
}, {
namePrefix: 'QY-QYSC'
}],
optionalServices: [].concat(GiikerCube.opservs, GanCube.opservs, GoCube.opservs, MoyuCube.opservs),
optionalManufacturerData: [].concat(GanCube.cics)
optionalServices: [].concat(GiikerCube.opservs, GanCube.opservs, GoCube.opservs, MoyuCube.opservs, QiyiCube.opservs),
optionalManufacturerData: [].concat(GanCube.cics, QiyiCube.cics)
});
}).then(function(device) {
DEBUG && console.log('[bluetooth]', device);
Expand Down

0 comments on commit 6e1dcc8

Please sign in to comment.