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 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.0 (2025-10-02)
2
+ ##### v2.3.1 (2025-10-22)
3
3
 
4
- ###### (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.
5
- ###### (2) Smart fan detection: Any lighting switch with a name ending in " Fan" or " Ex", or starting with "Fan " or "Ex ", will now be treated as a fan accessory.
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
- # Many thanks to John Talbot of homebridge-cmd4 and Mitch Williams of
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
- logQueryAirConDiagnostic "Unhandled $io $device $characteristic $value rc=$rc - this accessory is most likely offline"
136
+ logDiagnostic "Unhandled $io $device $characteristic $value rc=$rc - this accessory is most likely offline"
162
137
  elif [ "${io}" = "Get" ]; then
163
- logQueryAirConDiagnostic "Unhandled $io $device $characteristic rc=$rc - this accessory is most likely offline!"
138
+ logDiagnostic "Unhandled $io $device $characteristic rc=$rc - this accessory is most likely offline!"
164
139
  fi
165
140
  }
166
141
 
167
- function logQueryAirConDiagnostic()
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
- logQueryAirConDiagnostic "queryAirCon_calls_earlier_curl_timed_out $tf $t0 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
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
- logQueryAirConDiagnostic "queryAirCon_calls $tf $t0 $dt $useFileCache itr=$iteration $io $device $characteristic $url"
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
- logQueryAirConDiagnostic "queryAirCon_timed_out_so_copy_earlier_cache $t0 $t2 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
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
- logQueryAirConDiagnostic "queryAirCon_copy $t0 $t2 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
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
- logQueryAirConDiagnostic "queryAirCon_curl $t0 $t2 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
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
- logQueryAirConDiagnostic "queryAirCon_curl_invalid $t0 $t2 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
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
- logQueryAirConDiagnostic "queryAirCon_curl_failed $t0 $t2 $dt $useFileCache rc=$rc itr=$iteration $io $device $characteristic $url"
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
- logQueryAirConDiagnostic "setAirCon_curl $t3 rc=$rc itr=$i $io $device $characteristic $value $url"
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
- logQueryAirConDiagnostic "setAirCon_curl_failed $t3 rc=$rc $io $device $characteristic $value $url"
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
- logQueryAirConDiagnostic "setAirCon_setJson $t3 rc=$rc $io $device $characteristic $jqPath"
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
- logQueryAirConDiagnostic "setAirCon_setJson_failed $t3 rc=$rc $io $device $characteristic $jqPath"
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 -e "$jqPath" )
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
- logQueryAirConDiagnostic "parseMyAirDataWithJq_failed rc=$rc jqResult=$jqResult $io $device $characteristic $jqPath"
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}" = '"open"' ]; then
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}" = '"open"' ]; then
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}" = '"close"' ]; then
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}" = '"close"' ]; then
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}" = '"close"' ]; then
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}" = '"on"' && "${acMode}" = '"vent"' ]]; then
707
+ if [[ "${acState}" = "on" && "${acMode}" = "vent" ]]; then
733
708
  fanState=1
734
709
  else
735
710
  fanState=0
736
711
  fi
737
- logQueryAirConDiagnostic "queryFanTimer ${t0} ${t0} fanState=${fanState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
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}" = '"on"' && "${acMode}" = '"cool"' ]]; then
716
+ if [[ "${acState}" = "on" && "${acMode}" = "cool" ]]; then
742
717
  coolState=1
743
718
  else
744
719
  coolState=0
745
720
  fi
746
- logQueryAirConDiagnostic "queryCoolTimer ${t0} ${t0} coolState=${coolState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
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}" = '"on"' && "${acMode}" = '"heat"' ]]; then
725
+ if [[ "${acState}" = "on" && "${acMode}" = "heat" ]]; then
751
726
  heatState=1
752
727
  else
753
728
  heatState=0
754
729
  fi
755
- logQueryAirConDiagnostic "queryHeatTimer ${t0} ${t0} heatState=${heatState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
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
- logQueryAirConDiagnostic "updateFanTimer ${t0} ${t0} fanState=${fanState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
775
+ logDiagnostic "upd_FanTimer ${t0} fanState=${fanState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
801
776
  elif [ $coolTimerSpecified = true ]; then
802
- logQueryAirConDiagnostic "updateCoolTimer ${t0} ${t0} coolState=${coolState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
777
+ logDiagnostic "upd_CoolTimer ${t0} coolState=${coolState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
803
778
  elif [ $heatTimerSpecified = true ]; then
804
- logQueryAirConDiagnostic "updateHeatTimer ${t0} ${t0} heatState=${heatState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
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="update"
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
- logQueryAirConDiagnostic "${prefix}FanTimerStateFil${space} ${t0} ${t0} fanState=${fanState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
808
+ logDiagnostic "${prefix}_FanTimerState ${t0} fanState=${fanState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
836
809
  elif [ $coolTimerSpecified = true ]; then
837
- logQueryAirConDiagnostic "${prefix}CoolTimerStateFil${space} ${t0} ${t0} coolState=${coolState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
810
+ logDiagnostic "${prefix}_CoolTimerState ${t0} coolState=${coolState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
838
811
  elif [ $heatTimerSpecified = true ]; then
839
- logQueryAirConDiagnostic "${prefix}HeatTimerStateFil${space} ${t0} ${t0} heatState=${heatState} ${timeToOn} ${timeToOff} ${setTime} $io $device $characteristic"
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" = '"off"' ]; then
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
- '"heat"' )
1050
+ "heat" )
1078
1051
  # Thermostat in Heat Mode.
1079
1052
  echo 1
1080
1053
  exit 0
1081
1054
  ;;
1082
- '"cool"' )
1055
+ "cool" )
1083
1056
  # Thermostat in Cool Mode.
1084
1057
  echo 2
1085
1058
  exit 0
1086
1059
  ;;
1087
- '"vent"' )
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
- '"dry"' )
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" = '"open"' ]; then
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" = '"off"' ]; then
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
- '"heat"' )
1150
+ "heat" )
1178
1151
  # Fan does not support Heat Mode.
1179
1152
  echo 0
1180
1153
  exit 0
1181
1154
  ;;
1182
- '"cool"' )
1155
+ "cool" )
1183
1156
  # Fan does not support Cool Mode.
1184
1157
  echo 0
1185
1158
  exit 0
1186
1159
  ;;
1187
- '"vent"' )
1160
+ "vent" )
1188
1161
  # Set Fan to On.
1189
1162
  echo 1
1190
1163
  exit 0
1191
1164
  ;;
1192
- '"dry"' )
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" = '"open"' ]; then
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" = '"on"' ]; then
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" = '"on"' ]; then
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" = '"low"' ]; then
1340
+ if [ "$jqResult" = "low" ]; then
1368
1341
  #25% as low speed
1369
1342
  echo 25
1370
1343
  exit 0
1371
- elif [ "$jqResult" = '"medium"' ]; then
1344
+ elif [ "$jqResult" = "medium" ]; then
1372
1345
  #50% as medium speed
1373
1346
  echo 50
1374
1347
  exit 0
1375
- elif [ "$jqResult" = '"high"' ]; then
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}" = '"close"' ]; then
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" = '"open"' ]; then
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" = '"on"' ]; then
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
- [![verified-by-homebridge](https://badgen.net/badge/homebridge/verified/purple)](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
10
- [![homebridge-myplace](https://badgen.net/npm/v/homebridge-myplace?icon=npm)](https://www.npmjs.com/package/homebridge-myplace)
11
- [![npm](https://badgen.net/npm/dt/homebridge-myplace?label=downloads)](https://www.npmjs.com/package/homebridge-myplace)
12
- [![mit-license](https://badgen.net/npm/license/lodash)](https://github.com/uswong/homebridge-myplace/blob/master/LICENSE)
13
- [![join-discord](https://badgen.net/badge/icon/discord?icon=discord&label=homebridge-myplace)](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) or [macOS](https://github.com/homebridge/homebridge/wiki/Install-Homebridge-on-macOS), 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
- 4. 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 created the wonderful [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.
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
+ [![verified-by-homebridge](https://badgen.net/badge/homebridge/verified/purple)](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
10
+ [![homebridge-myplace](https://badgen.net/npm/v/homebridge-myplace?icon=npm)](https://www.npmjs.com/package/homebridge-myplace)
11
+ [![npm](https://badgen.net/npm/dt/homebridge-myplace?label=downloads)](https://www.npmjs.com/package/homebridge-myplace)
12
+ [![mit-license](https://badgen.net/npm/license/lodash)](https://github.com/uswong/homebridge-myplace/blob/master/LICENSE)
13
+ [![join-discord](https://badgen.net/badge/icon/discord?icon=discord&label=homebridge-myplace)](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.
@@ -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.0",
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.36.0",
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",
@@ -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 {