132 lines
3.2 KiB
JavaScript
132 lines
3.2 KiB
JavaScript
let wasm_inited_resolve;
|
|
const wasm_inited = new Promise((resolve) => {
|
|
wasm_inited_resolve = resolve;
|
|
});
|
|
|
|
document.addEventListener("wasm-loaded", () => {
|
|
wasm_inited_resolve(wasm_bindgen);
|
|
});
|
|
|
|
document.addEventListener("alpine:init", () => {
|
|
Alpine.store("mqtt", {
|
|
connected: false,
|
|
_client: null,
|
|
_topics: null,
|
|
_c: null,
|
|
|
|
_pendingPromises: {},
|
|
|
|
sendTime(time) {
|
|
if (!this.connected) return null;
|
|
|
|
const id = uuidv4();
|
|
const promise = new Promise((resolve, reject) => {
|
|
this._pendingPromises[id] = [resolve, reject];
|
|
});
|
|
this._publish({
|
|
id: id, // used to prevent replay attacks and to identify confirm messages
|
|
kind: "Time", // can be "time" or "confirm"
|
|
time: time, // only used for "time"
|
|
});
|
|
|
|
return promise;
|
|
},
|
|
|
|
async connect() {
|
|
if (this.connected) return;
|
|
|
|
const password = Alpine.store("localState").password;
|
|
const that = this;
|
|
const brokerDomain = "broker.emqx.io";
|
|
const url = `wss://${brokerDomain}:8084/mqtt`;
|
|
|
|
if (!password) return false;
|
|
|
|
const { Crypto } = await wasm_inited;
|
|
|
|
Alpine.store("localState")._state = 5;
|
|
|
|
// derive key from password
|
|
this._c = new Crypto(password, brokerDomain);
|
|
|
|
console.log("Test", this._encrypt("test"));
|
|
|
|
this._topics = {
|
|
time: Crypto.sha256(
|
|
this._encrypt(`org.speedclimbing.ok-ready-go.${password}.time`)
|
|
).toString(),
|
|
confirmation: Crypto.sha256(
|
|
this._encrypt(
|
|
`org.speedclimbing.ok-ready-go.${password}.confirmation`
|
|
)
|
|
).toString(),
|
|
};
|
|
|
|
console.log("Connecting to MQTT broker...");
|
|
|
|
const options = {
|
|
// Clean session
|
|
clean: true,
|
|
connectTimeout: 4000,
|
|
};
|
|
|
|
this._client = mqtt.connect(url, options);
|
|
|
|
this._client.on("connect", () => {
|
|
Alpine.store("localState")._state = 0;
|
|
|
|
that._client.subscribe(that._topics.confirmation);
|
|
this.connected = true;
|
|
});
|
|
|
|
this._client.on("message", (topic, message) => {
|
|
// message is Buffer
|
|
message = that._decrypt(message.toString());
|
|
const data = JSON.parse(message);
|
|
|
|
if (
|
|
topic !== that._topics.confirmation ||
|
|
data.kind !== "Confirm" ||
|
|
Object.keys(this._pendingPromises).indexOf(data.id) === -1
|
|
)
|
|
return;
|
|
|
|
console.log("<<< ", data);
|
|
this._pendingPromises[data.id][0]();
|
|
});
|
|
},
|
|
|
|
disconnect() {
|
|
if (!this.connected) return;
|
|
|
|
this._client.end(true);
|
|
|
|
this._client = null;
|
|
this.connected = false;
|
|
this._topics = null;
|
|
|
|
for (const promiseId in this._pendingPromises) {
|
|
this._pendingPromises[promiseId][1]();
|
|
}
|
|
|
|
this._pendingPromises = {};
|
|
},
|
|
|
|
_publish(data) {
|
|
const encryptedData = this._encrypt(JSON.stringify(data));
|
|
console.log(">>> ", data);
|
|
this._client.publish(this._topics.time, encryptedData, {
|
|
qos: 1,
|
|
retain: false,
|
|
});
|
|
},
|
|
|
|
_encrypt(data) {
|
|
return this._c.encrypt(data);
|
|
},
|
|
|
|
_decrypt(data) {
|
|
return this._c.decrypt(data);
|
|
},
|
|
});
|
|
});
|