@constructorfleet/ultimate-govee
Version:
Library for interacting with Govee devices written in Typescript.
190 lines • 7.66 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeviceOpState = exports.DeviceState = exports.filterCommands = void 0;
const rxjs_1 = require("rxjs");
const _ultimate_govee_common_1 = require("../../../common");
const common_1 = require("@nestjs/common");
const uuid_1 = require("uuid");
const equal_lib_1 = require("@santi100/equal-lib");
const states_types_1 = require("./states.types");
const StatusCommand = 'status';
const filterCommands = (commands, type, identifier) => commands
.filter((command) => {
if (type === undefined) {
return true;
}
const identifiers = identifier === undefined
? undefined
: typeof identifier === 'number'
? [identifier]
: identifier;
const [cmdType, ...cmdIdentifiers] = command.slice(0, 1 + (identifiers?.length ?? 0));
if (cmdType !== type) {
return false;
}
if (identifiers === undefined) {
return false;
}
return cmdIdentifiers.every((i, index) => identifiers[index] < 0 || i === identifiers[index]);
})
.map((command) => {
if (type === undefined && identifier === undefined) {
return command;
}
if (identifier === undefined) {
return command.slice(1);
}
return command.slice(1 + (typeof identifier === 'number' ? 1 : identifier.length));
});
exports.filterCommands = filterCommands;
class DeviceState {
get isCommandable() {
return this.stateToCommand !== undefined;
}
subscribe(observerOrNext) {
const sub = this.stateValue
.pipe((0, rxjs_1.distinctUntilChanged)((previous, current) => (0, equal_lib_1.deepEquality)(previous, current)))
.subscribe(observerOrNext);
this.subscriptions.push(sub);
return sub;
}
get value() {
return this.stateValue.getValue();
}
constructor(device, name, initialValue, parseOption = states_types_1.ParseOption.state) {
this.device = device;
this.name = name;
this.logger = new common_1.Logger(this.constructor.name);
this.pendingCommands = new Map();
this.commandBus = new rxjs_1.Subject();
this.clearCommand$ = new rxjs_1.Subject();
this.clearCommand = (0, rxjs_1.connectable)(this.clearCommand$);
this.history = new _ultimate_govee_common_1.FixedLengthStack(5);
this.subscriptions = [];
this.parseOption = parseOption;
this.history.enstack(initialValue);
this.stateValue = new _ultimate_govee_common_1.ForwardBehaviorSubject(initialValue);
this.subscriptions.push(this.subscribe((state) => this.history.enstack(state)));
this.subscriptions.push(this.device.status?.subscribe((status) => this.parse(status)), this.clearCommand.subscribe(({ commandId }) => this.pendingCommands.delete(commandId)));
}
previousState(last = 1) {
let state = undefined;
while (last > 0) {
state = this.history.destack();
last--;
}
if (state === undefined) {
return [];
}
return this.setState(state);
}
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
parseState(data) {
// no-op
}
parse(data) {
if (data.cmd && data.cmd !== StatusCommand) {
return;
}
const commandId = Array.from(this.pendingCommands.entries())
.filter(([_, statuses]) => statuses.some((s) => (0, _ultimate_govee_common_1.deepPartialCompare)(s.state, data.state)))
.map(([commandId, _]) => commandId)
.at(0);
this.parseState(data);
if (commandId !== undefined) {
this.clearCommand$.next({
commandId,
state: this.name,
value: this.value,
});
}
}
setState(nextState) {
const commandAndStatus = this.stateToCommand === undefined
? undefined
: this.stateToCommand(nextState);
if (commandAndStatus === undefined) {
return [];
}
const { command, status } = commandAndStatus;
const commandId = (0, uuid_1.v4)();
const commands = (Array.isArray(command) ? command : [command]).map((cmd) => ({
commandId,
...cmd,
}));
this.pendingCommands.set(commandId, Array.isArray(status) ? status : [status]);
return commands.map((cmd) => {
this.commandBus.next(cmd);
return cmd.commandId;
});
}
onModuleDestroy() {
this.subscriptions
.filter((sub) => sub !== undefined)
.forEach((sub) => sub.unsubscribe());
}
}
exports.DeviceState = DeviceState;
class DeviceOpState extends DeviceState {
constructor({ opType, identifier }, device, name, initialValue, parseOption = states_types_1.ParseOption.opCode) {
super(device, name, initialValue, parseOption);
this.opType = opType;
this.identifier = identifier;
}
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, no-unused-vars
parseOpCommand(opCommand) { }
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, no-unused-vars
parseMultiOpCommand(opCommands) { }
parse(data) {
if (this.parseOption.hasFlag(states_types_1.ParseOption.none)) {
return;
}
const commands = data.op?.command ?? [];
const foundCommands = this.filterOpCommands(commands);
if (commands.length > 0) {
if (this.parseOption.hasFlag(states_types_1.ParseOption.opCode)) {
foundCommands.forEach((command) => {
const commandId = Array.from(this.pendingCommands.entries()).find(([_, statuses]) => statuses.some((s) => s.op?.command
?.at(0)
?.every((c, i) => c === undefined || command[i] === c)))?.[0];
this.parseOpCommand(command);
if (commandId !== undefined) {
this.clearCommand$.next({
commandId,
state: this.name,
value: this.value,
});
}
});
}
else if (this.parseOption.hasFlag(states_types_1.ParseOption.multiOp)) {
const commandIds = Array.from(this.pendingCommands.entries())
.filter(([_, statuses]) => foundCommands.filter((command) => statuses.some((s) => s.op?.command
?.at(0)
?.every((c, i) => c === undefined || command[i] === c))))
?.map((entry) => entry[0]);
this.parseMultiOpCommand(foundCommands);
commandIds.forEach((commandId) => {
if (commandId !== undefined) {
this.clearCommand$.next({
commandId,
state: this.name,
value: this.value,
});
}
});
}
}
if (this.parseOption.hasFlag(states_types_1.ParseOption.state)) {
this.parseState(data);
}
}
filterOpCommands(opCommands) {
if (this.opType === null) {
return opCommands;
}
return (0, exports.filterCommands)(opCommands, this.opType, this.identifier ?? undefined);
}
}
exports.DeviceOpState = DeviceOpState;
//# sourceMappingURL=device.state.js.map