@constructorfleet/ultimate-govee
Version:
Library for interacting with Govee devices written in Typescript.
225 lines • 8.66 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Device = exports.DefaultFactory = void 0;
const rxjs_1 = require("rxjs");
const common_1 = require("@nestjs/common");
const _ultimate_govee_common_1 = require("../../common");
const equal_lib_1 = require("@santi100/equal-lib");
const _ultimate_govee_persist_1 = require("../../persist");
const device_state_1 = require("./states/device.state");
const mode_state_1 = require("./states/mode.state");
const device_refresh_event_1 = require("./cqrs/events/device-refresh.event");
const device_state_command_event_1 = require("./cqrs/events/device-state-command.event");
const device_state_changed_event_1 = require("./cqrs/events/device-state-changed.event");
const json_stringify_safe_1 = __importDefault(require("json-stringify-safe"));
exports.DefaultFactory = 'default';
const buildStates = (stateFactories, device) => stateFactories
.map((factory) => typeof factory === 'object'
? factory[device.model] ?? factory[exports.DefaultFactory]
: factory)
.filter((factory) => factory !== undefined)
.map((factory) => Array.isArray(factory) ? factory.map((f) => f(device)) : factory(device))
.flat();
class Device extends rxjs_1.BehaviorSubject {
get deviceType() {
return Device.deviceType;
}
addState(state) {
if (state instanceof device_state_1.DeviceOpState &&
state.identifier !== undefined &&
state.identifier !== null) {
this.opIdentifiers.add(state.identifier);
}
this.subscriptions.push(this.device.status.subscribe((status) => state.parse(status)));
this.deviceStates.set(state.name, state);
this.subscriptions.push(state.subscribe(() => {
this.next(this);
}));
this.subscriptions.push(state.clearCommand.subscribe((command) => {
this.eventBus.publish(new device_state_changed_event_1.DeviceStateChangedEvent(this, command.state, command.value, command.commandId, this.isDebug));
}));
this.subscriptions.push(state.commandBus.subscribe((cmd) => {
if (this.isDebug) {
this.logger.debug('Publishing state command');
}
this.eventBus.publish(new device_state_command_event_1.DeviceStateCommandEvent(this.id, state.name, {
deviceId: this.id,
commandId: cmd.commandId,
command: cmd.command,
cmdVersion: cmd.cmdVersion ?? 0,
type: cmd.type ?? 1,
data: {
...cmd.data,
...(cmd.data.command !== undefined
? { command: cmd.data.command.map((x) => (0, _ultimate_govee_common_1.hexToBase64)(x)) }
: {}),
},
}, {
iotTopic: this.iotTopic,
bleAddress: this.bleAddress,
}, this.isDebug));
setTimeout(() => this.refresh(), 2000);
}));
return state;
}
get id() {
return this.device.id;
}
get model() {
return this.device.model;
}
get name() {
return this.device.name;
}
get goodsType() {
return this.device.goodsType;
}
get pactCode() {
return this.device.pactCode;
}
get images() {
const resources = this.device.images;
if (resources === undefined) {
return {};
}
return {
sku: resources.imageUrl,
on: resources.onImageUrl,
off: resources.offImageUrl,
};
}
get pactType() {
return this.device.pactType;
}
get iotTopic() {
return this.device.iotTopic;
}
get bleAddress() {
return this.device.bleAddress;
}
get version() {
return this.device.version;
}
get states() {
return Object.fromEntries(Array.from(this.deviceStates.entries()));
}
currentStates() {
const result = Object.fromEntries(Object.entries(this.states ?? {}).map(([k, v]) => [
k,
k === mode_state_1.ModeStateName ? v?.activeMode?.name : v?.value,
]));
return result;
}
previousStates() {
return Object.fromEntries(Object.entries(this.states ?? {}).map(([k, v]) => [
k,
k === mode_state_1.ModeStateName
? v?.activeMode?.history.peekAll()
: v?.history.peekAll(),
]));
}
state(stateName) {
return this.deviceStates.get(stateName);
}
deviceStatus(status) {
if (status.cmd !== 'status') {
this.refresh$.next();
}
else {
this.device.status.next(status);
}
}
refresh() {
if (this.isDebug) {
this.logger.debug(`Refreshing state ${this.iotTopic} ${this.bleAddress}`);
}
this.eventBus.publish(new device_refresh_event_1.DeviceRefeshEvent(this.id, this.model, this.goodsType, {
iotTopic: this.iotTopic,
bleAddress: this.bleAddress,
}, Array.from(this.opIdentifiers), this.isDebug));
}
setState(stateName, nextState) {
return this.state(stateName)?.setState(nextState);
}
debug(isDebug) {
this.isDebug = isDebug;
return this;
}
constructor(device, eventBus, commandBus, stateFactories) {
super(undefined);
this.device = device;
this.eventBus = eventBus;
this.commandBus = commandBus;
this.deviceStates = new _ultimate_govee_common_1.DeltaMap({
isModified: (current, previous) => !(0, equal_lib_1.deepEquality)(current.value, previous.value),
});
this.opIdentifiers = new Set();
this.refresh$ = new rxjs_1.Subject();
this.subscriptions = [];
this.isDebug = false;
this.next(this);
this.subscriptions.push(this.refresh$.pipe((0, rxjs_1.sampleTime)(2000)).subscribe(() => this.refresh()));
this.logger = new common_1.Logger(`${this.constructor.name}-${device.name}`);
buildStates(stateFactories, device).forEach((state) => {
this.addState(state);
state.parse({
cmd: 'status',
...device.status.value,
});
});
this.subscriptions.push((0, rxjs_1.interval)(10000).subscribe(() => this.refresh()));
this.subscriptions.push(this.deviceStates.delta$.subscribe(() => {
this.next(this);
}));
this.subscribe(() => {
this.logState();
});
}
loggableState(deviceId) {
const state = {
deviceId,
name: this.name,
model: this.model,
type: this.constructor.name,
iotTopic: this.iotTopic,
bleAddress: this.bleAddress,
states: JSON.parse((0, json_stringify_safe_1.default)(this.currentStates() ?? {})),
history: JSON.parse((0, json_stringify_safe_1.default)(this.previousStates() ?? {})),
};
return state;
}
logState() {
if (this.isDebug) {
this.loggableState(this.id);
}
}
closeSubscriptions() {
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
}
onModuleDestroy() {
this.closeSubscriptions();
}
}
exports.Device = Device;
Device.deviceType = 'unknown';
__decorate([
(0, _ultimate_govee_persist_1.PersistResult)({
filename: 'govee.{0}.state.json',
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", void 0)
], Device.prototype, "loggableState", null);
//# sourceMappingURL=device.js.map