UNPKG

@constructorfleet/ultimate-govee

Version:

Library for interacting with Govee devices written in Typescript.

207 lines 8.68 kB
"use strict"; 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