iobroker.yamaha 2.9.1 → 2.10.0
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/README.md +21 -12
- package/admin/i18n/de.json +3 -2
- package/admin/i18n/en.json +3 -2
- package/admin/i18n/es.json +3 -2
- package/admin/i18n/fr.json +3 -2
- package/admin/i18n/it.json +3 -2
- package/admin/i18n/nl.json +3 -2
- package/admin/i18n/pl.json +3 -2
- package/admin/i18n/pt.json +3 -2
- package/admin/i18n/ru.json +3 -2
- package/admin/i18n/uk.json +3 -2
- package/admin/i18n/zh-cn.json +3 -2
- package/admin/jsonConfig.json +13 -0
- package/admin/yamaha.svg +5 -4
- package/build/device-management-helpers.js +3 -2
- package/build/device-management-helpers.js.map +2 -2
- package/build/device-management.js +44 -43
- package/build/device-management.js.map +2 -2
- package/build/lib/attempt-device.js +8 -6
- package/build/lib/attempt-device.js.map +2 -2
- package/build/lib/catalog/value-coerce.js +15 -0
- package/build/lib/catalog/value-coerce.js.map +2 -2
- package/build/lib/device-controller.js +6 -5
- package/build/lib/device-controller.js.map +2 -2
- package/build/lib/device-type.js +47 -11
- package/build/lib/device-type.js.map +2 -2
- package/build/lib/lifecycle/device-supervisor.js +25 -2
- package/build/lib/lifecycle/device-supervisor.js.map +2 -2
- package/build/lib/types.js.map +1 -1
- package/build/lib/xml/command-mapper.js +5 -1
- package/build/lib/xml/command-mapper.js.map +2 -2
- package/build/lib/xml/device-controller.js +31 -5
- package/build/lib/xml/device-controller.js.map +2 -2
- package/build/lib/ynca/catalog.js +5 -1
- package/build/lib/ynca/catalog.js.map +2 -2
- package/build/lib/ynca/ynca-client.js +19 -1
- package/build/lib/ynca/ynca-client.js.map +2 -2
- package/build/lib/yxc/command-mapper.js +5 -1
- package/build/lib/yxc/command-mapper.js.map +2 -2
- package/build/lib/yxc/device-controller.js +90 -1
- package/build/lib/yxc/device-controller.js.map +2 -2
- package/build/lib/yxc/http-client.js +15 -2
- package/build/lib/yxc/http-client.js.map +2 -2
- package/build/lib/yxc/push-receiver.js +15 -3
- package/build/lib/yxc/push-receiver.js.map +2 -2
- package/build/lib/yxc/push.js +26 -1
- package/build/lib/yxc/push.js.map +2 -2
- package/build/main.js +95 -22
- package/build/main.js.map +2 -2
- package/io-package.json +29 -27
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -74,6 +74,27 @@ Details on all settings, the object tree and the ports the adapter uses are in t
|
|
|
74
74
|
Placeholder for the next version (at the beginning of the line):
|
|
75
75
|
### **WORK IN PROGRESS**
|
|
76
76
|
-->
|
|
77
|
+
### 2.10.0 (2026-09-15)
|
|
78
|
+
|
|
79
|
+
- (krobipd) Fixed: A receiver the search found is searched for again after it moved to another address — until now that only worked for receivers found at start-up
|
|
80
|
+
- (krobipd) Fixed: A receiver that is unplugged or switched off at the mains now shows as disconnected within about 90 seconds instead of staying green for many minutes
|
|
81
|
+
- (krobipd) Fixed: A MusicCast device that stops answering a command is checked right away and shown as disconnected — until now that took up to 15 minutes
|
|
82
|
+
- (krobipd) Fixed: On receivers without live updates, a value you write is confirmed as soon as the receiver took it, instead of up to five minutes later
|
|
83
|
+
- (krobipd) Fixed: A zone name you changed on an older receiver stays after a reconnect — until now the previous name came back
|
|
84
|
+
- (krobipd) Fixed: Deleting a device from its card while it is still connecting no longer leaves parts of its object tree behind
|
|
85
|
+
- (krobipd) Fixed: Writing false, off or 0 to a switch datapoint now switches it off — until now any text, even the word false, switched it on
|
|
86
|
+
- (krobipd) Improved: The history of a datapoint only records values the receiver actually changed — a restart or a lost connection no longer adds identical entries
|
|
87
|
+
- (krobipd) Improved: MusicCast live updates now start on their own once a port another program held at start-up becomes free — before, only a restart helped
|
|
88
|
+
- (krobipd) New: Device pictograms in the object tree and on the device cards — receiver, stereo receiver, speaker, soundbar or CD system, readable in every theme, also for a device that is off
|
|
89
|
+
- (krobipd) Changed: The device card shows a speaker symbol; with the percent switch on it also shows the current volume as a percentage. The pencil and magnifier markers are gone
|
|
90
|
+
- (krobipd) Fixed: The adapter logo is readable in the Admin's dark themes as well — until now its dark strokes vanished on a dark background
|
|
91
|
+
- (krobipd) Changed: The instance settings show the fixed MusicCast event port, so the Admin warns when a second instance on the same host would take it
|
|
92
|
+
|
|
93
|
+
### 2.9.2 (2026-09-12)
|
|
94
|
+
|
|
95
|
+
- (krobipd) New: The device card shows a 0–100 % badge while that receiver's volume is in percent, so you can tell the two scales apart at a glance
|
|
96
|
+
- (krobipd) Fixed: The percent setting is made in one place again — the device's edit dialog; the extra switch on the card showed the wrong position and is gone
|
|
97
|
+
|
|
77
98
|
### 2.9.1 (2026-09-12)
|
|
78
99
|
|
|
79
100
|
- (krobipd) Fixed: A receiver the network search found keeps its datapoints when you add a device by hand — they stay with their history and are marked offline instead of deleted
|
|
@@ -100,18 +121,6 @@ Details on all settings, the object tree and the ports the adapter uses are in t
|
|
|
100
121
|
- (krobipd) Changed: After this update every receiver is asked about its abilities once more, so the first start takes a little longer than usual
|
|
101
122
|
- (krobipd) Fixed: A DAB receiver no longer logs a warning on every tuner poll — the frequency datapoint was limited to the FM band while the receiver reported DAB frequencies
|
|
102
123
|
|
|
103
|
-
### 2.7.2 (2026-09-09)
|
|
104
|
-
|
|
105
|
-
- (krobipd) Fixed: The volume readout now follows the scale the receiver is actually showing, so a receiver set to numbers no longer reports them as decibels.
|
|
106
|
-
- (krobipd) New: Inputs now appear under the names the receiver carries for them, so a socket named “Apple TV” reads that way instead of HDMI1.
|
|
107
|
-
- (krobipd) Changed: After this update every receiver is asked about its abilities once more, so the first start takes a little longer than usual.
|
|
108
|
-
|
|
109
|
-
### 2.7.1 (2026-09-09)
|
|
110
|
-
|
|
111
|
-
- (krobipd) Improved: The first connection asks a receiver only what its generation can answer, so zones, trigger sockets and per-input settings follow the device, not a catalogue.
|
|
112
|
-
- (krobipd) New: A datapoint the receiver reveals later now appears at once — a function it starts answering, a value it reports for the first time, a status field it begins delivering.
|
|
113
|
-
- (krobipd) Changed: A datapoint that never carried a value is removed only after two starts confirm it, so a receiver left in standby no longer loses datapoints it still has.
|
|
114
|
-
|
|
115
124
|
[Older changelogs can be found there](CHANGELOG_OLD.md)
|
|
116
125
|
|
|
117
126
|
## History
|
package/admin/i18n/de.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"invalidName": "Dieser Name kann nicht verwendet werden",
|
|
16
16
|
"networkInterfaceLabel": "Netzwerk-Interface",
|
|
17
17
|
"networkInterfaceTooltip": "Netzwerk-Interface für LAN-Discovery und -Steuerung. Standard (0.0.0.0 = alle Interfaces) beibehalten, außer dieser Host hat mehrere Netzwerk-Interfaces.",
|
|
18
|
+
"pushPortLabel": "MusicCast-Ereignisport",
|
|
19
|
+
"pushPortTooltip": "UDP-Port, an den MusicCast-Geräte ihre Änderungen melden. Vom Protokoll festgelegt; eine zweite Instanz auf diesem Host kann ihn nicht mitbenutzen.",
|
|
18
20
|
"dmAdd": "Yamaha-Gerät hinzufügen",
|
|
19
21
|
"dmEdit": "Bearbeiten",
|
|
20
22
|
"dmDelete": "Löschen",
|
|
@@ -608,6 +610,5 @@
|
|
|
608
610
|
"discoveryAuto": "Automatisch (solange die Liste leer ist)",
|
|
609
611
|
"discoveryAlways": "Immer",
|
|
610
612
|
"discoveryNever": "Nie",
|
|
611
|
-
"
|
|
612
|
-
"sourceDiscovered": "Im Netzwerk gefunden"
|
|
613
|
+
"volumeDeviceScale": "Lautstärke in der Skala des Receivers"
|
|
613
614
|
}
|
package/admin/i18n/en.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"invalidName": "This name cannot be used",
|
|
16
16
|
"networkInterfaceLabel": "Network Interface",
|
|
17
17
|
"networkInterfaceTooltip": "Select the network interface for LAN discovery and control. Keep the default (0.0.0.0 = all interfaces) unless this host has multiple network interfaces.",
|
|
18
|
+
"pushPortLabel": "MusicCast event port",
|
|
19
|
+
"pushPortTooltip": "UDP port on which MusicCast devices push their changes. Fixed by the protocol; a second instance on this host cannot share it.",
|
|
18
20
|
"dmAdd": "Add Yamaha device",
|
|
19
21
|
"dmEdit": "Edit",
|
|
20
22
|
"dmDelete": "Delete",
|
|
@@ -608,6 +610,5 @@
|
|
|
608
610
|
"discoveryAuto": "Automatically (while the list is empty)",
|
|
609
611
|
"discoveryAlways": "Always",
|
|
610
612
|
"discoveryNever": "Never",
|
|
611
|
-
"
|
|
612
|
-
"sourceDiscovered": "Found on the network"
|
|
613
|
+
"volumeDeviceScale": "Volume in the receiver's own scale"
|
|
613
614
|
}
|
package/admin/i18n/es.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"invalidName": "Este nombre no se puede utilizar",
|
|
16
16
|
"networkInterfaceLabel": "Interfaz de red",
|
|
17
17
|
"networkInterfaceTooltip": "Selecciona la interfaz de red para el descubrimiento y control LAN. Mantén el valor predeterminado (0.0.0.0 = todas) salvo que este host tenga varias interfaces.",
|
|
18
|
+
"pushPortLabel": "Puerto de eventos MusicCast",
|
|
19
|
+
"pushPortTooltip": "Puerto UDP en el que los dispositivos MusicCast envían sus cambios. Fijado por el protocolo; una segunda instancia en este host no puede compartirlo.",
|
|
18
20
|
"dmAdd": "Añadir dispositivo Yamaha",
|
|
19
21
|
"dmEdit": "Editar",
|
|
20
22
|
"dmDelete": "Eliminar",
|
|
@@ -608,6 +610,5 @@
|
|
|
608
610
|
"discoveryAuto": "Automático (mientras la lista esté vacía)",
|
|
609
611
|
"discoveryAlways": "Siempre",
|
|
610
612
|
"discoveryNever": "Nunca",
|
|
611
|
-
"
|
|
612
|
-
"sourceDiscovered": "Encontrado en la red"
|
|
613
|
+
"volumeDeviceScale": "Volumen en la escala propia del receptor"
|
|
613
614
|
}
|
package/admin/i18n/fr.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"invalidName": "Ce nom ne peut pas être utilisé",
|
|
16
16
|
"networkInterfaceLabel": "Interface réseau",
|
|
17
17
|
"networkInterfaceTooltip": "Sélectionnez l'interface réseau pour la découverte et le contrôle LAN. Conservez la valeur par défaut (0.0.0.0 = toutes) sauf si cet hôte possède plusieurs interfaces.",
|
|
18
|
+
"pushPortLabel": "Port d'événements MusicCast",
|
|
19
|
+
"pushPortTooltip": "Port UDP sur lequel les appareils MusicCast envoient leurs changements. Fixé par le protocole ; une seconde instance sur cet hôte ne peut pas le partager.",
|
|
18
20
|
"dmAdd": "Ajouter un appareil Yamaha",
|
|
19
21
|
"dmEdit": "Modifier",
|
|
20
22
|
"dmDelete": "Supprimer",
|
|
@@ -608,6 +610,5 @@
|
|
|
608
610
|
"discoveryAuto": "Automatique (tant que la liste est vide)",
|
|
609
611
|
"discoveryAlways": "Toujours",
|
|
610
612
|
"discoveryNever": "Jamais",
|
|
611
|
-
"
|
|
612
|
-
"sourceDiscovered": "Trouvé sur le réseau"
|
|
613
|
+
"volumeDeviceScale": "Volume dans l'échelle propre du récepteur"
|
|
613
614
|
}
|
package/admin/i18n/it.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"invalidName": "Questo nome non può essere utilizzato",
|
|
16
16
|
"networkInterfaceLabel": "Interfaccia di rete",
|
|
17
17
|
"networkInterfaceTooltip": "Seleziona l'interfaccia di rete per il rilevamento e il controllo LAN. Mantieni il valore predefinito (0.0.0.0 = tutte) a meno che questo host non abbia più interfacce.",
|
|
18
|
+
"pushPortLabel": "Porta eventi MusicCast",
|
|
19
|
+
"pushPortTooltip": "Porta UDP su cui i dispositivi MusicCast inviano le loro modifiche. Fissata dal protocollo; una seconda istanza su questo host non può condividerla.",
|
|
18
20
|
"dmAdd": "Aggiungi dispositivo Yamaha",
|
|
19
21
|
"dmEdit": "Modifica",
|
|
20
22
|
"dmDelete": "Elimina",
|
|
@@ -608,6 +610,5 @@
|
|
|
608
610
|
"discoveryAuto": "Automatico (finché l'elenco è vuoto)",
|
|
609
611
|
"discoveryAlways": "Sempre",
|
|
610
612
|
"discoveryNever": "Mai",
|
|
611
|
-
"
|
|
612
|
-
"sourceDiscovered": "Trovato in rete"
|
|
613
|
+
"volumeDeviceScale": "Volume nella scala propria del ricevitore"
|
|
613
614
|
}
|
package/admin/i18n/nl.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"invalidName": "Deze naam kan niet worden gebruikt",
|
|
16
16
|
"networkInterfaceLabel": "Netwerkinterface",
|
|
17
17
|
"networkInterfaceTooltip": "Selecteer de netwerkinterface voor LAN-detectie en -besturing. Behoud de standaard (0.0.0.0 = alle interfaces), tenzij deze host meerdere netwerkinterfaces heeft.",
|
|
18
|
+
"pushPortLabel": "MusicCast-gebeurtenispoort",
|
|
19
|
+
"pushPortTooltip": "UDP-poort waarop MusicCast-apparaten hun wijzigingen melden. Vastgelegd door het protocol; een tweede instantie op deze host kan hem niet delen.",
|
|
18
20
|
"dmAdd": "Yamaha-apparaat toevoegen",
|
|
19
21
|
"dmEdit": "Bewerken",
|
|
20
22
|
"dmDelete": "Verwijderen",
|
|
@@ -608,6 +610,5 @@
|
|
|
608
610
|
"discoveryAuto": "Automatisch (zolang de lijst leeg is)",
|
|
609
611
|
"discoveryAlways": "Altijd",
|
|
610
612
|
"discoveryNever": "Nooit",
|
|
611
|
-
"
|
|
612
|
-
"sourceDiscovered": "Gevonden in het netwerk"
|
|
613
|
+
"volumeDeviceScale": "Volume in de eigen schaal van de receiver"
|
|
613
614
|
}
|
package/admin/i18n/pl.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"invalidName": "Ta nazwa nie może być użyta",
|
|
16
16
|
"networkInterfaceLabel": "Interfejs sieciowy",
|
|
17
17
|
"networkInterfaceTooltip": "Wybierz interfejs sieciowy do wykrywania i sterowania LAN. Pozostaw domyślny (0.0.0.0 = wszystkie), chyba że ten host ma wiele interfejsów.",
|
|
18
|
+
"pushPortLabel": "Port zdarzeń MusicCast",
|
|
19
|
+
"pushPortTooltip": "Port UDP, na który urządzenia MusicCast wysyłają swoje zmiany. Ustalony przez protokół; druga instancja na tym hoście nie może go współdzielić.",
|
|
18
20
|
"dmAdd": "Dodaj urządzenie Yamaha",
|
|
19
21
|
"dmEdit": "Edytuj",
|
|
20
22
|
"dmDelete": "Usuń",
|
|
@@ -608,6 +610,5 @@
|
|
|
608
610
|
"discoveryAuto": "Automatycznie (dopóki lista jest pusta)",
|
|
609
611
|
"discoveryAlways": "Zawsze",
|
|
610
612
|
"discoveryNever": "Nigdy",
|
|
611
|
-
"
|
|
612
|
-
"sourceDiscovered": "Znalezione w sieci"
|
|
613
|
+
"volumeDeviceScale": "Głośność we własnej skali amplitunera"
|
|
613
614
|
}
|
package/admin/i18n/pt.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"invalidName": "Este nome não pode ser usado",
|
|
16
16
|
"networkInterfaceLabel": "Interface de rede",
|
|
17
17
|
"networkInterfaceTooltip": "Selecione a interface de rede para descoberta e controlo LAN. Mantenha o padrão (0.0.0.0 = todas) a menos que este host tenha várias interfaces.",
|
|
18
|
+
"pushPortLabel": "Porta de eventos MusicCast",
|
|
19
|
+
"pushPortTooltip": "Porta UDP na qual os dispositivos MusicCast enviam as suas alterações. Fixada pelo protocolo; uma segunda instância neste host não a pode partilhar.",
|
|
18
20
|
"dmAdd": "Adicionar dispositivo Yamaha",
|
|
19
21
|
"dmEdit": "Editar",
|
|
20
22
|
"dmDelete": "Eliminar",
|
|
@@ -608,6 +610,5 @@
|
|
|
608
610
|
"discoveryAuto": "Automático (enquanto a lista estiver vazia)",
|
|
609
611
|
"discoveryAlways": "Sempre",
|
|
610
612
|
"discoveryNever": "Nunca",
|
|
611
|
-
"
|
|
612
|
-
"sourceDiscovered": "Encontrado na rede"
|
|
613
|
+
"volumeDeviceScale": "Volume na escala própria do receptor"
|
|
613
614
|
}
|
package/admin/i18n/ru.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"invalidName": "Это имя нельзя использовать",
|
|
16
16
|
"networkInterfaceLabel": "Сетевой интерфейс",
|
|
17
17
|
"networkInterfaceTooltip": "Выберите сетевой интерфейс для обнаружения и управления по LAN. Оставьте значение по умолчанию (0.0.0.0 = все интерфейсы), если у хоста нет нескольких интерфейсов.",
|
|
18
|
+
"pushPortLabel": "Порт событий MusicCast",
|
|
19
|
+
"pushPortTooltip": "UDP-порт, на который устройства MusicCast отправляют свои изменения. Задан протоколом; второй экземпляр на этом хосте не может его использовать.",
|
|
18
20
|
"dmAdd": "Добавить устройство Yamaha",
|
|
19
21
|
"dmEdit": "Изменить",
|
|
20
22
|
"dmDelete": "Удалить",
|
|
@@ -608,6 +610,5 @@
|
|
|
608
610
|
"discoveryAuto": "Автоматически (пока список пуст)",
|
|
609
611
|
"discoveryAlways": "Всегда",
|
|
610
612
|
"discoveryNever": "Никогда",
|
|
611
|
-
"
|
|
612
|
-
"sourceDiscovered": "Найдено в сети"
|
|
613
|
+
"volumeDeviceScale": "Громкость в собственной шкале ресивера"
|
|
613
614
|
}
|
package/admin/i18n/uk.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"invalidName": "Це ім'я не можна використовувати",
|
|
16
16
|
"networkInterfaceLabel": "Мережевий інтерфейс",
|
|
17
17
|
"networkInterfaceTooltip": "Виберіть мережевий інтерфейс для виявлення та керування по LAN. Залиште типове значення (0.0.0.0 = всі інтерфейси), якщо хост не має кількох інтерфейсів.",
|
|
18
|
+
"pushPortLabel": "Порт подій MusicCast",
|
|
19
|
+
"pushPortTooltip": "UDP-порт, на який пристрої MusicCast надсилають свої зміни. Визначений протоколом; другий екземпляр на цьому хості не може його використовувати.",
|
|
18
20
|
"dmAdd": "Додати пристрій Yamaha",
|
|
19
21
|
"dmEdit": "Редагувати",
|
|
20
22
|
"dmDelete": "Видалити",
|
|
@@ -608,6 +610,5 @@
|
|
|
608
610
|
"discoveryAuto": "Автоматично (доки список порожній)",
|
|
609
611
|
"discoveryAlways": "Завжди",
|
|
610
612
|
"discoveryNever": "Ніколи",
|
|
611
|
-
"
|
|
612
|
-
"sourceDiscovered": "Знайдено в мережі"
|
|
613
|
+
"volumeDeviceScale": "Гучність у власній шкалі ресивера"
|
|
613
614
|
}
|
package/admin/i18n/zh-cn.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"invalidName": "此名称不可用",
|
|
16
16
|
"networkInterfaceLabel": "网络接口",
|
|
17
17
|
"networkInterfaceTooltip": "选择用于局域网发现和控制的网络接口。除非此主机有多个网络接口,否则请保留默认值(0.0.0.0 = 所有接口)。",
|
|
18
|
+
"pushPortLabel": "MusicCast 事件端口",
|
|
19
|
+
"pushPortTooltip": "MusicCast 设备向此 UDP 端口推送其变化。由协议固定;同一主机上的第二个实例无法共用。",
|
|
18
20
|
"dmAdd": "添加 Yamaha 设备",
|
|
19
21
|
"dmEdit": "编辑",
|
|
20
22
|
"dmDelete": "删除",
|
|
@@ -608,6 +610,5 @@
|
|
|
608
610
|
"discoveryAuto": "自动(列表为空时)",
|
|
609
611
|
"discoveryAlways": "始终",
|
|
610
612
|
"discoveryNever": "从不",
|
|
611
|
-
"
|
|
612
|
-
"sourceDiscovered": "网络中发现"
|
|
613
|
+
"volumeDeviceScale": "音量采用接收机自身的刻度"
|
|
613
614
|
}
|
package/admin/jsonConfig.json
CHANGED
|
@@ -81,6 +81,19 @@
|
|
|
81
81
|
"lg": 6,
|
|
82
82
|
"xl": 6
|
|
83
83
|
},
|
|
84
|
+
"port": {
|
|
85
|
+
"type": "port",
|
|
86
|
+
"label": "pushPortLabel",
|
|
87
|
+
"tooltip": "pushPortTooltip",
|
|
88
|
+
"disabled": true,
|
|
89
|
+
"min": 41100,
|
|
90
|
+
"max": 41100,
|
|
91
|
+
"xs": 12,
|
|
92
|
+
"sm": 12,
|
|
93
|
+
"md": 6,
|
|
94
|
+
"lg": 6,
|
|
95
|
+
"xl": 6
|
|
96
|
+
},
|
|
84
97
|
"xmlPollInterval": {
|
|
85
98
|
"newLine": true,
|
|
86
99
|
"type": "number",
|
package/admin/yamaha.svg
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none" stroke="#
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none" stroke="#78869a" stroke-width="22" stroke-linecap="round" stroke-linejoin="round">
|
|
2
2
|
<title>ioBroker.yamaha</title>
|
|
3
|
-
<!--
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
<!-- ONE fixed colour, never currentColor: the logo is embedded as <img>, which inherits
|
|
4
|
+
nothing. A media query would follow the OPERATING SYSTEM's scheme, not the Admin's
|
|
5
|
+
theme (the two disagree all the time), so the colour has to read on both: #78869a is
|
|
6
|
+
3.7:1 on white and 3.6–5.1:1 on the Admin's dark backgrounds (#303030 … #121212). -->
|
|
6
7
|
<circle cx="256" cy="256" r="234" />
|
|
7
8
|
<path d="M236,104 L236,212 L256,246 L276,212 L276,104" transform="rotate(0 256 256)" />
|
|
8
9
|
<path d="M236,104 L236,212 L256,246 L276,212 L276,104" transform="rotate(120 256 256)" />
|
|
@@ -54,8 +54,9 @@ function buildDeviceForm(usedIps) {
|
|
|
54
54
|
md: 6
|
|
55
55
|
},
|
|
56
56
|
// Per device, not per instance: the adapter serves several receivers, and one of them
|
|
57
|
-
// wanting percent says nothing about the others.
|
|
58
|
-
//
|
|
57
|
+
// wanting percent says nothing about the others. THE place to set it — 2.9.1 also had a
|
|
58
|
+
// switch control on the card itself, and one value reachable from two independently-read
|
|
59
|
+
// places is how the card came to show "off" while this checkbox showed "on".
|
|
59
60
|
volumeAsPercent: {
|
|
60
61
|
newLine: true,
|
|
61
62
|
type: "checkbox",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/device-management-helpers.ts"],
|
|
4
|
-
"sourcesContent": ["import type { JsonFormSchema } from \"@iobroker/dm-utils\";\nimport { t } from \"./lib/i18n\";\nimport { sanitizeId } from \"./lib/pure-helpers\";\nimport { TRANSPORT_LABELS } from \"./lib/ready-line\";\n\n/** Object-id segments the adapter reserves for its own tree \u2014 a device may not take them. */\nconst RESERVED_IDS = new Set([\"info\"]);\n/** IPv4 dotted-quad \u2014 the single source for both the frontend validator and the backend check. */\nconst IP_RE = /^(\\d{1,3}\\.){3}\\d{1,3}$/;\n\n/** The transports shown as card indicators \u2014 the single source shared with the ready-log line. */\nexport const TRANSPORTS = TRANSPORT_LABELS;\n\n/** One raw manual device row from `native.devices` (the name is optional). */\nexport interface ManualRow {\n /** Display name, or empty/absent when the user left it blank. */\n name?: string;\n /** The device IP address. */\n ip: string;\n}\n\n/** A running device as shown on a card, plus which source it came from (routes edit/delete). */\nexport interface CardDevice {\n /** The id-safe device id (object-tree path segment). */\n id: string;\n /** The device IP address. */\n ip: string;\n /** The card header name. */\n name: string;\n /** Where it lives: the manual `native.devices` table, or the auto-discovery store. */\n source: \"manual\" | \"discovered\";\n}\n\n/**\n * The id the object tree uses for a manual row: its name, or the ip when the name is blank.\n *\n * @param row the manual device row\n * @returns the id-safe device id\n */\nexport function rowId(row: ManualRow): string {\n return sanitizeId(row.name && row.name.length > 0 ? row.name : row.ip);\n}\n\n/**\n * The add/edit form for one receiver: a display name, the IP address and whether its volume\n * datapoints read 0\u2013100 %. The IP field\n * carries a live validator against a valid dotted-quad that is not already in use (the OK\n * button greys out on a clash). Labels are resolved translation objects so the embedded\n * form is language-correct.\n *\n * @param usedIps the IPs taken by OTHER devices (the edited device excluded)\n * @returns the jsonConfig panel schema for one device\n */\nexport function buildDeviceForm(usedIps: readonly string[]): JsonFormSchema {\n const ipList = JSON.stringify([...usedIps]);\n return {\n type: \"panel\",\n items: {\n name: {\n type: \"text\",\n label: t(\"columnName\"),\n sm: 12,\n md: 6,\n },\n ip: {\n type: \"text\",\n label: t(\"columnIp\"),\n validator: `!!(data.ip && ${IP_RE.toString()}.test(data.ip)) && !${ipList}.includes(data.ip)`,\n validatorErrorText: t(\"invalidIp\"),\n validatorNoSaveOnError: true,\n sm: 12,\n md: 6,\n },\n // Per device, not per instance: the adapter serves several receivers, and one of them\n // wanting percent says nothing about the others.
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,kBAAkB;AAClB,0BAA2B;AAC3B,wBAAiC;AAGjC,MAAM,eAAe,oBAAI,IAAI,CAAC,MAAM,CAAC;AAErC,MAAM,QAAQ;AAGP,MAAM,aAAa;AA4BnB,SAAS,MAAM,KAAwB;AAC5C,aAAO,gCAAW,IAAI,QAAQ,IAAI,KAAK,SAAS,IAAI,IAAI,OAAO,IAAI,EAAE;AACvE;AAYO,SAAS,gBAAgB,SAA4C;AAC1E,QAAM,SAAS,KAAK,UAAU,CAAC,GAAG,OAAO,CAAC;AAC1C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,MACL,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,WAAO,eAAE,YAAY;AAAA,QACrB,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,MACA,IAAI;AAAA,QACF,MAAM;AAAA,QACN,WAAO,eAAE,UAAU;AAAA,QACnB,WAAW,iBAAiB,MAAM,SAAS,CAAC,uBAAuB,MAAM;AAAA,QACzE,wBAAoB,eAAE,WAAW;AAAA,QACjC,wBAAwB;AAAA,QACxB,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import type { JsonFormSchema } from \"@iobroker/dm-utils\";\nimport { t } from \"./lib/i18n\";\nimport { sanitizeId } from \"./lib/pure-helpers\";\nimport { TRANSPORT_LABELS } from \"./lib/ready-line\";\n\n/** Object-id segments the adapter reserves for its own tree \u2014 a device may not take them. */\nconst RESERVED_IDS = new Set([\"info\"]);\n/** IPv4 dotted-quad \u2014 the single source for both the frontend validator and the backend check. */\nconst IP_RE = /^(\\d{1,3}\\.){3}\\d{1,3}$/;\n\n/** The transports shown as card indicators \u2014 the single source shared with the ready-log line. */\nexport const TRANSPORTS = TRANSPORT_LABELS;\n\n/** One raw manual device row from `native.devices` (the name is optional). */\nexport interface ManualRow {\n /** Display name, or empty/absent when the user left it blank. */\n name?: string;\n /** The device IP address. */\n ip: string;\n}\n\n/** A running device as shown on a card, plus which source it came from (routes edit/delete). */\nexport interface CardDevice {\n /** The id-safe device id (object-tree path segment). */\n id: string;\n /** The device IP address. */\n ip: string;\n /** The card header name. */\n name: string;\n /** Where it lives: the manual `native.devices` table, or the auto-discovery store. */\n source: \"manual\" | \"discovered\";\n}\n\n/**\n * The id the object tree uses for a manual row: its name, or the ip when the name is blank.\n *\n * @param row the manual device row\n * @returns the id-safe device id\n */\nexport function rowId(row: ManualRow): string {\n return sanitizeId(row.name && row.name.length > 0 ? row.name : row.ip);\n}\n\n/**\n * The add/edit form for one receiver: a display name, the IP address and whether its volume\n * datapoints read 0\u2013100 %. The IP field\n * carries a live validator against a valid dotted-quad that is not already in use (the OK\n * button greys out on a clash). Labels are resolved translation objects so the embedded\n * form is language-correct.\n *\n * @param usedIps the IPs taken by OTHER devices (the edited device excluded)\n * @returns the jsonConfig panel schema for one device\n */\nexport function buildDeviceForm(usedIps: readonly string[]): JsonFormSchema {\n const ipList = JSON.stringify([...usedIps]);\n return {\n type: \"panel\",\n items: {\n name: {\n type: \"text\",\n label: t(\"columnName\"),\n sm: 12,\n md: 6,\n },\n ip: {\n type: \"text\",\n label: t(\"columnIp\"),\n validator: `!!(data.ip && ${IP_RE.toString()}.test(data.ip)) && !${ipList}.includes(data.ip)`,\n validatorErrorText: t(\"invalidIp\"),\n validatorNoSaveOnError: true,\n sm: 12,\n md: 6,\n },\n // Per device, not per instance: the adapter serves several receivers, and one of them\n // wanting percent says nothing about the others. THE place to set it \u2014 2.9.1 also had a\n // switch control on the card itself, and one value reachable from two independently-read\n // places is how the card came to show \"off\" while this checkbox showed \"on\".\n volumeAsPercent: {\n newLine: true,\n type: \"checkbox\",\n label: t(\"volumeAsPercent\"),\n help: t(\"volumeAsPercent_help\"),\n sm: 12,\n md: 12,\n },\n },\n } as unknown as JsonFormSchema;\n}\n\n/**\n * A duplicate-IP/name or invalid-value clash against the other rows, as a ready-to-show\n * message \u2014 the backend safety net behind the form validator (the dialog validator may not\n * fire in every admin version; this never lets a bad row through). Each failure mode gets\n * its own message: a malformed IP is not the same problem as a name that is reserved or\n * already taken \u2014 reporting \"invalid IP\" for a name clash sends the user hunting in the\n * wrong field.\n *\n * @param rows the current manual rows\n * @param candidate the row being added/edited\n * @param exceptIndex the row position to ignore (the row being edited), or -1\n * @returns a translated clash message, or null when the row is fine\n */\nexport function findClash(\n rows: readonly ManualRow[],\n candidate: ManualRow,\n exceptIndex: number,\n): ioBroker.StringOrTranslated | null {\n if (!IP_RE.test(candidate.ip)) {\n return t(\"invalidIp\");\n }\n const id = rowId(candidate);\n if (id === \"\" || RESERVED_IDS.has(id)) {\n return t(\"invalidName\");\n }\n for (let i = 0; i < rows.length; i++) {\n if (i === exceptIndex) {\n continue;\n }\n if (rows[i].ip === candidate.ip || rowId(rows[i]) === id) {\n return t(\"duplicateDevice\");\n }\n }\n return null;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,kBAAkB;AAClB,0BAA2B;AAC3B,wBAAiC;AAGjC,MAAM,eAAe,oBAAI,IAAI,CAAC,MAAM,CAAC;AAErC,MAAM,QAAQ;AAGP,MAAM,aAAa;AA4BnB,SAAS,MAAM,KAAwB;AAC5C,aAAO,gCAAW,IAAI,QAAQ,IAAI,KAAK,SAAS,IAAI,IAAI,OAAO,IAAI,EAAE;AACvE;AAYO,SAAS,gBAAgB,SAA4C;AAC1E,QAAM,SAAS,KAAK,UAAU,CAAC,GAAG,OAAO,CAAC;AAC1C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,MACL,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,WAAO,eAAE,YAAY;AAAA,QACrB,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,MACA,IAAI;AAAA,QACF,MAAM;AAAA,QACN,WAAO,eAAE,UAAU;AAAA,QACnB,WAAW,iBAAiB,MAAM,SAAS,CAAC,uBAAuB,MAAM;AAAA,QACzE,wBAAoB,eAAE,WAAW;AAAA,QACjC,wBAAwB;AAAA,QACxB,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,iBAAiB;AAAA,QACf,SAAS;AAAA,QACT,MAAM;AAAA,QACN,WAAO,eAAE,iBAAiB;AAAA,QAC1B,UAAM,eAAE,sBAAsB;AAAA,QAC9B,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACF;AAeO,SAAS,UACd,MACA,WACA,aACoC;AACpC,MAAI,CAAC,MAAM,KAAK,UAAU,EAAE,GAAG;AAC7B,eAAO,eAAE,WAAW;AAAA,EACtB;AACA,QAAM,KAAK,MAAM,SAAS;AAC1B,MAAI,OAAO,MAAM,aAAa,IAAI,EAAE,GAAG;AACrC,eAAO,eAAE,aAAa;AAAA,EACxB;AACA,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAI,MAAM,aAAa;AACrB;AAAA,IACF;AACA,QAAI,KAAK,CAAC,EAAE,OAAO,UAAU,MAAM,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI;AACxD,iBAAO,eAAE,iBAAiB;AAAA,IAC5B;AAAA,EACF;AACA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -59,17 +59,15 @@ class YamahaDeviceManagement extends import_dm_utils.DeviceManagement {
|
|
|
59
59
|
await this.adapter.extendForeignObjectAsync(this.objId, { native: { devices: rows } });
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* Whether one device's volume datapoints read 0–100 %, for the edit dialog to prefill.
|
|
63
63
|
*
|
|
64
64
|
* @param deviceId the id-safe device id
|
|
65
|
-
* @returns
|
|
65
|
+
* @returns true when this device is set to percent
|
|
66
66
|
*/
|
|
67
|
-
async
|
|
67
|
+
async volumeAsPercentOf(deviceId) {
|
|
68
68
|
var _a;
|
|
69
69
|
const node = await this.adapter.getForeignObjectAsync(`${this.adapter.namespace}.${deviceId}`);
|
|
70
|
-
|
|
71
|
-
const now = Date.now();
|
|
72
|
-
return { val: on, ack: true, ts: now, lc: now, from: `system.adapter.${this.adapter.namespace}` };
|
|
70
|
+
return ((_a = node == null ? void 0 : node.native) == null ? void 0 : _a.volumeAsPercent) === true;
|
|
73
71
|
}
|
|
74
72
|
/**
|
|
75
73
|
* Set one device's percent switch. The value lives at the DEVICE object — one place the card,
|
|
@@ -127,17 +125,19 @@ class YamahaDeviceManagement extends import_dm_utils.DeviceManagement {
|
|
|
127
125
|
* @param context the load context
|
|
128
126
|
*/
|
|
129
127
|
async loadDevices(context) {
|
|
130
|
-
var _a;
|
|
128
|
+
var _a, _b;
|
|
131
129
|
for (const card of await this.cards()) {
|
|
132
130
|
const [model, node] = await Promise.all([
|
|
133
131
|
this.adapter.getForeignStateAsync(`${this.adapter.namespace}.${card.id}.info.model`),
|
|
134
132
|
this.adapter.getForeignObjectAsync(`${this.adapter.namespace}.${card.id}`)
|
|
135
133
|
]);
|
|
136
134
|
const label = typeof ((_a = node == null ? void 0 : node.common) == null ? void 0 : _a.name) === "string" ? node.common.name : void 0;
|
|
135
|
+
const percent = ((_b = node == null ? void 0 : node.native) == null ? void 0 : _b.volumeAsPercent) === true;
|
|
137
136
|
context.addDevice(
|
|
138
137
|
this.toDeviceInfo(
|
|
139
138
|
label && label !== card.id ? { ...card, name: label } : card,
|
|
140
|
-
typeof (model == null ? void 0 : model.val) === "string" ? model.val : void 0
|
|
139
|
+
typeof (model == null ? void 0 : model.val) === "string" ? model.val : void 0,
|
|
140
|
+
percent
|
|
141
141
|
)
|
|
142
142
|
);
|
|
143
143
|
}
|
|
@@ -145,14 +145,16 @@ class YamahaDeviceManagement extends import_dm_utils.DeviceManagement {
|
|
|
145
145
|
/**
|
|
146
146
|
* Build one device card: the live model, the IP as the identifier line, a connection
|
|
147
147
|
* status, and one indicator per connected protocol (hidden while that protocol is not
|
|
148
|
-
* connected).
|
|
149
|
-
*
|
|
148
|
+
* connected). The live values read from the device's own `info.*` states. Every card can be
|
|
149
|
+
* edited, a discovered one included: that is how a found receiver is given the fixed address
|
|
150
|
+
* the user just assigned it (see {@link editDevice}).
|
|
150
151
|
*
|
|
151
152
|
* @param card the running device
|
|
152
153
|
* @param model the device's reported model name, for the device-class icon
|
|
154
|
+
* @param percent whether this device's volume datapoints read 0–100 %, for the badge
|
|
153
155
|
* @returns the card descriptor
|
|
154
156
|
*/
|
|
155
|
-
toDeviceInfo(card, model) {
|
|
157
|
+
toDeviceInfo(card, model, percent = false) {
|
|
156
158
|
const base = `${this.adapter.namespace}.${card.id}`;
|
|
157
159
|
const del = {
|
|
158
160
|
id: "delete",
|
|
@@ -175,47 +177,46 @@ class YamahaDeviceManagement extends import_dm_utils.DeviceManagement {
|
|
|
175
177
|
status: {
|
|
176
178
|
connection: { stateId: `${base}.info.connection`, mapping: { true: "connected", false: "disconnected" } }
|
|
177
179
|
},
|
|
178
|
-
// No icon on the transports:
|
|
179
|
-
//
|
|
180
|
-
// colour carries it; `hideIfEmpty` shows
|
|
180
|
+
// No icon on the transports: dm-gui-components renders exactly 17 `fa-*` names (its own
|
|
181
|
+
// whitelist, `getFaIcon`) and inline `data:image/svg+xml` URLs — anything else is a "?".
|
|
182
|
+
// The transport label as text plus a green "on" colour carries it; `hideIfEmpty` shows
|
|
183
|
+
// only the protocols this device is connected over. Own glyphs go in as data URLs, drawn
|
|
184
|
+
// with `currentColor` so they take the indicator's colour (see device-type.ts).
|
|
181
185
|
indicators: [
|
|
182
|
-
// Where the address came from. The adapter treats the two differently — only a device
|
|
183
|
-
// the search found is looked for again when it drops off — so the card says which it is.
|
|
184
|
-
{
|
|
185
|
-
id: "device-source",
|
|
186
|
-
value: true,
|
|
187
|
-
icon: card.source === "manual" ? "fa-pencil" : "fa-search",
|
|
188
|
-
tooltip: (0, import_i18n.t)(card.source === "manual" ? "sourceManual" : "sourceDiscovered"),
|
|
189
|
-
color: "primary",
|
|
190
|
-
order: 10
|
|
191
|
-
},
|
|
192
186
|
...import_device_management_helpers.TRANSPORTS.map((tr) => ({
|
|
193
187
|
id: `transport-${tr.id}`,
|
|
194
188
|
value: { stateId: `${base}.info.transports.${tr.id}` },
|
|
195
189
|
text: tr.label,
|
|
196
190
|
colorOn: "ok",
|
|
197
191
|
hideIfEmpty: true
|
|
198
|
-
}))
|
|
192
|
+
})),
|
|
193
|
+
// The percent setting is SET in the edit dialog, but it has to be READABLE at a glance —
|
|
194
|
+
// otherwise the only way to find out what a receiver's volume datapoints carry is to open
|
|
195
|
+
// a dialog. With percent on, the glyph is a speaker with a percent sign and the main
|
|
196
|
+
// zone's live volume stands under it as "42 %"; in the device's own scale the plain
|
|
197
|
+
// speaker stands alone. Always shown (`hideIfEmpty: false` — 0 % is a value), and given
|
|
198
|
+
// a colour for BOTH states so the glyph does not grey out at the bottom of the scale.
|
|
199
|
+
// Deliberately not clickable: the dialog stays the one place that sets it.
|
|
200
|
+
{
|
|
201
|
+
id: "volume",
|
|
202
|
+
icon: (0, import_device_type.volumeIndicatorIcon)(percent),
|
|
203
|
+
value: percent ? { stateId: `${base}.volume` } : true,
|
|
204
|
+
...percent ? { showValue: true, unit: "%" } : {},
|
|
205
|
+
hideIfEmpty: false,
|
|
206
|
+
color: "primary",
|
|
207
|
+
colorOn: "primary",
|
|
208
|
+
tooltip: (0, import_i18n.t)(percent ? "volumeAsPercent" : "volumeDeviceScale"),
|
|
209
|
+
order: 20
|
|
210
|
+
}
|
|
199
211
|
],
|
|
200
212
|
// Edit on every card: a device the search found can be given the fixed address the user
|
|
201
213
|
// just assigned it, which makes it a manual device (see editDevice).
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
id: "volumeAsPercent",
|
|
209
|
-
type: "switch",
|
|
210
|
-
label: (0, import_i18n.t)("volumeAsPercent"),
|
|
211
|
-
description: (0, import_i18n.t)("volumeAsPercent_help"),
|
|
212
|
-
getStateHandler: async (deviceId) => this.volumePercentState(deviceId),
|
|
213
|
-
handler: async (deviceId, _controlId, state) => {
|
|
214
|
-
await this.applyVolumePercent(deviceId, state === true);
|
|
215
|
-
return this.volumePercentState(deviceId);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
]
|
|
214
|
+
// The percent switch is NOT a second control on the card: it lives in the edit dialog,
|
|
215
|
+
// next to name and address, because it is a decision about the device and not something
|
|
216
|
+
// flipped in passing. 2.9.1 had it in both places — the card control showed the wrong
|
|
217
|
+
// position while the dialog showed the right one, and two ways to set one value is one
|
|
218
|
+
// too many (krobi 2026-09-12: "für was hast du den das doppelt gemoppelt?").
|
|
219
|
+
actions: [edit, del]
|
|
219
220
|
};
|
|
220
221
|
}
|
|
221
222
|
/**
|
|
@@ -290,7 +291,7 @@ class YamahaDeviceManagement extends import_dm_utils.DeviceManagement {
|
|
|
290
291
|
}
|
|
291
292
|
const node = await this.adapter.getForeignObjectAsync(`${this.adapter.namespace}.${cardId}`);
|
|
292
293
|
const shownName = typeof ((_a = node == null ? void 0 : node.common) == null ? void 0 : _a.name) === "string" && node.common.name !== cardId ? node.common.name : card.name;
|
|
293
|
-
const percent =
|
|
294
|
+
const percent = await this.volumeAsPercentOf(cardId);
|
|
294
295
|
const data = await context.showForm(
|
|
295
296
|
(0, import_device_management_helpers.buildDeviceForm)(cards.filter((entry) => entry.id !== cardId).map((entry) => entry.ip)),
|
|
296
297
|
{ title: (0, import_i18n.t)("dmEditTitle"), data: { name: shownName, ip: card.ip, volumeAsPercent: percent } }
|