Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed sensor reset #160

Merged
merged 6 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .homeycompose/drivers/settings/alarmTimeout.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": "alarm_timeout",
"type": "number",
"label": {
"en": "Alarm Timeout"
},
"value": 10,
"min": 1,
"units": {
"en": "seconds"
}
}
11 changes: 11 additions & 0 deletions .homeycompose/drivers/settings/useAlarmTimeout.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "use_alarm_timeout",
"type": "checkbox",
"hint": {
"en": "Turn the alarm off a set time after the start signal, even if no end signal is received."
},
"label": {
"en": "Use Alarm Timeout"
},
"value": false
}
69 changes: 69 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2805,6 +2805,29 @@
},
"id": "sensor_contact",
"settings": [
{
"id": "use_alarm_timeout",
"type": "checkbox",
"hint": {
"en": "Turn the alarm off a set time after the start signal, even if no end signal is received."
},
"label": {
"en": "Use Alarm Timeout"
},
"value": false
},
{
"id": "alarm_timeout",
"type": "number",
"label": {
"en": "Alarm Timeout"
},
"value": 10,
"min": 1,
"units": {
"en": "seconds"
}
},
{
"id": "deviceSpecification",
"type": "label",
Expand Down Expand Up @@ -2870,6 +2893,29 @@
},
"id": "sensor_motion",
"settings": [
{
"id": "use_alarm_timeout",
"type": "checkbox",
"hint": {
"en": "Turn the alarm off a set time after the start signal, even if no end signal is received."
},
"label": {
"en": "Use Alarm Timeout"
},
"value": false
},
{
"id": "alarm_timeout",
"type": "number",
"label": {
"en": "Alarm Timeout"
},
"value": 10,
"min": 1,
"units": {
"en": "seconds"
}
},
{
"id": "deviceSpecification",
"type": "label",
Expand Down Expand Up @@ -2935,6 +2981,29 @@
},
"id": "sensor_smoke",
"settings": [
{
"id": "use_alarm_timeout",
"type": "checkbox",
"hint": {
"en": "Turn the alarm off a set time after the start signal, even if no end signal is received."
},
"label": {
"en": "Use Alarm Timeout"
},
"value": false
},
{
"id": "alarm_timeout",
"type": "number",
"label": {
"en": "Alarm Timeout"
},
"value": 10,
"min": 1,
"units": {
"en": "seconds"
}
},
{
"id": "deviceSpecification",
"type": "label",
Expand Down
42 changes: 15 additions & 27 deletions drivers/camera/device.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import TuyaOAuth2Device from '../../lib/TuyaOAuth2Device';
import * as TuyaOAuth2Util from '../../lib/TuyaOAuth2Util';
import { constIncludes, getFromMap } from '../../lib/TuyaOAuth2Util';
import { SettingsEvent, TuyaStatus } from '../../types/TuyaTypes';
Expand All @@ -7,10 +6,9 @@ import {
CAMERA_SETTING_LABELS,
SIMPLE_CAMERA_CAPABILITIES,
} from './TuyaCameraConstants';
import TuyaTimeOutAlarmDevice from '../../lib/TuyaTimeOutAlarmDevice';

module.exports = class TuyaOAuth2DeviceCamera extends TuyaOAuth2Device {
alarmTimeouts: Record<string, NodeJS.Timeout | undefined> = {};

module.exports = class TuyaOAuth2DeviceCamera extends TuyaTimeOutAlarmDevice {
async onOAuth2Init(): Promise<void> {
await super.onOAuth2Init();

Expand Down Expand Up @@ -126,29 +124,19 @@ module.exports = class TuyaOAuth2DeviceCamera extends TuyaOAuth2Device {
}

async setAlarm(capability: string): Promise<void> {
if (this.alarmTimeouts[capability] !== undefined) {
// Extend the existing timeout if already running
clearTimeout(this.alarmTimeouts[capability]);
} else {
// Trigger capability change if not
const deviceTriggerCard = this.homey.flow.getDeviceTriggerCard(`camera_${capability}_true`);
await deviceTriggerCard.trigger(this).catch(this.error);
await this.setCapabilityValue(capability, true).catch(this.error);
}
// Disable the alarm after a set time, since we only get an "on" event
const alarmTimeout = Math.round((this.getSetting('alarm_timeout') ?? 10) * 1000);
this.alarmTimeouts[capability] = setTimeout(() => this.resetAlarm(capability), alarmTimeout);
}

async resetAlarm(capability: string): Promise<void> {
// Clear the timeout for the next event
const currentTimeout = this.alarmTimeouts[capability];
clearTimeout(currentTimeout);
this.alarmTimeouts[capability] = undefined;
// Trigger capability change
const deviceTriggerCard = this.homey.flow.getDeviceTriggerCard(`camera_${capability}_false`);
await deviceTriggerCard.trigger(this).catch(this.error);
await this.setCapabilityValue(capability, false).catch(this.error);
await super.setAlarm(
capability,
async () => {
const deviceTriggerCard = this.homey.flow.getDeviceTriggerCard(`camera_${capability}_true`);
await deviceTriggerCard.trigger(this).catch(this.error);
await this.setCapabilityValue(capability, true).catch(this.error);
},
async () => {
const deviceTriggerCard = this.homey.flow.getDeviceTriggerCard(`camera_${capability}_false`);
await deviceTriggerCard.trigger(this).catch(this.error);
await this.setCapabilityValue(capability, false).catch(this.error);
},
);
}

// Map from up/idle/down to commands so the ternary UI shows arrows
Expand Down
11 changes: 1 addition & 10 deletions drivers/camera/driver.settings.compose.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,16 +189,7 @@
]
},
{
"id": "alarm_timeout",
"type": "number",
"label": {
"en": "Alarm Timeout"
},
"value": 10,
"min": 1,
"units": {
"en": "seconds"
}
"$extends": "alarmTimeout"
},
{
"$extends": "deviceSpecification"
Expand Down
13 changes: 11 additions & 2 deletions drivers/sensor_contact/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ import TuyaOAuth2DeviceSensor from '../../lib/TuyaOAuth2DeviceSensor';
import { TuyaStatus } from '../../types/TuyaTypes';

module.exports = class TuyaOAuth2DeviceSensorContact extends TuyaOAuth2DeviceSensor {
async onOAuth2Init(): Promise<void> {
await this.initAlarm('alarm_contact').catch(this.error);

return super.onOAuth2Init();
}

async onTuyaStatus(status: TuyaStatus, changedStatusCodes: string[]): Promise<void> {
await super.onTuyaStatus(status, changedStatusCodes);

// alarm_contact
if (typeof status['doorcontact_state'] === 'boolean') {
this.setCapabilityValue('alarm_contact', status['doorcontact_state']).catch(this.error);
if (
typeof status['doorcontact_state'] === 'boolean' &&
(!this.getSetting('use_alarm_timeout') || changedStatusCodes.includes('doorcontact_state'))
) {
this.setAlarmCapabilityValue('alarm_contact', status['doorcontact_state']).catch(this.error);
}
}
};
6 changes: 6 additions & 0 deletions drivers/sensor_contact/driver.settings.compose.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
[
{
"$extends": "useAlarmTimeout"
},
{
"$extends": "alarmTimeout"
},
{
"$extends": "deviceSpecification"
}
Expand Down
13 changes: 11 additions & 2 deletions drivers/sensor_motion/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ import TuyaOAuth2DeviceSensor from '../../lib/TuyaOAuth2DeviceSensor';
import { TuyaStatus } from '../../types/TuyaTypes';

module.exports = class TuyaOAuth2DeviceSensorMotion extends TuyaOAuth2DeviceSensor {
async onOAuth2Init(): Promise<void> {
await this.initAlarm('alarm_motion').catch(this.error);

return super.onOAuth2Init();
}

async onTuyaStatus(status: TuyaStatus, changedStatusCodes: string[]): Promise<void> {
await super.onTuyaStatus(status, changedStatusCodes);

// alarm_motion
if (typeof status['pir'] === 'string') {
this.setCapabilityValue('alarm_motion', status['pir'] === 'pir').catch(this.error);
if (
typeof status['pir'] === 'string' &&
(!this.getSetting('use_alarm_timeout') || changedStatusCodes.includes('pir'))
) {
this.setAlarmCapabilityValue('alarm_motion', status['pir'] === 'pir').catch(this.error);
}
}
};
6 changes: 6 additions & 0 deletions drivers/sensor_motion/driver.settings.compose.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
[
{
"$extends": "useAlarmTimeout"
},
{
"$extends": "alarmTimeout"
},
{
"$extends": "deviceSpecification"
}
Expand Down
14 changes: 14 additions & 0 deletions drivers/sensor_motion/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ module.exports = class TuyaOAuth2DriverSensorMotion extends TuyaOAuth2DriverSens
props.capabilities.push('alarm_motion');
}

if (!specifications) {
return props;
}

for (const specification of specifications.status) {
const tuyaCapability = specification.code;
const values = JSON.parse(specification.values);
if (tuyaCapability === 'pir') {
if (!values.range.includes('none')) {
props.settings['use_alarm_timeout'] = true;
}
}
}

return props;
}
};
13 changes: 11 additions & 2 deletions drivers/sensor_smoke/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ import TuyaOAuth2DeviceSensor from '../../lib/TuyaOAuth2DeviceSensor';
import { TuyaStatus } from '../../types/TuyaTypes';

module.exports = class TuyaOAuth2DeviceSensorSmoke extends TuyaOAuth2DeviceSensor {
async onOAuth2Init(): Promise<void> {
await this.initAlarm('alarm_smoke').catch(this.error);

return super.onOAuth2Init();
}

async onTuyaStatus(status: TuyaStatus, changedStatusCodes: string[]): Promise<void> {
await super.onTuyaStatus(status, changedStatusCodes);

// alarm_smoke
if (typeof status['smoke_sensor_status'] === 'string') {
this.setCapabilityValue('alarm_smoke', status['smoke_sensor_status'] === 'alarm').catch(this.error);
if (
typeof status['smoke_sensor_status'] === 'string' &&
(!this.getSetting('use_alarm_timeout') || changedStatusCodes.includes('smoke_sensor_status'))
) {
this.setAlarmCapabilityValue('alarm_smoke', status['smoke_sensor_status'] === 'alarm').catch(this.error);
}
}
};
6 changes: 6 additions & 0 deletions drivers/sensor_smoke/driver.settings.compose.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
[
{
"$extends": "useAlarmTimeout"
},
{
"$extends": "alarmTimeout"
},
{
"$extends": "deviceSpecification"
}
Expand Down
14 changes: 14 additions & 0 deletions drivers/sensor_smoke/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ module.exports = class TuyaOAuth2DriverSensorSmoke extends TuyaOAuth2DriverSenso
props.capabilities.push('alarm_smoke');
}

if (!specifications) {
return props;
}

for (const specification of specifications.status) {
const tuyaCapability = specification.code;
const values = JSON.parse(specification.values);
if (tuyaCapability === 'alarm_smoke') {
if (!values.range.includes('normal')) {
props.settings['use_alarm_timeout'] = true;
}
}
}

return props;
}
};
4 changes: 2 additions & 2 deletions lib/TuyaOAuth2Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,10 @@ export default class TuyaOAuth2Client extends OAuth2Client<TuyaOAuth2Token> {

onUpdateWebhook(): void {
if (this.__updateWebhookTimeout) {
clearTimeout(this.__updateWebhookTimeout);
this.homey.clearTimeout(this.__updateWebhookTimeout);
}

this.__updateWebhookTimeout = setTimeout(() => {
this.__updateWebhookTimeout = this.homey.setTimeout(() => {
Promise.resolve()
.then(async () => {
const keys = Array.from(this.registeredDevices.keys());
Expand Down
2 changes: 1 addition & 1 deletion lib/TuyaOAuth2Device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export default class TuyaOAuth2Device extends OAuth2Device<TuyaOAuth2Client> {
async onTuyaStatus(status: TuyaStatus, _changedStatusCodes: string[]): Promise<void> {
// Wait at least 100ms for initialization before trying to pass the barrier again
while (this.initBarrier) {
await new Promise(resolve => setTimeout(resolve, 100));
await new Promise(resolve => this.homey.setTimeout(resolve, 100));
}

this.log('onTuyaStatus', JSON.stringify(status));
Expand Down
Loading
Loading