UNPKG

@maxsaber/homebridge-govee

Version:

Homebridge plugin to integrate Govee devices into HomeKit.

175 lines (147 loc) 5.02 kB
import { Buffer } from 'node:buffer' import { resolve } from 'node:path' import url from 'node:url' import { device as iotDevice } from 'aws-iot-device-sdk' import { parseError } from '../utils/functions.js' import platformLang from '../utils/lang-en.js' const dirname = url.fileURLToPath(new URL('.', import.meta.url)) export default class { constructor(platform, iotFile) { this.accountTopic = platform.accountTopic this.device = iotDevice({ privateKey: Buffer.from(iotFile.key, 'utf8'), clientCert: Buffer.from(iotFile.cert, 'utf8'), caPath: resolve(dirname, './cert/AmazonRootCA1.pem'), clientId: `AP/${platform.accountId}/${platform.clientId}`, host: platform.iotEndpoint, enableMetrics: false, }) // A listener event for if the connection closes this.device.on('close', () => { platform.log.debugWarn('[AWS] %s.', platformLang.awsEventClose) this.connected = false }) // A listener event for if the connection reconnects this.device.on('reconnect', () => { platform.log.debug('[AWS] %s.', platformLang.awsEventReconnect) this.connected = true }) // A listener event for if the connection goes offline this.device.on('offline', () => { platform.log.debugWarn('[AWS] %s.', platformLang.awsEventOffline) this.connected = false }) // A listener event for if the connection creates an error this.device.on('error', (error) => { platform.log.debugWarn('[AWS] %s [%s].', platformLang.awsEventError, parseError(error)) this.connected = false }) // A listener event for receiving a message this.device.on('message', (topic, payload) => { const payloadString = Buffer.from(payload).toString() // Parse the message to JSON try { payload = JSON.parse(payloadString) } catch (error) { platform.log.debugWarn('[AWS] %s [%s].', platformLang.invalidJson, payloadString) return } // Older models may have the message in a msg property if (payload.msg) { try { payload = JSON.parse(payload.msg) } catch (error) { platform.log.debugWarn('[AWS] %s [%s].', platformLang.invalidJson, payloadString) return } } // Also JSON parse any data property that may exist, do not throw on error if (payload.data && typeof payload.data === 'string') { try { payload.data = JSON.parse(payload.data) } catch (error) { // Do nothing, leave as string } } // Log the received message if debug is enabled platform.log.debug('[AWS] %s [%s].', platformLang.awsEventMessage, payloadString) // Send the update to the receiver function platform.receiveUpdateAWS(payload) }) // A listener event for when the connection is created this.device.on('connect', () => { platform.log.debug('[AWS] %s.', platformLang.awsEventConnect) this.connected = true }) } async connect() { return new Promise((res, rej) => { this.device.subscribe(this.accountTopic, {}, (err) => { if (err) { rej(err) } else { res() } }) }) } async requestUpdate(accessory) { // Check if we are connected before attempting an update if (!this.connected) { throw new Error(platformLang.notAWSConn) } // Generate the AWS payload const payload = { msg: { cmd: 'status', cmdVersion: 2, transaction: `v_${Date.now()}000`, type: 0, }, } // Log the update if accessory debug is enabled accessory.logDebug(`[AWS] ${platformLang.sendingUpdate} ${JSON.stringify(payload)}`) // Add the account topic after logging payload.msg.accountTopic = this.accountTopic // Send the update over AWS return new Promise((res, rej) => { this.device.publish(accessory.context.awsTopic, JSON.stringify(payload), {}, (err) => { if (err) { rej(err) } else { res() } }) }) } async updateDevice(accessory, params) { // Check if we are connected before attempting an update if (!this.connected) { throw new Error(platformLang.notAWSConn) } // Generate the AWS payload const payload = { msg: { cmd: params.cmd, cmdVersion: 0, data: params.data, transaction: `v_${Date.now()}000`, type: 1, }, } // Log the update if accessory debug is enabled accessory.logDebug(`[AWS] ${platformLang.sendingUpdate} ${JSON.stringify(payload)}`) // Add the account topic after logging payload.msg.accountTopic = this.accountTopic // Send the update over AWS return new Promise((res, rej) => { this.device.publish(accessory.context.awsTopic, JSON.stringify(payload), {}, (err) => { if (err) { rej(err) } else { res() } }) }) } }