Skip to content

Commit

Permalink
feat: overall performance improvements
Browse files Browse the repository at this point in the history
With recent changes, the polling config was parsed three times. once possibly during a "Target" set.
By some better optimization, this is all done before the characteristics are added to the service where they are then available to IOS interactions.
No polling is checked at runtime.
  • Loading branch information
ztalbot2000 committed May 7, 2021
1 parent adf8b30 commit 80940ee
Show file tree
Hide file tree
Showing 10 changed files with 608 additions and 365 deletions.
174 changes: 88 additions & 86 deletions Cmd4Accessory.js

Large diffs are not rendered by default.

72 changes: 16 additions & 56 deletions Cmd4Platform.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,6 @@ const Cmd4Accessory = require( "./Cmd4Accessory" ).Cmd4Accessory;
let settings = require( "./cmd4Settings" );
const constants = require( "./cmd4Constants" );


// Function to return array split by inclusion
// returns [ truesArray, falsesArray ];
// Taken from https://stackoverflow.com/questions/11731072/dividing-an-array-by-filter-function
function partition(array, predicate)
{
return array.reduce( ( acc, item ) => ( acc[+!predicate( item )].push( item ), acc ), [ [], [] ] );
}

// Platform definition
class Cmd4Platform
{
Expand Down Expand Up @@ -106,7 +97,7 @@ class Cmd4Platform
this.toBeRestoredPlatforms = [ ];
}

this.discoverDevices( );
this.discoverDevices( this.log );

// Any accessory not reachable must have been removed, find them
this.toBeRestoredPlatforms.forEach( ( accessory ) =>
Expand Down Expand Up @@ -413,14 +404,13 @@ class Cmd4Platform
discoverDevices( )
{
let platform;
let log=this.log;
let accessory;

// loop over the config.json devices and register each one if it has not
// already been registered.
this.config.accessories && this.config.accessories.forEach( ( device ) =>
{
log.debug( `Fetching config.json Platform accessories.` );
this.log.debug( `Fetching config.json Platform accessories.` );
this.Service=this.api.hap.Service;

device.name = getAccessoryName( device );
Expand All @@ -446,7 +436,7 @@ class Cmd4Platform
return;
}

log.info('Restoring existing accessory from cache:', existingAccessory.displayName);
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName);

// if you need to update the accessory.context then you should run
// `api.updatePlatformAccessories`. eg.:
Expand Down Expand Up @@ -505,7 +495,7 @@ class Cmd4Platform
// Get the properties for this accessories device type
let devProperties = CMD4_DEVICE_TYPE_ENUM.properties[ accessory.typeIndex ];

log.debug( `Step 2. ${ accessory.displayName }.service = platform.getService( Service.${ devProperties.deviceName }, ${ accessory.subType })` );
this.log.debug( `Step 2. ${ accessory.displayName }.service = platform.getService( Service.${ devProperties.deviceName }, ${ accessory.subType })` );
accessory.service = platform.getService( devProperties.service, accessory.name, accessory.subType );

// Determine which characteristics, if any, will be polled. This
Expand All @@ -528,12 +518,12 @@ class Cmd4Platform
//
// the accessory does not yet exist, so we need to create it
//
log.info('Adding new platformAccessory:', displayName);
this.log.info('Adding new platformAccessory:', displayName);

// Create the new PlatformAccessory
if ( device.category == undefined )
{
log.debug( `Step 1. platformAccessory = new platformAccessory( ${ displayName }, ${ UUID } )` );
this.log.debug( `Step 1. platformAccessory = new platformAccessory( ${ displayName }, ${ UUID } )` );
platform = new this.api.platformAccessory( displayName, UUID );

} else
Expand All @@ -544,26 +534,26 @@ class Cmd4Platform

if ( ! category )
{
log.error( `Category specified: ${ device.category } is not a valid homebridge category.` );
this.log.error( `Category specified: ${ device.category } is not a valid homebridge category.` );
process.exit( 666 );
}

log.debug( `Step 1. platformAccessory = new platformAccessory( ${ displayName }, ${ UUID }, ${ category } )` );
this.log.debug( `Step 1. platformAccessory = new platformAccessory( ${ displayName }, ${ UUID }, ${ category } )` );

platform = new this.api.platformAccessory( displayName, UUID, category );
}

platform.Service = this.Service;

log.info( chalk.magenta( `Configuring platformAccessory: ` ) + `${ device.displayName }` );
this.log.info( chalk.magenta( `Configuring platformAccessory: ` ) + `${ device.displayName }` );
let that = this;
accessory = new Cmd4Accessory( that.log, device, this.api, [], this );
accessory.platform = platform

// Put the accessory into its correct collection array.
this.createdCmd4Accessories.push( accessory );

log.debug( `Created platformAccessory: ${ accessory.displayName }` );
this.log.debug( `Created platformAccessory: ${ accessory.displayName }` );

// Store a copy of the device object in the `accessory.context`
// the `context` property can be used to store any data about the
Expand All @@ -585,12 +575,12 @@ class Cmd4Platform
// Step 6. this.api.publishExternalAccessories( PLUGIN_NAME, [ this.tvAccessory ] );
if ( accessory.publishExternally )
{
log.debug( `Step 6. publishExternalAccessories( ${ settings.PLUGIN_NAME }, [ ${accessory.displayName } ] )` );
this.log.debug( `Step 6. publishExternalAccessories( ${ settings.PLUGIN_NAME }, [ ${accessory.displayName } ] )` );

this.api.publishExternalAccessories( settings.PLUGIN_NAME, [ platform ] );

} else {
log.debug( `Step 6. registerPlatformAccessories( ${ settings.PLUGIN_NAME }, ${ settings.PLATFORM_NAME }, [ ${ accessory.displayName } ] ) `);
this.log.debug( `Step 6. registerPlatformAccessories( ${ settings.PLUGIN_NAME }, ${ settings.PLATFORM_NAME }, [ ${ accessory.displayName } ] ) `);

this.api.registerPlatformAccessories( settings.PLUGIN_NAME, settings.PLATFORM_NAME, [ platform ] );
}
Expand Down Expand Up @@ -750,7 +740,7 @@ class Cmd4Platform
setTimeout( entry.accessory.characteristicPolling.bind(
entry.accessory, entry.accessory, entry.accTypeEnumIndex, entry.characteristicString, entry.timeout, entry.interval ), entry.interval );

if ( entryIndex == settings.arrayOfPollingCharacteristics.length -1 )
if ( entryIndex == settings.arrayOfAllStaggeredPollingCharacteristics.length -1 )
entry.accessory.log.info( `All characteristics are now being polled` );

}, delay );
Expand All @@ -776,53 +766,23 @@ class Cmd4Platform
// the low priority polling.
startPolling( staggeredStartDelay = 5000, queuedStartDelay = 5000 )
{
let arrayOfPollingCharacteristics = [ ];
let queuedArrayOfPollingCharacteristics = [ ];

// Only Unit testing could possibly have no polls at all.
if ( settings.arrayOfPollingCharacteristics.length == 0 )
{
this.log.debug( ` ! settings.arrayOfPollingCaracteristecs` );
return;
}

// Seperate what was meant to be polled into the old staggered method
// and the queued method
[ arrayOfPollingCharacteristics,
queuedArrayOfPollingCharacteristics] = partition(settings.arrayOfPollingCharacteristics, i => i.queueName === constants.DEFAULT_QUEUE_NAME);

if ( arrayOfPollingCharacteristics.length > 0 )
if ( settings.arrayOfAllStaggeredPollingCharacteristics.length > 0 )
{
let pollingTimer = setTimeout( ( ) =>
{
this.startStaggeredPolling( arrayOfPollingCharacteristics );
this.startStaggeredPolling( settings.arrayOfAllStaggeredPollingCharacteristics );
}, staggeredStartDelay );

this.pollingTimers.push( pollingTimer );
}

// Check for any queued characteristics
if ( queuedArrayOfPollingCharacteristics.length == 0 )
if ( Object.keys( settings.listOfCreatedPriorityQueues ).length == 0 )
{
this.log.debug( `No queued polling characteristics` );
return;
}

// Divide the polled characteristics into their respective queue
// Coerced to string in case the queue was a number
queuedArrayOfPollingCharacteristics.forEach( ( elem ) =>
{
let queue = settings.listOfCreatedPriorityQueues[ `${ elem.queueName }` ];

this.log.debug( `Adding ${ elem.accessory.displayName } ${ CMD4_ACC_TYPE_ENUM.properties[ elem.accTypeEnumIndex ].type } elem.timeout: ${ elem.timeout } elem.interval: ${ elem.interval } to Polled Queue ${ elem.queueName }` );
queue.addLowPriorityGetPolledQueueEntry( elem.accessory,
elem.accTypeEnumIndex,
elem.characteristicString,
elem.interval,
elem.timeout )

});

// Start polling of each queue of characteristics
Object.keys( settings.listOfCreatedPriorityQueues ).forEach( ( queueName ) =>
{
Expand Down
52 changes: 15 additions & 37 deletions Cmd4PriorityPollingQueue.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

// These would already be initialized by index.js
let CMD4_ACC_TYPE_ENUM = require( "./lib/CMD4_ACC_TYPE_ENUM" ).CMD4_ACC_TYPE_ENUM;
let CMD4_DEVICE_TYPE_ENUM = require( "./lib/CMD4_DEVICE_TYPE_ENUM" ).CMD4_DEVICE_TYPE_ENUM;


// Settings, Globals and Constants
Expand All @@ -14,14 +13,14 @@ const constants = require( "./cmd4Constants" );
// Pretty Colors
var chalk = require( "chalk" );

let isRelatedTargetCharacteristicInSameDevice = require( "./utils/isRelatedTargetCharacteristicInSameDevice" );
let trueTypeOf = require( "./utils/trueTypeOf" );


let HIGH_PRIORITY_SET = 0;
let HIGH_PRIORITY_GET = 1;
let LOW_PRIORITY_GET = 2;


class Cmd4PriorityPollingQueue
{
constructor( log, queueName, queueType = constants.DEFAULT_QUEUE_TYPE )
Expand Down Expand Up @@ -109,52 +108,31 @@ class Cmd4PriorityPollingQueue
queue.inProgressSets ++;
entry.accessory.setValue( entry.accTypeEnumIndex, entry.characteristicString, entry.timeout, entry.stateChangeResponseTime, entry.value, function ( error )
{

let relatedCurrentAccTypeEnumIndex = CMD4_ACC_TYPE_ENUM.properties[ entry.accTypeEnumIndex ].relatedCurrentAccTypeEnumIndex;
if ( error == 0 &&
relatedCurrentAccTypeEnumIndex != null &&
settings.arrayOfPollingCharacteristics.filter(
s_entry => s_entry.accessory.UUID == entry.accessory.UUID &&
s_entry.accTypeEnumIndex == relatedCurrentAccTypeEnumIndex
).length > 0 &&
isRelatedTargetCharacteristicInSameDevice(
entry.accessory.typeIndex,
entry.accTypeEnumIndex,
CMD4_DEVICE_TYPE_ENUM,
CMD4_ACC_TYPE_ENUM
) == relatedCurrentAccTypeEnumIndex )
relatedCurrentAccTypeEnumIndex != null )
{
let pollingID = Date.now( );
let relatedCharacteristic = CMD4_ACC_TYPE_ENUM.properties[ relatedCurrentAccTypeEnumIndex ].characteristic;
let relatedCurrentCharacteristicString = CMD4_ACC_TYPE_ENUM.properties[ relatedCurrentAccTypeEnumIndex ].type;
let stateChangeResponseTime = entry.stateChangeResponseTime;
if ( stateChangeResponseTime < queue.currentIntervalBeingUsed * .5 )
stateChangeResponseTime = queue.currentIntervalBeingUsed * .5;

setTimeout( ( ) =>
{
entry.accessory.getValue( relatedCharacteristic, entry.characteristicString, entry.timeout, function ( error, properValue, returnedPollingID )
{
// This function should only be called once, noted by the pollingID.
if ( pollingID != returnedPollingID )
{
entry.accessory.log.info("More entries for pollingID for related get");
entry.accessory.getValue( relatedCurrentAccTypeEnumIndex, relatedCurrentCharacteristicString, entry.timeout, function ( error, properValue) {
{
if ( error == 0 )
{
entry.accessory.log.debug( chalk.blue( `characteristicPolling Updating ${ entry.accessory.displayName } ${ relatedCurrentCharacteristicString }` ) + ` ${ properValue }` );

return;
}

pollingID = -1;
entry.accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ relatedCurrentAccTypeEnumIndex ].characteristic ).updateValue( properValue );
}

queue.inProgressSets --;
setTimeout( ( ) => { queue.processQueue( HIGH_PRIORITY_SET, queue ); }, 0);

}, pollingID );
}, stateChangeResponseTime );
}});
}

} else {
entry.callback( 0 );
queue.inProgressSets --;
setTimeout( ( ) => { queue.processQueue( HIGH_PRIORITY_SET, queue ); }, 0);

queue.inProgressSets --;
setTimeout( ( ) => { queue.processQueue( HIGH_PRIORITY_SET, queue ); }, 0);
}
}, true );
}

Expand Down
2 changes: 1 addition & 1 deletion Extras/Cmd4Scripts/State.js
Original file line number Diff line number Diff line change
Expand Up @@ -2149,7 +2149,7 @@ switch( io )
{
c = readData( device, characteristic );

if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` );
if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` );

// UUID: 00000024-0000-1000-8000-0026BB765291
// Type: public.hap.characteristic.obstruction-detected
Expand Down
2 changes: 1 addition & 1 deletion cmd4Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ exports.PLUGIN_NAME = "homebridge-cmd4";
// polled at the same time. Specifically a MyAir that has
// multiple fans, switches and temperature sensors, all in
// the same device of which a linkedAccessory is not an option.
exports.arrayOfPollingCharacteristics = [ ];
exports.arrayOfAllStaggeredPollingCharacteristics = [ ];
exports.listOfCreatedPriorityQueues = { };
Loading

0 comments on commit 80940ee

Please sign in to comment.