UNPKG

homebridge-risco-platform

Version:
961 lines (934 loc) 68.3 kB
var axios = require('axios'); var pollingtoevent = require('polling-to-event'); const iRiscoUserAgent = 'iRISCO/0002 CFNetwork/1197 Darwin/20.0.0'; const CommonNetError = { 'EAI_AGAIN': 'DNS Lookup Error. Verify your Internet Connection!!!', 'ETIMEDOUT': 'Request Timeout. If this persist, Verify your Internet Connection!!!', 'ENETUNREACH': 'Network Unreachable. Verify your Internet Connection!!!', 'ENOTFOUND': 'DNS Hostname Not Found. Verify your Internet Connection!!!' }; const JSONreplacer = () => { const visited = new WeakSet(); return (key, value) => { if (typeof value === 'object' && value !== null) { if (visited.has(value)) { return; } visited.add(value); } return value; }; }; const NetworkErrorMsg = (error) => { const CommonNetError = { 'EAI_AGAIN': 'DNS Lookup Error. Verify your Internet Connection!!!', 'ETIMEDOUT': 'Request Timeout. If this persist, Verify your Internet Connection!!!', 'ENETUNREACH': 'Network Unreachable. Verify your Internet Connection!!!', 'ENOTFOUND': 'DNS Hostname Not Found. Verify your Internet Connection!!!' }; if (error.response) { if (error.response.status >= 500) { return 'Connection problem due to an error from the RiscoCloud servers.'; } else { return `Bad HTTP Response: ${error.response.status}\nData: ${error.response.data}`; } } else if (CommonNetError[error.errno] !== undefined){ return CommonNetError[error.errno]; } else if (CommonNetError[error.code] !== undefined) { return CommonNetError[error.code]; } else { return `Error on Request : ${error.errno}\n Code : ${error.code}`; } } class RiscoPanelSession { constructor(aConfig, aLog, api) { // Do not create new object if already exist // Avoid multiple Session to RiscoCloud if (!(this instanceof RiscoPanelSession)) { return new RiscoPanelSession(aConfig, aLog); } this.Ready = false; this.DiscoveredAccessories ; this.risco_panel_name = aConfig['name']; this.polling = aConfig['polling'] || false; this.pollInterval = aConfig['pollInterval'] || 10000; this.riscoCloudDomainURL = aConfig['riscoCloudDomainURL'] || 'www.riscocloud.com' ; this.risco_username = encodeURIComponent(aConfig['riscoUsername']); this.risco_username = aConfig['riscoUsername']; this.risco_password = encodeURIComponent(aConfig['riscoPassword']); this.risco_password = aConfig['riscoPassword']; this.risco_siteId = aConfig['riscoSiteId']; this.risco_Language = `${aConfig['languageID'] || 'en'}-${aConfig['languageID']|| 'en'}`; this.risco_pincode = aConfig['riscoPIN']; this.logRCResponse = aConfig['logRCResponse'] ||false; this.Custom_Cmd = false; this.api = api; var self = this; this.Custom_armCommand = ( () => { const regtest = RegExp('\\d:.*'); if (regtest.test(aConfig['armCommand'])) { return 'armed'; } else { self.Custom_Cmd = true; return aConfig['armCommand']; } })() || 'armed'; this.Custom_nightCommand = ( () => { const regtest = RegExp('\\d:.*'); if (regtest.test(aConfig['nightCommand'])) { return 'partially'; } else { self.Custom_Cmd = true; return aConfig['nightCommand']; } })() || 'partially'; this.Custom_homeCommand = ( () => { const regtest = RegExp('\\d:.*'); if (regtest.test(aConfig['homeCommand'])) { return 'partially'; } else { self.Custom_Cmd = true; return aConfig['homeCommand']; } })() || 'partially'; this.Custom_disarmCommand = ( () => { const regtest = RegExp('\\d:.*'); if (regtest.test(aConfig['disarmCommand'])) { return 'partially'; } else { self.Custom_Cmd = true; return aConfig['disarmCommand']; } })() || 'disarmed'; this.Partition = aConfig['Partition']; this.Groups = aConfig['Groups']; this.Outputs = aConfig['Outputs']; this.Detectors = aConfig['Detectors']; this.log = aLog; this.req_counter = 0; this.accessToken; this.refreshToken; this.renewToken; this.SessionId; this.SessionRenew; this.SessionLogged = false; this.LastEvent = null; this.BadResponseCounter = 0; this.PanelDatas; this.long_event_name = ('RPS_long_%s', (this.risco_panel_name.toLowerCase()).normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/ /g, '_')); this.PollingLoop(); } PollingLoop() { var self = this; // set up polling if requested if (self.polling) { self.log.info('Starting polling with an interval of %s ms', self.pollInterval); var emitter = new pollingtoevent( (done) => { if ((self.Ready === true) && (self.SessionLogged)) { /*self.getCPStatesPoll( (err, result) => { done(err, result); });*/ self.UpdateCPStates( (err, result) => { done(err, result); }); } else { done(null, null); } }, { longpollEventName: self.long_event_name, longpolling: true, interval: self.pollInterval }); emitter.on(self.long_event_name, (state) => { if (state) { // Get OnceMore time Current State: self.log.info('New state detected: (%s) -> %s. Notify!', state, translateState(state)); self.securityService.setCharacteristic(Characteristic.SecuritySystemCurrentState, state); self.riscoCurrentState = state; } }); emitter.on('err', (err) => { self.log.error('Polling failed, error was %s', err); }); self.api.on('shutdown', () => { self.log.info('Shutdown detected, cleaning unused ressources'); self.log.debug('Remove All Listeners for %s', self.risco_panel_name); emitter.removeAllListeners(); }); } } async Login() { var self = this; self.log.debug('Entering Login Function'); try{ if (!self.SessionLogged) { const response = await axios({ url: `https://${self.riscoCloudDomainURL}/webapi/api/auth/login`, method: 'POST', json: true, headers: { 'User-Agent': `${iRiscoUserAgent}` }, data: { "userName": `${self.risco_username}`, "password": `${self.risco_password}` }, validateStatus(status) { return status == 200; }, maxRedirects: 0, }) .catch( error => { self.log.error(NetworkErrorMsg(error)); setTimeout( () => { self.SessionValidity(); }, 5000); return false; }); if (self.logRCResponse == true) {self.log.error(`Login RC_Response :\n${JSON.stringify(response.data, JSONreplacer(), 4)}`)}; if ((response.status !== undefined) && (response.status == 200) && (response.statusText == 'OK')) { if (response.data.status > 400) { self.log.error(`Error ${response.data.status}\n${response.data.errorText}`); return false; } self.log.info('Cloud Authentication Successfull'); self.accessToken = response.data.response.accessToken; self.refreshToken = response.data.response.refreshToken; self.renewToken = Date.parse(response.data.response.expiresAt); self.log.debug('Cloud accessToken :\n%s', self.accessToken); await self.GetSessionId(); return true; } else { throw new Error(`Bad HTTP Response on Cloud Auhtentication: ${response.status}`); } } else { return true; } } catch (err) { self.log.error('Error on Cloud Authentication:\n%s', err); self.SessionLogged = false; self.SessionId = ''; self.accessToken = ''; self.refreshToken = ''; self.renewToken = ''; self.SessionRenew = ''; setTimeout( () => { self.SessionValidity(); }, 5000); return false; } } async GetSessionId() { var self = this; self.log.debug('Get SessionId'); try { var response; response = await axios({ url: `https://${self.riscoCloudDomainURL}/webapi/api/wuws/site/${self.risco_siteId}/Login`, method: 'POST', json: true, headers: { 'User-Agent': `${iRiscoUserAgent}`, 'Authorization': `Bearer ${self.accessToken}` }, data: { 'languageId': `${self.risco_Language}`, 'pinCode': `${self.risco_pincode}` }, validateStatus(status) { return status == 200; }, maxRedirects: 0, }) .catch( error => { self.log.error(NetworkErrorMsg(error)); setTimeout( () => { self.SessionValidity(); }, 5000); return false; }); if (self.logRCResponse == true) {self.log.error(`GetSessionId RC_Response :\n${JSON.stringify(response.data, JSONreplacer(), 4)}`)}; if ((response.status !== undefined) && (response.status == 200) && (response.statusText == 'OK')) { if (response.data.status > 400) { if (response.data.status == 401) { return await self.Login(); } else if (response.data.status == 422) { self.log.error('Invalid Site ID code. Check your entry !!!'); return false; } else { self.log.error(`Error ${response.data.status}\n${response.data.errorText}`); return false; } } self.SessionLogged = true; self.log.debug('Cloud Session OK'); self.log.info('Retrieving Cloud Session: Ok'); self.SessionId = response.data.response.sessionId; self.SessionRenew = Date.parse(response.data.response.expiresAt); self.log.debug('Cloud SessionId :\n%s', self.SessionId); return true; } else { throw new Error(`Bad HTTP Response Get SessionId : ${response.status}`); } } catch (err) { self.log.error('Get SessionId error:\n%s', err ); self.SessionLogged = false; self.SessionId = ''; self.accessToken = ''; self.refreshToken = ''; self.renewToken = ''; self.SessionRenew = ''; setTimeout( () => { self.SessionValidity(); }, 5000); return false; } } async SessionValidity() { var self = this; self.log.debug('Entering SessionValidity Function'); try { if (!self.SessionLogged) { self.log.debug('Not Logged. Need to ReLogin'); return await self.Login(); } else { if (Date.now() >= self.renewToken) { self.SessionLogged = false; await self.Login(); } if (Date.now() >= self.SessionRenew) { await self.GetSessionId(); } return self.SessionLogged; } } catch (err) { self.log.error('Error on SessionValidity:\n%s', err); return false; } } async DiscoverParts(PartitionsDatas) { var self = this; self.log.debug('Entering DiscoverParts Function'); try { var Parts_Datas = {}; const ArmedStates = { 1: 'disarmed', 2: 'partial', 3: 'armed' }; self.log.info('Partinfo : %s', self.Partition) if (self.Partition == 'system') { var Part_Data = { Id: 0, name: self.risco_panel_name, longName: `part_0_${(self.risco_panel_name.toLowerCase()).normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/ /g, '_')}`, Required: true, childPart: ( () => { var listPart = []; Object.values(PartitionsDatas) .forEach( part => { listPart.push(part.id) }); return listPart; })(), accessorytype: 'System', previousState: null, actualState: ( () => { const countParts = Object.keys(PartitionsDatas).length; const DisarmedParts = Object.values(PartitionsDatas) .filter( part => (part.armedState == 1)); const ArmedParts = Object.values(PartitionsDatas) .filter( part => (part.armedState == 3)); if (Object.keys(DisarmedParts).length == countParts) { return 'disarmed'; } else if (Object.keys(ArmedParts).length == countParts) { return 'armed'; } else { return 'partial'; } })(), armCommand: this.Custom_armCommand, nightCommand: this.Custom_nightCommand, homeCommand: this.Custom_homeCommand, disarmCommand: this.Custom_disarmCommand, Ready: true, PReady: true, OnAlarm: false }; Parts_Datas[0] = Part_Data; Parts_Datas.type = 'system'; } else { for (var PartId in PartitionsDatas) { var Part_Data = { Id: PartitionsDatas[PartId].id, name: PartitionsDatas[PartId].name, longName: `part_${PartitionsDatas[PartId].id}_${(PartitionsDatas[PartId].name.toLowerCase()).normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/ /g, '_')}`, Required: null, accessorytype: 'Partitions', ExitDelay: 0, previousState: null, actualState: ArmedStates[PartitionsDatas[PartId].armedState], armCommand: this.Custom_armCommand, nightCommand: this.Custom_nightCommand, homeCommand: this.Custom_homeCommand, disarmCommand: this.Custom_disarmCommand, Ready: true, PReady: true, OnAlarm: false }; self.log.debug('Discovering Partition : %s with Id: %s', PartitionsDatas[PartId].name, PartitionsDatas[PartId].id); if (self.Partition == 'all') { self.log.debug('All Partitions Required'); Part_Data.Required = true; } else if (self.Partition != (self.Partition.split(',')) || (parseInt(self.Partition) != NaN)){ self.log.debug('Not All Partitions Required'); //Automatically convert string value to integer const Required_Zones = self.Partition.split(',').map( (item) => { return parseInt(item, 10); }); if (Required_Zones.includes(Part_Data.Id) !== false){ self.log.debug('Partitions "%s" Required', PartitionsDatas[PartId].name); Part_Data.Required = true; } else { self.log.debug('Partitions "%s" Not Required', PartitionsDatas[PartId].name); Part_Data.Required = false; } } else { self.log.debug('No Partitions Required'); Part_Data.Required = false; } Parts_Datas[Part_Data.Id] = Part_Data; } Parts_Datas.type = 'partition'; } self.log.info('Discovered %s Partitions', (Object.keys(Parts_Datas).length - 1 )); return Parts_Datas; } catch (err) { self.log.error('Error on Discovery Partition: %s', err); } } async DiscoverGroups(PartitionsDatas) { var self = this; try { const GroupsNames = { 0: 'Group A', 1: 'Group B', 2: 'Group C', 3: 'Group D' }; var GroupInfos = ( () => { var Groups_Datas = {}; Object.values(PartitionsDatas) .filter( partition => (partition.groups != null)) .map( partition => { Object.values(partition.groups) .forEach( groups => { if (Groups_Datas[groups.id] == undefined) { Groups_Datas[groups.id] = { Id: groups.id, name: GroupsNames[groups.id], longName: `group_${groups.id}_${(GroupsNames[groups.id].toLowerCase()).normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/ /g, '_')}`, parentPart: ( () => { return [partition.id]; })(), Required: false, accessorytype: 'Groups', previousState: null, actualState: ((groups.state == 2) ? 'disarmed' : 'armed'), OnAlarm: false } } else { if (Groups_Datas[groups.id].parentPart.indexOf(partition.id)) { Groups_Datas[groups.id].parentPart.push(partition.id); } } }); }); Object.values(Groups_Datas) .forEach( groups => { self.log.debug('Discovering Group : "%s" with Id: %s', groups.name, groups.Id); }); if (self.Groups == 'all') { self.log.debug('All Groups Required'); Object.values(Groups_Datas) .forEach( groups => { Groups_Datas[groups.Id].Required = true; }); } else if (self.Groups != (self.Groups.split(',')) || (parseInt(self.Groups) != NaN)) { self.log.debug('Not All Groups Required'); //Automatically convert string value to integer const Required_Groups = self.Groups.split(',').map( (item) => { return parseInt(item, 10); }); Object.values(Groups_Datas) .filter( groups => (Required_Groups.includes(groups.Id) !== false)) .map(groups => { Groups_Datas[groups.Id].Required = true; self.log.debug('Group "%s" Required', Group_Data.name); }); } self.log.info('Discovered %s Groups', (Object.keys(Groups_Datas).length)); self.log.debug(JSON.stringify(Groups_Datas, JSONreplacer(), 4)); return Groups_Datas; })(); return GroupInfos; } catch (err) { self.log.error('Error on Discovery Group: %s', err); } } async DiscoverOutputs(OutputsDatas) { var self = this; self.log.debug('Entering DiscoverOutputs Function'); try { var OutputInfo = ( () => { var Outputs_Datas = {}; for (var OutId in OutputsDatas) { const OutputId = OutputsDatas[OutId].deviceID; const OutputName = OutputsDatas[OutId].deviceName; var Output_Data = { Id: OutputId, name: OutputName, longName: `out_${OutputId}_${(OutputName.toLowerCase()).normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/ /g, '_')}`, Required: null, accessorytype: 'Outputs', Type: ( () => { if (OutputsDatas[OutId].deviceType == 2) { return 'pulse'; } else { return 'switch'; } })(), State: ( () => { if (OutputsDatas[OutId].deviceType == 2) { return false; } else { return ((OutputsDatas[OutId].lastCommand == 1) ? true : false); } })() }; self.log.debug('Discovering Outputs : %s with Id : %s', Output_Data.name, Output_Data.Id); if (self.Outputs == 'all') { self.log.debug('All Outputs Required'); Output_Data.Required = true; } else if (self.Outputs != (self.Outputs.split(',')) || (parseInt(self.Outputs) != NaN)){ self.log.debug('Not All Outputs Required'); //Automatically convert string value to integer const Required_Outputs = self.Outputs.split(',').map( (item) => { return parseInt(item, 10); }); if (Required_Outputs.includes(Output_Data.Id) !== false) { self.log.debug('Outputs "%s" Required', Output_Data.name); Output_Data.Required = true; } else { self.log.debug('Outputs "%s" Not Required', Output_Data.name); Output_Data.Required = false; } } else { self.log.debug('No Outputs Required'); Output_Data.Required = false; } self.log.debug('name : %s', Output_Data.name); self.log.debug('Id: %s', Output_Data.Id); self.log.debug('Command: %s', Output_Data.Command); self.log.debug('Type: %s', Output_Data.Type); self.log.debug('State: %s', Output_Data.State); Outputs_Datas[Output_Data.Id] = Output_Data; } self.log.info('Discovered %s Outputs', (Object.keys(Outputs_Datas).length)); self.log.debug(JSON.stringify(Outputs_Datas, JSONreplacer(), 4)); return Outputs_Datas; })(); return OutputInfo; } catch (err) { self.log.error('Error on Discovery Output:\n%s', err); } } async DiscoverDetectors(DetectorsDatas) { var self = this; try { var Detectors_Datas = {}; const PartEquiv = { 0: 4, 1: 1, 2: 2, 4: 3 }; var DetectorsInfos = ( () => { Object.values(DetectorsDatas) .forEach( detector => { const DetectorName = detector.zoneName; var Detector_Data = { Id: detector.zoneID, Bypassed: ((detector.status == 2) ? true : false), Partition: ( () => { const AssocMask = detector.partAssocMask .replace('A', '') .replace(/=/g, ''); var DPart = []; for (let charMask of AssocMask) { switch (charMask) { case 'Q': //Partition 1 id:0 DPart.push(0); break; case 'g': //Partition 2 id:1 DPart.push(1); break; case 'B': //Partition 3 id:2 DPart.push(2); break; case 'C': //Partition 4 id:3 DPart.push(3); break; case 'w': //Partition 1 and 2 id:0 and id:1 DPart.push.apply(DPart, [0,1]); break; case 'D': //Partition 3 and 4 id:2 and id:3 DPart.push.apply(DPart, [2,3]); break; } } return DPart.sort(function(a, b) { return a - b; }); })(), Required: false, accessorytype: 'Detector', name: DetectorName, longName: `det_${detector.zoneID}_${(DetectorName.toLowerCase()).normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/ /g, '_')}`, State: ( () => { //0 for inactive //1 for active //2 for bypassed switch (detector.status) { case 0: case 2: return false; break; case 1: return true; break; default: return false; break; } })(), OnAlarm: false }; Detectors_Datas[detector.zoneID] = Detector_Data; }); Object.values(Detectors_Datas) .forEach( detector => { self.log.debug('Discovering Detector : "%s" with Id: %s', detector.name, detector.Id); }); if (self.Detectors == 'all') { self.log.debug('All Detectors Required'); Object.values(Detectors_Datas) .forEach( detector => { Detectors_Datas[detector.Id].Required = true; }); } else if (self.Detectors != (self.Detectors.split(',')) || (parseInt(self.Detectors) != NaN)) { self.log.debug('Not All Detectors Required'); //Automatically convert string value to integer const Required_Detectors = self.Detectors.split(',').map( (item) => { return parseInt(item, 10); }); Object.values(Detectors_Datas) .filter( detector => (Required_Detectors.includes(detector.Id) !== false)) .map( detector => { Detectors_Datas[detector.Id].Required = true; self.log.debug('Detector "%s" Required', detector.name); }); } else { self.log.debug('No Detectors Required'); } return Detectors_Datas; })(); self.log.info('Discovered %s Detector(s)', Object.keys(DetectorsInfos).length); return DetectorsInfos; } catch (err) { self.log.error('Error on Discovery Detector:\n%s', err); return {}; } } /*async DiscoverCameras() { var self = this; try { await self.KeepAlive(); var response; do { response = await axios({ url: 'https://www.riscocloud.com/ELAS/WebUI/Cameras/Get', method: 'POST', headers: { Referer: 'https://www.riscocloud.com/ELAS/WebUI/MainPage/MainPage', Origin: 'https://www.riscocloud.com', Cookie: self.riscoCookies }, validateStatus(status) { return status >= 200 && status < 400; }, maxRedirects: 0, }) .catch( error => { if (error.response) { return Promise.reject(`Bad HTTP Response: ${error.response.status}\nData: ${error.response.data}`); } else { return Promise.reject(`Error on Request : ${error.errno}\n Code : ${error.code}`); } }); } while (self.IsValidResponse(response, 'DiscoverCameras') == false); if (response.status == 200) { self.log.debug('Cameras/Get status:\n%s',response.status); var CamerasInfos = (function() { self.log.debug(JSON.stringify(response.data, null, 4)); var Cameras_Datas = {}; for (var Camera in response.data.cameras.camInfo){ self.log.debug(JSON.stringify(response.data.cameras.camInfo[Camera], null, 4)); var Camera_Data = { Id: response.data.cameras.camInfo[Camera].id, name: response.data.cameras.camInfo[Camera].title, lastcapture: response.data.cameras.camInfo[Camera].photoSrc, isNet: response.data.cameras.camInfo[Camera].isNet, }; Cameras_Datas[Camera_Data.Id] = Camera_Data; } return Cameras_Datas; })(); self.log.info('Discovered %s Camera(s)', Object.keys(CamerasInfos).length); return CamerasInfos; } else { throw new Error(`Bad HTTP Response: ${response.status}`); } } catch (err) { self.log.error('Error on Discovery Camera:\n%s', err); } }*/ async getAlarmState(PanelDatas) { var self = this; self.log.debug('Entering getAlarmState function'); try { Object.values(PanelDatas.Partitions) .filter( partition => (partition.alarmState == 1)) .map( OnAlarmPart => { if (self.DiscoveredAccessories.Partitions.type == 'system'){ self.DiscoveredAccessories.Partitions[0].OnAlarm = true; } else { if (((this.Partition || 'none') != 'none') && (!(self.DiscoveredAccessories.Partitions[OnAlarmPart.id].OnAlarm))) { self.log.debug('Partition %s is Armed and under Alarm', self.DiscoveredAccessories.Partitions[OnAlarmPart.id].name); self.DiscoveredAccessories.Partitions[OnAlarmPart.id].OnAlarm = true; } if (((this.Groups || 'none') != 'none') && (OnAlarmPart.groups !== undefined)) { Object.values(OnAlarmPart.groups) .filter( group => (group.state == 3)) .map( group => { if ((self.DiscoveredAccessories.Groups[group.id].actualState != 'disarmed') && (!(self.DiscoveredAccessories.Groups[group.Id].OnAlarm))) { self.log.debug('Group %s is Armed and under Alarm', group.name); self.DiscoveredAccessories.Groups[group.Id].OnAlarm = true; } }); } } }); self.log.debug('Leaving getAlarmState function'); return Promise.resolve(); } catch (err) { self.log.error('Error on getAlarmState:\n%s', err); return Promise.reject(); } } async getPartsStates(PartitionsDatas) { var self = this; self.log.debug('Entering getPartStates function'); try { const ArmedStates = { 1: 'disarmed', 2: 'partial', 3: 'armed' }; if (self.DiscoveredAccessories.Partitions.type == 'system') { self.log.debug('System Mode'); self.DiscoveredAccessories.Partitions[0].previousState = self.DiscoveredAccessories.Partitions[0].actualState; self.DiscoveredAccessories.Partitions[0].actualState = ( () => { const countParts = Object.keys(PartitionsDatas.Partitions).length; const DisarmedParts = Object.values(PartitionsDatas.Partitions) .filter( part => (part.armedState == 1)); const ArmedParts = Object.values(PartitionsDatas.Partitions) .filter( part => (part.armedState == 3)); if (Object.keys(DisarmedParts).length == countParts) { return 'disarmed'; } else if (Object.keys(ArmedParts).length == countParts) { return 'armed'; } else { return 'partial'; } })(), self.log.debug('Previous State: %s', self.DiscoveredAccessories.Partitions[0].previousState); self.log.debug('Actual State: %s', self.DiscoveredAccessories.Partitions[0].actualState); var ExitDelay = 0; Object.values(self.DiscoveredAccessories.Partitions[0].childPart) .forEach( idpart => { const part_datas = Object.values(PartitionsDatas.Partitions) .filter( partition => (partition.id == idpart)); if (ExitDelay < Math.max(part_datas.exitDelayTO)) { ExitDelay = Math.max(part_datas.exitDelayTO); } }); if (ExitDelay != 0) { self.DiscoveredAccessories.Partitions[0].ExitDelay = ExitDelay; self.log.debug('Arming Delay Left: %s', self.DiscoveredAccessories.Partitions[0].ExitDelay); } if ((self.DiscoveredAccessories.Partitions[0].OnAlarm == true) && (self.DiscoveredAccessories.Partitions[0].actualState == 'disarmed')) { self.DiscoveredAccessories.Partitions[0].OnAlarm = false; } //Determine Occupancy State //Init Occupancy State Before Processing self.DiscoveredAccessories.Partitions[0].Ready = true; self.DiscoveredAccessories.Partitions[0].PReady = true; if (self.DiscoveredAccessories.Detectors !== undefined) { const OpenedSensor = Object.values(self.DiscoveredAccessories.Detectors) .filter( detector => ((detector.State === true) && (detector.Bypassed === false))); if (Object.keys(OpenedSensor).length > 0) { self.DiscoveredAccessories.Partitions[0].Ready = false; self.log.debug('Motion Is Detected, set System to Occupied'); if (Object.keys(Object.values(OpenedSensor) .filter(detector => detector.accessorytype != 'Detector')).length > 0) { self.DiscoveredAccessories.Partitions[0].PReady = false; } else { self.DiscoveredAccessories.Partitions[0].PReady = ((self.DiscoveredAccessories.Partitions[0].PReady) ? true : false ); } } else { self.DiscoveredAccessories.Partitions[0].Ready = ((self.DiscoveredAccessories.Partitions[0].Ready) ? true : false ); self.DiscoveredAccessories.Partitions[0].PReady = ((self.DiscoveredAccessories.Partitions[0].PReady) ? true : false ); self.log.debug('Motion Is Not Detected, set System to Not Occupied'); } } } else { self.log.debug('Partition Mode'); Object.values(PartitionsDatas.Partitions) .forEach( partition => { const PartId = partition.id; if ((Math.max(partition.exitDelayTO) != 0) && (self.DiscoveredAccessories.Partitions[PartId] !== undefined)) { self.DiscoveredAccessories.Partitions[PartId].ExitDelay = Math.max(partition.exitDelayTO); self.log.debug('Arming Delay Left for Part "%s": %s', self.DiscoveredAccessories.Partitions[PartId].name, self.DiscoveredAccessories.Partitions[PartId].ExitDelay); } }); if (PartitionsDatas.Partitions != null) { Object.values(self.DiscoveredAccessories.Partitions) .forEach( partition => { if (partition.Id !== undefined) { //Init Occupancy State Before Processing self.DiscoveredAccessories.Partitions[partition.Id].Ready = true; self.DiscoveredAccessories.Partitions[partition.Id].PReady = true; if (self.DiscoveredAccessories.Detectors !== undefined) { const OpenedSensor = Object.values(self.DiscoveredAccessories.Detectors) .filter( detector => ( (Object.values(detector.Partition) .some(parentpart => (parentpart == partition.Id)) && (detector.State === true) && (detector.Bypassed === false))) ); if (Object.keys(OpenedSensor).length > 0) { self.DiscoveredAccessories.Partitions[partition.Id].Ready = false; self.log.debug('Motion Is Detected, set System to Occupied'); if (Object.keys(Object.values(OpenedSensor) .filter( detector => (detector.accessorytype != 'Detector'))).length > 0) { self.DiscoveredAccessories.Partitions[partition.Id].PReady = false; } else { self.DiscoveredAccessories.Partitions[partition.Id].PReady = ((self.DiscoveredAccessories.Partitions[partition.Id].PReady) ? true : false ); } } else { self.DiscoveredAccessories.Partitions[partition.Id].Ready = ((self.DiscoveredAccessories.Partitions[partition.Id].Ready) ? true : false ); self.DiscoveredAccessories.Partitions[partition.Id].PReady = ((self.DiscoveredAccessories.Partitions[partition.Id].PReady) ? true : false ); self.log.debug('Motion Is Not Detected, set System to Not Occupied'); } } } }); } Object.values(PartitionsDatas.Partitions) .forEach( partition => { const Id = partition.id; if (self.DiscoveredAccessories.Partitions[Id] !== undefined) { self.DiscoveredAccessories.Partitions[Id].previousState = self.DiscoveredAccessories.Partitions[Id].actualState; self.DiscoveredAccessories.Partitions[Id].actualState = ArmedStates[partition.armedState] self.log.debug('Partition Id: %s Label: %s',Id, self.DiscoveredAccessories.Partitions[Id].name) self.log.debug('Previous State: %s', self.DiscoveredAccessories.Partitions[Id].previousState); self.log.debug('Actual State: %s', self.DiscoveredAccessories.Partitions[Id].actualState); } }); Object.values(self.DiscoveredAccessories.Partitions) .filter( partition => ((partition.OnAlarm == true) && (partition.actualState == 'disarmed'))) .forEach( partition => { self.log.debug('Partition %s Reset OnAlarm State', partition.name); self.DiscoveredAccessories.Partitions[partition.Id].OnAlarm = false; }); } self.log.debug('Leaving getPartStates function'); return true; } catch (err) { self.log.debug('Leaving getPartStates function'); self.log.error('Error on Get Partitions States: %s', err); return err; } } async getGroupsStates(PartitionsDatas) { var self = this; self.log.debug('Entering getGroupsStates function'); try { Object.values(PartitionsDatas) .filter( partition => (partition.groups !== null)) .map( partition => { Object.values(partition.groups) .forEach( groups => { if (self.DiscoveredAccessories.Groups[groups.id] !== undefined) { self.DiscoveredAccessories.Groups[groups.id].previousState = self.DiscoveredAccessories.Groups[groups.id].actualState; } }); }); Object.values(PartitionsDatas) .filter( partition => (partition.groups !== null)) .map( partition => { Object.values(partition.groups) .forEach( groups => { if (self.DiscoveredAccessories.Groups[groups.id] !== undefined) { self.DiscoveredAccessories.Groups[groups.id].actualState = ((groups.state == 2) ? 'disarmed' : 'armed'); } }); }); Object.values(self.DiscoveredAccessories.Groups) .forEach( groups => { self.log.debug('Group Id: %s Label: %s', groups.Id, groups.name) self.log.debug('Previous State: %s', groups.previousState); self.log.debug('Actual State: %s', groups.actualState); }); Object.values(self.DiscoveredAccessories.Groups) .filter( groups => ((groups.OnAlarm == true) && (groups.actualState == 'disarmed'))) .forEach( groups => { self.log.debug('Groups %s Reset OnAlarm State', groups.name); self.DiscoveredAccessories.Groups[groups.Id].OnAlarm = false; }); self.log.debug('Leaving getGroupsStates function'); return true; } catch (err) { self.log.error('Error on getGroupsStates: %s', err); return Promise.reject(); } } async getOutputsStates(OutputsDatas) { var self = this; self.log.debug('Entering getOutputsStates function'); try { Object.values(OutputsDatas) .forEach( output => { const Id = output.deviceID; if (self.DiscoveredAccessories.Outputs[Id] !== undefined) { if (output.deviceType == 2) { self.DiscoveredAccessories.Outputs[Id].State = false; } else { self.DiscoveredAccessories.Outputs[Id].State = ((output.lastCommand == 1) ? true : false); } self.log.debug('Output Id: %s Label: %s', Id, self.DiscoveredAccessories.Outputs[Id].name) } }); self.log.debug('Leaving getOutputsStates function'); return true; } catch (err) { self.log.error(