@constructorfleet/ultimate-govee
Version:
Library for interacting with Govee devices written in Typescript.
207 lines • 8.68 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
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 __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var DecoderService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DecoderService = void 0;
const common_1 = require("@nestjs/common");
const decoder_config_1 = require("./decoder.config");
const utils_1 = require("../../utils");
const decoder_types_1 = require("./decoder.types");
const class_transformer_1 = require("class-transformer");
const decoder_providers_1 = require("./decoder.providers");
const fs_1 = require("fs");
const path_1 = require("path");
const devices_1 = require("./devices");
let DecoderService = DecoderService_1 = class DecoderService {
constructor(config, decoder) {
this.config = config;
this.decoder = decoder;
this.logger = new common_1.Logger(DecoderService_1.name);
this.properties = {};
this.deviceSpecifications = {};
this.deviceProperties = {};
}
async decodeDevice(peripheral) {
const modelMatch = /(?:(?:GVH)|(?:GV)|(?:H))([A-Z0-9]{4})_?/.exec(peripheral.advertisement.localName);
if (!modelMatch) {
return undefined;
}
if (!modelMatch[1]) {
this.logger.warn(`Could not match model number for ${peripheral.id} ${peripheral.advertisement.localName}`);
return undefined;
}
this.logger.log(`Matched model H${modelMatch[1]}`);
const device = {
id: peripheral.id,
name: peripheral.advertisement.localName,
macAddress: peripheral.address,
uuid: peripheral.uuid,
manufacturerData: peripheral.advertisement.manufacturerData?.toString('hex'),
serviceData: [],
};
const deviceDecoder = devices_1.decodeDevice[modelMatch[1]];
if (deviceDecoder) {
this.logger.verbose('Using known model decoder...');
const deviceProps = deviceDecoder(peripheral.advertisement);
return {
...device,
...deviceProps,
};
}
const spec = await this.getDeviceSpec(`H${modelMatch[1] || ''}`);
if (spec === undefined) {
return undefined;
}
if (spec.conditions && !this.decoder.matches(device, spec.conditions)) {
return undefined;
}
this.logger.verbose('Using IoTManager model decoder...');
const state = this.decoder.decodeProperties(device, spec.properties ?? {});
const deccodedDevice = {
...device,
address: device.macAddress,
brand: spec.brand,
model: spec.model,
modelName: spec.modelName,
type: spec.type,
state,
};
return deccodedDevice;
}
async getCommonProperties() {
try {
this.logger.log('Retrieving common propertiest header');
const file = await this.getHeaderFile(this.config.commonPropertiesUrl);
if (!file) {
return;
}
this.processHeaderFile(file);
}
catch (err) {
this.logger.error(`Error retrieving header from ${this.config.commonPropertiesUrl}`);
}
}
async getDeviceSpec(model) {
let deviceSpec = this.deviceSpecifications[`_${model}_json`];
if (deviceSpec === undefined) {
const url = this.config.deviceJsonUrl(model);
try {
let file;
if ((0, fs_1.existsSync)((0, path_1.join)(__dirname, 'assets', `${model}_json.ts`))) {
file = await Promise.resolve(`${`./assets/${model}_json`}`).then(s => __importStar(require(s))).then((module) => module.spec);
}
else {
file = await this.getHeaderFile(url);
}
if (!file) {
return undefined;
}
this.processHeaderFile(file);
deviceSpec = this.deviceSpecifications[`_${model}_json`];
if (deviceSpec === undefined) {
return undefined;
}
}
catch (err) {
this.logger.debug(`Error retrieving header from ${url} for ${model}`, err);
return undefined;
}
}
deviceSpec.propertyMetadata =
this.properties[`_${model}_json_props`]?.properties;
return deviceSpec;
}
processHeaderFile(file) {
this.logger.debug('Processing header file');
const itemsInFile = this.findJsonInResponse(file);
itemsInFile.forEach((item) => {
if (item.name.endsWith('_props')) {
this.properties[item.name] = (0, class_transformer_1.plainToInstance)(decoder_types_1.DecoderPropertiesMetadata, item['value']);
}
else if (item.name.endsWith('_json')) {
this.deviceSpecifications[item.name] = (0, class_transformer_1.plainToInstance)(decoder_types_1.DecoderDeviceSpecification, item['value']);
}
});
const propertyMapping = file.match(/const char\* (_.*?_json_props) = (_.*?);/);
if (!propertyMapping) {
return;
}
this.deviceProperties[propertyMapping[1]] = propertyMapping[2];
}
async getHeaderFile(url) {
const headerFile = await (0, utils_1.request)(url, {
Accept: 'text/*',
}).get();
return headerFile.data;
}
findJsonInResponse(response) {
const matches = response.match(/const char\* (_.+?) = "(.*?)";/g);
return (matches
?.map((m) => ({
name: this.getJsonItemName(m),
value: this.getJsonItemValue(m),
}))
?.filter((i) => i['name'] !== undefined && i['value'] !== undefined) ??
[]);
}
getJsonItemName(item) {
const nameMatch = item.match(/char\* (_.+?) =/);
if (!nameMatch) {
return undefined;
}
return nameMatch[1];
}
getJsonItemValue(item) {
const valueMatch = item.match(/= "(.+?)";/);
if (!valueMatch) {
return undefined;
}
return JSON.parse(valueMatch[1].replace(/\\/g, ''));
}
async onApplicationBootstrap() {
this.logger.log('Loading decoder metadata...');
await this.getCommonProperties();
}
};
exports.DecoderService = DecoderService;
exports.DecoderService = DecoderService = DecoderService_1 = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, common_1.Inject)(decoder_config_1.DecoderConfig.KEY)),
__param(1, decoder_providers_1.InjectDecoder),
__metadata("design:paramtypes", [void 0, Object])
], DecoderService);
//# sourceMappingURL=decoder.service.js.map