@hov3rcraft/homebridge-eufy-robovac
Version:
Homebridge support for Eufy RoboVac
360 lines • 13.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RoboVac = exports.getErrorCodeFriendlyName = exports.errorCodeFriendlyNames = exports.ErrorCode = exports.CleanSpeed = exports.WorkStatus = exports.WorkMode = exports.Direction = exports.formatStatusResponse = exports.statusDpsFriendlyNames = exports.StatusDps = void 0;
const consoleLogger_1 = require("./consoleLogger");
const tuyapi_1 = __importDefault(require("tuyapi"));
var StatusDps;
(function (StatusDps) {
StatusDps["DEFAULT"] = "1";
StatusDps["RUNNING"] = "2";
StatusDps["DIRECTION"] = "3";
StatusDps["WORK_MODE"] = "5";
StatusDps["WORK_STATUS"] = "15";
StatusDps["GO_HOME"] = "101";
StatusDps["CLEAN_SPEED"] = "102";
StatusDps["FIND_ROBOT"] = "103";
StatusDps["BATTERY_LEVEL"] = "104";
StatusDps["ERROR_CODE"] = "106";
})(StatusDps || (exports.StatusDps = StatusDps = {}));
exports.statusDpsFriendlyNames = new Map([
[StatusDps.DEFAULT, "Default Property (ignore)"],
[StatusDps.RUNNING, "Running"],
[StatusDps.DIRECTION, "Direction"],
[StatusDps.WORK_MODE, "Work Mode"],
[StatusDps.WORK_STATUS, "Work Status"],
[StatusDps.GO_HOME, "Go Home"],
[StatusDps.CLEAN_SPEED, "Clean Speed"],
[StatusDps.FIND_ROBOT, "Find Robot"],
[StatusDps.BATTERY_LEVEL, "Battery Level"],
[StatusDps.ERROR_CODE, "Error Code"]
]);
function formatStatusResponse(statusResponse) {
let formattedStatus = `-- Status Start --\n`;
for (const dps of Object.values(StatusDps)) {
if (dps in statusResponse.dps) {
formattedStatus += `- ${exports.statusDpsFriendlyNames.get(dps)}: ${(statusResponse.dps)[dps]}\n`;
}
}
formattedStatus += `-- Status End --`;
return formattedStatus;
}
exports.formatStatusResponse = formatStatusResponse;
var Direction;
(function (Direction) {
Direction["FORWARD"] = "forward";
Direction["BACKWARD"] = "backward";
Direction["LEFT"] = "left";
Direction["RIGHT"] = "right";
})(Direction || (exports.Direction = Direction = {}));
var WorkMode;
(function (WorkMode) {
WorkMode["AUTO"] = "auto";
WorkMode["SMALL_ROOM"] = "SmallRoom";
WorkMode["SPOT"] = "Spot";
WorkMode["EDGE"] = "Edge";
WorkMode["NO_SWEEP"] = "Nosweep";
})(WorkMode || (exports.WorkMode = WorkMode = {}));
var WorkStatus;
(function (WorkStatus) {
// Cleaning
WorkStatus["RUNNING"] = "Running";
// Not in the dock, paused
WorkStatus["STAND_BY"] = "standby";
// Not in the dock - goes into this state after being paused for a while
WorkStatus["SLEEPING"] = "Sleeping";
// In the dock, charging
WorkStatus["CHARGING"] = "Charging";
// In the dock, full charged
WorkStatus["CHARGING_COMPLETED"] = "completed";
// Going home because battery is depleted or home was pressed
WorkStatus["RECHARGE_NEEDED"] = "Recharge";
})(WorkStatus || (exports.WorkStatus = WorkStatus = {}));
var CleanSpeed;
(function (CleanSpeed) {
CleanSpeed["STANDARD"] = "Standard";
CleanSpeed["BOOST_IQ"] = "Boost_IQ";
CleanSpeed["MAX"] = "Max";
CleanSpeed["NO_SUCTION"] = "No_suction";
})(CleanSpeed || (exports.CleanSpeed = CleanSpeed = {}));
var ErrorCode;
(function (ErrorCode) {
ErrorCode["NO_ERROR"] = "no_error";
ErrorCode["STUCK_5_MIN"] = "Stuck_5_min";
ErrorCode["CRASH_BAR_STUCK"] = "Crash_bar_stuck";
ErrorCode["SENSOR_DIRTY"] = "sensor_dirty";
ErrorCode["NOT_ENOUGH_POWER"] = "N_enough_pow";
ErrorCode["WHEEL_STUCK"] = "Wheel_stuck";
ErrorCode["S_BRUSH_STUCK"] = "S_brush_stuck";
ErrorCode["FAN_STUCK"] = "Fan_stuck";
ErrorCode["R_BRUSH_STUCK"] = "R_brush_stuck";
})(ErrorCode || (exports.ErrorCode = ErrorCode = {}));
exports.errorCodeFriendlyNames = new Map([
[ErrorCode.NO_ERROR, "No Error"],
[ErrorCode.STUCK_5_MIN, "Stuck (5 Minutes)"],
[ErrorCode.CRASH_BAR_STUCK, "Crash Bar Stuck"],
[ErrorCode.SENSOR_DIRTY, "Sensor Dirty"],
[ErrorCode.NOT_ENOUGH_POWER, "Not Enough Power"],
[ErrorCode.WHEEL_STUCK, "Wheel Stuck"],
[ErrorCode.S_BRUSH_STUCK, "Brush Stuck"],
[ErrorCode.FAN_STUCK, "Fan Stuck"],
[ErrorCode.R_BRUSH_STUCK, "Brush Stuck"]
]);
function getErrorCodeFriendlyName(errorCode) {
var _a;
return (_a = exports.errorCodeFriendlyNames.get(errorCode)) !== null && _a !== void 0 ? _a : errorCode;
}
exports.getErrorCodeFriendlyName = getErrorCodeFriendlyName;
class RoboVac {
constructor(config, dataReceivedCallback, cachingDuration, log = new consoleLogger_1.ConsoleLogger()) {
this.cachingDuration = cachingDuration;
this.log = log;
if (log instanceof consoleLogger_1.ConsoleLogger) {
this.consoleDebugLog = (log.logLevel <= 1);
}
else {
this.consoleDebugLog = false;
}
this.directConnect = (config.deviceIp != null && config.deviceIp != "");
this.api = new tuyapi_1.default({
id: config.deviceId,
key: config.localKey,
ip: this.directConnect ? config.deviceIp : undefined,
version: '3.3',
issueRefreshOnConnect: true
});
// Add event listeners
this.api.on('connected', () => {
log.info('Connected to device!');
});
this.api.on('disconnected', () => {
log.info('Disconnected from device.');
});
this.api.on('error', (error) => {
log.error('Error!', error);
this.disconnect();
});
this.api.on('dp-refresh', (data) => {
if (this.consoleDebugLog) {
try {
this.log.debug("Received dps refresh data from device:", "\n" + formatStatusResponse(data));
}
catch (e) {
this.log.debug("Received dps refresh data from device:", data);
}
}
else {
this.log.debug("Received dps refresh data from device:", data);
}
if (data.dps) {
Object.assign(this.lastStatus.dps, data.dps);
this.lastStatusUpdate = new Date();
dataReceivedCallback(data);
}
});
this.api.on('data', (data) => {
if (this.consoleDebugLog) {
try {
this.log.debug("Received data from device:", "\n" + formatStatusResponse(data));
}
catch (e) {
this.log.debug("Received data from device:", data);
}
}
else {
this.log.debug("Received data from device:", data);
}
if (data.dps) {
Object.assign(this.lastStatus.dps, data.dps);
this.lastStatusUpdate = new Date();
this.lastStatusValid = true;
dataReceivedCallback(data);
}
});
// init with default values
this.lastStatus = {
devId: "default - invalid",
dps: {
[StatusDps.DEFAULT]: false,
[StatusDps.RUNNING]: false,
[StatusDps.DIRECTION]: Direction.FORWARD,
[StatusDps.WORK_MODE]: WorkMode.NO_SWEEP,
[StatusDps.WORK_STATUS]: WorkStatus.CHARGING,
[StatusDps.GO_HOME]: false,
[StatusDps.CLEAN_SPEED]: CleanSpeed.NO_SUCTION,
[StatusDps.FIND_ROBOT]: false,
[StatusDps.BATTERY_LEVEL]: -1,
[StatusDps.ERROR_CODE]: "default - invalid",
}
};
this.lastStatusUpdate = new Date(0);
this.lastStatusValid = false;
this.ongoingStatusUpdate = null;
this.connect().catch((e) => {
this.log.error("Error during initial connect:", e);
});
}
async connect() {
if (!this.directConnect) {
// Find device on network if there is no ip specified in config
await this.api.find();
}
// Connect to device
await this.api.connect();
}
async disconnect() {
this.ongoingStatusUpdate = null;
this.lastStatusValid = false;
if (this.api.isConnected()) {
await this.api.disconnect();
}
}
getStatusCached() {
return this.lastStatusValid ? this.lastStatus : null;
}
async getStatus() {
if (!this.lastStatusValid || Math.abs(new Date().getTime() - this.lastStatusUpdate.getTime()) > this.cachingDuration) {
return this.getStatusFromDeviceSynchronized();
}
else {
this.log.debug("Status request within max status update age");
return this.lastStatus;
}
}
async getStatusFromDeviceSynchronized() {
if (this.ongoingStatusUpdate != null) {
this.log.debug("Duplicate status update request detected");
return this.ongoingStatusUpdate;
}
this.ongoingStatusUpdate = this.getStatusFromDevice();
return this.ongoingStatusUpdate;
}
async getStatusFromDevice() {
this.log.info("Fetching status update...");
try {
if (!this.api.isConnected()) {
await this.connect();
}
const schema = await this.api.get({ schema: true });
this.lastStatus = schema;
this.lastStatusUpdate = new Date();
this.lastStatusValid = true;
this.ongoingStatusUpdate = null;
this.log.debug("Status update retrieved.");
return this.lastStatus;
}
catch (e) {
this.log.error("An error occurred (during GET status update)!", e);
try {
this.disconnect();
}
catch (e2) {
}
throw e;
}
}
async set(dps, newValue) {
this.log.debug("Setting", exports.statusDpsFriendlyNames.get(dps), "to", newValue, "...");
try {
if (!this.api.isConnected()) {
await this.connect();
}
await this.api.set({ dps: parseInt(dps), set: newValue });
this.log.info("Setting", exports.statusDpsFriendlyNames.get(dps), "to", newValue, "successful.");
}
catch (e) {
this.log.error("An error occurred! (during SET of", exports.statusDpsFriendlyNames.get(dps), "to", newValue, "): ", e);
try {
this.disconnect();
}
catch (e2) {
}
throw e;
}
}
async getRunning() {
const robovacStatus = await this.getStatus();
return robovacStatus.dps[StatusDps.RUNNING];
}
async getDirection() {
const robovacStatus = await this.getStatus();
return robovacStatus.dps[StatusDps.DIRECTION];
}
async getWorkMode() {
const robovacStatus = await this.getStatus();
return robovacStatus.dps[StatusDps.WORK_MODE];
}
async getWorkStatus() {
const robovacStatus = await this.getStatus();
return robovacStatus.dps[StatusDps.WORK_STATUS];
}
async getGoHome() {
const robovacStatus = await this.getStatus();
return robovacStatus.dps[StatusDps.GO_HOME];
}
async getCleanSpeed() {
const robovacStatus = await this.getStatus();
return robovacStatus.dps[StatusDps.CLEAN_SPEED];
}
async getFindRobot() {
const robovacStatus = await this.getStatus();
return robovacStatus.dps[StatusDps.FIND_ROBOT];
}
async getBatteryLevel() {
const robovacStatus = await this.getStatus();
return robovacStatus.dps[StatusDps.BATTERY_LEVEL];
}
async getErrorCode() {
const robovacStatus = await this.getStatus();
return robovacStatus.dps[StatusDps.ERROR_CODE];
}
getRunningCached() {
return this.lastStatusValid ? this.lastStatus.dps[StatusDps.RUNNING] : null;
}
getDirectionCached() {
return this.lastStatusValid ? this.lastStatus.dps[StatusDps.DIRECTION] : null;
}
getWorkModeCached() {
return this.lastStatusValid ? this.lastStatus.dps[StatusDps.WORK_MODE] : null;
}
getWorkStatusCached() {
return this.lastStatusValid ? this.lastStatus.dps[StatusDps.WORK_STATUS] : null;
}
getGoHomeCached() {
return this.lastStatusValid ? this.lastStatus.dps[StatusDps.GO_HOME] : null;
}
getCleanSpeedCached() {
return this.lastStatusValid ? this.lastStatus.dps[StatusDps.CLEAN_SPEED] : null;
}
getFindRobotCached() {
return this.lastStatusValid ? this.lastStatus.dps[StatusDps.FIND_ROBOT] : null;
}
getBatteryLevelCached() {
return this.lastStatusValid ? this.lastStatus.dps[StatusDps.BATTERY_LEVEL] : null;
}
getErrorCodeCached() {
return this.lastStatusValid ? this.lastStatus.dps[StatusDps.ERROR_CODE] : null;
}
async setPlayPause(newValue) {
return this.set(StatusDps.RUNNING, newValue);
}
async setDirection(newValue) {
return this.set(StatusDps.DIRECTION, newValue);
}
async setWorkMode(newValue) {
return this.set(StatusDps.WORK_MODE, newValue);
}
async setGoHome(newValue) {
return this.set(StatusDps.GO_HOME, newValue);
}
async setCleanSpeed(newValue) {
return this.set(StatusDps.CLEAN_SPEED, newValue);
}
async setFindRobot(newValue) {
return this.set(StatusDps.FIND_ROBOT, newValue);
}
}
exports.RoboVac = RoboVac;
//# sourceMappingURL=robovac-api.js.map