reolink: Add polling to reolink discovered devices (#1744)

* Add polling to reolink discovered devices

* Don't poll battery devices

* Logic moved to forever cycle

* Poll battery devices only when they wake up

* Wrap each poll in try/catch

---------

Co-authored-by: Gianluca Ruocco <gianluca.ruocco@xarvio.com>
This commit is contained in:
apocaliss92
2025-02-28 07:29:12 +01:00
committed by GitHub
parent f5a4bab0a8
commit 1e709a058d
2 changed files with 111 additions and 7 deletions

View File

@@ -14,7 +14,6 @@ class ReolinkCameraSiren extends ScryptedDeviceBase implements OnOff {
constructor(public camera: ReolinkCamera, nativeId: string) {
super(nativeId);
this.on = false;
}
async turnOff() {
@@ -53,7 +52,6 @@ class ReolinkCameraSiren extends ScryptedDeviceBase implements OnOff {
class ReolinkCameraFloodlight extends ScryptedDeviceBase implements OnOff, Brightness {
constructor(public camera: ReolinkCamera, nativeId: string) {
super(nativeId);
this.on = false;
}
async setBrightness(brightness: number): Promise<void> {
@@ -81,7 +79,6 @@ class ReolinkCameraFloodlight extends ScryptedDeviceBase implements OnOff, Brigh
class ReolinkCameraPirSensor extends ScryptedDeviceBase implements OnOff {
constructor(public camera: ReolinkCamera, nativeId: string) {
super(nativeId);
this.on = false;
}
async turnOff() {
@@ -240,12 +237,63 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, DeviceProvider, R
await this.updateAbilities();
await this.updateDevice();
await this.reportDevices();
this.startDevicesStatesPolling();
})()
.catch(e => {
this.console.log('device refresh failed', e);
});
}
async pollDeviceStates() {
try {
const api = this.getClient();
try {
if (this.hasFloodlight() && this.floodlight) {
const { enabled } = await api.getWhiteLedState();
if (enabled !== this.floodlight.on) {
this.floodlight.on = enabled;
}
}
} catch { }
try {
if (this.hasSiren() && this.siren) {
const { enabled } = await api.getSiren();
if (enabled !== this.siren.on) {
this.siren.on = enabled;
}
}
} catch { }
try {
if (this.hasPirSensor() && this.pirSensor) {
const { enabled } = await api.getPirState();
if (enabled !== this.pirSensor.on) {
this.pirSensor.on = enabled;
}
}
} catch { }
} catch (e) {
this.console.error('Error in pollDeviceStates', e);
}
}
async startDevicesStatesPolling() {
if (
!this.hasBattery() &&
(this.hasFloodlight() || this.hasSiren() || this.hasPirSensor())
) {
while (true) {
await this.pollDeviceStates();
await sleep(1000 * 5);
}
}
}
async getVideoTextOverlays(): Promise<Record<string, VideoTextOverlay>> {
const client = this.getClient();
const osd = await client.getOsd();
@@ -458,6 +506,9 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, DeviceProvider, R
if (sleeping !== this.sleeping) {
this.sleeping = sleeping;
if (!sleeping) {
await this.pollDeviceStates();
}
}
if (batteryPercent !== this.batteryLevel) {
this.batteryLevel = batteryPercent;

View File

@@ -476,6 +476,31 @@ export class ReolinkCameraClient {
}
}
async getSiren() {
const url = new URL(`http://${this.host}/api.cgi`);
const body = [{
cmd: 'GetAudioAlarmV20',
action: 0,
param: { channel: this.channelId }
}];
const response = await this.requestWithLogin({
url,
method: 'POST',
responseType: 'json',
}, this.createReadable(body));
const error = response.body?.[0]?.error;
if (error) {
this.console.error('error during call to getSiren', JSON.stringify(body), error);
}
return {
enabled: response.body?.[0]?.value?.Audio?.enable === 1
};
}
async setSiren(on: boolean, duration?: number) {
const url = new URL(`http://${this.host}/api.cgi`);
const params = url.searchParams;
@@ -515,6 +540,31 @@ export class ReolinkCameraClient {
};
}
async getWhiteLedState() {
const url = new URL(`http://${this.host}/api.cgi`);
const body = [{
cmd: 'GetWhiteLed',
action: 0,
param: { channel: this.channelId }
}];
const response = await this.requestWithLogin({
url,
method: 'POST',
responseType: 'json',
}, this.createReadable(body));
const error = response.body?.[0]?.error;
if (error) {
this.console.error('error during call to getWhiteLedState', JSON.stringify(body), error);
}
return {
enabled: response.body?.[0]?.value?.WhiteLed?.state === 1
};
}
async setWhiteLedState(on?: boolean, brightness?: number) {
const url = new URL(`http://${this.host}/api.cgi`);
@@ -608,7 +658,7 @@ export class ReolinkCameraClient {
};
}
async getPirState(on?: boolean) {
async getPirState() {
const url = new URL(`http://${this.host}/api.cgi`);
const body = [{
@@ -625,10 +675,13 @@ export class ReolinkCameraClient {
const error = response.body?.[0]?.error;
if (error) {
this.console.error('error during call to setWhiteLedState', JSON.stringify(body), error);
this.console.error('error during call to getPirState', JSON.stringify(body), error);
}
return response.body?.[0]?.value?.pirInfo;
return {
enabled: response.body?.[0]?.value?.pirInfo?.enable === 1,
state: response.body?.[0]?.value?.pirInfo
};
}
async setPirState(on: boolean) {
@@ -637,7 +690,7 @@ export class ReolinkCameraClient {
const currentPir = await this.getPirState();
const newState = on ? 1 : 0;
if (!currentPir || currentPir.enable === newState) {
if (!currentPir || currentPir.state?.enable === newState) {
return;
}