homebridge-myplace 2.2.3 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -3
- package/Cmd5Platform.js +17 -74
- package/Cmd5PriorityPollingQueue.js +8 -8
- package/MyPlace.sh +90 -117
- package/README.md +180 -176
- package/RUNNING_CHANGELOG.md +12 -0
- package/config.schema.json +13 -3
- package/eslint.config.mjs +66 -0
- package/package.json +11 -9
- package/utils/createMyPlaceConfig.js +424 -438
- package/utils/devicesAutoDiscovery.js +53 -0
- package/utils/removeTempDir.js +27 -0
- package/utils/scanDevicesWithOpenPort.js +72 -0
- package/utils/updateConfig.js +160 -0
- package/utils/versionChecker.js +1 -1
- package/.eslintrc.json +0 -40
- package/.github/ISSUE_TEMPLATE/bug-report.md +0 -44
- package/.github/ISSUE_TEMPLATE/feature-request.md +0 -27
- package/.github/ISSUE_TEMPLATE/support-request.md +0 -52
- package/.github/pull_request_template.md +0 -26
- package/.husky/pre-commit +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
### Homebridge-myplace - An independent plugin for Homebridge bringing Advantage Air MyPlace system, its smaller siblings (E-zone, MyAir, MyAir4, etc) and its cousins (e.g. Fujitsu AnywAir) to Homekit
|
|
2
|
-
##### v2.
|
|
2
|
+
##### v2.3.1 (2025-10-22)
|
|
3
3
|
|
|
4
|
-
###### (1)
|
|
5
|
-
###### (2)
|
|
4
|
+
###### (1) Allow up to 5 retries of inaccessible device before proceeding to auto-discovery.
|
|
5
|
+
###### (2) Minor bug fixes.
|
package/Cmd5Platform.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
const { getAccessoryName,
|
|
5
5
|
getAccessoryDisplayName } = require( "./utils/getAccessoryNameFunctions" );
|
|
6
6
|
const { parseAddQueueTypes } = require( "./Cmd5PriorityPollingQueue" );
|
|
7
|
+
const { removeTempDir } = require( "./utils/removeTempDir" );
|
|
8
|
+
const { updateConfig } = require( "./utils/updateConfig" );
|
|
7
9
|
|
|
8
10
|
let Logger = require( "./utils/Logger" );
|
|
9
11
|
let getAccessoryUUID = require( "./utils/getAccessoryUUID" );
|
|
@@ -15,7 +17,6 @@ let trueTypeOf = require( "./utils/trueTypeOf" );
|
|
|
15
17
|
let HV = require( "./utils/HV" );
|
|
16
18
|
|
|
17
19
|
// Essential variables
|
|
18
|
-
const { spawnSync } = require('child_process');
|
|
19
20
|
let createAccessorysInformationService = require( "./utils/createAccessorysInformationService" );
|
|
20
21
|
|
|
21
22
|
// Pretty Colors
|
|
@@ -36,8 +37,6 @@ const Cmd5Accessory = require( "./Cmd5Accessory" ).Cmd5Accessory;
|
|
|
36
37
|
// Settings, Globals and Constants
|
|
37
38
|
let settings = require( "./cmd5Settings" );
|
|
38
39
|
const constants = require( "./cmd5Constants" );
|
|
39
|
-
const path = require('path');
|
|
40
|
-
const fs = require('fs');
|
|
41
40
|
|
|
42
41
|
// Platform definition
|
|
43
42
|
class Cmd5Platform
|
|
@@ -105,83 +104,22 @@ class Cmd5Platform
|
|
|
105
104
|
|
|
106
105
|
// didFinishLaunching is only called after the
|
|
107
106
|
// registerPlatform completes.
|
|
108
|
-
api.on( "didFinishLaunching", (
|
|
107
|
+
api.on( "didFinishLaunching", async () =>
|
|
109
108
|
{
|
|
110
109
|
this.log.info( chalk.green( "MyPlace Platform didFinishLaunching" ) );
|
|
111
110
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const directoryPath = process.env.TMPDIR || "/tmp";
|
|
116
|
-
const files = fs.readdirSync(directoryPath);
|
|
117
|
-
const filteredFiles = files.filter(file => file.match(/^(AA|BB)-\d{3}$/));
|
|
118
|
-
|
|
119
|
-
filteredFiles.forEach(file => {
|
|
120
|
-
const sdir = `${directoryPath}/${file}`;
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
fs.rmSync(sdir, { recursive: true, force: true });
|
|
124
|
-
this.log.info(`Temporary working directory ${sdir} removed`);
|
|
125
|
-
} catch (err) {
|
|
126
|
-
this.log.error(` Unable to remove temporary working directory ${sdir}: [${err}]`);
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
} catch (err) {
|
|
130
|
-
this.log.error(`Unable to scan and remove temporary working directory: [${err}]`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Run ConfigCreator to update/refresh the MyPlace config
|
|
134
|
-
this.log.info( chalk.yellow( "Running createMyPlacConfig..." ) );
|
|
135
|
-
try {
|
|
136
|
-
// Build args for up to 3 devices
|
|
137
|
-
let args = [];
|
|
138
|
-
for (let i = 0; i < 3; i++) {
|
|
139
|
-
const device = this.config.devices[i];
|
|
140
|
-
if (device) {
|
|
141
|
-
const ipPort = `${device.ipAddress || ''}:${device.port || ''}`;
|
|
142
|
-
const name = device.name || '';
|
|
143
|
-
const extraTimers = device.extraTimers ?? false;
|
|
144
|
-
const debug = device.debug ?? false;
|
|
145
|
-
args.push(ipPort, name, extraTimers, debug);
|
|
146
|
-
} else {
|
|
147
|
-
// If device is missing, push empty args
|
|
148
|
-
args.push('', '', '', '');
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Add __dirname and "homebridge" as last two arguments
|
|
153
|
-
args.push(`${__dirname}/MyPlace.sh`);
|
|
154
|
-
|
|
155
|
-
// Run the createMyPlaceconfig.js script
|
|
156
|
-
const scriptPath = path.resolve(__dirname, 'utils', 'createMyPlaceConfig.js');
|
|
157
|
-
this.log.debug('Running script:', scriptPath, args);
|
|
158
|
-
|
|
159
|
-
const result = spawnSync('node', [scriptPath, ...args], { encoding: 'utf8', maxBuffer: 5 * 1024 * 1024 });
|
|
111
|
+
// Update the config
|
|
112
|
+
// The original config has no config for accessories and that needs to be generated by createMyplaceConfig.js
|
|
113
|
+
this.config = await updateConfig( this.config, this.log, __dirname );
|
|
160
114
|
|
|
161
|
-
|
|
162
|
-
const jsonText = result.stdout.trim();
|
|
163
|
-
|
|
164
|
-
if (status.includes('DONE')) {
|
|
165
|
-
this.config = JSON.parse(jsonText);
|
|
166
|
-
this.log.info(status);
|
|
167
|
-
this.log.debug('Updated config:\n' + JSON.stringify(this.config));
|
|
168
|
-
} else {
|
|
169
|
-
this.log.error(status);
|
|
170
|
-
this.log.warn('Proceeding with original config — no accessories will be created.');
|
|
171
|
-
}
|
|
172
|
-
} catch (err) {
|
|
173
|
-
this.log.warn('ERROR: createMyPlaceConfig failed:', err);
|
|
174
|
-
this.log.warn('Proceeding with original config — no accessories will be created.');
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Now process these using the updated config
|
|
115
|
+
// Now process this updated config
|
|
178
116
|
this.parseConfigForCmd5Directives( this.config );
|
|
179
117
|
this.hV.update( this );
|
|
180
118
|
this.processNewCharacteristicDefinitions( );
|
|
181
119
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
this.
|
|
120
|
+
// scan the platform accessories (devices) to identify which ones to be restored from cache
|
|
121
|
+
this.log.info( chalk.yellow( "Scanning the config and the cache for accessories to be removed or restored from cache..." ) );
|
|
122
|
+
this.scanToBeRemovedOrRestored( this.log );
|
|
185
123
|
|
|
186
124
|
// Any accessory NOT to be restored should be removed, find them
|
|
187
125
|
this.toBeRestoredPlatforms.forEach( ( accessory ) =>
|
|
@@ -194,6 +132,9 @@ class Cmd5Platform
|
|
|
194
132
|
// if it is not already in cache
|
|
195
133
|
this.discoverDevices( this.log );
|
|
196
134
|
|
|
135
|
+
// Remove MyPlace shell script temporary working directories on Homebridge RESTART
|
|
136
|
+
removeTempDir( this.log );
|
|
137
|
+
|
|
197
138
|
// Let the Polling Begin
|
|
198
139
|
this.startPolling();
|
|
199
140
|
|
|
@@ -409,7 +350,7 @@ class Cmd5Platform
|
|
|
409
350
|
}
|
|
410
351
|
|
|
411
352
|
// Scan the platform accessories and identify devices to be restored from cache
|
|
412
|
-
|
|
353
|
+
scanToBeRemovedOrRestored( )
|
|
413
354
|
{
|
|
414
355
|
// Homebridge can only handle a max of 150 accessories per bridge, as such, it is prudent to identify and remove those accessories
|
|
415
356
|
// which are not to be restored before creating new ones
|
|
@@ -475,7 +416,7 @@ class Cmd5Platform
|
|
|
475
416
|
let duplicatePlatformAccessory = this.createdCmd5Platforms.find(accessory => accessory.UUID === existingAccessory.UUID);
|
|
476
417
|
if ( duplicatePlatformAccessory )
|
|
477
418
|
{
|
|
478
|
-
this.log( chalk.red( `Error duplicate platform accessory: ${ duplicatePlatformAccessory.
|
|
419
|
+
this.log.error( chalk.red( `Error duplicate platform accessory: ${ duplicatePlatformAccessory.displayName } uuid:${ duplicatePlatformAccessory.UUID }` ) );
|
|
479
420
|
// Next in for.Each object iteration
|
|
480
421
|
return;
|
|
481
422
|
}
|
|
@@ -581,6 +522,8 @@ class Cmd5Platform
|
|
|
581
522
|
|
|
582
523
|
platform.Service = this.Service;
|
|
583
524
|
|
|
525
|
+
platform.context.device = device; // ⬅️ Store device config for restoration
|
|
526
|
+
|
|
584
527
|
this.log.info( chalk.magenta( `Configuring platformAccessory: ` ) + `${ device.displayName }` );
|
|
585
528
|
let that = this;
|
|
586
529
|
accessory = new Cmd5Accessory( that.log, device, this.api, [ ], this );
|
|
@@ -120,10 +120,10 @@ class Cmd5PriorityPollingQueue
|
|
|
120
120
|
if ( this.state_cmd.match( /MyPlace.sh'$/ ) )
|
|
121
121
|
{
|
|
122
122
|
// Reject Set requests to change Fanv2 rotationSpeed or to turn off myZone for zones with temperature sensors
|
|
123
|
-
if ( this.typeIndex == 20 && this.displayName.match ( / Zone$/ ) &&
|
|
124
|
-
( characteristicString == 'RotationSpeed' ||
|
|
125
|
-
( characteristicString == 'RotationDirection' && value == 1 )
|
|
126
|
-
)
|
|
123
|
+
if ( this.typeIndex == 20 && this.displayName.match ( / Zone$/ ) &&
|
|
124
|
+
( characteristicString == 'RotationSpeed' ||
|
|
125
|
+
( characteristicString == 'RotationDirection' && value == 1 )
|
|
126
|
+
)
|
|
127
127
|
)
|
|
128
128
|
{
|
|
129
129
|
let storedValue = this.cmd5Storage.getStoredValueForIndex( accTypeEnumIndex );
|
|
@@ -132,8 +132,8 @@ class Cmd5PriorityPollingQueue
|
|
|
132
132
|
return;
|
|
133
133
|
}
|
|
134
134
|
// Reject Set requests to change Thermostat targetHeatingCoolingState for Zone Thermostat
|
|
135
|
-
if ( this.typeIndex == 57 && this.displayName.match ( / Thermostat$/ ) &&
|
|
136
|
-
characteristicString == 'TargetHeatingCoolingState'
|
|
135
|
+
if ( this.typeIndex == 57 && this.displayName.match ( / Thermostat$/ ) &&
|
|
136
|
+
characteristicString == 'TargetHeatingCoolingState'
|
|
137
137
|
)
|
|
138
138
|
{
|
|
139
139
|
let storedValue = this.cmd5Storage.getStoredValueForIndex( accTypeEnumIndex );
|
|
@@ -143,13 +143,13 @@ class Cmd5PriorityPollingQueue
|
|
|
143
143
|
}
|
|
144
144
|
// Turn on the Zone when this Zone is set as myZone
|
|
145
145
|
else if ( this.typeIndex == 20 && this.displayName.match ( / Zone$/ ) &&
|
|
146
|
-
characteristicString == 'RotationDirection' && value == 0
|
|
146
|
+
characteristicString == 'RotationDirection' && value == 0
|
|
147
147
|
)
|
|
148
148
|
{
|
|
149
149
|
this.service.getCharacteristic( 'Active' ).updateValue( 1 );
|
|
150
150
|
}
|
|
151
151
|
else if ( this.displayName.match( / FanSpeed$/ ) )
|
|
152
|
-
{
|
|
152
|
+
{
|
|
153
153
|
// Abort if 'on' or 'rotationSpeed' value = 0, this accessory is always on
|
|
154
154
|
if ( value == 0 )
|
|
155
155
|
{
|