homebridge-hatch-baby-rest
Version:
Homebridge plugin for Hatch Rest/Restore WiFi sound machines
117 lines (116 loc) • 4.49 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.IotDevice = exports.MAX_IOT_VALUE = void 0;
exports.convertFromPercentage = convertFromPercentage;
exports.convertToPercentage = convertToPercentage;
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const util_1 = require("../shared/util");
function assignState(previousState, changes) {
const state = Object.assign({}, previousState);
for (const key in changes) {
if (typeof changes[key] === 'object') {
state[key] = assignState(previousState[key] || {}, changes[key]);
}
else {
state[key] = changes[key];
}
}
return state;
}
exports.MAX_IOT_VALUE = 65535;
function convertFromPercentage(percentage) {
return Math.ceil((percentage / 100) * exports.MAX_IOT_VALUE);
}
function convertToPercentage(value) {
return Math.floor((value * 100) / exports.MAX_IOT_VALUE);
}
class IotDevice {
get mqttClient() {
return this.onIotClient.getValue();
}
get id() {
return this.info.id;
}
get name() {
return this.info.name;
}
get macAddress() {
return this.info.macAddress;
}
constructor(info, onIotClient) {
this.info = info;
this.onIotClient = onIotClient;
this.onCurrentState = new rxjs_1.BehaviorSubject(null);
this.onStatusToken = new rxjs_1.Subject();
this.previousUpdatePromise = Promise.resolve();
this.onState = this.onCurrentState.pipe((0, operators_1.filter)((state) => state !== null));
onIotClient
.pipe((0, rxjs_1.skip)(1))
.subscribe((client) => this.registerMqttClient(client));
this.registerMqttClient(onIotClient.getValue());
}
registerMqttClient(mqttClient) {
const { thingName } = this.info;
let getClientToken;
mqttClient.on('status', (topic, message, clientToken, status) => {
if (topic !== thingName) {
// status for a different thing
return;
}
this.onStatusToken.next(clientToken);
if (clientToken === getClientToken) {
const { state } = status;
this.onCurrentState.next(assignState(state.reported, state.desired));
}
});
mqttClient.on('foreignStateChange', (topic, message, s) => {
const currentState = this.onCurrentState.getValue();
if (!currentState || topic !== thingName) {
return;
}
this.onCurrentState.next(assignState(assignState(currentState, s.state.reported), s.state.desired));
});
this.previousUpdatePromise = this.previousUpdatePromise
.catch((_) => {
// ignore errors, they shouldn't be possible
void _;
})
.then(() => new Promise((resolve) => {
mqttClient.on('connect', () => {
mqttClient.register(thingName, {}, () => {
getClientToken = mqttClient.get(thingName);
resolve((0, rxjs_1.firstValueFrom)(this.onStatusToken.pipe((0, operators_1.filter)((token) => token === getClientToken))));
});
});
}));
}
getCurrentState() {
return (0, rxjs_1.firstValueFrom)(this.onState);
}
update(update) {
this.previousUpdatePromise = this.previousUpdatePromise
.catch((_) => {
// ignore errors, they shouldn't be possible
void _;
})
.then(() => {
if (!this.mqttClient) {
(0, util_1.logError)(`Unable to Update ${this.name} - No MQTT Client Registered`);
return;
}
const updateToken = this.mqttClient.update(this.info.thingName, {
state: {
desired: update,
},
});
if (!updateToken) {
(0, util_1.logError)(`Failed to apply update to ${this.name} because another update was in progress: ${JSON.stringify(update)}`);
}
const requestComplete = (0, rxjs_1.firstValueFrom)(this.onStatusToken.pipe((0, operators_1.filter)((token) => token === updateToken)));
// wait a max of 30 seconds to finish request
return Promise.race([requestComplete, (0, util_1.delay)(30000)]);
});
}
}
exports.IotDevice = IotDevice;