Closed bderleta closed 6 months ago
Also, depending on the runningmode
(1 == manual mode, 2 == auto mode), for command #4 the argument is just a level (1-10), or temperature (8 - 36 degrees). The checksum you can calculate manually. There are also commands for controlling heater options:
setTime() {
if (2 == this.$ble.md && this.$ble.isconnecting) {
let e = new Date;
this.cmd = 10, this.data = 60 * e.getHours() + e.getMinutes()
}
}
...
isauto_change: function(e) {
this.cmd = 13, this.data = e, setTimeout((() => {
this.cmd = 13, this.data = e
}), 500), this.isauto = e
},
autotime_change: function(e) {
this.cmd = 11, this.data = this.time2number(e.detail.value), setTimeout((() => {
this.cmd = 11, this.data = this.time2number(e.detail.value)
}), 500), this.autotime = this.time2number(e.detail.value), y("log", "at pages/parameter/parameter.vue:455", e.detail.value), y("log", "at pages/parameter/parameter.vue:456", this.data)
},
runtime_change: function(e) {
this.cmd = 12, this.data = this.time2number(e.detail.value), setTimeout((() => {
this.cmd = 12, this.data = this.time2number(e.detail.value)
}), 500), this.runtime = this.time2number(e.detail.value)
},
tankvolume_change: function(e) {
this.cmd = 16, this.data = 5 * e.detail.value, setTimeout((() => {
this.cmd = 16, this.data = 5 * e.detail.value
}), 500), this.tankvolume = 5 * e.detail.value
},
oilpumptype_change: function(e) {
this.cmd = 17, this.data = e.detail.value, setTimeout((() => {
this.cmd = 17, this.data = e.detail.value
}), 500), this.oilpumptype = e.detail.value
},
automaticheating_change: function(e) {
this.cmd = 18, this.data = e, setTimeout((() => {
this.cmd = 18, this.data = e
}), 800), this.automaticheating = e
},
tempoffset_change: function(e) {
this.cmd = 15, this.data = e.detail.value, setTimeout((() => {
this.cmd = 15, this.data = e.detail.value
}), 800), this.tempoffset = e.detail.value
},
language_change: function(e) {
this.cmd = 14, this.data = e.detail.value, this.language = e.detail.value
},
number2time(e) {
if (e > 1439 || e < 0) return "--:--";
var t = e / 60,
n = t.toFixed().length,
i = t.toFixed();
return 1 == n && (i = "0" + i), i = 1 == (n = (t = e % 60).toFixed().length) ? i + ":0" + t.toFixed() : i + ":" + t.toFixed()
},
time2number(e) {
var t = e.slice(0, 2),
n = parseInt(t);
return t = e.slice(-2), n = 60 * n + parseInt(t)
},
And this is the parsing code for various types of characteristics change message (it seems there are three different formats possible)
if (e.onnotify = !0,
y("log", "at ble/ble.js:700", je = new Uint8Array(t.data)),
170 == e.u8tonumber(je[0]) && 85 == e.u8tonumber(je[1])
)
e.md = 1,
e.lasttime = Date.now(),
e.runningstate = e.u8tonumber(je[3]).valueOf(),
e.errcode = e.u8tonumber(je[4]).valueOf(),
e.runningstep = e.u8tonumber(je[5]).valueOf(),
e.altitude = e.u8tonumber(je[6]) + 256 * e.u8tonumber(je[7]),
e.runningmode = e.u8tonumber(je[8]).valueOf(),
1 == e.runningmode ? e.setlevel = e.u8tonumber(je[9]).valueOf() : 2 == e.runningmode ? (e.settemp = e.u8tonumber(je[9]).valueOf(),
e.setlevel = e.u8tonumber(je[10]).valueOf() + 1) : 0 == e.runningmode && (e.setlevel = e.u8tonumber(je[10]).valueOf() + 1),
e.supplyvoltage = ((256 * e.u8tonumber(je[12]) + e.u8tonumber(je[11])) / 10).toFixed(1),
e.casetemp = e.UnsignToSign(256 * je[14] + je[13]),
e.cabtemp = e.UnsignToSign(256 * je[16] + je[15]),
clearInterval(0),
recved = !0;
else if (170 == e.u8tonumber(je[0]) && 102 == e.u8tonumber(je[1]))
e.lasttime = Date.now(),
e.runningstate = e.u8tonumber(je[3]).valueOf(),
e.errcode = e.u8tonumber(je[17]).valueOf(),
e.runningstep = e.u8tonumber(je[5]).valueOf(),
e.altitude = e.u8tonumber(je[6]) + 256 * e.u8tonumber(je[7]),
e.runningmode = e.u8tonumber(je[8]).valueOf(),
1 == e.runningmode ? e.setlevel = e.u8tonumber(je[9]).valueOf() : 2 == e.runningmode ? (e.settemp = e.u8tonumber(je[9]).valueOf(),
e.setlevel = e.u8tonumber(je[10]).valueOf() + 1) : 0 == e.runningmode && (e.setlevel = e.u8tonumber(je[10]).valueOf() + 1),
e.supplyvoltage = ((256 * e.u8tonumber(je[12]) + e.u8tonumber(je[11])) / 10).toFixed(1),
e.casetemp = e.UnsignToSign(256 * je[14] + je[13]),
e.cabtemp = e.UnsignToSign(256 * je[16] + je[15]),
e.md = 3,
clearInterval(0),
e.isconnecting = !0,
recved = !0;
else if (170 == e.u8tonumber(je[0]) && 136 == e.u8tonumber(je[1])) {
let t = new Uint8Array(je),
n = 256 * t[29] + t[30];
y("log", "at ble/ble.js:765", n),
t[29] = Ie[0],
t[30] = Ie[1],
y("log", "at ble/ble.js:772", Array.prototype.map.call(t, (e => ("00" + e.toString(16)).slice(-2))).join(""));
let i = Le(t, 31);
y("log", "at ble/ble.js:774", i),
i == n && (
e.lasttime = Date.now(),
e.runningstate = e.u8tonumber(t[3]).valueOf(),
e.errcode = e.u8tonumber(t[4]).valueOf(),
e.runningstep = e.u8tonumber(t[5]).valueOf(),
e.altitude = (e.u8tonumber(t[6]) + 256 * e.u8tonumber(t[7])).toString(),
e.runningmode = e.u8tonumber(t[8]).valueOf(),
1 == e.runningmode ? e.setlevel = e.u8tonumber(t[9]).valueOf() : 2 == e.runningmode ? (e.settemp = e.u8tonumber(t[9]).valueOf(),
e.setlevel = e.u8tonumber(t[10]).valueOf() + 1) : 0 == e.runningmode && (e.setlevel = e.u8tonumber(t[10]).valueOf() + 1),
e.supplyvoltage = parseInt((256 * t[12] + t[11]) / 10),
e.casetemp = e.UnsignToSign(256 * t[14] + t[13]),
e.cabtemp = e.UnsignToSign(256 * t[16] + t[15]),
e.autotime = 256 * e.u8tonumber(t[18]) + e.u8tonumber(t[17]),
e.runtime = 256 * e.u8tonumber(t[20]) + e.u8tonumber(t[19]),
e.isauto = e.u8tonumber(t[21]),
e.language = e.u8tonumber(t[22]),
e.tempoffset = e.u8tonumber(t[23]),
e.tankvolume = e.u8tonumber(t[24]),
e.oilpumptype = e.u8tonumber(t[25]),
e.bluetoothswitch = !e.u8tonumber(t[26]),
e.remotecontrolmatching = !e.u8tonumber(t[27]),
e.automaticheating = e.u8tonumber(t[28]),
e.md = 2,
e.isconnecting = !0,
recved = !0,
clearInterval(0)
)
}
y("log", "at ble/ble.js:817", e.md), recved && (this.isconnecting = !0)
...
u8tonumber: function(e) {
return e < 0 ? e + 256 : e
},
UnsignToSign: function(e) {
if (e > 32767.5) {
e |= -65536
}
return e
}
...
function Le(e, t) {
if (t > 0) {
for (var n = 65535, i = 0; i < t; i++) {
n ^= e[i];
for (var o = 0; o < 8; o++) n = 0 != (1 & n) ? n >> 1 ^ 40961 : n >> 1
}
return ((65280 & n) >> 8) + 256 * (255 & n)
}
return 0
}
The complexity of your code, which you showed me, surpasses my understanding. Mine was written by ChatGPT, and the commands are simply the dump of Bluetooth data exchanged between the burner and the app.
What those commands specifically do is unknown even to me. As soon as someone more experienced than me writes the code, I'm thinking of removing it. I'm content only with the fact that it served as inspiration for someone else to do better.
This is not my code, those are fragments of the reverse engineered source of the original application from Android.
was written by ChatGPT
I don't believe... anyway, got it, I will publish all my findings and credit you for inspiration and research. https://github.com/bderleta/vevor-ble-bridge/
This is not my code, those are fragments of the reverse engineered source of the original application from Android.
was written by ChatGPT
I don't believe... anyway, got it, I will publish all my findings and credit you for inspiration and research. https://github.com/bderleta/vevor-ble-bridge/
I don't speak English either, the entire code, even the README.md, and even this response are being translated for me by ChatGPT. All true.
I took a peek into the app source code. There is a cryptic state variable called
md
. For all packets, the first byte is 170 (0xAA). For all packets except whenmd == 2
, second byte is 85 (0x55). If themd != 2
, so if second byte is 85, the third constant byte isfloor(passkey/100)
, and fourth is(passkey%100)
. Which is exactly as in your library - 12 (0x0c) and 34 (0x22). If themd == 2
, the second byte is 136 (0x88), and third and fourth bytes are random. I have seen only being it used with command byte == 1 and arg == 0 (see below) and not any other commands.Fifth byte is a command byte, sixth is
(arg % 256)
, seventh isfloor(arg / 256)
and eight is the sum of bytes 3 .. 7 (o[2] .. o[6]
) .This is the excerpt responsible (comment and formatting by me):