Skip to content

Commit

Permalink
Add 'mal' SecuritySystem support. (#246)
Browse files Browse the repository at this point in the history
* Add support for 'mal' type as SecuritySystem

* 1.7.0-beta.30

* Cleanup

* Add device name

* Remove onDeviceStatusUpdate since it is not needed.

Implementation on SecuritySystemCurrentState already checks the sos_state.

* Add fix for night mode.

* Unify SecuritySystemCurrentState and SecuritySystemTargetState into SecuritySystemState

* Update Supported Devices list
  • Loading branch information
bFollon authored Feb 26, 2023
1 parent c1845f9 commit 90db4f0
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 4 deletions.
2 changes: 1 addition & 1 deletion SUPPORTED_DEVICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Most category code is pinyin abbreviation of Chinese name.

| Name | Name (zh) | Code | Homebridge Service | Supported | Links |
| ---- | ---- | ---- | ---- | ---- | ---- |
| Alarm Host | 报警主机 | mal | | | [Documentation](https://developer.tuya.com/en/docs/iot/categorymal?id=Kaiuz33clqxaf) |
| Alarm Host | 报警主机 | mal | Security System | | [Documentation](https://developer.tuya.com/en/docs/iot/categorymal?id=Kaiuz33clqxaf) |
| Smart Camera | 智能摄像机 | sp | Motion Sensor<br> Doorbell || [Documentation](https://developer.tuya.com/en/docs/iot/categorysp?id=Kaiuz35leyo12) |
| Siren Alarm | 声光报警传感器 | sgbj | | | [Documentation](https://developer.tuya.com/en/docs/iot/categorysgbj?id=Kaiuz37tlpbnu) |
| Gas Alarm | 燃气报警传感器 | rqbj | Leak Sensor || [Documentation](https://developer.tuya.com/en/docs/iot/categoryrqbj?id=Kaiuz3d162ubw) |
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@0x5e/homebridge-tuya-platform",
"version": "1.7.0-beta.29",
"version": "1.7.0-beta.30",
"description": "Fork version of official Tuya Homebridge plugin. Brings a bunch of bug fix and new device support.",
"license": "MIT",
"repository": {
Expand Down
4 changes: 4 additions & 0 deletions src/accessory/AccessoryFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import AirConditionerAccessory from './AirConditionerAccessory';
import IRControlHubAccessory from './IRControlHubAccessory';
import IRGenericAccessory from './IRGenericAccessory';
import IRAirConditionerAccessory from './IRAirConditionerAccessory';
import SecuritySystemAccessory from './SecuritySystemAccessory';


export default class AccessoryFactory {
Expand Down Expand Up @@ -176,6 +177,9 @@ export default class AccessoryFactory {
case 'jtmspro':
handler = new LockAccessory(platform, accessory);
break;
case 'mal':
handler = new SecuritySystemAccessory(platform, accessory);
break;

// Other
case 'scene':
Expand Down
29 changes: 29 additions & 0 deletions src/accessory/SecuritySystemAccessory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import BaseAccessory from './BaseAccessory';
import { configureSecuritySystemCurrentState, configureSecuritySystemTargetState } from './characteristic/SecuritySystemState';
import { configureName } from './characteristic/Name';

const SCHEMA_CODE = {
MASTER_MODE: ['master_mode'],
SOS_STATE: ['sos_state'],
};

export default class SecuritySystemAccessory extends BaseAccessory {

requiredSchema() {
return [SCHEMA_CODE.MASTER_MODE, SCHEMA_CODE.SOS_STATE];
}

isNightArm = false;

configureServices() {
const service = this.accessory.getService(this.Service.SecuritySystem)
|| this.accessory.addService(this.Service.SecuritySystem);

configureName(this, service, this.device.name);

configureSecuritySystemCurrentState(this, service, this.getSchema(...SCHEMA_CODE.MASTER_MODE),
this.getSchema(...SCHEMA_CODE.SOS_STATE));
configureSecuritySystemTargetState(this, service, this.getSchema(...SCHEMA_CODE.MASTER_MODE),
this.getSchema(...SCHEMA_CODE.SOS_STATE));
}
}
91 changes: 91 additions & 0 deletions src/accessory/characteristic/SecuritySystemState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { Service } from 'homebridge';
import { TuyaDeviceSchema } from '../../device/TuyaDevice';
import BaseAccessory from '../BaseAccessory';
import SecuritySystemAccessory from '../SecuritySystemAccessory';

const TUYA_CODES = {
MASTER_MODE: {
ARMED: 'arm',
DISARMED: 'disarmed',
HOME: 'home',
},
};

function getTuyaHomebridgeMap(accessory: BaseAccessory) {
const tuyaHomebridgeMap = new Map();

tuyaHomebridgeMap.set(TUYA_CODES.MASTER_MODE.ARMED, accessory.Characteristic.SecuritySystemCurrentState.AWAY_ARM);
tuyaHomebridgeMap.set(TUYA_CODES.MASTER_MODE.DISARMED, accessory.Characteristic.SecuritySystemCurrentState.DISARMED);
tuyaHomebridgeMap.set(TUYA_CODES.MASTER_MODE.HOME, accessory.Characteristic.SecuritySystemCurrentState.STAY_ARM);
tuyaHomebridgeMap.set(accessory.Characteristic.SecuritySystemCurrentState.AWAY_ARM, TUYA_CODES.MASTER_MODE.ARMED);
tuyaHomebridgeMap.set(accessory.Characteristic.SecuritySystemCurrentState.DISARMED, TUYA_CODES.MASTER_MODE.DISARMED);
tuyaHomebridgeMap.set(accessory.Characteristic.SecuritySystemCurrentState.STAY_ARM, TUYA_CODES.MASTER_MODE.HOME);
tuyaHomebridgeMap.set(accessory.Characteristic.SecuritySystemCurrentState.NIGHT_ARM, TUYA_CODES.MASTER_MODE.HOME);

return tuyaHomebridgeMap;
}

export function configureSecuritySystemCurrentState(accessory: SecuritySystemAccessory, service: Service,
masterModeSchema?: TuyaDeviceSchema, sosStateSchema?: TuyaDeviceSchema) {
if (!masterModeSchema || !sosStateSchema) {
return;
}

const tuyaHomebridgeMap = getTuyaHomebridgeMap(accessory);

service.getCharacteristic(accessory.Characteristic.SecuritySystemCurrentState)
.onGet(() => {
const alarmTriggered = accessory.getStatus(sosStateSchema.code)!.value;

if (alarmTriggered) {
return accessory.Characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED;
} else {
const currentState = accessory.getStatus(masterModeSchema.code)!.value;
if (currentState === TUYA_CODES.MASTER_MODE.HOME) {
return accessory.isNightArm ? accessory.Characteristic.SecuritySystemCurrentState.NIGHT_ARM :
accessory.Characteristic.SecuritySystemCurrentState.STAY_ARM;
}

return tuyaHomebridgeMap.get(currentState);
}
});
}

export function configureSecuritySystemTargetState(accessory: SecuritySystemAccessory, service: Service,
masterModeSchema?: TuyaDeviceSchema, sosStateSchema?: TuyaDeviceSchema) {
if (!masterModeSchema || !sosStateSchema) {
return;
}

const tuyaHomebridgeMap = getTuyaHomebridgeMap(accessory);

service.getCharacteristic(accessory.Characteristic.SecuritySystemTargetState)
.onGet(() => {
const currentState = accessory.getStatus(masterModeSchema.code)!.value;
if (currentState === TUYA_CODES.MASTER_MODE.HOME) {
return accessory.isNightArm ? accessory.Characteristic.SecuritySystemCurrentState.NIGHT_ARM :
accessory.Characteristic.SecuritySystemCurrentState.STAY_ARM;
}

return tuyaHomebridgeMap.get(currentState);
})
.onSet(value => {

const sosState = accessory.getStatus(sosStateSchema.code)?.value;

// If we received a request to disarm the alarm, we make sure sos_state is set to false
if (sosState && value === accessory.Characteristic.SecuritySystemTargetState.DISARM) {
accessory.sendCommands([{
code: sosStateSchema.code,
value: false,
}], true);
}

accessory.isNightArm = value === accessory.Characteristic.SecuritySystemTargetState.NIGHT_ARM;

accessory.sendCommands([{
code: masterModeSchema.code,
value: tuyaHomebridgeMap.get(value),
}], true);
});
}

0 comments on commit 90db4f0

Please sign in to comment.