From 6634f66820531a19a7c001c87151936059eac5aa Mon Sep 17 00:00:00 2001 From: JDierkse Date: Mon, 3 Mar 2025 14:40:36 +0100 Subject: [PATCH] Add SmokeDetectorAccessory --- platform.ts | 53 ++++++++++--- smokeDetectorAccessory.ts | 160 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 9 deletions(-) create mode 100644 smokeDetectorAccessory.ts diff --git a/platform.ts b/platform.ts index 1bbfe27..62ca62e 100644 --- a/platform.ts +++ b/platform.ts @@ -11,7 +11,10 @@ import { environmentSensorAccessory } from './environmentSensorAccessory' import { doorWindowSensorAccessory } from './doorWindowSensorAccessory' import { motionSensorAccessory } from './motionSensorAccessory' import { cameraAccessory } from './cameraAccessory' +//import { activityAccessory } from './activityAccessory' import { thermostatAccessory } from './thermostatAccessory' +import { smokeDetectorAccessory } from './smokeDetectorAccessory' +import { securitySystemAccessory } from './securitySystemAccessory' import { httpServer } from './httpServer' export class domoticaPlatform implements DynamicPlatformPlugin { @@ -113,15 +116,15 @@ export class domoticaPlatform implements DynamicPlatformPlugin { this.log.debug('cameraAccessory.discoverDevices Error ->' + error) }) - this.discoveryStarted('activityAccessory') - activityAccessory.discoverDevices(this) - .then((devices) => { - this.processDiscoveredDevices(devices) - this.discoveryCompleted('activityAccessory') - }) - .catch((error) => { - this.log.debug('activityAccessory.discoverDevices Error ->' + error) - }) +// this.discoveryStarted('activityAccessory') +// activityAccessory.discoverDevices(this) +// .then((devices) => { +// this.processDiscoveredDevices(devices) +// this.discoveryCompleted('activityAccessory') +// }) +// .catch((error) => { +// this.log.debug('activityAccessory.discoverDevices Error ->' + error) +// }) this.discoveryStarted('thermostatAccessory') thermostatAccessory.discoverDevices(this) @@ -132,6 +135,26 @@ export class domoticaPlatform implements DynamicPlatformPlugin { .catch((error) => { this.log.debug('thermostatAccessory.discoverDevices Error ->' + error) }) + + this.discoveryStarted('smokeDetectorAccessory') + smokeDetectorAccessory.discoverDevices(this) + .then((devices) => { + this.processDiscoveredDevices(devices) + this.discoveryCompleted('smokeDetectorAccessory') + }) + .catch((error) => { + this.log.debug('smokeDetectorAccessory.discoverDevices Error ->' + error) + }) + + this.discoveryStarted('securitySystemAccessory') + securitySystemAccessory.discoverDevices(this) + .then((devices) => { + this.processDiscoveredDevices(devices) + this.discoveryCompleted('securitySystemAccessory') + }) + .catch((error) => { + this.log.debug('securitySystemAccessory.discoverDevices Error ->' + error) + }) } processDiscoveredDevices(domoticaDevices: device[]) { @@ -164,8 +187,14 @@ export class domoticaPlatform implements DynamicPlatformPlugin { this.accessories.push(new motionSensorAccessory(this, existingAccessory)) } else if (existingAccessory.context.device.type == 'Camera') { this.accessories.push(new cameraAccessory(this, existingAccessory)) +// } else if (existingAccessory.context.device.type == 'Activity') { +// this.accessories.push(new activityAccessory(this, existingAccessory)) } else if (existingAccessory.context.device.type == 'Thermostat') { this.accessories.push(new thermostatAccessory(this, existingAccessory)) + } else if (existingAccessory.context.device.type == 'SmokeDetector') { + this.accessories.push(new smokeDetectorAccessory(this, existingAccessory)) + } else if (existingAccessory.context.device.type == 'SecuritySystem') { + this.accessories.push(new securitySystemAccessory(this, existingAccessory)) } } else { this.log.info('+ ' + device.type + ': ' + device.deviceName) @@ -187,8 +216,14 @@ export class domoticaPlatform implements DynamicPlatformPlugin { this.accessories.push(new motionSensorAccessory(this, accessory)) } else if (accessory.context.device.type == 'Camera') { this.accessories.push(new cameraAccessory(this, accessory)) +// } else if (accessory.context.device.type == 'Activity') { +// this.accessories.push(new activityAccessory(this, accessory)) } else if (accessory.context.device.type == 'Thermostat') { this.accessories.push(new thermostatAccessory(this, accessory)) + } else if (accessory.context.device.type == 'SmokeDetector') { + this.accessories.push(new smokeDetectorAccessory(this, accessory)) + } else if (accessory.context.device.type == 'SecuritySystem') { + this.accessories.push(new securitySystemAccessory(this, accessory)) } this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]) diff --git a/smokeDetectorAccessory.ts b/smokeDetectorAccessory.ts new file mode 100644 index 0000000..4d78fbb --- /dev/null +++ b/smokeDetectorAccessory.ts @@ -0,0 +1,160 @@ +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge' + +import { accessory } from './accessory' +import { device } from './device' +import { httpRequest, response } from './httpPromise' +import { domoticaPlatform } from './platform' + +export class smokeDetectorAccessory extends accessory { + private service: Service + + private state = { + SmokeDetected: false, + Tampered: false, // TODO + LowBattery: false, + Active: false + } + + static async discoverDevices(platform: domoticaPlatform): Promise { + return new Promise((resolve, reject) => { + let smokedetectorDevices: device[] = [] + + httpRequest("http://" + platform.config.hostname + ':' + platform.config.port + '/API/inventory/smokedetector') + .then((response) => { + let devices = JSON.parse(response.body) + + for (const device of devices) { + if (device.enabled == "true") { + let object: device = { + type: "SmokeDetector", + typeId: device.id, + uniqueId: "SmokeDetector." + device.id, + deviceName: device.name, + switchable: "false", + dimmable: "false", + detailedType: device.type + } + smokedetectorDevices.push(object) + } + } + + resolve(smokedetectorDevices) + }) + .catch((error) => { + reject('smokeDetectorAccessory::discoverDevices Error ->' + error) + }) + }) + } + + constructor( + private readonly platform: domoticaPlatform, + private readonly accessory: PlatformAccessory, + ) { + super("SmokeDetector", accessory.context.device.typeId) + this.accessory.getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic(this.platform.Characteristic.Manufacturer, 'SmokeDetector') + .setCharacteristic(this.platform.Characteristic.Model, 'ZSDR-850') + .setCharacteristic(this.platform.Characteristic.SerialNumber, '10000' + accessory.context.device.typeId) + + this.service = this.accessory.getService(this.platform.Service.SmokeSensor) || this.accessory.addService(this.platform.Service.SmokeSensor) + + this.service.getCharacteristic(this.platform.Characteristic.SmokeDetected) + .onGet(this.getSmokeDetected.bind(this)) + + this.service.getCharacteristic(this.platform.Characteristic.StatusLowBattery) + .onGet(this.getLowBattery.bind(this)) + + this.service.getCharacteristic(this.platform.Characteristic.StatusActive) + .onGet(this.getActive.bind(this)) + + this.setName(accessory.context.device.deviceName) + this.update() + } + + setName(name) { + this.service.setCharacteristic(this.platform.Characteristic.Name, name) + } + + setValue(key, value) { + if (key == "state" && value != "") { + if (value == "smoke") { + this.state.SmokeDetected = true + } else { + this.state.SmokeDetected = false + } + this.service.updateCharacteristic(this.platform.Characteristic.SmokeDetected, this.state.SmokeDetected) + } + else if (key == "battery" && value != "") { + if (value < 25) { + this.state.LowBattery = true + } else { + this.state.LowBattery = false + } + this.service.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.state.LowBattery) + } + else if (key == "online" && value != "") { + if (value == "false") { + this.state.Active = false + } else { + this.state.Active = true + } + this.service.updateCharacteristic(this.platform.Characteristic.StatusActive, this.state.Active) + } + } + + update() { + const data = JSON.stringify([{ + type: 'smokedetector', + query: 'state' + }]) + + httpRequest("http://" + this.platform.config.hostname + ':' + this.platform.config.port + '/API/Status', data) + .then((response) => { + const devices = JSON.parse(response.body) + + if (devices.hasOwnProperty('smokedetector')) { + for (const device of devices.smokedetector) { + if (device.id == this.accessory.context.device.typeId) { + if (device.state == 'smoke') { + this.state.SmokeDetected = true + } else { + this.state.SmokeDetected = false + } + if (device.battery < 25) { + this.state.LowBattery = true + } else { + this.state.LowBattery = false + } + + if (device.online == "false") { + this.state.Active = false + } else { + this.state.Active = true + } + + this.service.updateCharacteristic(this.platform.Characteristic.SmokeDetected, this.state.SmokeDetected) + this.service.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.state.LowBattery) + this.service.updateCharacteristic(this.platform.Characteristic.StatusActive, this.state.Active) + + break + } + } + } + }) + .catch((error) => { + this.platform.log.debug('smokeDetectorAccessory::update Error ->' + error) + }) + } + + getSmokeDetected(): CharacteristicValue { + return this.state.SmokeDetected + } + + getLowBattery(): CharacteristicValue { + return this.state.LowBattery + } + + getActive(): CharacteristicValue { + return this.state.Active + } +} \ No newline at end of file