@viguza/homebridge-ezviz
Version:
A short description about what your plugin does.
89 lines • 3.76 kB
JavaScript
const POLL_INTERVAL_MS = 30_000;
const MOTION_WINDOW_MS = 60_000;
export class MotionSensor {
api;
platform;
accessory;
service;
motionDetected = false;
lastSeenAlarmTime = undefined;
clearTimer = null;
usingMqtt = false;
pollInterval = null;
constructor(api, platform, accessory) {
this.api = api;
this.platform = platform;
this.accessory = accessory;
this.accessory.getService(this.platform.Service.AccessoryInformation)
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'EZVIZ')
.setCharacteristic(this.platform.Characteristic.Model, 'Motion Sensor')
.setCharacteristic(this.platform.Characteristic.SerialNumber, accessory.context.serial);
this.service = this.accessory.getService(this.platform.Service.MotionSensor) ||
this.accessory.addService(this.platform.Service.MotionSensor);
this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName);
this.service.getCharacteristic(this.platform.Characteristic.MotionDetected)
.onGet(() => this.motionDetected);
this.poll();
this.pollInterval = setInterval(() => this.poll(), POLL_INTERVAL_MS);
}
// Called by MQTT — real-time push, trigger immediately regardless of timestamp.
onMqttAlarm() {
this.usingMqtt = true;
this.triggerMotion();
}
stopPolling() {
if (this.pollInterval) {
clearInterval(this.pollInterval);
this.pollInterval = null;
this.platform.log.debug(`${this.accessory.displayName}: polling stopped`);
}
}
get serial() {
return this.accessory.context.serial;
}
triggerMotion() {
// Reset the 60s auto-clear window from now
if (this.clearTimer) {
clearTimeout(this.clearTimer);
}
this.clearTimer = setTimeout(() => this.clearMotion(), MOTION_WINDOW_MS);
if (!this.motionDetected) {
this.motionDetected = true;
this.service.updateCharacteristic(this.platform.Characteristic.MotionDetected, true);
this.platform.log.info(`${this.accessory.displayName}: motion detected${this.usingMqtt ? ' (MQTT)' : ''}`);
}
}
clearMotion() {
if (this.clearTimer) {
clearTimeout(this.clearTimer);
this.clearTimer = null;
}
this.motionDetected = false;
this.service.updateCharacteristic(this.platform.Characteristic.MotionDetected, false);
this.platform.log.debug(`${this.accessory.displayName}: motion cleared`);
}
// Poll uses change detection: trigger when the REST API alarm timestamp changes,
// regardless of how old that timestamp is. Clears are handled by the timer only.
async poll() {
try {
const alarmTime = await this.api.getLastAlarmTime(this.serial);
if (alarmTime === null) {
return;
}
if (this.lastSeenAlarmTime === undefined) {
this.lastSeenAlarmTime = alarmTime;
this.platform.log.debug(`${this.accessory.displayName}: initialised alarmTime=${alarmTime}`);
return;
}
if (alarmTime !== this.lastSeenAlarmTime) {
this.platform.log.debug(`${this.accessory.displayName}: new alarm via poll (${this.lastSeenAlarmTime} → ${alarmTime})`);
this.lastSeenAlarmTime = alarmTime;
this.triggerMotion();
}
}
catch (error) {
this.platform.log.error(`${this.accessory.displayName}: motion poll failed:`, error);
}
}
}
//# sourceMappingURL=motion-sensor.js.map