homebridge-multiple-switch 1.7.0-beta.1 → 1.7.0-beta.10
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 +55 -0
- package/config.schema.json +10 -5
- package/homebridge-ui/public/i18n/ar.json +3 -3
- package/homebridge-ui/public/i18n/de.json +3 -3
- package/homebridge-ui/public/i18n/en.json +3 -3
- package/homebridge-ui/public/i18n/es.json +3 -3
- package/homebridge-ui/public/i18n/fr.json +3 -3
- package/homebridge-ui/public/i18n/it.json +3 -3
- package/homebridge-ui/public/i18n/ja.json +3 -3
- package/homebridge-ui/public/i18n/ko.json +3 -3
- package/homebridge-ui/public/i18n/nl.json +3 -3
- package/homebridge-ui/public/i18n/pl.json +3 -3
- package/homebridge-ui/public/i18n/pt.json +3 -3
- package/homebridge-ui/public/i18n/ru.json +3 -3
- package/homebridge-ui/public/i18n/tr.json +3 -3
- package/homebridge-ui/public/i18n/zh-CN.json +3 -3
- package/homebridge-ui/public/index.html +22 -37
- package/index.js +23 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.7.0-beta.10] - 2026-05-17
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Auto-off timer leak: rapidly toggling a switch no longer accumulates stale timeouts — each new auto-off cancels the previous one
|
|
7
|
+
- Duplicate device name now logs a clear warning instead of silently causing UUID collision
|
|
8
|
+
|
|
9
|
+
## [1.7.0-beta.9] - 2026-05-17
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Default State is now a 3-option dropdown: **Remember Last State** / **On** / **Off**
|
|
13
|
+
- Remember: keeps the last known state across restarts (new default)
|
|
14
|
+
- On: always starts ON on every Homebridge restart
|
|
15
|
+
- Off: always starts OFF on every Homebridge restart
|
|
16
|
+
- Backward compatible: old boolean `true`/`false` configs still work correctly
|
|
17
|
+
|
|
18
|
+
## [1.7.0-beta.8] - 2026-05-17
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Default State now applies on every Homebridge restart, not just on first creation — switches always start in their configured default state
|
|
22
|
+
|
|
23
|
+
## [1.7.0-beta.7] - 2026-05-17
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- `"type"` removed from switch schema `required` array (field no longer exists at switch level)
|
|
27
|
+
- New devices and switches no longer include stale `type` field on creation
|
|
28
|
+
- Removed unused `masterSwitchType` key from all 14 i18n locale files
|
|
29
|
+
|
|
30
|
+
## [1.7.0-beta.6] - 2026-05-17
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- Master Switch now uses the device-level switch type automatically — separate type selector removed
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
- Master Switch Type selector from UI and schema (`masterSwitchType` config field is no longer used)
|
|
37
|
+
|
|
38
|
+
## [1.7.0-beta.5] - 2026-05-17
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- Switch fields (Switch Name, Auto Turn Off, Default State) now use the same 3-equal-column grid as device fields
|
|
42
|
+
|
|
43
|
+
## [1.7.0-beta.4] - 2026-05-17
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
- Auto Turn Off column narrowed to 120px so Default State checkbox has more breathing room
|
|
47
|
+
|
|
48
|
+
## [1.7.0-beta.3] - 2026-05-17
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
- Switch card: Switch Name, Auto Turn Off and Default State are now on a single compact row
|
|
52
|
+
|
|
53
|
+
## [1.7.0-beta.2] - 2026-05-17
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
- Switch Type description now appears below the dropdown, consistent with Switch Behavior Mode
|
|
57
|
+
|
|
3
58
|
## [1.7.0-beta.1] - 2026-05-17
|
|
4
59
|
|
|
5
60
|
### Changed
|
package/config.schema.json
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"masterSwitch": {
|
|
48
48
|
"title": "Master Switch",
|
|
49
|
-
"description": "Add a master switch that turns all switches on or off at once.
|
|
49
|
+
"description": "Add a master switch that turns all switches on or off at once. Uses the same type as the device.",
|
|
50
50
|
"type": "boolean",
|
|
51
51
|
"default": false
|
|
52
52
|
},
|
|
@@ -64,9 +64,14 @@
|
|
|
64
64
|
},
|
|
65
65
|
"defaultState": {
|
|
66
66
|
"title": "Default State",
|
|
67
|
-
"description": "
|
|
68
|
-
"type": "
|
|
69
|
-
"default":
|
|
67
|
+
"description": "Switch state on every Homebridge restart.",
|
|
68
|
+
"type": "string",
|
|
69
|
+
"default": "remember",
|
|
70
|
+
"oneOf": [
|
|
71
|
+
{ "title": "Remember Last State", "enum": ["remember"] },
|
|
72
|
+
{ "title": "On", "enum": ["on"] },
|
|
73
|
+
{ "title": "Off", "enum": ["off"] }
|
|
74
|
+
]
|
|
70
75
|
},
|
|
71
76
|
"delayOff": {
|
|
72
77
|
"title": "Auto Turn Off (ms)",
|
|
@@ -76,7 +81,7 @@
|
|
|
76
81
|
"minimum": 0
|
|
77
82
|
}
|
|
78
83
|
},
|
|
79
|
-
"required": ["name"
|
|
84
|
+
"required": ["name"]
|
|
80
85
|
}
|
|
81
86
|
}
|
|
82
87
|
},
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "مفتاح",
|
|
16
16
|
"typeOutlet": "مقبس",
|
|
17
17
|
"defaultState": "الحالة الافتراضية",
|
|
18
|
-
"defaultStateDesc": "حالة
|
|
18
|
+
"defaultStateDesc": "حالة المفتاح عند كل إعادة تشغيل لـ Homebridge.",
|
|
19
|
+
"remember": "تذكر آخر حالة",
|
|
19
20
|
"delayOff": "إيقاف تلقائي (مللي ثانية)",
|
|
20
21
|
"delayOffDesc": "يتم الإيقاف تلقائيًا بعد هذا العدد من المللي ثانية. اضبط على 0 للتعطيل.",
|
|
21
22
|
"addSwitch": "إضافة مفتاح",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "يمكن تشغيل مفتاح واحد فقط في نفس الوقت. تشغيل واحد يقوم بإيقاف جميع الآخرين تلقائياً.",
|
|
34
35
|
"masterSwitch": "المفتاح الرئيسي",
|
|
35
36
|
"masterSwitchDesc": "يضيف مفتاحاً رئيسياً يقوم بتشغيل أو إيقاف جميع المفاتيح دفعة واحدة.",
|
|
36
|
-
"switchSingular": "مفتاح"
|
|
37
|
-
"masterSwitchType": "نوع المفتاح الرئيسي"
|
|
37
|
+
"switchSingular": "مفتاح"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "Schalter",
|
|
16
16
|
"typeOutlet": "Steckdose",
|
|
17
17
|
"defaultState": "Standardzustand",
|
|
18
|
-
"defaultStateDesc": "
|
|
18
|
+
"defaultStateDesc": "Schaltzustand bei jedem Neustart von Homebridge.",
|
|
19
|
+
"remember": "Letzten Zustand merken",
|
|
19
20
|
"delayOff": "Automatisch ausschalten (ms)",
|
|
20
21
|
"delayOffDesc": "Schaltet nach dieser Anzahl von Millisekunden automatisch aus. Zum Deaktivieren auf 0 setzen.",
|
|
21
22
|
"addSwitch": "Schalter hinzufügen",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "Es kann nur ein Schalter gleichzeitig eingeschaltet sein. Das Einschalten eines Schalters schaltet alle anderen automatisch aus.",
|
|
34
35
|
"masterSwitch": "Hauptschalter",
|
|
35
36
|
"masterSwitchDesc": "Fügt einen Hauptschalter hinzu, der alle Schalter auf einmal ein- oder ausschaltet.",
|
|
36
|
-
"switchSingular": "Schalter"
|
|
37
|
-
"masterSwitchType": "Hauptschaltertyp"
|
|
37
|
+
"switchSingular": "Schalter"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "Switch",
|
|
16
16
|
"typeOutlet": "Outlet",
|
|
17
17
|
"defaultState": "Default State",
|
|
18
|
-
"defaultStateDesc": "
|
|
18
|
+
"defaultStateDesc": "Switch state on every Homebridge restart.",
|
|
19
|
+
"remember": "Remember Last State",
|
|
19
20
|
"delayOff": "Auto Turn Off (ms)",
|
|
20
21
|
"delayOffDesc": "Automatically turn off after this many milliseconds. Set to 0 to disable.",
|
|
21
22
|
"addSwitch": "Add Switch",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "Only one switch can be on at a time. Turning one on automatically turns off all others.",
|
|
34
35
|
"masterSwitch": "Master Switch",
|
|
35
36
|
"masterSwitchDesc": "Adds a master switch that turns all switches on or off at once.",
|
|
36
|
-
"switchSingular": "switch"
|
|
37
|
-
"masterSwitchType": "Master Switch Type"
|
|
37
|
+
"switchSingular": "switch"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "Interruptor",
|
|
16
16
|
"typeOutlet": "Enchufe",
|
|
17
17
|
"defaultState": "Estado predeterminado",
|
|
18
|
-
"defaultStateDesc": "Estado
|
|
18
|
+
"defaultStateDesc": "Estado del interruptor en cada reinicio de Homebridge.",
|
|
19
|
+
"remember": "Recordar último estado",
|
|
19
20
|
"delayOff": "Apagado automático (ms)",
|
|
20
21
|
"delayOffDesc": "Se apaga automáticamente después de estos milisegundos. Establecer en 0 para desactivar.",
|
|
21
22
|
"addSwitch": "Agregar interruptor",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "Solo un interruptor puede estar encendido a la vez. Encender uno apaga automáticamente todos los demás.",
|
|
34
35
|
"masterSwitch": "Interruptor maestro",
|
|
35
36
|
"masterSwitchDesc": "Agrega un interruptor maestro que enciende o apaga todos los interruptores a la vez.",
|
|
36
|
-
"switchSingular": "interruptor"
|
|
37
|
-
"masterSwitchType": "Tipo de interruptor maestro"
|
|
37
|
+
"switchSingular": "interruptor"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "Interrupteur",
|
|
16
16
|
"typeOutlet": "Prise",
|
|
17
17
|
"defaultState": "État par défaut",
|
|
18
|
-
"defaultStateDesc": "État
|
|
18
|
+
"defaultStateDesc": "État de l'interrupteur à chaque redémarrage de Homebridge.",
|
|
19
|
+
"remember": "Mémoriser le dernier état",
|
|
19
20
|
"delayOff": "Arrêt automatique (ms)",
|
|
20
21
|
"delayOffDesc": "S'éteint automatiquement après ce nombre de millisecondes. Mettre à 0 pour désactiver.",
|
|
21
22
|
"addSwitch": "Ajouter un interrupteur",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "Un seul interrupteur peut être activé à la fois. En activer un désactive automatiquement tous les autres.",
|
|
34
35
|
"masterSwitch": "Interrupteur principal",
|
|
35
36
|
"masterSwitchDesc": "Ajoute un interrupteur principal qui active ou désactive tous les interrupteurs en une seule fois.",
|
|
36
|
-
"switchSingular": "interrupteur"
|
|
37
|
-
"masterSwitchType": "Type d'interrupteur principal"
|
|
37
|
+
"switchSingular": "interrupteur"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "Interruttore",
|
|
16
16
|
"typeOutlet": "Presa",
|
|
17
17
|
"defaultState": "Stato predefinito",
|
|
18
|
-
"defaultStateDesc": "Stato
|
|
18
|
+
"defaultStateDesc": "Stato dell'interruttore ad ogni riavvio di Homebridge.",
|
|
19
|
+
"remember": "Ricorda ultimo stato",
|
|
19
20
|
"delayOff": "Spegnimento automatico (ms)",
|
|
20
21
|
"delayOffDesc": "Si spegne automaticamente dopo questi millisecondi. Impostare a 0 per disattivare.",
|
|
21
22
|
"addSwitch": "Aggiungi interruttore",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "Solo un interruttore può essere acceso alla volta. Accenderne uno spegne automaticamente tutti gli altri.",
|
|
34
35
|
"masterSwitch": "Interruttore principale",
|
|
35
36
|
"masterSwitchDesc": "Aggiunge un interruttore principale che accende o spegne tutti gli interruttori contemporaneamente.",
|
|
36
|
-
"switchSingular": "interruttore"
|
|
37
|
-
"masterSwitchType": "Tipo di interruttore principale"
|
|
37
|
+
"switchSingular": "interruttore"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "スイッチ",
|
|
16
16
|
"typeOutlet": "コンセント",
|
|
17
17
|
"defaultState": "デフォルト状態",
|
|
18
|
-
"defaultStateDesc": "Homebridge
|
|
18
|
+
"defaultStateDesc": "Homebridge を再起動するたびのスイッチ状態。",
|
|
19
|
+
"remember": "最後の状態を記憶",
|
|
19
20
|
"delayOff": "自動オフ(ミリ秒)",
|
|
20
21
|
"delayOffDesc": "指定したミリ秒後に自動的にオフになります。無効にするには 0 に設定してください。",
|
|
21
22
|
"addSwitch": "スイッチを追加",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "同時に1つのスイッチのみオンにできます。1つをオンにすると他はすべて自動的にオフになります。",
|
|
34
35
|
"masterSwitch": "マスタースイッチ",
|
|
35
36
|
"masterSwitchDesc": "すべてのスイッチを一度にオン/オフするマスタースイッチを追加します。",
|
|
36
|
-
"switchSingular": "スイッチ"
|
|
37
|
-
"masterSwitchType": "マスタースイッチタイプ"
|
|
37
|
+
"switchSingular": "スイッチ"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "스위치",
|
|
16
16
|
"typeOutlet": "콘센트",
|
|
17
17
|
"defaultState": "기본 상태",
|
|
18
|
-
"defaultStateDesc": "Homebridge
|
|
18
|
+
"defaultStateDesc": "Homebridge를 재시작할 때마다의 스위치 상태.",
|
|
19
|
+
"remember": "마지막 상태 기억",
|
|
19
20
|
"delayOff": "자동 꺼짐 (ms)",
|
|
20
21
|
"delayOffDesc": "지정된 밀리초 후 자동으로 꺼집니다. 비활성화하려면 0으로 설정하세요.",
|
|
21
22
|
"addSwitch": "스위치 추가",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "한 번에 하나의 스위치만 켤 수 있습니다. 하나를 켜면 다른 모든 것이 자동으로 꺼집니다.",
|
|
34
35
|
"masterSwitch": "마스터 스위치",
|
|
35
36
|
"masterSwitchDesc": "모든 스위치를 한 번에 켜거나 끄는 마스터 스위치를 추가합니다.",
|
|
36
|
-
"switchSingular": "스위치"
|
|
37
|
-
"masterSwitchType": "마스터 스위치 유형"
|
|
37
|
+
"switchSingular": "스위치"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "Schakelaar",
|
|
16
16
|
"typeOutlet": "Stopcontact",
|
|
17
17
|
"defaultState": "Standaardstatus",
|
|
18
|
-
"defaultStateDesc": "
|
|
18
|
+
"defaultStateDesc": "Schakelaarstatus bij elke herstart van Homebridge.",
|
|
19
|
+
"remember": "Laatste status onthouden",
|
|
19
20
|
"delayOff": "Automatisch uitschakelen (ms)",
|
|
20
21
|
"delayOffDesc": "Schakelt automatisch uit na dit aantal milliseconden. Stel in op 0 om uit te schakelen.",
|
|
21
22
|
"addSwitch": "Schakelaar toevoegen",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "Er kan slechts één schakelaar tegelijk aan staan. Het inschakelen van één schakelaar schakelt alle andere automatisch uit.",
|
|
34
35
|
"masterSwitch": "Hoofdschakelaar",
|
|
35
36
|
"masterSwitchDesc": "Voegt een hoofdschakelaar toe die alle schakelaars in één keer in- of uitschakelt.",
|
|
36
|
-
"switchSingular": "schakelaar"
|
|
37
|
-
"masterSwitchType": "Type hoofdschakelaar"
|
|
37
|
+
"switchSingular": "schakelaar"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "Przełącznik",
|
|
16
16
|
"typeOutlet": "Gniazdko",
|
|
17
17
|
"defaultState": "Stan domyślny",
|
|
18
|
-
"defaultStateDesc": "
|
|
18
|
+
"defaultStateDesc": "Stan przełącznika przy każdym ponownym uruchomieniu Homebridge.",
|
|
19
|
+
"remember": "Zapamiętaj ostatni stan",
|
|
19
20
|
"delayOff": "Automatyczne wyłączenie (ms)",
|
|
20
21
|
"delayOffDesc": "Automatycznie wyłącza się po podanej liczbie milisekund. Ustaw 0, aby wyłączyć.",
|
|
21
22
|
"addSwitch": "Dodaj przełącznik",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "Tylko jeden przełącznik może być włączony jednocześnie. Włączenie jednego automatycznie wyłącza wszystkie pozostałe.",
|
|
34
35
|
"masterSwitch": "Przełącznik główny",
|
|
35
36
|
"masterSwitchDesc": "Dodaje przełącznik główny, który włącza lub wyłącza wszystkie przełączniki jednocześnie.",
|
|
36
|
-
"switchSingular": "przełącznik"
|
|
37
|
-
"masterSwitchType": "Typ przełącznika głównego"
|
|
37
|
+
"switchSingular": "przełącznik"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "Interruptor",
|
|
16
16
|
"typeOutlet": "Tomada",
|
|
17
17
|
"defaultState": "Estado padrão",
|
|
18
|
-
"defaultStateDesc": "Estado
|
|
18
|
+
"defaultStateDesc": "Estado do interruptor em cada reinício do Homebridge.",
|
|
19
|
+
"remember": "Lembrar último estado",
|
|
19
20
|
"delayOff": "Desligamento automático (ms)",
|
|
20
21
|
"delayOffDesc": "Desliga automaticamente após estes milissegundos. Defina como 0 para desativar.",
|
|
21
22
|
"addSwitch": "Adicionar interruptor",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "Apenas um interruptor pode estar ligado por vez. Ligar um desliga automaticamente todos os outros.",
|
|
34
35
|
"masterSwitch": "Interruptor principal",
|
|
35
36
|
"masterSwitchDesc": "Adiciona um interruptor principal que liga ou desliga todos os interruptores de uma só vez.",
|
|
36
|
-
"switchSingular": "interruptor"
|
|
37
|
-
"masterSwitchType": "Tipo de interruptor principal"
|
|
37
|
+
"switchSingular": "interruptor"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "Выключатель",
|
|
16
16
|
"typeOutlet": "Розетка",
|
|
17
17
|
"defaultState": "Состояние по умолчанию",
|
|
18
|
-
"defaultStateDesc": "
|
|
18
|
+
"defaultStateDesc": "Состояние переключателя при каждом перезапуске Homebridge.",
|
|
19
|
+
"remember": "Запомнить последнее состояние",
|
|
19
20
|
"delayOff": "Автоматическое выключение (мс)",
|
|
20
21
|
"delayOffDesc": "Автоматически выключается через указанное количество миллисекунд. Установите 0 для отключения.",
|
|
21
22
|
"addSwitch": "Добавить переключатель",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "Одновременно может быть включён только один переключатель. Включение одного автоматически выключает все остальные.",
|
|
34
35
|
"masterSwitch": "Главный переключатель",
|
|
35
36
|
"masterSwitchDesc": "Добавляет главный переключатель, который включает или выключает все переключатели одновременно.",
|
|
36
|
-
"switchSingular": "переключатель"
|
|
37
|
-
"masterSwitchType": "Тип главного переключателя"
|
|
37
|
+
"switchSingular": "переключатель"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "Anahtar",
|
|
16
16
|
"typeOutlet": "Priz",
|
|
17
17
|
"defaultState": "Varsayılan Durum",
|
|
18
|
-
"defaultStateDesc": "Homebridge
|
|
18
|
+
"defaultStateDesc": "Her Homebridge yeniden başlatmasında anahtarın durumu.",
|
|
19
|
+
"remember": "Son Durumu Hatırla",
|
|
19
20
|
"delayOff": "Otomatik Kapanma (ms)",
|
|
20
21
|
"delayOffDesc": "Belirtilen milisaniye sonra otomatik kapanır. Devre dışı bırakmak için 0 yazın.",
|
|
21
22
|
"addSwitch": "Anahtar Ekle",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "Aynı anda yalnızca bir anahtar açık olabilir. Birini açmak diğerlerini otomatik kapatır.",
|
|
34
35
|
"masterSwitch": "Ana Anahtar",
|
|
35
36
|
"masterSwitchDesc": "Tüm anahtarları tek seferde açan veya kapatan bir ana anahtar ekler.",
|
|
36
|
-
"switchSingular": "anahtar"
|
|
37
|
-
"masterSwitchType": "Ana Anahtar Tipi"
|
|
37
|
+
"switchSingular": "anahtar"
|
|
38
38
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typeSwitch": "开关",
|
|
16
16
|
"typeOutlet": "插座",
|
|
17
17
|
"defaultState": "默认状态",
|
|
18
|
-
"defaultStateDesc": "Homebridge
|
|
18
|
+
"defaultStateDesc": "每次 Homebridge 重启时的开关状态。",
|
|
19
|
+
"remember": "记住上次状态",
|
|
19
20
|
"delayOff": "自动关闭(毫秒)",
|
|
20
21
|
"delayOffDesc": "在指定的毫秒数后自动关闭。设置为 0 以禁用。",
|
|
21
22
|
"addSwitch": "添加开关",
|
|
@@ -33,6 +34,5 @@
|
|
|
33
34
|
"behaviorSingleDesc": "同一时间只能有一个开关处于打开状态。打开一个会自动关闭所有其他开关。",
|
|
34
35
|
"masterSwitch": "主开关",
|
|
35
36
|
"masterSwitchDesc": "添加一个主开关,可以一次性打开或关闭所有开关。",
|
|
36
|
-
"switchSingular": "开关"
|
|
37
|
-
"masterSwitchType": "主开关类型"
|
|
37
|
+
"switchSingular": "开关"
|
|
38
38
|
}
|
|
@@ -233,7 +233,8 @@
|
|
|
233
233
|
config.devices.push({
|
|
234
234
|
name: '',
|
|
235
235
|
switchBehavior: 'independent',
|
|
236
|
-
|
|
236
|
+
switchType: 'outlet',
|
|
237
|
+
switches: [{ name: '', defaultState: 'remember', delayOff: 0 }],
|
|
237
238
|
});
|
|
238
239
|
expandedDevices.add(di);
|
|
239
240
|
expandedSwitches.add(`${di}_0`);
|
|
@@ -289,22 +290,11 @@
|
|
|
289
290
|
input.addEventListener('change', () => {
|
|
290
291
|
const di = parseInt(input.dataset.dev);
|
|
291
292
|
config.devices[di].masterSwitch = input.checked;
|
|
292
|
-
if (!input.checked) {
|
|
293
|
-
delete config.devices[di].masterSwitchType;
|
|
294
|
-
}
|
|
295
293
|
render();
|
|
296
294
|
save();
|
|
297
295
|
});
|
|
298
296
|
});
|
|
299
297
|
|
|
300
|
-
// Master switch type
|
|
301
|
-
document.querySelectorAll('.master-type-field').forEach(input => {
|
|
302
|
-
input.addEventListener('change', () => {
|
|
303
|
-
const di = parseInt(input.dataset.dev);
|
|
304
|
-
config.devices[di].masterSwitchType = input.value;
|
|
305
|
-
save();
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
298
|
|
|
309
299
|
document.querySelectorAll('.btn-remove-device').forEach(btn => {
|
|
310
300
|
btn.addEventListener('click', (e) => {
|
|
@@ -322,7 +312,7 @@
|
|
|
322
312
|
btn.addEventListener('click', () => {
|
|
323
313
|
const di = parseInt(btn.dataset.dev);
|
|
324
314
|
const si = config.devices[di].switches.length;
|
|
325
|
-
config.devices[di].switches.push({ name: '',
|
|
315
|
+
config.devices[di].switches.push({ name: '', defaultState: 'remember', delayOff: 0 });
|
|
326
316
|
expandedSwitches.add(`${di}_${si}`);
|
|
327
317
|
render();
|
|
328
318
|
save();
|
|
@@ -347,7 +337,7 @@
|
|
|
347
337
|
const si = parseInt(input.dataset.sw);
|
|
348
338
|
const field = input.dataset.field;
|
|
349
339
|
if (field === 'defaultState') {
|
|
350
|
-
config.devices[di].switches[si][field] = input.
|
|
340
|
+
config.devices[di].switches[si][field] = input.value;
|
|
351
341
|
} else if (field === 'delayOff') {
|
|
352
342
|
config.devices[di].switches[si][field] = parseInt(input.value) || 0;
|
|
353
343
|
} else {
|
|
@@ -414,30 +404,22 @@
|
|
|
414
404
|
</div>
|
|
415
405
|
<div class="form-group">
|
|
416
406
|
<label>${t.switchType || 'Switch Type'}</label>
|
|
417
|
-
<div class="desc">${t.switchTypeDesc || ''}</div>
|
|
418
407
|
<select class="dev-field" data-dev="${di}" data-field="switchType">
|
|
419
408
|
<option value="outlet" ${!dev.switchType || dev.switchType === 'outlet' ? 'selected' : ''}>${t.typeOutlet || 'Outlet'}</option>
|
|
420
409
|
<option value="switch" ${dev.switchType === 'switch' ? 'selected' : ''}>${t.typeSwitch || 'Switch'}</option>
|
|
421
410
|
</select>
|
|
411
|
+
<div class="desc" style="margin-top:6px">${t.switchTypeDesc || ''}</div>
|
|
422
412
|
</div>
|
|
423
413
|
</div>
|
|
424
414
|
|
|
425
415
|
${isIndependent ? `
|
|
426
416
|
<div class="master-option">
|
|
427
|
-
<div
|
|
428
|
-
<
|
|
429
|
-
|
|
430
|
-
<
|
|
431
|
-
|
|
432
|
-
<div class="desc" style="margin-bottom:0">${t.masterSwitchDesc || ''}</div>
|
|
433
|
-
</div>
|
|
417
|
+
<div class="toggle-wrap">
|
|
418
|
+
<input type="checkbox" class="master-toggle" data-dev="${di}" ${dev.masterSwitch ? 'checked' : ''}>
|
|
419
|
+
<div>
|
|
420
|
+
<label style="margin-bottom:0">${t.masterSwitch || 'Master Switch'}</label>
|
|
421
|
+
<div class="desc" style="margin-bottom:0">${t.masterSwitchDesc || ''}</div>
|
|
434
422
|
</div>
|
|
435
|
-
${dev.masterSwitch ? `
|
|
436
|
-
<select class="master-type-field" data-dev="${di}" style="width:auto;min-width:120px;flex-shrink:0">
|
|
437
|
-
<option value="switch" ${dev.masterSwitchType === 'switch' || !dev.masterSwitchType ? 'selected' : ''}>${t.typeSwitch || 'Switch'}</option>
|
|
438
|
-
<option value="outlet" ${dev.masterSwitchType === 'outlet' ? 'selected' : ''}>${t.typeOutlet || 'Outlet'}</option>
|
|
439
|
-
</select>
|
|
440
|
-
` : ''}
|
|
441
423
|
</div>
|
|
442
424
|
</div>
|
|
443
425
|
` : ''}
|
|
@@ -456,6 +438,8 @@
|
|
|
456
438
|
const isOpen = expandedSwitches.has(`${di}_${si}`);
|
|
457
439
|
const swLabel = sw.name || `#${si + 1}`;
|
|
458
440
|
const typeLabel = deviceSwitchType === 'switch' ? (t.typeSwitch || 'Switch') : (t.typeOutlet || 'Outlet');
|
|
441
|
+
const ds = sw.defaultState;
|
|
442
|
+
const dsValue = (ds === true || ds === 'on') ? 'on' : (ds === false || ds === 'off') ? 'off' : 'remember';
|
|
459
443
|
|
|
460
444
|
return `
|
|
461
445
|
<div class="switch-card">
|
|
@@ -470,21 +454,22 @@
|
|
|
470
454
|
</div>
|
|
471
455
|
</div>
|
|
472
456
|
<div class="switch-body ${isOpen ? '' : 'collapsed'}">
|
|
473
|
-
<div class="inline-row">
|
|
474
|
-
<div class="form-group">
|
|
457
|
+
<div class="inline-row" style="grid-template-columns: 1fr 1fr 1fr; align-items: end;">
|
|
458
|
+
<div class="form-group" style="margin-bottom:0">
|
|
475
459
|
<label>${t.switchName || 'Switch Name'}</label>
|
|
476
460
|
<input type="text" class="sw-field" data-dev="${di}" data-sw="${si}" data-field="name" value="${esc(sw.name || '')}">
|
|
477
461
|
</div>
|
|
478
|
-
<div class="form-group">
|
|
462
|
+
<div class="form-group" style="margin-bottom:0">
|
|
479
463
|
<label>${t.delayOff || 'Auto Turn Off (ms)'}</label>
|
|
480
464
|
<input type="number" class="sw-field" data-dev="${di}" data-sw="${si}" data-field="delayOff" min="0" value="${sw.delayOff || 0}">
|
|
481
465
|
</div>
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
466
|
+
<div class="form-group" style="margin-bottom:0">
|
|
467
|
+
<label>${t.defaultState || 'Default State'}</label>
|
|
468
|
+
<select class="sw-field" data-dev="${di}" data-sw="${si}" data-field="defaultState">
|
|
469
|
+
<option value="remember" ${dsValue === 'remember' ? 'selected' : ''}>${t.remember || 'Remember'}</option>
|
|
470
|
+
<option value="on" ${dsValue === 'on' ? 'selected' : ''}>${t.on || 'On'}</option>
|
|
471
|
+
<option value="off" ${dsValue === 'off' ? 'selected' : ''}>${t.off || 'Off'}</option>
|
|
472
|
+
</select>
|
|
488
473
|
</div>
|
|
489
474
|
</div>
|
|
490
475
|
</div>
|
package/index.js
CHANGED
|
@@ -23,6 +23,7 @@ class MultipleSwitchPlatform {
|
|
|
23
23
|
this.Characteristic = api.hap.Characteristic;
|
|
24
24
|
this.cachedAccessories = new Map();
|
|
25
25
|
this.deviceServices = new Map();
|
|
26
|
+
this.autoOffTimers = new Map();
|
|
26
27
|
|
|
27
28
|
this.api.on('didFinishLaunching', () => {
|
|
28
29
|
this.log.info('MultipleSwitchPlatform started.');
|
|
@@ -78,6 +79,10 @@ class MultipleSwitchPlatform {
|
|
|
78
79
|
const hasMaster = behavior === 'independent' && device.masterSwitch === true;
|
|
79
80
|
const uuid = this.api.hap.uuid.generate(name);
|
|
80
81
|
|
|
82
|
+
if (this.deviceServices.has(uuid)) {
|
|
83
|
+
this.log.warn(`Duplicate device name "${name}" detected. Each device must have a unique name.`);
|
|
84
|
+
}
|
|
85
|
+
|
|
81
86
|
let accessory = this.cachedAccessories.get(uuid);
|
|
82
87
|
const isNew = !accessory;
|
|
83
88
|
|
|
@@ -119,7 +124,7 @@ class MultipleSwitchPlatform {
|
|
|
119
124
|
|
|
120
125
|
// Create master switch if enabled
|
|
121
126
|
if (hasMaster) {
|
|
122
|
-
const MasterServiceClass = this.getServiceClass(device.
|
|
127
|
+
const MasterServiceClass = this.getServiceClass(device.switchType || 'outlet');
|
|
123
128
|
const masterService = accessory.addService(MasterServiceClass, 'Master', MASTER_SUBTYPE);
|
|
124
129
|
|
|
125
130
|
this.setServiceName(masterService, 'Master');
|
|
@@ -145,8 +150,16 @@ class MultipleSwitchPlatform {
|
|
|
145
150
|
|
|
146
151
|
services.set(subtype, service);
|
|
147
152
|
|
|
148
|
-
|
|
149
|
-
|
|
153
|
+
const ds = sw.defaultState;
|
|
154
|
+
if (ds === false || ds === 'off') {
|
|
155
|
+
accessory.context.switchStates[subtype] = false;
|
|
156
|
+
} else if (ds === true || ds === 'on') {
|
|
157
|
+
accessory.context.switchStates[subtype] = true;
|
|
158
|
+
} else {
|
|
159
|
+
// 'remember' or undefined — keep existing state; initialise to false on first run
|
|
160
|
+
if (accessory.context.switchStates[subtype] === undefined) {
|
|
161
|
+
accessory.context.switchStates[subtype] = false;
|
|
162
|
+
}
|
|
150
163
|
}
|
|
151
164
|
});
|
|
152
165
|
|
|
@@ -204,13 +217,19 @@ class MultipleSwitchPlatform {
|
|
|
204
217
|
}
|
|
205
218
|
|
|
206
219
|
scheduleAutoOff(accessory, service, sw, subtype) {
|
|
207
|
-
|
|
220
|
+
const timerKey = `${accessory.UUID}_${subtype}`;
|
|
221
|
+
if (this.autoOffTimers.has(timerKey)) {
|
|
222
|
+
clearTimeout(this.autoOffTimers.get(timerKey));
|
|
223
|
+
}
|
|
224
|
+
const timer = setTimeout(() => {
|
|
225
|
+
this.autoOffTimers.delete(timerKey);
|
|
208
226
|
if (accessory.context.switchStates[subtype]) {
|
|
209
227
|
accessory.context.switchStates[subtype] = false;
|
|
210
228
|
service.updateCharacteristic(this.Characteristic.On, false);
|
|
211
229
|
this.log.info(`[${sw.name}] auto-off after ${sw.delayOff}ms`);
|
|
212
230
|
}
|
|
213
231
|
}, sw.delayOff);
|
|
232
|
+
this.autoOffTimers.set(timerKey, timer);
|
|
214
233
|
}
|
|
215
234
|
|
|
216
235
|
getServiceClass(type) {
|
package/package.json
CHANGED