homebridge-myplace 2.3.0 → 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 -5
- package/Cmd5PriorityPollingQueue.js +8 -8
- package/MyPlace.sh +90 -117
- package/README.md +180 -180
- package/RUNNING_CHANGELOG.md +5 -0
- package/package.json +2 -2
- package/utils/updateConfig.js +54 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +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.3.
|
|
2
|
+
##### v2.3.1 (2025-10-22)
|
|
3
3
|
|
|
4
|
-
###### (1)
|
|
5
|
-
###### (2)
|
|
6
|
-
###### (3) Accessory limit option: Added an optional parameter to cap the number of accessories created by this plugin (Homebridge supports a maximum of 149 accessories per bridge).
|
|
7
|
-
###### (4) Stability improvements: Bug fixes and under-the-hood enhancements for improved reliability and robustness.
|
|
4
|
+
###### (1) Allow up to 5 retries of inaccessible device before proceeding to auto-discovery.
|
|
5
|
+
###### (2) Minor bug fixes.
|
|
@@ -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
|
{
|
package/MyPlace.sh
CHANGED
|
@@ -1,32 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
3
|
#########################################################################
|
|
4
|
-
#
|
|
5
|
-
# homebridge-cmd4-advantageair for all their inital works from which this
|
|
6
|
-
# work was evolved from...
|
|
7
|
-
#
|
|
8
|
-
# Evolution credited to @uswong:
|
|
9
|
-
# 1. Fan speed control (2022)
|
|
10
|
-
# 2. Store myAirData in cache up to 2 minutes (2022)
|
|
11
|
-
# 3. Extend the control to lights and things (2022)
|
|
12
|
-
# 4. Update cache on Set request (2022)
|
|
13
|
-
# 5. Zone closing control (2022)
|
|
14
|
-
# 6. ConfigCreator - Created homebridge UI and developed scriptes to automate
|
|
15
|
-
# the generation of configuration file required (2023)
|
|
16
|
-
# 7. Optional extra timers to turn on aircon in specific mode (2023)
|
|
17
|
-
# 8. Added myZone capability (2023)
|
|
18
|
-
# 9. Added temperature control for zones with temperature sensor (2024)
|
|
19
|
-
#
|
|
20
|
-
# Game changer 1: storing myAirData in cache up to 2 minutes was a real game
|
|
21
|
-
# changer. This allows a big system with hundreds of devices to run properly,
|
|
22
|
-
# otherwise the AdvantageAir tablet will get overwhelmed with Get requests and
|
|
23
|
-
# will stop working.
|
|
24
|
-
#
|
|
25
|
-
# Game changer 2: ConfigCreator was another game changer. It was a dreadful
|
|
26
|
-
# task to create a configureation file especially for a big system with
|
|
27
|
-
# hundred of devices, many users shy away from it. To be able to automate
|
|
28
|
-
# the generation of the configureation file make this plugin so easy and
|
|
29
|
-
# simple to set up.
|
|
4
|
+
# This shell script interacts with the AdvantageAir device
|
|
30
5
|
#########################################################################
|
|
31
6
|
|
|
32
7
|
# Lets be explicit
|
|
@@ -158,13 +133,13 @@ function logError()
|
|
|
158
133
|
} > "$fileName"
|
|
159
134
|
|
|
160
135
|
if [ "${io}" = "Set" ]; then
|
|
161
|
-
|
|
136
|
+
logDiagnostic "Unhandled $io $device $characteristic $value rc=$rc - this accessory is most likely offline"
|
|
162
137
|
elif [ "${io}" = "Get" ]; then
|
|
163
|
-
|
|
138
|
+
logDiagnostic "Unhandled $io $device $characteristic rc=$rc - this accessory is most likely offline!"
|
|
164
139
|
fi
|
|
165
140
|
}
|
|
166
141
|
|
|
167
|
-
function
|
|
142
|
+
function logDiagnostic()
|
|
168
143
|
{
|
|
169
144
|
if [ "$debugSpecified" != true ]; then
|
|
170
145
|
return
|
|
@@ -223,16 +198,16 @@ function queryAirCon()
|
|
|
223
198
|
tf=$(cat "$dateFile")
|
|
224
199
|
dt=$(( t0 - tf ))
|
|
225
200
|
if [ "$dt" -le 120 ]; then
|
|
226
|
-
useFileCache=true
|
|
201
|
+
useFileCache=true
|
|
227
202
|
elif [[ "$dt" -gt 180 && -f "$lockFile" ]]; then # an earlier curl may have timed out
|
|
228
203
|
tlf=$(cat "$lockFile")
|
|
229
204
|
dtlf=$(( t0 - tlf ))
|
|
230
205
|
|
|
231
206
|
# If earlier curl timed out, recover by removing the lockFile and try again in 1.2s (max 5 tries)
|
|
232
|
-
if [ "$dtlf" -ge 60 ]; then
|
|
207
|
+
if [ "$dtlf" -ge 60 ]; then
|
|
233
208
|
rm "$lockFile"
|
|
234
209
|
rc=99
|
|
235
|
-
|
|
210
|
+
logDiagnostic "get_data_earlier_curl_timed_out $tf $t0 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
|
|
236
211
|
|
|
237
212
|
# To test the logic, issue this comment
|
|
238
213
|
if [ "$selfTest" = "TEST_ON" ]; then
|
|
@@ -243,7 +218,7 @@ function queryAirCon()
|
|
|
243
218
|
fi
|
|
244
219
|
fi
|
|
245
220
|
fi
|
|
246
|
-
|
|
221
|
+
logDiagnostic "get_data $tf $t0 $dt $useFileCache itr=$iteration $io $device $characteristic $url"
|
|
247
222
|
|
|
248
223
|
# If $lockFile is detected, iterate until it is deleted or 60s whichever is earlier
|
|
249
224
|
# The $lockfile can be there for 1s to 60s (even beyond occasionally) for a big system, with an average of ~6s
|
|
@@ -258,9 +233,9 @@ function queryAirCon()
|
|
|
258
233
|
|
|
259
234
|
# earlier curl has timed out (timeout:60000) - this rarely happen (<0.1% of the time)
|
|
260
235
|
# flag it, remove the $lockFile and copy the existing cached file, try again next cycle.
|
|
261
|
-
if [ "$dtlf" -ge 60 ]; then
|
|
236
|
+
if [ "$dtlf" -ge 60 ]; then
|
|
262
237
|
rc=98
|
|
263
|
-
|
|
238
|
+
logDiagnostic "get_timed_out_so_copy_earlier_cache $t0 $t2 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
|
|
264
239
|
|
|
265
240
|
# Remove the lockFile
|
|
266
241
|
rm "$lockFile"
|
|
@@ -269,12 +244,12 @@ function queryAirCon()
|
|
|
269
244
|
if [ "$selfTest" = "TEST_ON" ]; then
|
|
270
245
|
echo "Earlier \"curl\" to getSystemData has timed out, recover and just copy the earlier cache"
|
|
271
246
|
fi
|
|
272
|
-
break
|
|
247
|
+
break
|
|
273
248
|
fi
|
|
274
249
|
done
|
|
275
250
|
myAirData=$( cat "$MY_AIRDATA_FILE" )
|
|
276
251
|
rc=$?
|
|
277
|
-
|
|
252
|
+
logDiagnostic "get_copy $t0 $t2 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
|
|
278
253
|
|
|
279
254
|
# To test the logic, issue this comment
|
|
280
255
|
if [ "$selfTest" = "TEST_ON" ]; then
|
|
@@ -297,24 +272,24 @@ function queryAirCon()
|
|
|
297
272
|
#Need to parse to ensure the json file is not empty
|
|
298
273
|
parseMyAirDataWithJq ".aircons.$ac.info" "${exitOnFail}"
|
|
299
274
|
if [ "$rc" = "0" ]; then
|
|
300
|
-
t2=$(date '+%s')
|
|
275
|
+
t2=$(date '+%s')
|
|
301
276
|
echo "$t2" > "$dateFile" # overwrite $dateFile
|
|
302
|
-
#if $myAirData is not the same as the cached file, overwrite it with the new $myAirData
|
|
277
|
+
#if $myAirData is not the same as the cached file, overwrite it with the new $myAirData
|
|
303
278
|
if [ -f "$MY_AIRDATA_FILE" ]; then isMyAirDataSameAsCached; fi
|
|
304
279
|
if [ $sameAsCached = false ]; then echo "$myAirData" > "$MY_AIRDATA_FILE"; fi
|
|
305
280
|
dt=$((t2 - t0)) # time-taken for curl command to complete
|
|
306
|
-
|
|
281
|
+
logDiagnostic "get_curl $t0 $t2 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
|
|
307
282
|
else
|
|
308
283
|
echo "{}" > "$MY_AIRDATA_FILE"
|
|
309
|
-
echo "$t2" > "$dateFile"
|
|
310
|
-
|
|
284
|
+
echo "$t2" > "$dateFile"
|
|
285
|
+
logDiagnostic "get_curl_invalid $t0 $t2 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
|
|
311
286
|
# just in case
|
|
312
287
|
unset myAirData
|
|
313
288
|
fi
|
|
314
289
|
else
|
|
315
290
|
echo "{}" > "$MY_AIRDATA_FILE"
|
|
316
|
-
echo "$t2" > "$dateFile"
|
|
317
|
-
|
|
291
|
+
echo "$t2" > "$dateFile"
|
|
292
|
+
logDiagnostic "get_curl_failed $t0 $t2 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
|
|
318
293
|
unset myAirData
|
|
319
294
|
fi
|
|
320
295
|
rm "$lockFile"
|
|
@@ -347,7 +322,7 @@ function isMyAirDataSameAsCached()
|
|
|
347
322
|
return
|
|
348
323
|
fi
|
|
349
324
|
# For aircon system with temperature sensors, "rssi" and "measuredTemp" are changing all the time
|
|
350
|
-
# do not need to compare "rssi" but if "measuredTemp" is changed, cached file will be updated
|
|
325
|
+
# do not need to compare "rssi" but if "measuredTemp" is changed, cached file will be updated
|
|
351
326
|
# compare only the aircons, lights and things - all the rest does not matter
|
|
352
327
|
aircons=$(echo "$myAirData"|jq -ec ".aircons[]"|sed s/rssi\":[0-9]*/rssi\":0/g)
|
|
353
328
|
lights=$(echo "$myAirData"|jq -ec ".myLights.lights[]")
|
|
@@ -387,9 +362,9 @@ function setAirConUsingIteration()
|
|
|
387
362
|
curlResult=$(curl --fail -s -g "$url")
|
|
388
363
|
rc=$?
|
|
389
364
|
curlResult=$(echo "${curlResult}" | grep false)
|
|
390
|
-
if [[ "$rc" = "0" && -n "${curlResult}" ]]; then rc=5; fi
|
|
365
|
+
if [[ "$rc" = "0" && -n "${curlResult}" ]]; then rc=5; fi
|
|
391
366
|
|
|
392
|
-
|
|
367
|
+
logDiagnostic "set_curl $t3 rc=$rc itr=$i $io $device $characteristic $value $url"
|
|
393
368
|
|
|
394
369
|
if [ "$rc" = "0" ]; then
|
|
395
370
|
# update $MY_AIRDATA_FILE directly instead of fetching a new copy from AdvantageAir controller after a set command
|
|
@@ -400,7 +375,7 @@ function setAirConUsingIteration()
|
|
|
400
375
|
fi
|
|
401
376
|
|
|
402
377
|
if [ "$exitOnFail" = "1" ]; then
|
|
403
|
-
|
|
378
|
+
logDiagnostic "set_curl_failed $t3 rc=$rc $io $device $characteristic $value $url"
|
|
404
379
|
logError "SetAirCon_curl failed" "" "$io" "$url"
|
|
405
380
|
exit $rc
|
|
406
381
|
fi
|
|
@@ -412,7 +387,7 @@ function setAirConUsingIteration()
|
|
|
412
387
|
function updateMyAirDataCachedFile()
|
|
413
388
|
{
|
|
414
389
|
local url="$1"
|
|
415
|
-
|
|
390
|
+
|
|
416
391
|
# This script to parse the curl $url: input - 'http://192.168.0.31:2025/setAircon?json={ac1:{zones:{z04:{state:open}}}}'
|
|
417
392
|
# into jq set path: output - '.aircons.ac1.zones.z04.state="open"'
|
|
418
393
|
|
|
@@ -420,11 +395,11 @@ function updateMyAirDataCachedFile()
|
|
|
420
395
|
# output - '.aircons.ac1.zones.z04.value=90'
|
|
421
396
|
|
|
422
397
|
# input - 'http://192.168.0.31:2025/setAircon?json={ac1:{info:{state:on,mode:vent}}}'
|
|
423
|
-
# output1 - '.aircons.ac1.info.state="on"
|
|
398
|
+
# output1 - '.aircons.ac1.info.state="on"
|
|
424
399
|
# output2 - '.aircons.ac1.info.mode="vent"
|
|
425
400
|
|
|
426
401
|
local setNumber
|
|
427
|
-
local setMode
|
|
402
|
+
local setMode
|
|
428
403
|
local jqPathToSetJson
|
|
429
404
|
local jqPathToSetJsonState
|
|
430
405
|
|
|
@@ -454,7 +429,7 @@ function updateMyAirDataCachedFile()
|
|
|
454
429
|
;;
|
|
455
430
|
setLight)
|
|
456
431
|
setJqPath=$(echo "$setJqPath"|sed s/setLight?json=id./.myLights.lights.\"/|sed s/,/\"./)
|
|
457
|
-
if [ -n "$setNumber" ]; then
|
|
432
|
+
if [ -n "$setNumber" ]; then
|
|
458
433
|
jqPathToSetJson=$setJqPath
|
|
459
434
|
else # value is a string
|
|
460
435
|
setJqPath=${setJqPath//=/=\"}
|
|
@@ -471,7 +446,7 @@ function updateMyAirDataCachedFile()
|
|
|
471
446
|
|
|
472
447
|
function updateMyAirDataCachedFileWithJq()
|
|
473
448
|
{
|
|
474
|
-
# this script to use jq to update the $MY_AIRDATA_FILE
|
|
449
|
+
# this script to use jq to update the $MY_AIRDATA_FILE
|
|
475
450
|
|
|
476
451
|
local url="$1"
|
|
477
452
|
local jqPath="$2"
|
|
@@ -481,13 +456,13 @@ function updateMyAirDataCachedFileWithJq()
|
|
|
481
456
|
rc=$?
|
|
482
457
|
if [ "$rc" = "0" ]; then
|
|
483
458
|
echo "$updatedMyAirData" > "$MY_AIRDATA_FILE"
|
|
484
|
-
|
|
459
|
+
logDiagnostic "set_json $t3 rc=$rc $io $device $characteristic $jqPath"
|
|
485
460
|
if [ "$selfTest" = "TEST_ON" ]; then
|
|
486
461
|
# For Testing, you can compare whats sent
|
|
487
462
|
echo "Setting json: $jqPath"
|
|
488
463
|
fi
|
|
489
464
|
else
|
|
490
|
-
|
|
465
|
+
logDiagnostic "set_json_failed $t3 rc=$rc $io $device $characteristic $jqPath"
|
|
491
466
|
logError "setAirCon_setJson jq failed" "$jqPath" "" "$url"
|
|
492
467
|
exit $rc
|
|
493
468
|
fi
|
|
@@ -503,7 +478,7 @@ function parseMyAirDataWithJq()
|
|
|
503
478
|
# Understanding jq options
|
|
504
479
|
# -e (Upon bad data, exit with return code )
|
|
505
480
|
# Updates global variable jqResult
|
|
506
|
-
jqResult=$( echo "$myAirData" | jq -
|
|
481
|
+
jqResult=$( echo "$myAirData" | jq -er "$jqPath" )
|
|
507
482
|
rc=$?
|
|
508
483
|
if [ "$rc" = "1" ] && [ "$jqResult" = false ]; then # "false" is an acceptable answer
|
|
509
484
|
rc=0
|
|
@@ -514,7 +489,7 @@ function parseMyAirDataWithJq()
|
|
|
514
489
|
echo "Parsing for jqPath failed: $jqPath";
|
|
515
490
|
fi
|
|
516
491
|
if [ "$exitOnFail" = "1" ]; then
|
|
517
|
-
|
|
492
|
+
logDiagnostic "parseMyAirDataWithJq_failed rc=$rc jqResult=$jqResult $io $device $characteristic $jqPath"
|
|
518
493
|
logError "jq failed" "$rc" "$jqResult" "" "$jqPath"
|
|
519
494
|
exit $rc
|
|
520
495
|
fi
|
|
@@ -590,17 +565,17 @@ function closeZoneWithZoneOpenCounter()
|
|
|
590
565
|
|
|
591
566
|
function setMyZoneToAnOpenedZoneWithTempSensorWithPriorityToCzones()
|
|
592
567
|
{
|
|
593
|
-
# set myZone to an open zone with priority to an open cZone.
|
|
568
|
+
# set myZone to an open zone with priority to an open cZone.
|
|
594
569
|
# Note: this routine is only called for the case where zoneOpen > noOfConstants.
|
|
595
570
|
|
|
596
571
|
for cZone in $cZone1 $cZone2 $cZone3; do
|
|
597
572
|
if [[ "${cZone}" != "z00" && "${cZone}" != "${zone}" ]]; then
|
|
598
573
|
parseMyAirDataWithJq ".aircons.$ac.zones.${cZone}.state"
|
|
599
|
-
if [ "${jqResult}" =
|
|
574
|
+
if [ "${jqResult}" = "open" ]; then
|
|
600
575
|
parseMyAirDataWithJq ".aircons.$ac.zones.${cZone}.rssi"
|
|
601
576
|
if [ "${jqResult}" != "0" ]; then
|
|
602
577
|
myZone="${cZone}"
|
|
603
|
-
myZoneValue=$((10#$( echo "${myZone}" | cut -d"z" -f2 )))
|
|
578
|
+
myZoneValue=$((10#$( echo "${myZone}" | cut -d"z" -f2 )))
|
|
604
579
|
setAirConUsingIteration "http://$IP:$PORT/setAircon?json={$ac:{info:{myZone:$myZoneValue}}}"
|
|
605
580
|
myZoneAssigned=true
|
|
606
581
|
# the target temperature of myZone needs to be copied to the system target temperature
|
|
@@ -617,7 +592,7 @@ function setMyZoneToAnOpenedZoneWithTempSensorWithPriorityToCzones()
|
|
|
617
592
|
ZoneStr=$( printf "z%02d" "${Zone}" )
|
|
618
593
|
if [[ "${ZoneStr}" != "${zone}" && "${ZoneStr}" != "${cZone1}" && "${ZoneStr}" != "${cZone2}" && "${ZoneStr}" != "${cZone3}" ]]; then
|
|
619
594
|
parseMyAirDataWithJq ".aircons.$ac.zones.${ZoneStr}.state"
|
|
620
|
-
if [ "${jqResult}" =
|
|
595
|
+
if [ "${jqResult}" = "open" ]; then
|
|
621
596
|
parseMyAirDataWithJq ".aircons.$ac.zones.${ZoneStr}.rssi"
|
|
622
597
|
if [ "${jqResult}" != "0" ]; then
|
|
623
598
|
myZone="${ZoneStr}"
|
|
@@ -635,13 +610,13 @@ function setMyZoneToAnOpenedZoneWithTempSensorWithPriorityToCzones()
|
|
|
635
610
|
|
|
636
611
|
function openAclosedCzone()
|
|
637
612
|
{
|
|
638
|
-
# Open up a closed cZone
|
|
613
|
+
# Open up a closed cZone
|
|
639
614
|
# Note: this routine is only called for the case where zoneOpen = noOfConstants.
|
|
640
615
|
|
|
641
616
|
for cZone in $cZone1 $cZone2 $cZone3; do
|
|
642
617
|
if [ "${cZone}" != "z00" ]; then
|
|
643
618
|
parseMyAirDataWithJq ".aircons.$ac.zones.${cZone}.state"
|
|
644
|
-
if [ "${jqResult}" =
|
|
619
|
+
if [ "${jqResult}" = "close" ]; then
|
|
645
620
|
openZoneInFullWithZoneOpenCounter "${cZone}"
|
|
646
621
|
return
|
|
647
622
|
fi
|
|
@@ -656,7 +631,7 @@ function openAclosedZoneWithTempSensorWithPriorityToCzones()
|
|
|
656
631
|
for cZone in $cZone1 $cZone2 $cZone3; do
|
|
657
632
|
if [[ "${cZone}" != "z00" && "${cZone}" != "${zone}" ]]; then
|
|
658
633
|
parseMyAirDataWithJq ".aircons.$ac.zones.${cZone}.state"
|
|
659
|
-
if [ "${jqResult}" =
|
|
634
|
+
if [ "${jqResult}" = "close" ]; then
|
|
660
635
|
parseMyAirDataWithJq ".aircons.$ac.zones.${cZone}.rssi"
|
|
661
636
|
if [ "${jqResult}" != "0" ]; then
|
|
662
637
|
openZoneInFullWithZoneOpenCounter "${cZone}"
|
|
@@ -670,7 +645,7 @@ function openAclosedZoneWithTempSensorWithPriorityToCzones()
|
|
|
670
645
|
ZoneStr=$( printf "z%02d" "${Zone}" )
|
|
671
646
|
if [[ "${ZoneStr}" != "${zone}" && "${ZoneStr}" != "${cZone1}" && "${ZoneStr}" != "${cZone2}" && "${ZoneStr}" != "${cZone3}" ]]; then
|
|
672
647
|
parseMyAirDataWithJq ".aircons.$ac.zones.${ZoneStr}.state"
|
|
673
|
-
if [ "${jqResult}" =
|
|
648
|
+
if [ "${jqResult}" = "close" ]; then
|
|
674
649
|
parseMyAirDataWithJq ".aircons.$ac.zones.${ZoneStr}.rssi"
|
|
675
650
|
if [ "${jqResult}" != "0" ]; then
|
|
676
651
|
openZoneInFullWithZoneOpenCounter "${ZoneStr}"
|
|
@@ -729,30 +704,30 @@ function queryTimerStateFile()
|
|
|
729
704
|
|
|
730
705
|
# Get the state of the fan mode
|
|
731
706
|
if [ $fanTimerSpecified = true ]; then
|
|
732
|
-
if [[ "${acState}" =
|
|
707
|
+
if [[ "${acState}" = "on" && "${acMode}" = "vent" ]]; then
|
|
733
708
|
fanState=1
|
|
734
709
|
else
|
|
735
710
|
fanState=0
|
|
736
711
|
fi
|
|
737
|
-
|
|
712
|
+
logDiagnostic "get_FanTimer ${t0} fanState=${fanState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
|
|
738
713
|
|
|
739
714
|
# Get the state of the cool mode
|
|
740
715
|
elif [ $coolTimerSpecified = true ]; then
|
|
741
|
-
if [[ "${acState}" =
|
|
716
|
+
if [[ "${acState}" = "on" && "${acMode}" = "cool" ]]; then
|
|
742
717
|
coolState=1
|
|
743
718
|
else
|
|
744
719
|
coolState=0
|
|
745
720
|
fi
|
|
746
|
-
|
|
721
|
+
logDiagnostic "get_CoolTimer ${t0} coolState=${coolState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
|
|
747
722
|
|
|
748
723
|
# Get the state of the heat mode
|
|
749
724
|
elif [ $heatTimerSpecified = true ]; then
|
|
750
|
-
if [[ "${acState}" =
|
|
725
|
+
if [[ "${acState}" = "on" && "${acMode}" = "heat" ]]; then
|
|
751
726
|
heatState=1
|
|
752
727
|
else
|
|
753
728
|
heatState=0
|
|
754
729
|
fi
|
|
755
|
-
|
|
730
|
+
logDiagnostic "get_HeatTimer ${t0} heatState=${heatState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
|
|
756
731
|
fi
|
|
757
732
|
}
|
|
758
733
|
|
|
@@ -762,7 +737,7 @@ function updateTimer()
|
|
|
762
737
|
local mode="$2"
|
|
763
738
|
|
|
764
739
|
if [ "$selfTest" = "TEST_ON" ]; then
|
|
765
|
-
t0=$((setTime + 2))
|
|
740
|
+
t0=$((setTime + 2))
|
|
766
741
|
fi
|
|
767
742
|
|
|
768
743
|
# Update fan timer
|
|
@@ -797,11 +772,11 @@ function updateTimer()
|
|
|
797
772
|
|
|
798
773
|
# Diagnostic logging
|
|
799
774
|
if [ $fanTimerSpecified = true ]; then
|
|
800
|
-
|
|
775
|
+
logDiagnostic "upd_FanTimer ${t0} fanState=${fanState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
|
|
801
776
|
elif [ $coolTimerSpecified = true ]; then
|
|
802
|
-
|
|
777
|
+
logDiagnostic "upd_CoolTimer ${t0} coolState=${coolState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
|
|
803
778
|
elif [ $heatTimerSpecified = true ]; then
|
|
804
|
-
|
|
779
|
+
logDiagnostic "upd_HeatTimer ${t0} heatState=${heatState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
|
|
805
780
|
fi
|
|
806
781
|
|
|
807
782
|
if [ "${mode}" = "vent" ]; then mode="fan"; fi
|
|
@@ -820,23 +795,21 @@ function updateTimerStateFile()
|
|
|
820
795
|
|
|
821
796
|
# Diagnostic logging
|
|
822
797
|
if [ "${io}" = "Get" ]; then
|
|
823
|
-
prefix="
|
|
824
|
-
space=""
|
|
798
|
+
prefix="upd"
|
|
825
799
|
else
|
|
826
800
|
prefix="set"
|
|
827
|
-
space="e "
|
|
828
801
|
fi
|
|
829
802
|
|
|
830
803
|
if [ "$selfTest" = "TEST_ON" ]; then
|
|
831
|
-
echo "Update the timer state file: ${TIMER_STATE_FILE} with timeToOn: ${timeToOn} and timeToOff: ${timeToOff}"
|
|
804
|
+
echo "Update the timer state file: ${TIMER_STATE_FILE} with timeToOn: ${timeToOn} and timeToOff: ${timeToOff}"
|
|
832
805
|
fi
|
|
833
806
|
|
|
834
807
|
if [ $fanTimerSpecified = true ]; then
|
|
835
|
-
|
|
808
|
+
logDiagnostic "${prefix}_FanTimerState ${t0} fanState=${fanState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
|
|
836
809
|
elif [ $coolTimerSpecified = true ]; then
|
|
837
|
-
|
|
810
|
+
logDiagnostic "${prefix}_CoolTimerState ${t0} coolState=${coolState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
|
|
838
811
|
elif [ $heatTimerSpecified = true ]; then
|
|
839
|
-
|
|
812
|
+
logDiagnostic "${prefix}_HeatTimerState ${t0} heatState=${heatState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
|
|
840
813
|
fi
|
|
841
814
|
}
|
|
842
815
|
|
|
@@ -893,7 +866,7 @@ if [ $argEND -ge $argSTART ]; then
|
|
|
893
866
|
PORT="2025"
|
|
894
867
|
;;
|
|
895
868
|
noOtherThermostat)
|
|
896
|
-
# If only the control unit Thermostat is specified, ie no zone Thermostat is defined
|
|
869
|
+
# If only the control unit Thermostat is specified, ie no zone Thermostat is defined
|
|
897
870
|
noOtherThermostat=true
|
|
898
871
|
;;
|
|
899
872
|
fanSpeed)
|
|
@@ -1002,9 +975,9 @@ if [ "$io" = "Get" ]; then
|
|
|
1002
975
|
# Gets the current temperature.
|
|
1003
976
|
CurrentTemperature )
|
|
1004
977
|
# check whether a zone is defined, if so, use the measuredTemp of this zone
|
|
1005
|
-
# if not, check myZone is defined, if so, use the measuredTemp of myZone
|
|
1006
|
-
# if not, check rssi is defined for cZone1, if so, use measuredTemp of cZone1
|
|
1007
|
-
# if not again, use setTemp
|
|
978
|
+
# if not, check myZone is defined, if so, use the measuredTemp of myZone
|
|
979
|
+
# if not, check rssi is defined for cZone1, if so, use measuredTemp of cZone1
|
|
980
|
+
# if not again, use setTemp
|
|
1008
981
|
|
|
1009
982
|
if [ $zoneSpecified = true ]; then
|
|
1010
983
|
parseMyAirDataWithJq ".aircons.$ac.zones.$zone.measuredTemp"
|
|
@@ -1013,7 +986,7 @@ if [ "$io" = "Get" ]; then
|
|
|
1013
986
|
fi
|
|
1014
987
|
parseMyAirDataWithJq ".aircons.$ac.info.myZone"
|
|
1015
988
|
myZone=$( printf "z%02d" "$jqResult" )
|
|
1016
|
-
if [ "${myZone}" != "z00" ]; then
|
|
989
|
+
if [ "${myZone}" != "z00" ]; then
|
|
1017
990
|
parseMyAirDataWithJq ".aircons.$ac.zones.$myZone.measuredTemp"
|
|
1018
991
|
echo "$jqResult"
|
|
1019
992
|
exit 0
|
|
@@ -1025,7 +998,7 @@ if [ "$io" = "Get" ]; then
|
|
|
1025
998
|
parseMyAirDataWithJq ".aircons.$ac.zones.$cZone1.measuredTemp"
|
|
1026
999
|
echo "$jqResult"
|
|
1027
1000
|
exit 0
|
|
1028
|
-
else
|
|
1001
|
+
else
|
|
1029
1002
|
parseMyAirDataWithJq ".aircons.$ac.info.setTemp"
|
|
1030
1003
|
echo "$jqResult"
|
|
1031
1004
|
exit 0
|
|
@@ -1041,7 +1014,7 @@ if [ "$io" = "Get" ]; then
|
|
|
1041
1014
|
# check whether myZone is defined
|
|
1042
1015
|
parseMyAirDataWithJq ".aircons.$ac.info.myZone"
|
|
1043
1016
|
myZone=$( printf "z%02d" "$jqResult" )
|
|
1044
|
-
if [ "${myZone}" != "z00" ]; then
|
|
1017
|
+
if [ "${myZone}" != "z00" ]; then
|
|
1045
1018
|
parseMyAirDataWithJq ".aircons.$ac.zones.$myZone.setTemp"
|
|
1046
1019
|
echo "$jqResult"
|
|
1047
1020
|
exit 0
|
|
@@ -1058,8 +1031,8 @@ if [ "$io" = "Get" ]; then
|
|
|
1058
1031
|
parseMyAirDataWithJq ".aircons.$ac.info.state"
|
|
1059
1032
|
state="$jqResult"
|
|
1060
1033
|
# If the state of the Aircon is Off, set Main Thermostat to Off (0) and Zone Thermostat to Auto (3)
|
|
1061
|
-
if [ "$state" =
|
|
1062
|
-
if [ $zoneSpecified = true ]; then
|
|
1034
|
+
if [ "$state" = "off" ]; then
|
|
1035
|
+
if [ $zoneSpecified = true ]; then
|
|
1063
1036
|
# set to "AUTO" for zoneThermostat
|
|
1064
1037
|
echo 3
|
|
1065
1038
|
exit 0
|
|
@@ -1074,17 +1047,17 @@ if [ "$io" = "Get" ]; then
|
|
|
1074
1047
|
parseMyAirDataWithJq ".aircons.$ac.info.mode"
|
|
1075
1048
|
mode="$jqResult"
|
|
1076
1049
|
case "$mode" in
|
|
1077
|
-
|
|
1050
|
+
"heat" )
|
|
1078
1051
|
# Thermostat in Heat Mode.
|
|
1079
1052
|
echo 1
|
|
1080
1053
|
exit 0
|
|
1081
1054
|
;;
|
|
1082
|
-
|
|
1055
|
+
"cool" )
|
|
1083
1056
|
# Thermostat in Cool Mode.
|
|
1084
1057
|
echo 2
|
|
1085
1058
|
exit 0
|
|
1086
1059
|
;;
|
|
1087
|
-
|
|
1060
|
+
"vent" )
|
|
1088
1061
|
# for Fan mode, set main Thermostat to Off (0) or zone Thermostat to Auto (3)
|
|
1089
1062
|
if [ $zoneSpecified = true ]; then
|
|
1090
1063
|
echo 3
|
|
@@ -1094,7 +1067,7 @@ if [ "$io" = "Get" ]; then
|
|
|
1094
1067
|
exit 0
|
|
1095
1068
|
fi
|
|
1096
1069
|
;;
|
|
1097
|
-
|
|
1070
|
+
"dry" )
|
|
1098
1071
|
# Dry mode, set Thermostat to Auto Mode as a proxy.
|
|
1099
1072
|
echo 3
|
|
1100
1073
|
exit 0
|
|
@@ -1134,7 +1107,7 @@ if [ "$io" = "Get" ]; then
|
|
|
1134
1107
|
if [ $zoneSpecified = true ]; then
|
|
1135
1108
|
# Damper open/closed = Switch on/off = 1/0
|
|
1136
1109
|
parseMyAirDataWithJq ".aircons.$ac.zones.$zone.state"
|
|
1137
|
-
if [ "$jqResult" =
|
|
1110
|
+
if [ "$jqResult" = "open" ]; then
|
|
1138
1111
|
echo 1
|
|
1139
1112
|
exit 0
|
|
1140
1113
|
else
|
|
@@ -1164,7 +1137,7 @@ if [ "$io" = "Get" ]; then
|
|
|
1164
1137
|
# fanSpecified is true when no zone (z01) given or timer given
|
|
1165
1138
|
# Updates global variable jqResult
|
|
1166
1139
|
parseMyAirDataWithJq ".aircons.$ac.info.state"
|
|
1167
|
-
if [ "$jqResult" =
|
|
1140
|
+
if [ "$jqResult" = "off" ]; then
|
|
1168
1141
|
echo 0
|
|
1169
1142
|
exit 0
|
|
1170
1143
|
else
|
|
@@ -1174,22 +1147,22 @@ if [ "$io" = "Get" ]; then
|
|
|
1174
1147
|
parseMyAirDataWithJq ".aircons.$ac.info.mode"
|
|
1175
1148
|
mode="$jqResult"
|
|
1176
1149
|
case "$mode" in
|
|
1177
|
-
|
|
1150
|
+
"heat" )
|
|
1178
1151
|
# Fan does not support Heat Mode.
|
|
1179
1152
|
echo 0
|
|
1180
1153
|
exit 0
|
|
1181
1154
|
;;
|
|
1182
|
-
|
|
1155
|
+
"cool" )
|
|
1183
1156
|
# Fan does not support Cool Mode.
|
|
1184
1157
|
echo 0
|
|
1185
1158
|
exit 0
|
|
1186
1159
|
;;
|
|
1187
|
-
|
|
1160
|
+
"vent" )
|
|
1188
1161
|
# Set Fan to On.
|
|
1189
1162
|
echo 1
|
|
1190
1163
|
exit 0
|
|
1191
1164
|
;;
|
|
1192
|
-
|
|
1165
|
+
"dry" )
|
|
1193
1166
|
# Fan does not support Dry Mode.
|
|
1194
1167
|
echo 0
|
|
1195
1168
|
exit 0
|
|
@@ -1204,7 +1177,7 @@ if [ "$io" = "Get" ]; then
|
|
|
1204
1177
|
elif [ $zoneSpecified = true ]; then
|
|
1205
1178
|
# Damper open/closed = Switch on/off = 1/0
|
|
1206
1179
|
parseMyAirDataWithJq ".aircons.$ac.zones.$zone.state"
|
|
1207
|
-
if [ "$jqResult" =
|
|
1180
|
+
if [ "$jqResult" = "open" ]; then
|
|
1208
1181
|
echo 1
|
|
1209
1182
|
exit 0
|
|
1210
1183
|
else
|
|
@@ -1287,7 +1260,7 @@ if [ "$io" = "Get" ]; then
|
|
|
1287
1260
|
exit 0
|
|
1288
1261
|
elif [ $lightSpecified = true ]; then
|
|
1289
1262
|
parseMyAirDataWithJq ".myLights.lights.\"${lightID}\".state"
|
|
1290
|
-
if [ "$jqResult" =
|
|
1263
|
+
if [ "$jqResult" = "on" ]; then
|
|
1291
1264
|
echo 1
|
|
1292
1265
|
exit 0
|
|
1293
1266
|
else
|
|
@@ -1332,17 +1305,17 @@ if [ "$io" = "Get" ]; then
|
|
|
1332
1305
|
elif [ $timerEnabled = true ]; then
|
|
1333
1306
|
parseMyAirDataWithJq ".aircons.$ac.info.state"
|
|
1334
1307
|
# Get the timer countDowqnToOff value if the state of the aircon is "on"
|
|
1335
|
-
if [ "$jqResult" =
|
|
1308
|
+
if [ "$jqResult" = "on" ]; then
|
|
1336
1309
|
parseMyAirDataWithJq ".aircons.$ac.info.countDownToOff"
|
|
1337
1310
|
timerInPercentage=$(((jqResult / 6) + (jqResult % 6 > 0)))
|
|
1338
|
-
timerInPercentage=$((timerInPercentage < 100? timerInPercentage : 100))
|
|
1311
|
+
timerInPercentage=$((timerInPercentage < 100? timerInPercentage : 100))
|
|
1339
1312
|
echo $((timerInPercentage > 1? timerInPercentage : 1))
|
|
1340
1313
|
exit 0
|
|
1341
1314
|
# Get the timer countDownToOn value if the state of the aircon is "off"
|
|
1342
1315
|
else
|
|
1343
1316
|
parseMyAirDataWithJq ".aircons.$ac.info.countDownToOn"
|
|
1344
1317
|
timerInPercentage=$(((jqResult / 6) + (jqResult % 6 > 0)))
|
|
1345
|
-
timerInPercentage=$((timerInPercentage < 100? timerInPercentage : 100))
|
|
1318
|
+
timerInPercentage=$((timerInPercentage < 100? timerInPercentage : 100))
|
|
1346
1319
|
echo $timerInPercentage
|
|
1347
1320
|
exit 0
|
|
1348
1321
|
fi
|
|
@@ -1364,15 +1337,15 @@ if [ "$io" = "Get" ]; then
|
|
|
1364
1337
|
else
|
|
1365
1338
|
# Update the current fan speed
|
|
1366
1339
|
parseMyAirDataWithJq ".aircons.$ac.info.fan"
|
|
1367
|
-
if [ "$jqResult" =
|
|
1340
|
+
if [ "$jqResult" = "low" ]; then
|
|
1368
1341
|
#25% as low speed
|
|
1369
1342
|
echo 25
|
|
1370
1343
|
exit 0
|
|
1371
|
-
elif [ "$jqResult" =
|
|
1344
|
+
elif [ "$jqResult" = "medium" ]; then
|
|
1372
1345
|
#50% as medium speed
|
|
1373
1346
|
echo 50
|
|
1374
1347
|
exit 0
|
|
1375
|
-
elif [ "$jqResult" =
|
|
1348
|
+
elif [ "$jqResult" = "high" ]; then
|
|
1376
1349
|
#90% as high speed
|
|
1377
1350
|
echo 90
|
|
1378
1351
|
exit 0
|
|
@@ -1431,7 +1404,7 @@ if [ "$io" = "Set" ]; then
|
|
|
1431
1404
|
else
|
|
1432
1405
|
setAirConUsingIteration "http://$IP:$PORT/setAircon?json={$ac:{info:{setTemp:$value}}}"
|
|
1433
1406
|
# if myZone is defined, set the target temperature to the defined myZone too
|
|
1434
|
-
if [ "${myZone}" != "z00" ]; then
|
|
1407
|
+
if [ "${myZone}" != "z00" ]; then
|
|
1435
1408
|
setAirConUsingIteration "http://$IP:$PORT/setAircon?json={$ac:{zones:{$myZone:{setTemp:$value}}}}"
|
|
1436
1409
|
elif [ $noOtherThermostat = true ]; then
|
|
1437
1410
|
# if myZone is not defined and noOtherThermostat=true, set the target temperature to all zones with temperature sensors
|
|
@@ -1477,7 +1450,7 @@ if [ "$io" = "Set" ]; then
|
|
|
1477
1450
|
# Before setting myZone open the zone if it is currently closed
|
|
1478
1451
|
myZoneValue=$(( 10#$( echo "${zone}" | cut -d'z' -f2 ) ))
|
|
1479
1452
|
parseMyAirDataWithJq ".aircons.$ac.zones.$zone.state"
|
|
1480
|
-
if [ "${jqResult}" =
|
|
1453
|
+
if [ "${jqResult}" = "close" ]; then
|
|
1481
1454
|
setAirConUsingIteration "http://$IP:$PORT/setAircon?json={$ac:{zones:{$zone:{state:open}}}}"
|
|
1482
1455
|
fi
|
|
1483
1456
|
setAirConUsingIteration "http://$IP:$PORT/setAircon?json={$ac:{info:{myZone:$myZoneValue}}}"
|
|
@@ -1515,7 +1488,7 @@ if [ "$io" = "Set" ]; then
|
|
|
1515
1488
|
# the aircon system before closing any zone:
|
|
1516
1489
|
# > if the only zone(s) open is/are the constant zone(s), leave it open (and set it to 100%).
|
|
1517
1490
|
# > if the constant zone(s) is/are in close state, and to close this zone will reduce the number
|
|
1518
|
-
# > of zones open below ${noOfConstants}, then one constant zone will open (and set to 100%) before
|
|
1491
|
+
# > of zones open below ${noOfConstants}, then one constant zone will open (and set to 100%) before
|
|
1519
1492
|
# > closing this zone.
|
|
1520
1493
|
|
|
1521
1494
|
# retrieve myZone, nZones, noOfConstants, cZone1, cZone2 and cZone3 from $myAirData
|
|
@@ -1547,7 +1520,7 @@ if [ "$io" = "Set" ]; then
|
|
|
1547
1520
|
do
|
|
1548
1521
|
zoneStr=$( printf "z%02d" "$a" )
|
|
1549
1522
|
parseMyAirDataWithJq ".aircons.$ac.zones.$zoneStr.state"
|
|
1550
|
-
if [ "$jqResult" =
|
|
1523
|
+
if [ "$jqResult" = "open" ]; then
|
|
1551
1524
|
zoneOpen=$((zoneOpen + 1))
|
|
1552
1525
|
fi
|
|
1553
1526
|
done
|
|
@@ -1557,7 +1530,7 @@ if [ "$io" = "Set" ]; then
|
|
|
1557
1530
|
# If there are more than "$noOfConstants" zones open, it is safe to close this zone.
|
|
1558
1531
|
# BUT if this zone is myZone then set myZone to an open cZone or if all cZones are
|
|
1559
1532
|
# closed, set myZone to a next open zone before closing this zone.
|
|
1560
|
-
if [ "${zone}" = "${myZone}" ]; then
|
|
1533
|
+
if [ "${zone}" = "${myZone}" ]; then
|
|
1561
1534
|
setMyZoneToAnOpenedZoneWithTempSensorWithPriorityToCzones
|
|
1562
1535
|
if [ "$myZoneAssigned" = false ]; then
|
|
1563
1536
|
openAclosedZoneWithTempSensorWithPriorityToCzones
|
|
@@ -1577,8 +1550,8 @@ if [ "$io" = "Set" ]; then
|
|
|
1577
1550
|
else
|
|
1578
1551
|
# If only "$noOfConstants" zones open and the zone to close is not a cZone but a myZone, then open a
|
|
1579
1552
|
# closed zone with temperature sensor with priority to cZones and set myZone to it before closing this
|
|
1580
|
-
# zone. If the zone to close is also not a myZone, then just open a closed cZone before closing this zone.
|
|
1581
|
-
if [ "${zone}" = "${myZone}" ]; then
|
|
1553
|
+
# zone. If the zone to close is also not a myZone, then just open a closed cZone before closing this zone.
|
|
1554
|
+
if [ "${zone}" = "${myZone}" ]; then
|
|
1582
1555
|
openAclosedZoneWithTempSensorWithPriorityToCzones
|
|
1583
1556
|
setMyZoneToAnOpenedZoneWithTempSensorWithPriorityToCzones
|
|
1584
1557
|
else
|
|
@@ -1709,7 +1682,7 @@ if [ "$io" = "Set" ]; then
|
|
|
1709
1682
|
# Make 10% to 1 hour (1% = 6 minutes) and capped at a max of 600 minutes
|
|
1710
1683
|
timerInMinutes=$((value * 6))
|
|
1711
1684
|
parseMyAirDataWithJq ".aircons.$ac.info.state"
|
|
1712
|
-
if [ "$jqResult" =
|
|
1685
|
+
if [ "$jqResult" = "on" ]; then
|
|
1713
1686
|
setAirConUsingIteration "http://$IP:$PORT/setAircon?json={$ac:{info:{countDownToOff:$timerInMinutes}}}"
|
|
1714
1687
|
else
|
|
1715
1688
|
setAirConUsingIteration "http://$IP:$PORT/setAircon?json={$ac:{info:{countDownToOn:$timerInMinutes}}}"
|
package/README.md
CHANGED
|
@@ -1,180 +1,180 @@
|
|
|
1
|
-
<span align="center">
|
|
2
|
-
|
|
3
|
-
<p align="center">
|
|
4
|
-
<img src="Screenshots/homebridge-myplace.png" width="220">
|
|
5
|
-
</p>
|
|
6
|
-
|
|
7
|
-
# homebridge-myplace
|
|
8
|
-
|
|
9
|
-
[](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
|
|
10
|
-
[](https://www.npmjs.com/package/homebridge-myplace)
|
|
11
|
-
[](https://www.npmjs.com/package/homebridge-myplace)
|
|
12
|
-
[](https://github.com/uswong/homebridge-myplace/blob/master/LICENSE)
|
|
13
|
-
[](https://discord.gg/uaEXgtt72q)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</span>
|
|
17
|
-
|
|
18
|
-
An independent plugin bringing [Advantage Air](https://www.advantageair.com.au/) MyPlace System, its smaller siblings (E-zone, MyAir, MyAir4, etc) and its cousins (e.g. Fujitsu AnywAir) to Homekit.
|
|
19
|
-
|
|
20
|
-
## Supported Advantage Air Control Units
|
|
21
|
-
* [MyPlace](https://apps.apple.com/au/app/myplace/id996398299)
|
|
22
|
-
* [MyAir4](https://apps.apple.com/au/app/myair4/id925994861)
|
|
23
|
-
* [MyAir](https://apps.apple.com/au/app/myair/id481563583)
|
|
24
|
-
* [E-zone](https://apps.apple.com/au/app/e-zone/id925994857)
|
|
25
|
-
* [Fujitsu anywAIR](https://apps.apple.com/au/app/anywair/id1509639853)
|
|
26
|
-
|
|
27
|
-
## Installation
|
|
28
|
-
1. Install Homebridge via these instructions for [Raspbian](https://github.com/homebridge/homebridge/wiki/Install-Homebridge-on-Raspbian), [HOOBS](https://support.hoobs.org/docs)
|
|
29
|
-
|
|
30
|
-
2. Make sure that <B>jq</B> and <B>curl</B> are installed. Try to install <B>jq-1.7</B> if you can. It is much faster.
|
|
31
|
-
|
|
32
|
-
3. Find the `homebridge-myplace` plugin via the Homebridge UI 'Plugins' tab search function, once found, click the blue *down-arrow* at the bottom right to install.
|
|
33
|
-
|
|
34
|
-
<p align="left">
|
|
35
|
-
<img width="400px" src="Screenshots/MyPlaceInstall3.png">
|
|
36
|
-
</p>
|
|
37
|
-
|
|
38
|
-
Once installed, `Config: homebridge-myplace` UI will pop up, then follow the steps outlined in Step 4 below.
|
|
39
|
-
|
|
40
|
-
<img width="750px" alt="image" src="https://github.com/user-attachments/assets/0f60e1ee-b7d6-46de-959b-5e42a8ede80f" />
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
If for some reasons, the `Config: homebridge-myplace` UI did not pop up, locate your newly installed `Homebridge Myplace` plugin and click on the three dots at the bottom right and select `Plugin Config` to get to the `Homebridge MyPlace` UI.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
In <B>Device Settings</B> area, fill out the `Name`, `IP Address` and `PORT used` fields (default PORT is `2025` for most users, Fujitsu anywAIR users set this to `10211` ) and check/uncheck the self-explanatory checkboxes for `Include extra timers` and `Enable detailed debug log for this device`, click `SAVE` then `RESTART`.
|
|
48
|
-
|
|
49
|
-
For advanced users, you can expand the **Advanced Plugin Settings** to `Specify the maximum number of accessories (between 1 to 149) to be configured for this plugin`. Homebridge has a grand limit of 149 accessories that can be bridged; it may crash if the total number of accessories from all installed plugins exceeds this limit. You can also check `Enable detailed debug log for this plugin` to view detailed logs and help diagnose any plugin issues.
|
|
50
|
-
|
|
51
|
-
For users who do not have access to Homebridge UI have to make sure that a config, as shown in the example below, is in the homebridge config.json:
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
{
|
|
55
|
-
"name": "MyPlace",
|
|
56
|
-
"debug": false,
|
|
57
|
-
"maxAccessories": 120,
|
|
58
|
-
"devices": [
|
|
59
|
-
{
|
|
60
|
-
"name": "Aircon",
|
|
61
|
-
"ipAddress": "192.168.50.117",
|
|
62
|
-
"port": 2025,
|
|
63
|
-
"extraTimers": true,
|
|
64
|
-
"debug": false
|
|
65
|
-
}
|
|
66
|
-
],
|
|
67
|
-
"platform": "MyPlace"
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
From version 2.3.0 onwards, if the `ipAddress` is missing, incorrectly formatted, or inaccessible, the plugin will automatically discover your connected AdvantageAir systems and configure them.
|
|
72
|
-
|
|
73
|
-
## How it Looks and Works
|
|
74
|
-
### (A) Aircon System (MyAir, E-zone, etc) has the following typical Homekit tiles:
|
|
75
|
-
<p align="left">
|
|
76
|
-
<img width="300px" src="Screenshots/Aircon_homekit_tiles.png">
|
|
77
|
-
</p>
|
|
78
|
-
|
|
79
|
-
#### (i) Thermostat, Fan Switch and Fan Speed Control
|
|
80
|
-
<p align="left">
|
|
81
|
-
<img width="240px" src="Screenshots/Thermostat_homekit_UI.png">
|
|
82
|
-
<img width="240px" src="Screenshots/FanSwitch_homekit_UI.png">
|
|
83
|
-
</p>
|
|
84
|
-
|
|
85
|
-
<B>Thermostat</B> is where the desired target temperature can be set.
|
|
86
|
-
|
|
87
|
-
<B>Thermostat</B> has 4 modes- <B>Off</B>, <B>Cool</B>, <B>Heat</B> and <B>Auto</B>. It does not have <B>dry</B> and <B>fan</B> modes. As such, the <B>Thermostat Auto</B> mode is repurposed as <B>dry</B> mode in this plugin and a separate <B>Fan</B> switch is used for <B>fan</B> mode.
|
|
88
|
-
|
|
89
|
-
Both the <B>Thermostat</B> and <B>Fan</B> switch has associated fan speed control and either one can be used to set the fan speed. It is duplicated only for convenience. The fan speed control has 4 tiers - <B>low</B>, <B>mid</B>, <B>high</B> and <B>auto</B> or <B>ezfan</B> and the %-rotationSpeed is snapped to 25% for <B>low</B>, 50% for <B>mid</B>, 90% for <B>high</B> and 100% for <B>auto</B> or <B>ezfan</B>.
|
|
90
|
-
|
|
91
|
-
#### (ii) Timer
|
|
92
|
-
<p align="left">
|
|
93
|
-
<img width="240px" src="Screenshots/Timer_homekit_UI.png">
|
|
94
|
-
</p>
|
|
95
|
-
|
|
96
|
-
A timer is repurposed from <B>Lightbulb</B> accessory and its <B>%-brightness</B> as proxy for timer duration, configured to represent 6 minutes per 1%. So a 10%-timer is a 60-minutes or 1-hour timer and 25%-timer is a 2.5-hours timer. A maximum of 10-hour timer (100%-timer) can be set. This timer will turn ON or OFF the Aircon system.
|
|
97
|
-
|
|
98
|
-
If <B>Include extra timers</B> was selected during the setup process, 3 more timers would have been created- a <B>Fan Timer</B>, a <B>Cool Timer</B> and a <B>Heat Timer</B>:
|
|
99
|
-
<p align="left">
|
|
100
|
-
<img width="300px" src="Screenshots/FancyTimers_homekit_tiles.png">
|
|
101
|
-
</p>
|
|
102
|
-
|
|
103
|
-
These timers will turn ON the Aircon system in specific mode as their names suggest.
|
|
104
|
-
|
|
105
|
-
*Please note that the icons on the Timer tiles in the example above have been deliberately changed to distinguish them from lights.*
|
|
106
|
-
|
|
107
|
-
#### (iii) Zone Control
|
|
108
|
-
<p align="left">
|
|
109
|
-
<img width="760px" src="Screenshots/variousZoneControls_homekit_UI.png">
|
|
110
|
-
</p>
|
|
111
|
-
|
|
112
|
-
Zone control is repurposed from <B>Fan</B> accessory with its <B>%-rotationSpeed</B> as proxy for Zone damper %-open and <B>rotationDirection</B> as <B>myZone</B> button. <B>Thermostat</B> accessory is used for setting the Zone target temperature.
|
|
113
|
-
|
|
114
|
-
There are three possible Zone Control configurations depending on the setup of your Aircon system:
|
|
115
|
-
|
|
116
|
-
<B>(a) A non temperature controlled Zone:</B> If your system has no temperature sensors, then there will only be a simple <B>Fan</B> accessory for your Zone Control. The Fan slider is for adjusting the desired damper %-open manually.
|
|
117
|
-
|
|
118
|
-
<B>(b) A temperature controlled Zone but without myZone defined:</B> If your system has temperature sensors but <B>myZone</B> is not defined, then there will be a simple <B>Fan</B> accessory and a <B>Thermostat</B> accessory.
|
|
119
|
-
|
|
120
|
-
The Fan slider here is to show the damper %-open for this zone for your INFO only. It is set automatically by the system, it cannot be adjusted manually. To turn off the Zone, slide the slider to zero. To turn it on, tap anywhere within the slider.
|
|
121
|
-
|
|
122
|
-
The <B>Thermostat</B> is for setting the target temperature for this zone by moving the big white dot. The small white dot is an indicating of the measured temperature of the Zone for your INFO. The mode on this <B>Thermostat</B> is also just for your INFO only. It will show `Cool` or `Heat` when the Aircon mode is `Cool` or `Heat` respectively. It will show `Auto` when the state of the Aircon is `Off` or the mode is `vent` or `dry`. You cannot change the state or the mode of the Aircon here.
|
|
123
|
-
|
|
124
|
-
<B>(c) A temperature controlled Zone with myZone defined:</B> If your system has temperature sensors and <B>myZone</B> is defined, then there will be a round button for setting this zone as <B>myZone</B> in addition to a simple <B>Fan</B> accessory and a <B>Thermostat</B> accessory as detailed in <B>(b)</B> above.
|
|
125
|
-
|
|
126
|
-
Please note that <B>myZone</B> cannot be turned `Off` as per AdvantageAir system design. It can only be turned `Off` by setting another zone as <B>myZone</B>. However, if a Zone, which is set as <B>myZone</B>, is turned off by sliding the Fan slider to zero, this plugin will automatically set another open Zone to be <B>myZone</B> with preference to an open Constant Zone. If there is no other open Zone, then this plugin will open a Zone with preference to a Constant Zone and set it as <B>myZone</B>.
|
|
127
|
-
|
|
128
|
-
### (B) Lights
|
|
129
|
-
<p align="left">
|
|
130
|
-
<img width="300px" src="Screenshots/Lights_homekit_tiles.png">
|
|
131
|
-
</p>
|
|
132
|
-
|
|
133
|
-
<p align="left">
|
|
134
|
-
<img width="240px" src="Screenshots/LightWithDimmer_homekit_UI.png">
|
|
135
|
-
<img width="240px" src="Screenshots/LightSwitch_homekit_UI.png">
|
|
136
|
-
</p>
|
|
137
|
-
|
|
138
|
-
Light with dimmer has a slider to control its brightness while a light without dimmer just has a simple ON/OFF light switch.
|
|
139
|
-
|
|
140
|
-
### (C) Fans
|
|
141
|
-
|
|
142
|
-
<p align="left">
|
|
143
|
-
<img width="150px" src="Screenshots/Fan_homekit_tile.jpg">
|
|
144
|
-
</p>
|
|
145
|
-
|
|
146
|
-
<p align="left">
|
|
147
|
-
<img width="240px" src="Screenshots/Fan_homekit_UI.jpg">
|
|
148
|
-
</p>
|
|
149
|
-
|
|
150
|
-
From version 2.3.0 onwards, any light switch whose name ends with ‘ Fan’ or ‘ Ex’, or begins with ‘Fan ’ or ‘Ex ’, will be treated as a fan accessory. In HomeKit, its icon will appear as a fan (as shown above) instead of a lightbulb.
|
|
151
|
-
|
|
152
|
-
### (D) Garage Door and Blinds
|
|
153
|
-
<p align="left">
|
|
154
|
-
<img width="300px" src="Screenshots/Garage&Blinds_homekit_tiles.png">
|
|
155
|
-
</p>
|
|
156
|
-
|
|
157
|
-
<p align="left">
|
|
158
|
-
<img width="240px" src="Screenshots/Garage_homekit_UI.png">
|
|
159
|
-
<img width="240px" src="Screenshots/Blinds_homekit_UI.png">
|
|
160
|
-
</p>
|
|
161
|
-
|
|
162
|
-
Garage Door is either Opened or Closed, hence it appears as a simple switch while Blinds can be partially open, as such, it has a slider to set the %-open.
|
|
163
|
-
|
|
164
|
-
## How You Can Help
|
|
165
|
-
* Report Bugs/Errors by opening Issues/Tickets.
|
|
166
|
-
* Suggest Improvements and Features you would like to see!
|
|
167
|
-
* If you are loving this plugin or using this plugin, please feel free to give me a `Star`!
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
## Special Thanks
|
|
171
|
-
|
|
172
|
-
This project would not have been possible without the work of others in the Homebridge community.
|
|
173
|
-
|
|
174
|
-
1. Many thanks to [Mitch Williams](https://github.com/mitch7391) who has
|
|
175
|
-
2. Many thanks also to [John Talbot](https://github.com/ztalbot2000) for his fantastic [homebridge-cmd4](https://github.com/ztalbot2000/homebridge-cmd4) plugin which I forked and reused most of the original logic, with some modifications and adjustments to meet the requirements of this plugin.
|
|
176
|
-
3. And never forget to thank my beautiful wife who has put up with my obsession on this.....
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
## LICENSE
|
|
180
|
-
This plugin is distributed under the MIT license. See [LICENSE](https://github.com/uswong/homebridge-myplace/blob/main/LICENSE) for details.
|
|
1
|
+
<span align="center">
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="Screenshots/homebridge-myplace.png" width="220">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
# homebridge-myplace
|
|
8
|
+
|
|
9
|
+
[](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
|
|
10
|
+
[](https://www.npmjs.com/package/homebridge-myplace)
|
|
11
|
+
[](https://www.npmjs.com/package/homebridge-myplace)
|
|
12
|
+
[](https://github.com/uswong/homebridge-myplace/blob/master/LICENSE)
|
|
13
|
+
[](https://discord.gg/uaEXgtt72q)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
</span>
|
|
17
|
+
|
|
18
|
+
An independent plugin bringing [Advantage Air](https://www.advantageair.com.au/) MyPlace System, its smaller siblings (E-zone, MyAir, MyAir4, etc) and its cousins (e.g. Fujitsu AnywAir) to Homekit.
|
|
19
|
+
|
|
20
|
+
## Supported Advantage Air Control Units
|
|
21
|
+
* [MyPlace](https://apps.apple.com/au/app/myplace/id996398299)
|
|
22
|
+
* [MyAir4](https://apps.apple.com/au/app/myair4/id925994861)
|
|
23
|
+
* [MyAir](https://apps.apple.com/au/app/myair/id481563583)
|
|
24
|
+
* [E-zone](https://apps.apple.com/au/app/e-zone/id925994857)
|
|
25
|
+
* [Fujitsu anywAIR](https://apps.apple.com/au/app/anywair/id1509639853)
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
1. Install Homebridge via these instructions for [Raspbian](https://github.com/homebridge/homebridge/wiki/Install-Homebridge-on-Raspbian), [HOOBS](https://support.hoobs.org/docs), [macOS](https://github.com/homebridge/homebridge/wiki/Install-Homebridge-on-macOS) or [Windows](https://github.com/homebridge/homebridge/wiki/Install-Homebridge-on-Virtual-Machine), if you have not already.
|
|
29
|
+
|
|
30
|
+
2. Make sure that <B>jq</B> and <B>curl</B> are installed. Try to install <B>jq-1.7</B> if you can. It is much faster.
|
|
31
|
+
|
|
32
|
+
3. Find the `homebridge-myplace` plugin via the Homebridge UI 'Plugins' tab search function, once found, click the blue *down-arrow* at the bottom right to install.
|
|
33
|
+
|
|
34
|
+
<p align="left">
|
|
35
|
+
<img width="400px" src="Screenshots/MyPlaceInstall3.png">
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
Once installed, `Config: homebridge-myplace` UI will pop up, then follow the steps outlined in Step 4 below.
|
|
39
|
+
|
|
40
|
+
<img width="750px" alt="image" src="https://github.com/user-attachments/assets/0f60e1ee-b7d6-46de-959b-5e42a8ede80f" />
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
If for some reasons, the `Config: homebridge-myplace` UI did not pop up, locate your newly installed `Homebridge Myplace` plugin and click on the three dots at the bottom right and select `Plugin Config` to get to the `Homebridge MyPlace` UI.
|
|
44
|
+
|
|
45
|
+
5. Configuring MyPlace plugin:
|
|
46
|
+
|
|
47
|
+
In <B>Device Settings</B> area, fill out the `Name`, `IP Address` and `PORT used` fields (default PORT is `2025` for most users, Fujitsu anywAIR users set this to `10211` ) and check/uncheck the self-explanatory checkboxes for `Include extra timers` and `Enable detailed debug log for this device`, click `SAVE` then `RESTART`.
|
|
48
|
+
|
|
49
|
+
For advanced users, you can expand the **Advanced Plugin Settings** to `Specify the maximum number of accessories (between 1 to 149) to be configured for this plugin`. Homebridge has a grand limit of 149 accessories that can be bridged; it may crash if the total number of accessories from all installed plugins exceeds this limit. You can also check `Enable detailed debug log for this plugin` to view detailed logs and help diagnose any plugin issues.
|
|
50
|
+
|
|
51
|
+
For users who do not have access to Homebridge UI have to make sure that a config, as shown in the example below, is in the homebridge config.json:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
{
|
|
55
|
+
"name": "MyPlace",
|
|
56
|
+
"debug": false,
|
|
57
|
+
"maxAccessories": 120,
|
|
58
|
+
"devices": [
|
|
59
|
+
{
|
|
60
|
+
"name": "Aircon",
|
|
61
|
+
"ipAddress": "192.168.50.117",
|
|
62
|
+
"port": 2025,
|
|
63
|
+
"extraTimers": true,
|
|
64
|
+
"debug": false
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"platform": "MyPlace"
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
From version 2.3.0 onwards, if the `ipAddress` is missing, incorrectly formatted, or inaccessible, the plugin will automatically discover your connected AdvantageAir systems and configure them.
|
|
72
|
+
|
|
73
|
+
## How it Looks and Works
|
|
74
|
+
### (A) Aircon System (MyAir, E-zone, etc) has the following typical Homekit tiles:
|
|
75
|
+
<p align="left">
|
|
76
|
+
<img width="300px" src="Screenshots/Aircon_homekit_tiles.png">
|
|
77
|
+
</p>
|
|
78
|
+
|
|
79
|
+
#### (i) Thermostat, Fan Switch and Fan Speed Control
|
|
80
|
+
<p align="left">
|
|
81
|
+
<img width="240px" src="Screenshots/Thermostat_homekit_UI.png">
|
|
82
|
+
<img width="240px" src="Screenshots/FanSwitch_homekit_UI.png">
|
|
83
|
+
</p>
|
|
84
|
+
|
|
85
|
+
<B>Thermostat</B> is where the desired target temperature can be set.
|
|
86
|
+
|
|
87
|
+
<B>Thermostat</B> has 4 modes- <B>Off</B>, <B>Cool</B>, <B>Heat</B> and <B>Auto</B>. It does not have <B>dry</B> and <B>fan</B> modes. As such, the <B>Thermostat Auto</B> mode is repurposed as <B>dry</B> mode in this plugin and a separate <B>Fan</B> switch is used for <B>fan</B> mode.
|
|
88
|
+
|
|
89
|
+
Both the <B>Thermostat</B> and <B>Fan</B> switch has associated fan speed control and either one can be used to set the fan speed. It is duplicated only for convenience. The fan speed control has 4 tiers - <B>low</B>, <B>mid</B>, <B>high</B> and <B>auto</B> or <B>ezfan</B> and the %-rotationSpeed is snapped to 25% for <B>low</B>, 50% for <B>mid</B>, 90% for <B>high</B> and 100% for <B>auto</B> or <B>ezfan</B>.
|
|
90
|
+
|
|
91
|
+
#### (ii) Timer
|
|
92
|
+
<p align="left">
|
|
93
|
+
<img width="240px" src="Screenshots/Timer_homekit_UI.png">
|
|
94
|
+
</p>
|
|
95
|
+
|
|
96
|
+
A timer is repurposed from <B>Lightbulb</B> accessory and its <B>%-brightness</B> as proxy for timer duration, configured to represent 6 minutes per 1%. So a 10%-timer is a 60-minutes or 1-hour timer and 25%-timer is a 2.5-hours timer. A maximum of 10-hour timer (100%-timer) can be set. This timer will turn ON or OFF the Aircon system.
|
|
97
|
+
|
|
98
|
+
If <B>Include extra timers</B> was selected during the setup process, 3 more timers would have been created- a <B>Fan Timer</B>, a <B>Cool Timer</B> and a <B>Heat Timer</B>:
|
|
99
|
+
<p align="left">
|
|
100
|
+
<img width="300px" src="Screenshots/FancyTimers_homekit_tiles.png">
|
|
101
|
+
</p>
|
|
102
|
+
|
|
103
|
+
These timers will turn ON the Aircon system in specific mode as their names suggest.
|
|
104
|
+
|
|
105
|
+
*Please note that the icons on the Timer tiles in the example above have been deliberately changed to distinguish them from lights.*
|
|
106
|
+
|
|
107
|
+
#### (iii) Zone Control
|
|
108
|
+
<p align="left">
|
|
109
|
+
<img width="760px" src="Screenshots/variousZoneControls_homekit_UI.png">
|
|
110
|
+
</p>
|
|
111
|
+
|
|
112
|
+
Zone control is repurposed from <B>Fan</B> accessory with its <B>%-rotationSpeed</B> as proxy for Zone damper %-open and <B>rotationDirection</B> as <B>myZone</B> button. <B>Thermostat</B> accessory is used for setting the Zone target temperature.
|
|
113
|
+
|
|
114
|
+
There are three possible Zone Control configurations depending on the setup of your Aircon system:
|
|
115
|
+
|
|
116
|
+
<B>(a) A non temperature controlled Zone:</B> If your system has no temperature sensors, then there will only be a simple <B>Fan</B> accessory for your Zone Control. The Fan slider is for adjusting the desired damper %-open manually.
|
|
117
|
+
|
|
118
|
+
<B>(b) A temperature controlled Zone but without myZone defined:</B> If your system has temperature sensors but <B>myZone</B> is not defined, then there will be a simple <B>Fan</B> accessory and a <B>Thermostat</B> accessory.
|
|
119
|
+
|
|
120
|
+
The Fan slider here is to show the damper %-open for this zone for your INFO only. It is set automatically by the system, it cannot be adjusted manually. To turn off the Zone, slide the slider to zero. To turn it on, tap anywhere within the slider.
|
|
121
|
+
|
|
122
|
+
The <B>Thermostat</B> is for setting the target temperature for this zone by moving the big white dot. The small white dot is an indicating of the measured temperature of the Zone for your INFO. The mode on this <B>Thermostat</B> is also just for your INFO only. It will show `Cool` or `Heat` when the Aircon mode is `Cool` or `Heat` respectively. It will show `Auto` when the state of the Aircon is `Off` or the mode is `vent` or `dry`. You cannot change the state or the mode of the Aircon here.
|
|
123
|
+
|
|
124
|
+
<B>(c) A temperature controlled Zone with myZone defined:</B> If your system has temperature sensors and <B>myZone</B> is defined, then there will be a round button for setting this zone as <B>myZone</B> in addition to a simple <B>Fan</B> accessory and a <B>Thermostat</B> accessory as detailed in <B>(b)</B> above.
|
|
125
|
+
|
|
126
|
+
Please note that <B>myZone</B> cannot be turned `Off` as per AdvantageAir system design. It can only be turned `Off` by setting another zone as <B>myZone</B>. However, if a Zone, which is set as <B>myZone</B>, is turned off by sliding the Fan slider to zero, this plugin will automatically set another open Zone to be <B>myZone</B> with preference to an open Constant Zone. If there is no other open Zone, then this plugin will open a Zone with preference to a Constant Zone and set it as <B>myZone</B>.
|
|
127
|
+
|
|
128
|
+
### (B) Lights
|
|
129
|
+
<p align="left">
|
|
130
|
+
<img width="300px" src="Screenshots/Lights_homekit_tiles.png">
|
|
131
|
+
</p>
|
|
132
|
+
|
|
133
|
+
<p align="left">
|
|
134
|
+
<img width="240px" src="Screenshots/LightWithDimmer_homekit_UI.png">
|
|
135
|
+
<img width="240px" src="Screenshots/LightSwitch_homekit_UI.png">
|
|
136
|
+
</p>
|
|
137
|
+
|
|
138
|
+
Light with dimmer has a slider to control its brightness while a light without dimmer just has a simple ON/OFF light switch.
|
|
139
|
+
|
|
140
|
+
### (C) Fans
|
|
141
|
+
|
|
142
|
+
<p align="left">
|
|
143
|
+
<img width="150px" src="Screenshots/Fan_homekit_tile.jpg">
|
|
144
|
+
</p>
|
|
145
|
+
|
|
146
|
+
<p align="left">
|
|
147
|
+
<img width="240px" src="Screenshots/Fan_homekit_UI.jpg">
|
|
148
|
+
</p>
|
|
149
|
+
|
|
150
|
+
From version 2.3.0 onwards, any light switch whose name ends with ‘ Fan’ or ‘ Ex’, or begins with ‘Fan ’ or ‘Ex ’, will be treated as a fan accessory. In HomeKit, its icon will appear as a fan (as shown above) instead of a lightbulb.
|
|
151
|
+
|
|
152
|
+
### (D) Garage Door and Blinds
|
|
153
|
+
<p align="left">
|
|
154
|
+
<img width="300px" src="Screenshots/Garage&Blinds_homekit_tiles.png">
|
|
155
|
+
</p>
|
|
156
|
+
|
|
157
|
+
<p align="left">
|
|
158
|
+
<img width="240px" src="Screenshots/Garage_homekit_UI.png">
|
|
159
|
+
<img width="240px" src="Screenshots/Blinds_homekit_UI.png">
|
|
160
|
+
</p>
|
|
161
|
+
|
|
162
|
+
Garage Door is either Opened or Closed, hence it appears as a simple switch while Blinds can be partially open, as such, it has a slider to set the %-open.
|
|
163
|
+
|
|
164
|
+
## How You Can Help
|
|
165
|
+
* Report Bugs/Errors by opening Issues/Tickets.
|
|
166
|
+
* Suggest Improvements and Features you would like to see!
|
|
167
|
+
* If you are loving this plugin or using this plugin, please feel free to give me a `Star`!
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
## Special Thanks
|
|
171
|
+
|
|
172
|
+
This project would not have been possible without the work of others in the Homebridge community.
|
|
173
|
+
|
|
174
|
+
1. Many thanks to [Mitch Williams](https://github.com/mitch7391) who has initiated [homebridge-cmd4-AdvantageAir](https://github.com/mitch7391/homebridge-cmd4-AdvantageAir) plugin and has allowed me to participate in its development and in the process I have leant a lot about GitHub and on **bash** and **javascript** coding in homebridge environment.
|
|
175
|
+
2. Many thanks also to [John Talbot](https://github.com/ztalbot2000) for his fantastic [homebridge-cmd4](https://github.com/ztalbot2000/homebridge-cmd4) plugin which I forked and reused most of the original logic, with some modifications and adjustments to meet the requirements of this plugin.
|
|
176
|
+
3. And never forget to thank my beautiful wife who has put up with my obsession on this.....
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
## LICENSE
|
|
180
|
+
This plugin is distributed under the MIT license. See [LICENSE](https://github.com/uswong/homebridge-myplace/blob/main/LICENSE) for details.
|
package/RUNNING_CHANGELOG.md
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
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.3.1 (2025-10-22)
|
|
3
|
+
|
|
4
|
+
###### (1) Allow up to 5 retries of inaccessible device before proceeding to auto-discovery.
|
|
5
|
+
###### (2) Minor bug fixes.
|
|
6
|
+
|
|
2
7
|
##### v2.3.0 (2025-10-02)
|
|
3
8
|
|
|
4
9
|
###### (1) Automatic device discovery: AdvantageAir devices can now be auto-discovered if no IP is specified, or if the configured IP is invalid or inaccessible.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-myplace",
|
|
3
3
|
"description": "Exec Plugin bringing Advanatge Air MyPlace system to Homekit",
|
|
4
|
-
"version": "2.3.
|
|
4
|
+
"version": "2.3.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Ung Sing"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@commitlint/cli": "^17.6.7",
|
|
34
34
|
"@commitlint/config-conventional": "^16.0.0",
|
|
35
|
-
"@eslint/js": "^9.
|
|
35
|
+
"@eslint/js": "^9.37.0",
|
|
36
36
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
37
37
|
"@typescript-eslint/parser": "^8.0.0",
|
|
38
38
|
"background-eslint-hook": "^1.0.0",
|
package/utils/updateConfig.js
CHANGED
|
@@ -3,6 +3,11 @@ const { devicesAutoDiscovery } = require("./devicesAutoDiscovery");
|
|
|
3
3
|
const { createMyPlaceConfig } = require("./createMyPlaceConfig");
|
|
4
4
|
const chalk = require("chalk");
|
|
5
5
|
|
|
6
|
+
// Helper function to delay execution
|
|
7
|
+
function delay(ms) {
|
|
8
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
async function updateConfig(config, log, pluginPath) {
|
|
7
12
|
// Enforce maxAccessories: default 149 (Homebridge hard limit)
|
|
8
13
|
const maxAccessories = (typeof config.maxAccessories === "number"
|
|
@@ -80,12 +85,61 @@ async function updateConfig(config, log, pluginPath) {
|
|
|
80
85
|
return myplaceConfig;
|
|
81
86
|
} catch (err) {
|
|
82
87
|
const autoDiscoveryErrors = ["wrong format", "inaccessible"];
|
|
88
|
+
const isInaccessibleError = err.message.toLowerCase().includes("inaccessible");
|
|
89
|
+
|
|
83
90
|
doDevicesAutoDiscovery = autoDiscoveryErrors.some(e =>
|
|
84
91
|
err.message.toLowerCase().includes(e)
|
|
85
92
|
);
|
|
86
93
|
|
|
87
94
|
if (doDevicesAutoDiscovery && portsToTry.length > 0) {
|
|
88
95
|
log.warn(`⚠️ ${err.message}`);
|
|
96
|
+
|
|
97
|
+
// Special handling for "inaccessible" errors - wait 5 seconds and retry up to 5 times
|
|
98
|
+
if (isInaccessibleError) {
|
|
99
|
+
const maxRetries = 5;
|
|
100
|
+
let retryCount = 0;
|
|
101
|
+
let retrySuccess = false;
|
|
102
|
+
let lastRetryError = err;
|
|
103
|
+
|
|
104
|
+
while (retryCount < maxRetries && !retrySuccess) {
|
|
105
|
+
retryCount++;
|
|
106
|
+
log.info(chalk.yellow(`⏳ Device inaccessible, waiting 5 seconds before retry (${retryCount}/${maxRetries})...`));
|
|
107
|
+
await delay(5000);
|
|
108
|
+
|
|
109
|
+
// Retry createMyPlaceConfig immediately without auto-discovery
|
|
110
|
+
try {
|
|
111
|
+
log.info(chalk.yellow(`🔄 Retrying createMyPlaceConfig (attempt ${retryCount}/${maxRetries})...`));
|
|
112
|
+
const myplaceConfig = await createMyPlaceConfig(config, pluginPath);
|
|
113
|
+
log.info(chalk.green(`✅ SUCCESS! createMyPlaceConfig completed after ${retryCount} retry attempt(s)!`));
|
|
114
|
+
|
|
115
|
+
// Enforce accessories limit on retry success
|
|
116
|
+
if (Array.isArray(myplaceConfig.accessories) &&
|
|
117
|
+
myplaceConfig.accessories.length > maxAccessories) {
|
|
118
|
+
log.warn(`⚠️ Configured accessories exceed limit of ${maxAccessories}. ` +
|
|
119
|
+
`Only the first ${maxAccessories} will be bridged, ` +
|
|
120
|
+
`${myplaceConfig.accessories.length - maxAccessories} ignored.`);
|
|
121
|
+
myplaceConfig.accessories = myplaceConfig.accessories.slice(0, maxAccessories);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return myplaceConfig;
|
|
125
|
+
} catch (retryErr) {
|
|
126
|
+
lastRetryError = retryErr;
|
|
127
|
+
log.warn(`⚠️ Retry attempt ${retryCount}/${maxRetries} failed: ${retryErr.message}`);
|
|
128
|
+
|
|
129
|
+
// Check if it's still an "inaccessible" error for the next retry
|
|
130
|
+
if (!retryErr.message.toLowerCase().includes("inaccessible")) {
|
|
131
|
+
log.warn("⚠️ Error type changed, stopping retries and proceeding to auto-discovery...");
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// If we exhausted all retries and still failed, log the final error
|
|
138
|
+
if (!retrySuccess) {
|
|
139
|
+
log.warn(`❌ All ${maxRetries} retry attempts failed. Last error: ${lastRetryError.message}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
89
143
|
if (devicesAutoDiscoveryCounter === 0) {
|
|
90
144
|
log.info(chalk.yellow("🔍 Starting devices auto-discovery..."));
|
|
91
145
|
} else {
|