UNPKG

bt-sensors-plugin-sk

Version:

Bluetooth Sensors for Signalk - see https://www.npmjs.com/package/bt-sensors-plugin-sk for a list of supported classes

142 lines (121 loc) 4.49 kB
const VC = require( './VictronConstants.js'); const Images = require('./VictronImages.js') const BTSensor = require("../../BTSensor.js"); const crypto = require('node:crypto'); function sleep(x) { return new Promise((resolve) => { setTimeout(() => { resolve(x); }, x); }); } class VictronSensor extends BTSensor{ static Domain = BTSensor.SensorDomains.electrical constructor(device,config,gattConfig){ super(device,config,gattConfig) this.encryptionKey = config?.encryptionKey } static async identifyMode(device, mode){ var md = await this.getDeviceProp(device,'ManufacturerData') if (md==undefined || !Object.hasOwn(md,0x2e1)) return null else { if (md[0x2e1].value[0]==0x10) { if (md[0x2e1].value[4]==mode) return this else return null } var hasDataPacket=false device.helper._prepare() device.helper.on("PropertiesChanged", (props)=> { if (Object.hasOwn(props,'ManufacturerData')){ md = props['ManufacturerData'].value hasDataPacket=md[0x2e1].value[0]==0x10 } }) while (!hasDataPacket) { await sleep(500) } device.helper.removeListeners() if (md[0x2e1].value[4]==mode) return this else return null } } async init(){ await super.init() this.addParameter( "encryptionKey", { title:"Encryption Key" } ) this.model_id=this.getManufacturerData(0x2e1)?.readUInt16LE(2)??"Unknown" this._schema.title = this.getName() } alarmReason(alarmValue){ return this.constructor.AlarmReason[alarmValue] } getModelName(){ const m = VC.MODEL_ID_MAP[this.model_id] if(m) { if(typeof m == 'string' || m instanceof String ) { return m } else { return m.name } } return this.constructor.name+" (Model ID:"+this.model_id+")" } decrypt(data){ if (!this.encryptionKey) throw new Error("Unable to decrypt: no encryption key set") const encMethod = 'aes-128-ctr'; const iv = data.readUInt16LE(5); const key = Buffer.from(this.encryptionKey,'hex') const ivBuffer = Buffer.alloc(16); // 128 bits = 16 bytes ivBuffer.writeUInt16LE(iv) var encData = Buffer.from([...data.slice(8)]) const decipher = crypto.createDecipheriv(encMethod, key, ivBuffer) const decData=decipher.update(encData) return Buffer.from(decData) } getName(){ return `Victron ${this.getModelName()}` } propertiesChanged(props){ super.propertiesChanged(props) if (this.usingGATT()) return if (!props.hasOwnProperty("ManufacturerData")) return try{ const md = this.getManufacturerData(0x2e1) if (md && md.length && md[0]==0x10){ const decData=this.decrypt(md) this.emitValuesFrom(decData) } } catch (error) { throw new Error(`Unable to read data from ${ this.getDisplayName()}: ${error}` ) } } initGATTConnection(){ throw new Error( "GATT Connection unimplemented for "+this.getDisplayName()) } getImage(){ const m = VC.MODEL_ID_MAP[this.model_id] if (m && m.image) return m.image else return Images.generic } getDescription(){ //return `<img src="https://www.victronenergy.com/_next/image?url=https%3A%2F%2Fwww.victronenergy.com%2Fupload%2Fproducts%2FSmartShunt%2520500_nw.png&w=1080&q=70"" height="150" align=”top” ></img>` return `<img src="../bt-sensors-plugin-sk/images/${this.getImage()}" width="200" style="float: left; margin: 20px;" ></img> To get the encryption key for your device, follow the instructions <a href=https://communityarchive.victronenergy.com/questions/187303/victron-bluetooth-advertising-protocol.html target="_victron_encrypt">here</a>` } } module.exports=VictronSensor