2020-01-27 20:36:52 +00:00
'use strict' ;
2017-07-18 11:28:49 +00:00
2020-12-31 11:05:17 +00:00
const UnifiEvents = require ( 'ubnt-unifi' )
2020-01-27 20:36:52 +00:00
const manifest = require ( './package.json' ) ;
2020-12-31 11:05:17 +00:00
const url = require ( 'url' ) ;
2017-07-18 11:28:49 +00:00
2020-01-27 20:36:52 +00:00
var Service , Characteristic ;
2017-07-18 11:28:49 +00:00
module . exports = function ( homebridge ) {
2020-01-27 20:36:52 +00:00
Service = homebridge . hap . Service ;
Characteristic = homebridge . hap . Characteristic ;
homebridge . registerAccessory ( 'homebridge-unifi-occupancy-sensor' ,
'UniFi Occupancy Sensor' , OccupancySensor )
} ;
2017-07-18 11:28:49 +00:00
class OccupancySensor {
2020-01-27 20:36:52 +00:00
constructor ( log , config ) {
this . log = log ;
this . name = config . name ;
this . occupancyService = new Service . OccupancySensor ( this . name ) ;
2020-01-29 08:48:32 +00:00
this . watch = config . monitor || [ ] ;
if ( config . watch ) {
for ( const watched of config . watch ) {
if ( typeof watched === 'string' || watched instanceof String ) {
this . watch . push ( { "device" : watched , "ap" : undefined } ) ;
} else {
this . watch . push ( watched ) ;
}
2020-01-27 20:36:52 +00:00
}
2020-01-29 08:48:32 +00:00
}
2020-01-27 20:36:52 +00:00
this . watchGuests = config . watchGuests ;
this . mode = config . mode || 'any' ;
2020-01-28 06:44:06 +00:00
this . interval = config . interval || 1800 ;
2020-12-31 11:05:17 +00:00
this . controller = url . parse ( config . unifi . controller ) ;
2017-07-18 11:28:49 +00:00
this . unifi = new UnifiEvents ( {
2020-12-31 11:05:17 +00:00
host : this . controller . hostname ,
port : this . controller . port ,
2017-07-18 11:28:49 +00:00
username : config . unifi . username ,
password : config . unifi . password ,
site : config . unifi . site || 'default' ,
2020-12-31 11:05:17 +00:00
insecure : ! config . unifi . secure || true ,
unifios : config . unifi . unifios || false ,
2017-07-18 11:28:49 +00:00
listen : true
2020-01-27 20:36:52 +00:00
} ) ;
2017-07-18 11:28:49 +00:00
2020-12-31 11:05:17 +00:00
this . unifi . on ( '*.connected' , ( data ) => {
2020-01-29 08:48:32 +00:00
this . log . debug ( ` Device Connected Event Received from UniFi Controller: ${ data . msg } ` ) ;
2018-04-13 09:31:41 +00:00
return this . checkOccupancy ( )
2020-01-27 20:36:52 +00:00
} ) ;
2017-07-18 11:28:49 +00:00
2020-12-31 11:05:17 +00:00
this . unifi . on ( '*.disconnected' , ( data ) => {
2020-01-29 08:48:32 +00:00
this . log . debug ( ` Device Disconnected Event Received from UniFi Controller: ${ data . msg } ` ) ;
2018-04-13 09:31:41 +00:00
return this . checkOccupancy ( )
2020-01-27 20:36:52 +00:00
} ) ;
2017-07-18 11:28:49 +00:00
2020-01-27 20:36:52 +00:00
this . occupancyDetected = Characteristic . OccupancyDetected . OCCUPANCY _NOT _DETECTED ;
this . checkOccupancy ( ) ;
2020-01-28 06:44:06 +00:00
setInterval ( this . checkOccupancy . bind ( this ) , this . interval * 1000 )
2017-07-18 11:28:49 +00:00
}
2020-01-27 20:36:52 +00:00
checkGuest ( isGuest , mac ) {
2017-07-18 11:28:49 +00:00
if ( this . watchGuests && isGuest ) {
2020-01-29 08:48:32 +00:00
this . log . debug ( ` Device [ ${ mac } ] is connected to a guest network and guest network monitoring is enabled. ` ) ;
2017-07-18 11:28:49 +00:00
return true
} else if ( ! this . watchGuests && isGuest ) {
2020-01-29 08:48:32 +00:00
this . log . debug ( ` Device [ ${ mac } ] is connected to a guest network but guest network monitoring is NOT enabled. ` ) ;
2017-07-18 11:28:49 +00:00
return false
} else {
2020-01-29 08:48:32 +00:00
this . log . debug ( ` Device [ ${ mac } ] is NOT connected to a guest network. ` ) ;
2017-07-18 11:28:49 +00:00
return true
}
}
2020-01-27 20:36:52 +00:00
isInWatchlist ( device ) {
return this . watch . some ( watchedDevice => (
watchedDevice . device === device . mac && ( watchedDevice . ap === undefined || watchedDevice . ap === device . ap _mac ) )
) ;
}
checkOccupancy ( ) {
2020-01-29 08:48:32 +00:00
this . log . debug ( 'Getting list of connected clients from UniFi Controller...' ) ;
2020-01-27 20:36:52 +00:00
2020-12-31 11:05:17 +00:00
return this . unifi . get ( 'stat/sta' )
2020-01-27 20:36:52 +00:00
. then ( ( res ) => {
2020-01-29 08:48:32 +00:00
this . log . debug ( ` ${ res . data . length } devices are currently connected to the UniFi network, checking each one to see if any are on the watch list... ` ) ;
2020-01-27 20:36:52 +00:00
let activeDevices = res . data . filter ( ( device ) => {
2020-01-29 08:48:32 +00:00
this . log . debug ( ` Device [ ${ device . mac } , ${ device . ap _mac } ] HOSTNAME: " ${ device . hostname } " , GUEST: " ${ device . is _guest } ", SSID: " ${ device . essid } " ` ) ;
2020-01-27 20:36:52 +00:00
if ( this . isInWatchlist ( device ) && this . checkGuest ( device . is _guest , device . mac ) ) {
2020-01-29 08:48:32 +00:00
this . log . debug ( ` Device [ ${ device . mac } , ${ device . ap _mac } ] Device is on the watch list. Going to trigger occupancy. ` ) ;
2020-01-27 20:36:52 +00:00
return true
} else {
2020-01-29 08:48:32 +00:00
this . log . debug ( ` Device [ ${ device . mac } ] Ignoring. Not on the watch list. ` ) ;
2020-01-27 20:36:52 +00:00
return false
}
} ) ;
2020-01-29 08:48:32 +00:00
this . log . debug ( ` Monitored devices found: ` , activeDevices . map ( x => x . mac ) ) ;
2020-01-27 20:36:52 +00:00
if ( this . mode === 'none' ) {
if ( activeDevices . length > 0 ) {
this . log ( ` ${ activeDevices . length } monitored device(s) found. Accessory is in mode "none" so NOT triggering occupancy. ` ) ;
this . occupancyDetected = Characteristic . OccupancyDetected . OCCUPANCY _NOT _DETECTED
} else {
this . log ( ` ${ activeDevices . length } monitored device(s) found. Accessory is in mode "none" so triggering occupancy. ` ) ;
this . occupancyDetected = Characteristic . OccupancyDetected . OCCUPANCY _DETECTED
}
} else if ( this . mode === 'all' ) {
if ( activeDevices . length === this . watch . length ) {
this . log ( ` ${ activeDevices . length } monitored device(s) found. Accessory is in mode "all" and all watched devices are connected so triggering occupancy. ` ) ;
this . occupancyDetected = Characteristic . OccupancyDetected . OCCUPANCY _DETECTED
2017-07-18 11:28:49 +00:00
} else {
2020-01-27 20:36:52 +00:00
this . log ( ` ${ activeDevices . length } monitored device(s) found. Accessory is in mode "all" and not all watched devices are connected so NOT triggering occupancy. ` ) ;
this . occupancyDetected = Characteristic . OccupancyDetected . OCCUPANCY _NOT _DETECTED
2017-07-18 11:28:49 +00:00
}
2020-01-27 20:36:52 +00:00
} else {
if ( activeDevices . length > 0 ) {
this . log ( ` ${ activeDevices . length } monitored device(s) found. Accessory is in mode "any" so triggering occupancy. ` ) ;
this . occupancyDetected = Characteristic . OccupancyDetected . OCCUPANCY _DETECTED
} else {
this . log ( ` ${ activeDevices . length } monitored device(s) found. Accessory is in mode "any" so NOT triggering occupancy. ` ) ;
this . occupancyDetected = Characteristic . OccupancyDetected . OCCUPANCY _NOT _DETECTED
}
}
2018-05-10 11:22:44 +00:00
2020-01-27 20:36:52 +00:00
this . setOccupancyDetected ( this . occupancyDetected )
} )
. catch ( ( err ) => {
this . log ( ` ERROR: Failed to check occupancy: ${ err . message } ` )
} )
2017-07-18 11:28:49 +00:00
}
2020-01-27 20:36:52 +00:00
getOccupancyDetected ( callback ) {
2017-07-31 10:42:54 +00:00
return callback ( null , this . occupancyDetected )
2017-07-18 11:28:49 +00:00
}
2020-01-27 20:36:52 +00:00
setOccupancyDetected ( value ) {
2017-07-18 11:28:49 +00:00
return this . occupancyService . setCharacteristic ( Characteristic . OccupancyDetected , value )
}
2020-01-27 20:36:52 +00:00
getServices ( ) {
2017-07-18 11:28:49 +00:00
var informationService = new Service . AccessoryInformation ( )
2020-01-27 20:36:52 +00:00
. setCharacteristic ( Characteristic . Manufacturer , 'oznu' )
. setCharacteristic ( Characteristic . Model , 'unifi-occupancy' )
. setCharacteristic ( Characteristic . SerialNumber , manifest . version ) ;
2017-07-18 11:28:49 +00:00
this . occupancyService
2020-01-27 20:36:52 +00:00
. getCharacteristic ( Characteristic . OccupancyDetected )
. on ( 'get' , this . getOccupancyDetected . bind ( this ) ) ;
2017-07-18 11:28:49 +00:00
return [ informationService , this . occupancyService ]
}
}