homebridge-xiaomi-roborock-vacuum
Version:
Xiaomi Vacuum Cleaner - 1st (Mi Robot), 2nd (Roborock S50 + S55), 3rd Generation (Roborock S6) and S5 Max - plugin for Homebridge.
136 lines (115 loc) • 3.27 kB
JavaScript
"use strict";
const fs = require("fs");
const path = require("path");
const mkdirp = require("mkdirp");
const AppDirectory = require("appdirectory");
const dirs = new AppDirectory("miio");
const CHECK_TIME = 1000;
const MAX_STALE_TIME = 120000;
const debug = require("debug")("miio:tokens");
/**
* Shared storage for tokens of devices. Keeps a simple JSON file synced
* with tokens connected to device ids.
*/
class Tokens {
constructor() {
this._file = path.join(dirs.userData(), "tokens.json");
this._data = {};
this._lastSync = 0;
}
get(deviceId) {
const now = Date.now();
const diff = now - this._lastSync;
if (diff > CHECK_TIME) {
return this._loadAndGet(deviceId);
}
return Promise.resolve(this._get(deviceId));
}
_get(deviceId) {
return this._data[deviceId];
}
_loadAndGet(deviceId) {
return this._load()
.then(() => this._get(deviceId))
.catch(() => null);
}
_load() {
if (this._loading) return this._loading;
return (this._loading = new Promise((resolve, reject) => {
debug("Loading token storage from", this._file);
fs.stat(this._file, (err, stat) => {
if (err) {
delete this._loading;
if (err.code === "ENOENT") {
debug("Token storage does not exist");
this._lastSync = Date.now();
resolve(this._data);
} else {
reject(err);
}
return;
}
if (!stat.isFile()) {
// tokens.json does not exist
delete this._loading;
reject(new Error("tokens.json exists but is not a file"));
} else if (
Date.now() - this._lastSync > MAX_STALE_TIME ||
stat.mtime.getTime() > this._lastSync
) {
debug("Loading tokens");
fs.readFile(this._file, (err, result) => {
this._data = JSON.parse(result.toString());
this._lastSync = Date.now();
delete this._loading;
resolve(this._data);
});
} else {
delete this._loading;
this._lastSync = Date.now();
resolve(this._data);
}
});
}));
}
update(deviceId, token) {
return this._load().then(() => {
this._data[deviceId] = token;
if (this._saving) {
this._dirty = true;
return this._saving;
}
return (this._saving = new Promise((resolve, reject) => {
const save = () => {
debug("About to save tokens");
fs.writeFile(
this._file,
JSON.stringify(this._data, null, 2),
(err) => {
if (err) {
reject(err);
} else {
if (this._dirty) {
debug("Redoing save due to multiple updates");
this._dirty = false;
save();
} else {
delete this._saving;
resolve();
}
}
}
);
};
mkdirp(dirs.userData(), (err) => {
if (err) {
reject(err);
return;
}
save();
});
}));
});
}
}
module.exports = new Tokens();