diff --git a/CODEOWNERS b/CODEOWNERS
index c8e6a7be309a7..3645013951239 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -159,6 +159,7 @@
/bundles/org.openhab.binding.hpprinter/ @cossey
/bundles/org.openhab.binding.http/ @J-N-K
/bundles/org.openhab.binding.hue/ @cweitkamp @andrewfg
+/bundles/org.openhab.binding.huesync/ @pgfeller
/bundles/org.openhab.binding.hydrawise/ @digitaldan
/bundles/org.openhab.binding.hyperion/ @tavalin
/bundles/org.openhab.binding.iammeter/ @lewei50
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index d2496ca8f2772..f2684cc5cd208 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -796,6 +796,11 @@
org.openhab.binding.hue
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.huesync
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.hydrawise
diff --git a/bundles/org.openhab.binding.huesync/NOTICE b/bundles/org.openhab.binding.huesync/NOTICE
new file mode 100644
index 0000000000000..38d625e349232
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the openHAB project.
+
+* Project home: https://www.openhab.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/openhab/openhab-addons
diff --git a/bundles/org.openhab.binding.huesync/README.md b/bundles/org.openhab.binding.huesync/README.md
new file mode 100644
index 0000000000000..9e95c5136794f
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/README.md
@@ -0,0 +1,220 @@
+# hueSync Binding
+
+
+This binding integrates the [Play HDMI Sync Box](https://www.philips-hue.com/en-us/p/hue-philips-hue-play-hdmi-sync-box/046677579753) into openHAB.
+The integration happens directly through the Hue [HDMI Sync Box API](https://developers.meethue.com/develop/hue-entertainment/hue-hdmi-sync-box-api/).
+
+- [hueSync Binding](#huesync-binding)
+ - [Supported Things](#supported-things)
+ - [Discovery](#discovery)
+ - [Configuration](#configuration)
+ - [Thing(s)](#things)
+ - [Channels](#channels)
+ - [Firmware Information](#firmware-information)
+ - [HDMI connections \[in|out\]](#hdmi-connections-inout)
+ - [Commands](#commands)
+ - [Example Configuration](#example-configuration)
+ - [huesyncbox.things](#huesyncboxthings)
+ - [huesyncbox.items](#huesyncboxitems)
+ - [example.sitemap](#examplesitemap)
+
+## Supported Things
+
+This binding provides only one thing type: **`box`**.
+Each thing will represent a Hue Play HDMI sync box.
+
+## Discovery
+
+The binding supports auto discovery using [mDNS](https://en.wikipedia.org/wiki/Multicast_DNS) to find devices in the local network.
+
+Make sure the device is connected to the network (the LED on the Sync Box is white or red).
+If the LED is blinking blue, you need to setup the device using the official [Hue Sync App](https://www.philips-hue.com/en-in/explore-hue/propositions/entertainment/hue-sync).
+
+If the device is not discovered you can check if it is properly configured and discoverable:
+
+
+
+ Linux (Ubuntu based distributions)
+
+ ```bash
+ $ avahi-browse --resolve _huesync._tcp
+ + wlp0s20f3 IPv4 HueSyncBox-XXXXXXXXXXX _huesync._tcp local
+ = wlp0s20f3 IPv4 HueSyncBox-XXXXXXXXXXX _huesync._tcp local
+ hostname = [XXXXXXXXXXX.local]
+ address = [192.168.0.12]
+ port = [443]
+ txt = ["name=Sync Box" "devicetype=HSB1" "uniqueid=XXXXXXXXXXX" "path=/api"]
+ ```
+
+
+
+
+mDNS uses link-local multicast addresses; its scope is limited to a single physical or logical LAN ([Layer 3](https://en.wikipedia.org/wiki/OSI_model#Layer_3:_Network_layer")).
+If your device is not automatically discovered, create a thing and manually configure the "host" parameter.
+
+To communicate with the sync box, you need to couple the thing with the hardware (registration).
+The thing will start this process automatically.
+To complete the registration you just press the "coupling" button on the sync box for 3 seconds.:
+
+![Device Registration](doc/device_registration.png)
+
+For special use cases it is possible to configure the id and token manually in the **advanced configuration** settings section.
+
+## Configuration
+
+### Thing(s)
+
+| Name | Type | Description | Default | Required | Advanced |
+| -------------------- | ------- | --------------------------------- | ------- | -------- | -------- |
+| host | text | IP address of the device | N/A | yes | no |
+| port | integer | Port of the HDMI Sync Box. | 443 | yes | yes |
+| registrationId | text | Application Registration Id | N/A | no | yes |
+| apiAccessToken | text | API Access Token | N/A | no | yes |
+| statusUpdateInterval | integer | Status Update Interval in seconds | 10 | yes | yes |
+
+## Channels
+
+### Firmware Information
+
+Information about the installed device firmware and available updates.
+
+| Channel | Type | Read/Write | Description |
+| ------------------ | ------ | ---------- | --------------------------------- |
+| firmware | String | R | Installed firmware version |
+| available-firmware | String | R | Latest available firmware version |
+
+### HDMI connections [in\|out]
+
+Information about a HDMI input connection.
+
+| Channel | Type | Read/Write | Description |
+| ------- | ------ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| type | String | R | Friendly Type generic video game music xbox playstation nintendoswitch phone desktop laptop appletv roku shield chromecast firetv diskplayer settopbox satellite avreceiver soundbar hdmiswitch |
+| status | String | R | Device connection status unplugged plugged linked unknown |
+| name | String | R | Friendly Name |
+| mode | String | R | Mode video game music powersave passthrough |
+
+### Commands
+
+| Channel | Type | Read/Write | Description |
+| ----------- | -------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| mode | String | R/W | Hue Sync operation mode video game music powersave passthrough |
+| hdmi-source | Switch | R/W | Source |
+| sync-active | Switch | R/W | Synchronization OFF in case of powersave or passthrough mode, and ON in case of video, game or music mode. When changed from OFF to ON, it will start syncing in last used mode for current source. When changed from ON to OFF, will set passthrough mode.
|
+| brightness | Number:Dimensionless | R/W | Brightness
0 = max reduction 100 = no brightness reduction/boost compared to input 200 = max boost
|
+
+## Example Configuration
+
+### huesyncbox.things
+
+```java
+Thing huesync:box:LivingRoom "Philips Hue HDMI Sync Box, LivingRoom" [
+ host="192.168.2.115",
+ httpPollingInterval=60,
+ apiAccessToken="yourTokenGoesHere=",
+ registrationId="8",
+ port=443,
+ statusUpdateInterval=10
+]
+```
+
+### huesyncbox.items
+
+Both item and sitemap configuration example use the `iconify` support for the `firmware` as well as `input1` and `input2`.
+Those icons loaded if needed from the internet and not suited for a pure offline setup.
+The other items use the `classic` icons.
+Read the documentation about the offline provider for `iconify` icons, or use the `classic` icons bundled with openHAB.
+
+```java
+// Firmware
+Group firmware "Firmware Information"
+String firmware_version "Current Firmware" (firmware) {channel="huesync:box:LivingRoom:device-firmware#firmware"}
+String latest_firmware_version "Latest Firmware" (firmware) {channel="huesync:box:LivingRoom:device-firmware#available-firmware"}
+
+//HDMI Input 1
+Group hdmi_in1 "HDMI 1"
+String friendly_name_input1 "Friendly Name" (hdmi_in1) {channel="huesync:box:LivingRoom:device-hdmi-in-1#name"}
+String friendly_type_input1 "Friendly Type" (hdmi_in1) {channel="huesync:box:LivingRoom:device-hdmi-in-1#type"}
+String hdmi_connection_status_input1 "Connection Status" (hdmi_in1) {channel="huesync:box:LivingRoom:device-hdmi-in-1#status"}
+String last_sync_mode_input1 "Last Sync Mode " (hdmi_in1) {channel="huesync:box:LivingRoom:device-hdmi-in-1#mode"}
+
+//HDMI Input 2
+Group hdmi_in2
+String friendly_name_input2 "Friendly Name" {channel="huesync:box:LivingRoom:device-hdmi-in-2#name"}
+String friendly_type_input2 "Friendly Type" {channel="huesync:box:LivingRoom:device-hdmi-in-2#type"}
+String hdmi_connection_status_input2 "Connection Status" {channel="huesync:box:LivingRoom:device-hdmi-in-2#status"}
+String last_sync_mode_input2 "Last Sync Mode" {channel="huesync:box:LivingRoom:device-hdmi-in-2#mode"}
+
+//HDMI Input 3
+String friendly_name_input3 "Friendly Name" {channel="huesync:box:LivingRoom:device-hdmi-in-3#name"}
+String friendly_type_input3 "Friendly Type" {channel="huesync:box:LivingRoom:device-hdmi-in-3#type"}
+String hdmi_connection_status_input3 "Connection Status" {channel="huesync:box:LivingRoom:device-hdmi-in-3#status"}
+String last_sync_mode_input3 "Last Sync Mode" {channel="huesync:box:LivingRoom:device-hdmi-in-3#mode"}
+
+//HDMI Input 4
+String friendly_name_input4 "Friendly Name" {channel="huesync:box:LivingRoom:device-hdmi-in-4#name"}
+String friendly_type_input4 "Friendly Type" {channel="huesync:box:LivingRoom:device-hdmi-in-4#type"}
+String hdmi_connection_status_input4 "Connection Status" {channel="huesync:box:LivingRoom:device-hdmi-in-4#status"}
+String last_sync_mode_input4 "Last Sync Mode" {channel="huesync:box:LivingRoom:device-hdmi-in-4#mode"}
+
+
+//HDMI output
+String friendly_name_output "Friendly Name" {channel="huesync:box:LivingRoom:device-hdmi-out#name"}
+String friendly_type_output "Friendly Type" {channel="huesync:box:LivingRoom:device-hdmi-out#type"}
+String hdmi_connection_status_output "Connection Status" {channel="huesync:box:LivingRoom:device-hdmi-out#status"}
+String last_sync_mode_output "Last Sync Mode" {channel="huesync:box:LivingRoom:device-hdmi-out#mode"}
+
+//Commands
+String huesync_mode "Mode" {channel="huesync:box:LivingRoom:device-commands#mode"}
+Switch sync_active "Sync active" {channel="huesync:box:LivingRoom:device-commands#sync-active"}
+Switch hdmi_active "HDMI active" {channel="huesync:box:LivingRoom:device-commands#hdmi-active"}
+String hdmi_source "HDMI Source" {channel="huesync:box:LivingRoom:device-commands#hdmi-source"}
+Dimmer huesync_brightness "Brightness" {channel="huesync:box:LivingRoom:device-commands#brightness"}
+
+```
+
+### example.sitemap
+
+```java
+sitemap demo label="Hue Sync Box" {
+ Frame {
+ Group item=firmware
+ }
+ Frame label="Commands" icon=settings {
+ Text item=huesync_mode
+ Text item=hdmi_active
+ Switch item=sync_active
+ Text item=hdmi_source
+ Buttongrid label="HDMI Source" staticIcon=player {
+ Button row=1 column=1 item=hdmi_source label="Source 1" stateless click=input1
+ Button row=2 column=1 item=hdmi_source label="Source 2" stateless click=input2
+ Button row=3 column=1 item=hdmi_source label="Source 3" stateless click=input3
+ Button row=4 column=1 item=hdmi_source label="Source 4" stateless click=input4
+ }
+ Selection item=hdmi_source mappings=[input1="Source 1", input2="Source 2", input3="Source 3", input3="Source 4"]
+
+ Slider item=huesync_brightness minValue=0 maxValue=200 step=10
+ }
+ Frame label="HDMI Inputs 1 & 2" icon="iconify:mdi:hdmi-port" {
+ Default item=hdmi_in1
+
+ Group item=hdmi_in2 label="HDMI 2" icon="iconify:mdi:hdmi-port" {
+ Default item=friendly_name_input2 icon="iconify:mdi:text"
+ Default item=friendly_type_input2 icon="iconify:mdi:devices"
+ Default item=hdmi_connection_status_input2 icon="iconify:mdi:connection"
+ Default item=last_sync_mode_input2 icon="iconify:mdi:multimedia"
+ }
+ }
+ Frame label="HDMI 3" icon=player {
+ Default item=friendly_name_input3
+ Default item=friendly_type_input3
+ Default item=hdmi_connection_status_input3
+ Default item=last_sync_mode_input3
+ }
+ Frame label="HDMI 4" icon=player {
+ Text item=friendly_name_input4
+ Text item=friendly_type_input4
+ Text item=hdmi_connection_status_input4
+ Text item=last_sync_mode_input4
+ }
+```
diff --git a/bundles/org.openhab.binding.huesync/doc/device_registration.png b/bundles/org.openhab.binding.huesync/doc/device_registration.png
new file mode 100644
index 0000000000000..1afa074091b4f
Binary files /dev/null and b/bundles/org.openhab.binding.huesync/doc/device_registration.png differ
diff --git a/bundles/org.openhab.binding.huesync/pom.xml b/bundles/org.openhab.binding.huesync/pom.xml
new file mode 100644
index 0000000000000..7ca10a423d798
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 4.3.0-SNAPSHOT
+
+
+ org.openhab.binding.huesync
+
+ openHAB Add-ons :: Bundles :: Hue Sync Box Binding
+
+
diff --git a/bundles/org.openhab.binding.huesync/src/main/feature/feature.xml b/bundles/org.openhab.binding.huesync/src/main/feature/feature.xml
new file mode 100644
index 0000000000000..3d292e7237ad3
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/feature/feature.xml
@@ -0,0 +1,10 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ openhab-transport-mdns
+ mvn:org.openhab.addons.bundles/org.openhab.binding.huesync/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HdmiChannels.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HdmiChannels.java
new file mode 100644
index 0000000000000..cb3355b1293d5
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HdmiChannels.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+public class HdmiChannels {
+ public String name;
+ public String type;
+ public String mode;
+ public String status;
+
+ public HdmiChannels(String name, String type, String mode, String status) {
+ this.name = name;
+ this.type = type;
+ this.mode = mode;
+ this.status = status;
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java
new file mode 100644
index 0000000000000..2c59c18b75936
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/HueSyncConstants.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link HueSyncConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+public class HueSyncConstants {
+ public static class ENDPOINTS {
+ public static final String DEVICE = "device";
+ public static final String REGISTRATIONS = "registrations";
+ public static final String HDMI = "hdmi";
+ public static final String EXECUTION = "execution";
+
+ public static class COMMANDS {
+ public static final String MODE = "mode";
+ public static final String SYNC = "syncActive";
+ public static final String HDMI = "hdmiActive";
+ public static final String SOURCE = "hdmiSource";
+ public static final String BRIGHTNESS = "brightness";
+ }
+ }
+
+ public static class CHANNELS {
+ public static class DEVICE {
+ public static class INFORMATION {
+ public static final String FIRMWARE = "device-firmware#firmware";
+ public static final String FIRMWARE_AVAILABLE = "device-firmware#available-firmware";
+ }
+ }
+
+ public static class COMMANDS {
+ public static final String MODE = "device-commands#mode";
+ public static final String SYNC = "device-commands#sync-active";
+ public static final String HDMI = "device-commands#hdmi-active";
+ public static final String SOURCE = "device-commands#hdmi-source";
+ public static final String BRIGHTNESS = "device-commands#brightness";
+ }
+
+ public static class HDMI {
+ public static final HdmiChannels IN_1 = new HdmiChannels("device-hdmi-in-1#name", "device-hdmi-in-1#type",
+ "device-hdmi-in-1#mode", "device-hdmi-in-1#status");
+ public static final HdmiChannels IN_2 = new HdmiChannels("device-hdmi-in-2#name", "device-hdmi-in-2#type",
+ "device-hdmi-in-2#mode", "device-hdmi-in-2#status");
+ public static final HdmiChannels IN_3 = new HdmiChannels("device-hdmi-in-3#name", "device-hdmi-in-3#type",
+ "device-hdmi-in-3#mode", "device-hdmi-in-3#status");
+ public static final HdmiChannels IN_4 = new HdmiChannels("device-hdmi-in-4#name", "device-hdmi-in-4#type",
+ "device-hdmi-in-4#mode", "device-hdmi-in-4#status");
+
+ public static final HdmiChannels OUT = new HdmiChannels("device-hdmi-out#name", "device-hdmi-out#type",
+ "device-hdmi-out#mode", "device-hdmi-out#status");
+ }
+ }
+
+ public static final String APPLICATION_NAME = "openHAB";
+
+ /** Minimal API Version required. Only apiLevel >= 7 is supported. */
+ public static final Integer MINIMAL_API_VERSION = 7;
+
+ public static final String BINDING_ID = "huesync";
+ public static final String THING_TYPE_ID = "box";
+ public static final ThingTypeUID THING_TYPE_UID = new ThingTypeUID(BINDING_ID, THING_TYPE_ID);
+
+ public static final String PARAMETER_HOST = "host";
+ public static final String PARAMETER_PORT = "port";
+
+ public static final Integer REGISTRATION_INITIAL_DELAY = 3;
+ public static final Integer REGISTRATION_INTERVAL = 1;
+
+ public static final String REGISTRATION_ID = "registrationId";
+ public static final String API_TOKEN = "apiAccessToken";
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDevice.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDevice.java
new file mode 100644
index 0000000000000..57a5ce5b3ed63
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDevice.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.device;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * HDMI Sync Box Device Information
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ *
+ * @see Hue
+ * HDMI Sync Box API
+ */
+@NonNullByDefault
+public class HueSyncDevice {
+ /** Friendly name of the device */
+ public @Nullable String name;
+ /** Device Type identifier */
+ public @Nullable String deviceType;
+ /**
+ * Capitalized hex string of the 6 byte / 12 characters device id without
+ * delimiters. Used as unique id on label, certificate common name, hostname
+ * etc.
+ */
+ public @Nullable String uniqueId;
+ /**
+ * Increased between firmware versions when api changes. Only apiLevel >= 7 is
+ * supported.
+ */
+ public int apiLevel = 0;
+ /**
+ * User readable version of the device firmware, starting with decimal major
+ * .minor .maintenance format e.g. “1.12.3”
+ */
+ public @Nullable String firmwareVersion;
+ /**
+ * Build number of the firmware. Unique for every build with newer builds
+ * guaranteed a higher number than older.
+ */
+ public int buildNumber = 0;
+
+ public boolean termsAgreed;
+
+ /** uninitialized, disconnected, lan, wan */
+ public @Nullable String wifiState;
+ public @Nullable String ipAddress;
+
+ public @Nullable HueSyncDeviceCapabilitiesInfo capabilities;
+
+ public boolean beta;
+ public boolean overheating;
+ public boolean bluetooth;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceCapabilitiesInfo.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceCapabilitiesInfo.java
new file mode 100644
index 0000000000000..8049f1557dbf4
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceCapabilitiesInfo.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.device;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * HDMI Sync Box Device Information Capabilities
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ *
+ * @see Hue
+ * HDMI Sync Box API
+ */
+@NonNullByDefault
+public class HueSyncDeviceCapabilitiesInfo {
+ /** The total number of IR codes configurable */
+ public int maxIrCodes;
+ /** The total number of Presets configurable */
+ public int maxPresets;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceDetailed.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceDetailed.java
new file mode 100644
index 0000000000000..12bb1ff358c6d
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceDetailed.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.device;
+
+import java.util.Date;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * HDMI Sync Box Device Information - Extended information (only available
+ * to registered clients)
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ *
+ * @see Hue
+ * HDMI Sync Box API
+ */
+@NonNullByDefault
+public class HueSyncDeviceDetailed extends HueSyncDevice {
+ public @Nullable HueSyncDeviceDetailedWifiInfo wifi;
+ public @Nullable HueSyncDeviceDetailedUpdateInfo update;
+
+ /** UTC time when last check for update was performed. */
+ public @Nullable Date lastCheckedUpdate;
+ /**
+ * Build number that is available to update to. Item is set to null when there
+ * is no update available.
+ */
+ public int updatableBuildNumber;
+ /**
+ * User readable version of the firmware the device can upgrade to. Item is set
+ * to null when there is no update available.
+ */
+ public @Nullable String updatableFirmwareVersion;
+ /**
+ * 1 = regular;
+ * 0 = off in powersave, passthrough or sync mode;
+ * 2 = dimmed in powersave or passthrough mode and off in sync mode
+ */
+ public int ledMode = -1;
+
+ /** none, doSoftwareRestart, doFirmwareUpdate */
+ public @Nullable String action;
+ public @Nullable String pushlink;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceDetailedUpdateInfo.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceDetailedUpdateInfo.java
new file mode 100644
index 0000000000000..73c99bb466b24
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceDetailedUpdateInfo.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.device;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * HDMI Sync Box Device Information - Automatic Firmware update
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ *
+ * @see Hue
+ * HDMI Sync Box API
+ */
+@NonNullByDefault
+public class HueSyncDeviceDetailedUpdateInfo {
+ /**
+ * Sync Box checks daily for a firmware update. If true, an available update
+ * will automatically be installed. This will be postponed if Sync Box is
+ * passing through content to the TV and being used.
+ */
+ public boolean autoUpdateEnabled;
+ /**
+ * TC hour when the automatic update will check and execute, values 0 – 23.
+ * Default is 10. Ideally this value should be set to 3AM according to user’s
+ * timezone.
+ */
+ public int autoUpdateTime;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceDetailedWifiInfo.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceDetailedWifiInfo.java
new file mode 100644
index 0000000000000..0c99f86acb5eb
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/device/HueSyncDeviceDetailedWifiInfo.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.device;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * HDMI Sync Box Device Information - Wifi connection information
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ *
+ * @see Hue
+ * HDMI Sync Box API
+ */
+@NonNullByDefault
+public class HueSyncDeviceDetailedWifiInfo {
+ /** Wifi SSID */
+ public @Nullable String ssid;
+ /**
+ * 0 = not connected;
+ * 1 = weak;
+ * 2 = fair;
+ * 3 = good;
+ * 4 = excellent
+ */
+ public int strength;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecution.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecution.java
new file mode 100644
index 0000000000000..88a51b78b0052
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecution.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.execution;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Root object for execution resource
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class HueSyncExecution {
+ private final Logger logger = LoggerFactory.getLogger(HueSyncExecution.class);
+
+ public static final List KNOWN_MODES = Collections
+ .unmodifiableList(Arrays.asList("powersave", "passthrough", "video", "game", "music"));
+
+ private @Nullable String mode;
+
+ /**
+ *
+ * @return powersave, passthrough, video, game, music
+ */
+ @JsonProperty("mode")
+ public @Nullable String getMode() {
+ return this.mode;
+ }
+
+ /**
+ *
+ * @apiNote More modes can be added in the future, so clients must gracefully
+ * handle modes they don’t recognize. If an unknown mode is received, a
+ * warning will be logged and mode will fallback to "unknown"
+ *
+ * @param mode powersave, passthrough, video, game, music
+ */
+ public void setMode(String mode) {
+ if (!HueSyncExecution.KNOWN_MODES.contains(mode)) {
+ logger.warn(
+ "device mode [{}] is not known by this version of the binding. Please open an issue to notify the maintainer(s). Fallback will be used. ",
+ mode);
+ }
+
+ this.mode = HueSyncExecution.KNOWN_MODES.contains(mode) ? mode : "unknown";
+ }
+
+ /**
+ * Reports `false` in case of `powersave` or `passthrough` mode, and `true` in case of `video`, `game`, or `music`
+ * mode.
+ * When changed from false to true, it will start syncing in last used mode for current source.
+ * When changed from true to false, will set passthrough mode.
+ */
+ public boolean syncActive;
+ /**
+ * Reports `false` in case of `powersave mode`, and true in case of `passthrough`, `video`, `game`, `music` mode.
+ * When changed from false to true, it will set passthrough mode. When changed from `true` to `false`, will set
+ * powersave mode.
+ */
+ public boolean hdmiActive;
+
+ /**
+ * Currently selected hdmi input: `input1`, `input2`, `input3,` `input4`
+ */
+ public @Nullable String hdmiSource;
+
+ public @Nullable String hueTarget;
+ public @Nullable String lastSyncMode;
+ public @Nullable String preset;
+
+ /**
+ * brightness:
+ * - Get, Put
+ * - number, uint
+ * - 0 ... 200 (100 = no brightness reduction/boost compared to input, 0 = max reduction, 200 = max boost)
+ */
+ public int brightness;
+
+ public @Nullable HueSyncExecutionVideo video;
+ public @Nullable HueSyncExecutionGame game;
+ public @Nullable HueSyncExecutionMusic music;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecutionGame.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecutionGame.java
new file mode 100644
index 0000000000000..f806d2efa425c
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecutionGame.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.execution;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class HueSyncExecutionGame {
+ public @Nullable String intensity;
+
+ public boolean backgroundLighting;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecutionMusic.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecutionMusic.java
new file mode 100644
index 0000000000000..d56a92782d4d5
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecutionMusic.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.execution;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class HueSyncExecutionMusic {
+ public @Nullable String intensity;
+ public @Nullable String palette;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecutionVideo.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecutionVideo.java
new file mode 100644
index 0000000000000..46cb01e574a91
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/execution/HueSyncExecutionVideo.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.execution;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class HueSyncExecutionVideo {
+ public @Nullable String intensity;
+
+ public boolean backgroundLighting;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/hdmi/HueSyncHdmi.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/hdmi/HueSyncHdmi.java
new file mode 100644
index 0000000000000..d4457528b4339
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/hdmi/HueSyncHdmi.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.hdmi;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class HueSyncHdmi {
+ public @Nullable HueSyncHdmiConnectionInfo input1;
+ public @Nullable HueSyncHdmiConnectionInfo input2;
+ public @Nullable HueSyncHdmiConnectionInfo input3;
+ public @Nullable HueSyncHdmiConnectionInfo input4;
+
+ public @Nullable HueSyncHdmiConnectionInfo output;
+
+ /** x @ – */
+ public @Nullable String contentSpecs;
+
+ /** Current content specs supported for video sync (video/game mode) */
+ public boolean videoSyncSupported;
+ /** Current content specs supported for audio sync (music mode) */
+ public boolean audioSyncSupported;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/hdmi/HueSyncHdmiConnectionInfo.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/hdmi/HueSyncHdmiConnectionInfo.java
new file mode 100644
index 0000000000000..1dac4c657e80c
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/hdmi/HueSyncHdmiConnectionInfo.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.hdmi;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ *
+ */
+@NonNullByDefault
+public class HueSyncHdmiConnectionInfo {
+ /** Friendly name, not empty */
+ public @Nullable String name;
+ /**
+ * Friendly type:
+ * generic,
+ * video,
+ * game,
+ * music,
+ * xbox,
+ * playstation,
+ * nintendoswitch,
+ * phone,
+ * desktop,
+ * laptop,
+ * appletv,
+ * roku,
+ * shield,
+ * chromecast,
+ * firetv,
+ * diskplayer,
+ * settopbox,
+ * satellite,
+ * avreceiver,
+ * soundbar,
+ * hdmiswitch
+ */
+ public @Nullable String type;
+ /**
+ * unplugged,
+ * plugged,
+ * linked,
+ * unknown
+ */
+ public @Nullable String status;
+ /**
+ * video,
+ * game,
+ * music
+ */
+ public @Nullable String lastSyncMode;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/registration/HueSyncRegistration.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/registration/HueSyncRegistration.java
new file mode 100644
index 0000000000000..89b2343dab412
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/registration/HueSyncRegistration.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.registration;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ */
+@NonNullByDefault
+public class HueSyncRegistration {
+ public String registrationId = "";
+ public String accessToken = "";
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/registration/HueSyncRegistrationRequest.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/registration/HueSyncRegistrationRequest.java
new file mode 100644
index 0000000000000..d7651a6872f89
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/api/dto/registration/HueSyncRegistrationRequest.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.api.dto.registration;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ */
+@NonNullByDefault
+public class HueSyncRegistrationRequest {
+ /** User recognizable name of registered application */
+ public @Nullable String appName;
+ /** User recognizable name of application instance. */
+ public @Nullable String instanceName;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/config/HueSyncConfiguration.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/config/HueSyncConfiguration.java
new file mode 100644
index 0000000000000..08c87729b2117
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/config/HueSyncConfiguration.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Binding configuration parameters,
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+public class HueSyncConfiguration {
+ public String registrationId = "";
+ public String apiAccessToken = "";
+ public String host = "";
+ public Integer port = 443;
+ public Integer statusUpdateInterval = 10;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncAuthenticationResult.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncAuthenticationResult.java
new file mode 100644
index 0000000000000..d33081d86afb4
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncAuthenticationResult.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.connection;
+
+import java.net.URI;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.api.Authentication.Result;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpHeader;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ */
+@NonNullByDefault
+public class HueSyncAuthenticationResult implements Result {
+ private final String token;
+ private final URI uri;
+
+ public HueSyncAuthenticationResult(URI uri, String token) {
+ this.uri = uri;
+ this.token = token;
+ }
+
+ public String getToken() {
+ return this.token;
+ }
+
+ @Override
+ public URI getURI() {
+ return this.uri;
+ }
+
+ @Override
+ public void apply(@Nullable Request request) {
+ if (request != null && !request.getHeaders().contains(HttpHeader.AUTHORIZATION)) {
+ request.header(HttpHeader.AUTHORIZATION, "Bearer " + this.token);
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java
new file mode 100644
index 0000000000000..afc670b364420
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncConnection.java
@@ -0,0 +1,248 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.connection;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.cert.CertificateException;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpResponseException;
+import org.eclipse.jetty.client.api.AuthenticationStore;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MimeTypes;
+import org.openhab.binding.huesync.internal.HueSyncConstants.ENDPOINTS;
+import org.openhab.binding.huesync.internal.exceptions.HueSyncConnectionException;
+import org.openhab.core.io.net.http.TlsTrustManagerProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ */
+@NonNullByDefault
+public class HueSyncConnection {
+ public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ /**
+ * Request format: The Sync Box API can be accessed locally via HTTPS on root level (port 443,
+ * /api/v1), resource level /api/v1/ and in some cases sub-resource level
+ * /api/v1//.
+ */
+ private static final String REQUEST_FORMAT = "https://%s:%s/%s/%s";
+ private static final String API = "api/v1";
+ private final Logger logger = LoggerFactory.getLogger(HueSyncConnection.class);
+
+ private final Integer port;
+ private final String host;
+
+ private final ServiceRegistration> tlsProviderService;
+ private final HttpClient httpClient;
+ private final URI deviceUri;
+
+ private Optional authentication = Optional.empty();
+
+ protected String registrationId = "";
+
+ public HueSyncConnection(HttpClient httpClient, String host, Integer port)
+ throws CertificateException, IOException, URISyntaxException {
+ this.host = host;
+ this.port = port;
+
+ this.deviceUri = new URI(String.format("https://%s:%s", this.host, this.port));
+
+ HueSyncTrustManagerProvider trustManagerProvider = new HueSyncTrustManagerProvider(this.host, this.port);
+ BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
+
+ this.tlsProviderService = context.registerService(TlsTrustManagerProvider.class.getName(), trustManagerProvider,
+ null);
+ this.httpClient = httpClient;
+ }
+
+ public void updateAuthentication(String id, String token) {
+ this.removeAuthentication();
+
+ if (!id.isBlank() && !token.isBlank()) {
+ this.registrationId = id;
+
+ this.authentication = Optional.of(new HueSyncAuthenticationResult(this.deviceUri, token));
+ this.httpClient.getAuthenticationStore().addAuthenticationResult(this.authentication.get());
+ }
+ }
+
+ // #region protected
+ protected @Nullable T executeRequest(HttpMethod method, String endpoint, String payload,
+ @Nullable Class type) {
+ try {
+ return this.processedResponse(this.executeRequest(method, endpoint, payload), type);
+ } catch (ExecutionException e) {
+ this.handleExecutionException(e);
+ } catch (InterruptedException | TimeoutException e) {
+ this.logger.warn("{}", e.getMessage());
+ }
+
+ return null;
+ }
+
+ protected @Nullable T executeGetRequest(String endpoint, Class type) {
+ try {
+ return this.processedResponse(this.executeGetRequest(endpoint), type);
+ } catch (ExecutionException e) {
+ this.handleExecutionException(e);
+ } catch (InterruptedException | TimeoutException e) {
+ this.logger.warn("{}", e.getMessage());
+ }
+
+ return null;
+ }
+
+ protected boolean isRegistered() {
+ return this.authentication.isPresent();
+ }
+
+ protected void unregisterDevice() {
+ if (this.isRegistered()) {
+ try {
+ String endpoint = ENDPOINTS.REGISTRATIONS + "/" + this.registrationId;
+ ContentResponse response = this.executeRequest(HttpMethod.DELETE, endpoint);
+
+ if (response.getStatus() == HttpStatus.OK_200) {
+ this.removeAuthentication();
+ }
+ } catch (InterruptedException | TimeoutException | ExecutionException e) {
+ this.logger.warn("{}", e.getMessage());
+ }
+ }
+ }
+
+ protected void dispose() {
+ this.tlsProviderService.unregister();
+ }
+ // #endregion
+
+ // #region private
+ private @Nullable T processedResponse(Response response, @Nullable Class type) {
+ int status = response.getStatus();
+ try {
+ /*
+ * 400 Invalid State: Registration in progress
+ *
+ * 401 Authentication failed: If credentials are missing or invalid, errors out. If
+ * credentials are missing, continues on to GET only the Configuration state when
+ * unauthenticated, to allow for device identification.
+ *
+ * 404 Invalid URI Path: Accessing URI path which is not supported
+ *
+ * 500 Internal: Internal errors like out of memory
+ */
+ switch (status) {
+ case HttpStatus.OK_200 -> {
+ return (type != null && (response instanceof ContentResponse))
+ ? this.deserialize(((ContentResponse) response).getContentAsString(), type)
+ : null;
+ }
+ case HttpStatus.BAD_REQUEST_400 -> this.logger.debug("registration in progress: no token received yet");
+ case HttpStatus.UNAUTHORIZED_401 -> {
+ this.authentication = Optional.empty();
+ throw new HueSyncConnectionException("@text/connection.invalid-login");
+ }
+ case HttpStatus.NOT_FOUND_404 -> this.logger.warn("invalid device URI or API endpoint");
+ case HttpStatus.INTERNAL_SERVER_ERROR_500 -> this.logger.warn("hue sync box server problem");
+ default -> this.logger.warn("unexpected HTTP status: {}", status);
+ }
+ } catch (HueSyncConnectionException e) {
+ this.logger.warn("{}", e.getMessage());
+ }
+ return null;
+ }
+
+ private @Nullable T deserialize(String json, Class type) {
+ try {
+ return OBJECT_MAPPER.readValue(json, type);
+ } catch (JsonProcessingException | NoClassDefFoundError e) {
+ this.logger.error("{}", e.getMessage());
+
+ return null;
+ }
+ }
+
+ private ContentResponse executeRequest(HttpMethod method, String endpoint)
+ throws InterruptedException, TimeoutException, ExecutionException {
+ return this.executeRequest(method, endpoint, "");
+ }
+
+ private ContentResponse executeGetRequest(String endpoint)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ String uri = String.format(REQUEST_FORMAT, this.host, this.port, API, endpoint);
+
+ return httpClient.GET(uri);
+ }
+
+ private ContentResponse executeRequest(HttpMethod method, String endpoint, String payload)
+ throws InterruptedException, TimeoutException, ExecutionException {
+ String uri = String.format(REQUEST_FORMAT, this.host, this.port, API, endpoint);
+
+ Request request = this.httpClient.newRequest(uri).method(method);
+
+ this.logger.trace("uri: {}", uri);
+ this.logger.trace("method: {}", method);
+ this.logger.trace("payload: {}", payload);
+
+ if (!payload.isBlank()) {
+ request.header(HttpHeader.CONTENT_TYPE, MimeTypes.Type.APPLICATION_JSON_UTF_8.toString())
+ .content(new StringContentProvider(payload));
+ }
+
+ return request.send();
+ }
+
+ private void handleExecutionException(ExecutionException e) {
+ this.logger.warn("{}", e.getMessage());
+
+ Throwable cause = e.getCause();
+ if (cause != null && cause instanceof HttpResponseException) {
+ processedResponse(((HttpResponseException) cause).getResponse(), null);
+ }
+ }
+
+ private void removeAuthentication() {
+ AuthenticationStore store = this.httpClient.getAuthenticationStore();
+ store.clearAuthenticationResults();
+ this.httpClient.setAuthenticationStore(store);
+
+ this.registrationId = "";
+ this.authentication = Optional.empty();
+ }
+
+ // #endregion
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java
new file mode 100644
index 0000000000000..c6e590dcc5db0
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncDeviceConnection.java
@@ -0,0 +1,197 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.connection;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.security.cert.CertificateException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.http.HttpMethod;
+import org.openhab.binding.huesync.internal.HueSyncConstants;
+import org.openhab.binding.huesync.internal.HueSyncConstants.ENDPOINTS;
+import org.openhab.binding.huesync.internal.api.dto.device.HueSyncDevice;
+import org.openhab.binding.huesync.internal.api.dto.device.HueSyncDeviceDetailed;
+import org.openhab.binding.huesync.internal.api.dto.execution.HueSyncExecution;
+import org.openhab.binding.huesync.internal.api.dto.hdmi.HueSyncHdmi;
+import org.openhab.binding.huesync.internal.api.dto.registration.HueSyncRegistration;
+import org.openhab.binding.huesync.internal.api.dto.registration.HueSyncRegistrationRequest;
+import org.openhab.binding.huesync.internal.config.HueSyncConfiguration;
+import org.openhab.binding.huesync.internal.exceptions.HueSyncConnectionException;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+/**
+ * Handles the connection to a Hue HDMI Sync Box using the official API.
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ */
+@NonNullByDefault
+public class HueSyncDeviceConnection {
+ private final Logger logger = LoggerFactory.getLogger(HueSyncDeviceConnection.class);
+
+ private final HueSyncConnection connection;
+
+ private final Map> deviceCommandExecutors = new HashMap<>();
+
+ public HueSyncDeviceConnection(HttpClient httpClient, HueSyncConfiguration configuration)
+ throws CertificateException, IOException, URISyntaxException {
+ this.connection = new HueSyncConnection(httpClient, configuration.host, configuration.port);
+
+ registerCommandHandlers();
+ }
+
+ // #region private
+
+ private void registerCommandHandlers() {
+ this.deviceCommandExecutors.put(HueSyncConstants.CHANNELS.COMMANDS.MODE,
+ defaultHandler(HueSyncConstants.ENDPOINTS.COMMANDS.MODE));
+ this.deviceCommandExecutors.put(HueSyncConstants.CHANNELS.COMMANDS.SOURCE,
+ defaultHandler(HueSyncConstants.ENDPOINTS.COMMANDS.SOURCE));
+ this.deviceCommandExecutors.put(HueSyncConstants.CHANNELS.COMMANDS.BRIGHTNESS,
+ defaultHandler(HueSyncConstants.ENDPOINTS.COMMANDS.BRIGHTNESS));
+ this.deviceCommandExecutors.put(HueSyncConstants.CHANNELS.COMMANDS.SYNC,
+ defaultHandler(HueSyncConstants.ENDPOINTS.COMMANDS.SYNC));
+ this.deviceCommandExecutors.put(HueSyncConstants.CHANNELS.COMMANDS.HDMI,
+ defaultHandler(HueSyncConstants.ENDPOINTS.COMMANDS.HDMI));
+ }
+
+ private Consumer defaultHandler(String endpoint) {
+ return command -> {
+ execute(endpoint, command);
+ };
+ }
+
+ private void execute(String key, Command command) {
+ this.logger.debug("Command executor: {} - {}", key, command);
+
+ if (!this.connection.isRegistered()) {
+ this.logger.warn("Device is not registered - ignoring command: {}", command);
+ return;
+ }
+
+ String value;
+
+ if (command instanceof QuantityType quantityCommand) {
+ value = Integer.toString(quantityCommand.intValue());
+ } else if (command instanceof OnOffType) {
+ value = command == OnOffType.ON ? "true" : "false";
+ } else if (command instanceof StringType) {
+ value = '"' + command.toString() + '"';
+ } else {
+ this.logger.warn("Type [{}] not supported by this connection", command.getClass().getCanonicalName());
+ return;
+ }
+
+ String json = String.format("{ \"%s\": %s }", key, value);
+
+ this.connection.executeRequest(HttpMethod.PUT, ENDPOINTS.EXECUTION, json, null);
+ }
+
+ // #endregion
+
+ public void executeCommand(Channel channel, Command command) {
+ String uid = channel.getUID().getAsString();
+ String commandId = channel.getUID().getId();
+
+ this.logger.debug("Channel UID: {} - Command: {}", uid, command.toFullString());
+
+ if (RefreshType.REFRESH.equals(command)) {
+ return;
+ }
+
+ if (this.deviceCommandExecutors.containsKey(commandId)) {
+ Objects.requireNonNull(this.deviceCommandExecutors.get(commandId)).accept(command);
+ } else {
+ this.logger.error("No executor registered for command {} - please report this as an issue", commandId);
+ }
+ }
+
+ public @Nullable HueSyncDevice getDeviceInfo() {
+ return this.connection.executeGetRequest(ENDPOINTS.DEVICE, HueSyncDevice.class);
+ }
+
+ public @Nullable HueSyncDeviceDetailed getDetailedDeviceInfo() {
+ return this.connection.isRegistered()
+ ? this.connection.executeRequest(HttpMethod.GET, ENDPOINTS.DEVICE, "", HueSyncDeviceDetailed.class)
+ : null;
+ }
+
+ public @Nullable HueSyncHdmi getHdmiInfo() {
+ return this.connection.isRegistered()
+ ? this.connection.executeRequest(HttpMethod.GET, ENDPOINTS.HDMI, "", HueSyncHdmi.class)
+ : null;
+ }
+
+ public @Nullable HueSyncExecution getExecutionInfo() {
+ return this.connection.isRegistered()
+ ? this.connection.executeRequest(HttpMethod.GET, ENDPOINTS.EXECUTION, "", HueSyncExecution.class)
+ : null;
+ }
+
+ public @Nullable HueSyncRegistration registerDevice(String id) throws HueSyncConnectionException {
+ if (!id.isBlank()) {
+ try {
+ HueSyncRegistrationRequest dto = new HueSyncRegistrationRequest();
+ dto.appName = HueSyncConstants.APPLICATION_NAME;
+ dto.instanceName = id;
+
+ String payload = HueSyncConnection.OBJECT_MAPPER.writeValueAsString(dto);
+
+ HueSyncRegistration registration = this.connection.executeRequest(HttpMethod.POST,
+ ENDPOINTS.REGISTRATIONS, payload, HueSyncRegistration.class);
+ if (registration != null) {
+ this.connection.updateAuthentication(id, registration.accessToken);
+
+ return registration;
+ }
+ } catch (JsonProcessingException e) {
+ this.logger.warn("{}", e.getMessage());
+ }
+ }
+ return null;
+ }
+
+ public boolean isRegistered() {
+ return this.connection.isRegistered();
+ }
+
+ public void unregisterDevice() {
+ this.connection.unregisterDevice();
+ }
+
+ public void dispose() {
+ this.connection.dispose();
+ }
+
+ public void updateConfiguration(HueSyncConfiguration config) {
+ this.logger.debug("Connection configuration update for device {}:{} - Registration Id [{}]", config.host,
+ config.port, config.registrationId);
+
+ this.connection.updateAuthentication(config.registrationId, config.apiAccessToken);
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncTrustManagerProvider.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncTrustManagerProvider.java
new file mode 100644
index 0000000000000..a6ad57a9dcefb
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/connection/HueSyncTrustManagerProvider.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.connection;
+
+import java.io.IOException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.X509ExtendedTrustManager;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.io.net.http.PEMTrustManager;
+import org.openhab.core.io.net.http.TlsTrustManagerProvider;
+
+/**
+ * Provides a {@link PEMTrustManager} to allow secure connections to a Hue HDMI
+ * Sync Box
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ */
+@NonNullByDefault
+public class HueSyncTrustManagerProvider implements TlsTrustManagerProvider {
+ private final String host;
+ private final Integer port;
+
+ private final X509ExtendedTrustManager trustManager;
+
+ public HueSyncTrustManagerProvider(String host, Integer port) throws IOException, CertificateException {
+ this.trustManager = PEMTrustManager.getInstanceFromServer("https://" + host);
+ this.port = port;
+ this.host = host;
+ }
+
+ @Override
+ public String getHostName() {
+ return this.host + ":" + this.port;
+ }
+
+ @Override
+ public X509ExtendedTrustManager getTrustManager() {
+ return this.trustManager;
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/discovery/HueSyncDiscoveryParticipant.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/discovery/HueSyncDiscoveryParticipant.java
new file mode 100644
index 0000000000000..2365a98b173dc
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/discovery/HueSyncDiscoveryParticipant.java
@@ -0,0 +1,143 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.discovery;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jmdns.ServiceInfo;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.huesync.internal.HueSyncConstants;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
+import org.openhab.core.thing.ThingRegistry;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link HueSyncDiscoveryParticipant} is responsible for discovering
+ * the remote huesync.boxes using mDNS discovery service.
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+@Component(service = MDNSDiscoveryParticipant.class, configurationPid = "mdnsdiscovery.huesync")
+public class HueSyncDiscoveryParticipant implements MDNSDiscoveryParticipant {
+ private final Logger logger = LoggerFactory.getLogger(HueSyncDiscoveryParticipant.class);
+
+ /**
+ *
+ * Match the hostname + identifier of the discovered huesync-box.
+ * Input is like "HueSyncBox-XXXXXXXXXXXX._huesync._tcp.local."
+ *
+ * @see·
+ * Service·Name·and·Transport·Protocol·Port·Number·Registry
+ */
+ private static final String SERVICE_TYPE = "_huesync._tcp.local.";
+
+ private boolean autoDiscoveryEnabled = true;
+
+ protected final ThingRegistry thingRegistry;
+
+ @Activate
+ public HueSyncDiscoveryParticipant(final @Reference ThingRegistry thingRegistry) {
+ this.thingRegistry = thingRegistry;
+ }
+
+ @Override
+ public Set getSupportedThingTypeUIDs() {
+ return Collections.singleton(HueSyncConstants.THING_TYPE_UID);
+ }
+
+ @Override
+ public String getServiceType() {
+ return SERVICE_TYPE;
+ }
+
+ @Override
+ public @Nullable DiscoveryResult createResult(ServiceInfo service) {
+ if (this.autoDiscoveryEnabled) {
+ ThingUID uid = getThingUID(service);
+ if (uid != null) {
+ try {
+ logger.debug("HDMI Sync Box {} discovered at {}:{}", service.getName(),
+ service.getHostAddresses()[0], service.getPort());
+
+ Map properties = new HashMap<>();
+
+ properties.put(HueSyncConstants.PARAMETER_HOST, service.getHostAddresses()[0]);
+ properties.put(HueSyncConstants.PARAMETER_PORT, service.getPort());
+
+ return DiscoveryResultBuilder.create(uid).withLabel(service.getName()).withProperties(properties)
+ .build();
+ } catch (Exception e) {
+ logger.debug("Unable to query device information for {}: {}", service.getQualifiedName(),
+ e.getMessage());
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public @Nullable ThingUID getThingUID(ServiceInfo service) {
+ String id = service.getName();
+ String[] addresses = service.getHostAddresses();
+
+ if (addresses.length == 0 || id == null || id.isBlank()) {
+ logger.debug("Incomplete mDNS device discovery information - {} ignored.",
+ id == null ? "[name: null]" : id);
+ return null;
+ }
+
+ return new ThingUID(HueSyncConstants.THING_TYPE_UID, id);
+ }
+
+ @Activate
+ protected void activate(ComponentContext componentContext) {
+ updateService(componentContext);
+ }
+
+ @Modified
+ protected void modified(ComponentContext componentContext) {
+ updateService(componentContext);
+ }
+
+ private void updateService(ComponentContext componentContext) {
+ String autoDiscoveryPropertyValue = (String) componentContext.getProperties()
+ .get(DiscoveryService.CONFIG_PROPERTY_BACKGROUND_DISCOVERY);
+
+ if (autoDiscoveryPropertyValue != null && !autoDiscoveryPropertyValue.isBlank()) {
+ boolean value = Boolean.parseBoolean(autoDiscoveryPropertyValue);
+ if (value != this.autoDiscoveryEnabled) {
+ logger.debug("{} update: {} - {}", DiscoveryService.CONFIG_PROPERTY_BACKGROUND_DISCOVERY,
+ autoDiscoveryPropertyValue, value);
+ this.autoDiscoveryEnabled = value;
+ }
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncApiException.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncApiException.java
new file mode 100644
index 0000000000000..c4096dfec7dfe
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncApiException.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.exceptions;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+public class HueSyncApiException extends HueSyncException {
+ private static final long serialVersionUID = 0L;
+
+ public HueSyncApiException(String message) {
+ super(message);
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncConnectionException.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncConnectionException.java
new file mode 100644
index 0000000000000..b42393814da1c
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncConnectionException.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.exceptions;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+public class HueSyncConnectionException extends HueSyncException {
+ private static final long serialVersionUID = 0L;
+
+ public HueSyncConnectionException(String message) {
+ super(message);
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncException.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncException.java
new file mode 100644
index 0000000000000..583d169e8b814
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncException.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.exceptions;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.huesync.internal.i18n.HueSyncLocalizer;
+
+/**
+ * Base class for all HueSyncExceptions
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ */
+@NonNullByDefault
+public abstract class HueSyncException extends Exception {
+ private static final long serialVersionUID = 0L;
+
+ public HueSyncException(String message) {
+ super(message.startsWith("@text") ? HueSyncLocalizer.getResourceString(message) : message);
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncTaskException.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncTaskException.java
new file mode 100644
index 0000000000000..c3c288ff7bbd5
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/exceptions/HueSyncTaskException.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.exceptions;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+public class HueSyncTaskException extends HueSyncException {
+ private static final long serialVersionUID = 0L;
+
+ public HueSyncTaskException(String message) {
+ super(message);
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/factory/HueSyncHandlerFactory.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/factory/HueSyncHandlerFactory.java
new file mode 100644
index 0000000000000..5ce343649ba1f
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/factory/HueSyncHandlerFactory.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.factory;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.security.cert.CertificateException;
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.huesync.internal.HueSyncConstants;
+import org.openhab.binding.huesync.internal.handler.HueSyncHandler;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link HueSyncHandlerFactory} is responsible for creating things and
+ * thing
+ * handlers.
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.huesync", service = ThingHandlerFactory.class)
+public class HueSyncHandlerFactory extends BaseThingHandlerFactory {
+
+ private final HttpClientFactory httpClientFactory;
+ private final Logger logger = LoggerFactory.getLogger(HueSyncHandlerFactory.class);
+
+ @Activate
+ public HueSyncHandlerFactory(@Reference final HttpClientFactory httpClientFactory) throws Exception {
+ this.httpClientFactory = httpClientFactory;
+ }
+
+ private static final Set SUPPORTED_THING_TYPES_UIDS = Collections
+ .singleton(HueSyncConstants.THING_TYPE_UID);
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+ if (HueSyncConstants.THING_TYPE_UID.equals(thingTypeUID)) {
+ try {
+ return new HueSyncHandler(thing, this.httpClientFactory);
+ } catch (IOException | URISyntaxException | CertificateException e) {
+ this.logger.warn("It was not possible to create a handler for {}: {}", thingTypeUID.getId(),
+ e.getMessage());
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java
new file mode 100644
index 0000000000000..22ba24b596328
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/HueSyncHandler.java
@@ -0,0 +1,349 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.handler;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.security.cert.CertificateException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
+import org.openhab.binding.huesync.internal.HdmiChannels;
+import org.openhab.binding.huesync.internal.HueSyncConstants;
+import org.openhab.binding.huesync.internal.api.dto.device.HueSyncDevice;
+import org.openhab.binding.huesync.internal.api.dto.device.HueSyncDeviceDetailed;
+import org.openhab.binding.huesync.internal.api.dto.execution.HueSyncExecution;
+import org.openhab.binding.huesync.internal.api.dto.hdmi.HueSyncHdmi;
+import org.openhab.binding.huesync.internal.api.dto.hdmi.HueSyncHdmiConnectionInfo;
+import org.openhab.binding.huesync.internal.api.dto.registration.HueSyncRegistration;
+import org.openhab.binding.huesync.internal.config.HueSyncConfiguration;
+import org.openhab.binding.huesync.internal.connection.HueSyncDeviceConnection;
+import org.openhab.binding.huesync.internal.exceptions.HueSyncApiException;
+import org.openhab.binding.huesync.internal.handler.tasks.HueSyncRegistrationTask;
+import org.openhab.binding.huesync.internal.handler.tasks.HueSyncUpdateTask;
+import org.openhab.binding.huesync.internal.handler.tasks.HueSyncUpdateTaskResult;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.io.net.http.HttpClientFactory;
+import org.openhab.core.library.types.DecimalType;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.StringType;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.State;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link HueSyncHandler} is responsible for handling commands, which are sent to one of the
+ * channels.
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+public class HueSyncHandler extends BaseThingHandler {
+ private static final String REGISTER = "Registration";
+ private static final String POLL = "Update";
+
+ private static final String PROPERTY_API_VERSION = "apiVersion";
+
+ private final Logger logger = LoggerFactory.getLogger(HueSyncHandler.class);
+
+ Map> tasks = new HashMap<>();
+
+ private Optional deviceInfo = Optional.empty();
+
+ private final HueSyncDeviceConnection connection;
+ private final HttpClient httpClient;
+
+ public HueSyncHandler(Thing thing, HttpClientFactory httpClientFactory)
+ throws CertificateException, IOException, URISyntaxException {
+ super(thing);
+
+ this.httpClient = httpClientFactory.getCommonHttpClient();
+
+ this.connection = new HueSyncDeviceConnection(this.httpClient, this.getConfigAs(HueSyncConfiguration.class));
+ }
+
+ // #region private
+ private Runnable initializeConnection() {
+ return () -> {
+ this.deviceInfo = Optional.ofNullable(this.connection.getDeviceInfo());
+ this.deviceInfo.ifPresent(info -> {
+ setProperty(Thing.PROPERTY_SERIAL_NUMBER, info.uniqueId != null ? info.uniqueId : "");
+ setProperty(Thing.PROPERTY_MODEL_ID, info.deviceType);
+ setProperty(Thing.PROPERTY_FIRMWARE_VERSION, info.firmwareVersion);
+
+ setProperty(HueSyncHandler.PROPERTY_API_VERSION, String.format("%d", info.apiLevel));
+
+ try {
+ this.checkCompatibility();
+ } catch (HueSyncApiException e) {
+ this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
+ } finally {
+ this.startTasks();
+ }
+ });
+ };
+ }
+
+ private void stopTask(@Nullable ScheduledFuture> task) {
+ if (task == null || task.isCancelled() || task.isDone()) {
+ return;
+ }
+
+ task.cancel(true);
+ }
+
+ private @Nullable ScheduledFuture> executeTask(Runnable task, long initialDelay, long interval) {
+ return scheduler.scheduleWithFixedDelay(task, initialDelay, interval, TimeUnit.SECONDS);
+ }
+
+ private void startTasks() {
+ this.stopTasks();
+
+ this.connection.updateConfiguration(this.getConfigAs(HueSyncConfiguration.class));
+
+ Runnable task = null;
+ String id = this.connection.isRegistered() ? POLL : REGISTER;
+
+ this.logger.debug("startTasks - [{}]", id);
+
+ long initialDelay = 0;
+ long interval = 0;
+
+ switch (id) {
+ case POLL -> {
+ initialDelay = 0;
+ interval = this.getConfigAs(HueSyncConfiguration.class).statusUpdateInterval;
+
+ this.updateStatus(ThingStatus.ONLINE);
+
+ task = new HueSyncUpdateTask(this.connection, this.deviceInfo.get(),
+ deviceStatus -> this.handleUpdate(deviceStatus));
+ }
+ case REGISTER -> {
+ initialDelay = HueSyncConstants.REGISTRATION_INITIAL_DELAY;
+ interval = HueSyncConstants.REGISTRATION_INTERVAL;
+
+ this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
+ "@text/thing.config.huesync.box.registration");
+
+ task = new HueSyncRegistrationTask(this.connection, this.deviceInfo.get(),
+ registration -> this.handleRegistration(registration));
+ }
+ }
+
+ if (task != null) {
+ logger.debug("Starting task [{}]", id);
+ this.tasks.put(id, this.executeTask(task, initialDelay, interval));
+ }
+ }
+
+ private void stopTasks() {
+ logger.debug("Stopping {} task(s): {}", this.tasks.values().size(), String.join(",", this.tasks.keySet()));
+
+ this.tasks.values().forEach(task -> this.stopTask(task));
+ this.tasks.clear();
+
+ this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
+ "@text/thing.config.huesync.box.registration");
+ }
+
+ private void handleUpdate(@Nullable HueSyncUpdateTaskResult dto) {
+ try {
+ HueSyncUpdateTaskResult update = Optional.ofNullable(dto).get();
+
+ try {
+ this.updateFirmwareInformation(Optional.ofNullable(update.deviceStatus).get());
+ } catch (NoSuchElementException e) {
+ this.logMissingUpdateInformation("device");
+ }
+
+ this.updateHdmiInformation(Optional.ofNullable(update.hdmiStatus).get());
+ this.updateExecutionInformation(Optional.ofNullable(update.execution).get());
+ } catch (NoSuchElementException e) {
+ Configuration configuration = this.editConfiguration();
+
+ configuration.put(HueSyncConstants.REGISTRATION_ID, "");
+ configuration.put(HueSyncConstants.API_TOKEN, "");
+
+ this.updateConfiguration(configuration);
+
+ this.startTasks();
+ }
+ }
+
+ private void logMissingUpdateInformation(String api) {
+ this.logger.warn("Device information - {} status missing", api);
+ }
+
+ private void updateHdmiInformation(HueSyncHdmi hdmiStatus) {
+ updateHdmiStatus(HueSyncConstants.CHANNELS.HDMI.IN_1, hdmiStatus.input1);
+ updateHdmiStatus(HueSyncConstants.CHANNELS.HDMI.IN_2, hdmiStatus.input2);
+ updateHdmiStatus(HueSyncConstants.CHANNELS.HDMI.IN_3, hdmiStatus.input3);
+ updateHdmiStatus(HueSyncConstants.CHANNELS.HDMI.IN_4, hdmiStatus.input4);
+
+ updateHdmiStatus(HueSyncConstants.CHANNELS.HDMI.OUT, hdmiStatus.output);
+ }
+
+ private void updateHdmiStatus(HdmiChannels channels, @Nullable HueSyncHdmiConnectionInfo hdmiStatusInfo) {
+ if (hdmiStatusInfo != null) {
+ this.updateState(channels.name, new StringType(hdmiStatusInfo.name));
+ this.updateState(channels.type, new StringType(hdmiStatusInfo.type));
+ this.updateState(channels.mode, new StringType(hdmiStatusInfo.lastSyncMode));
+ this.updateState(channels.status, new StringType(hdmiStatusInfo.status));
+ }
+ }
+
+ private void updateFirmwareInformation(HueSyncDeviceDetailed deviceStatus) {
+ State firmwareState = new StringType(deviceStatus.firmwareVersion);
+ State firmwareAvailableState = new StringType(
+ deviceStatus.updatableFirmwareVersion != null ? deviceStatus.updatableFirmwareVersion
+ : deviceStatus.firmwareVersion);
+
+ setProperty(Thing.PROPERTY_FIRMWARE_VERSION, deviceStatus.firmwareVersion);
+ setProperty(HueSyncHandler.PROPERTY_API_VERSION, String.format("%d", deviceStatus.apiLevel));
+
+ this.updateState(HueSyncConstants.CHANNELS.DEVICE.INFORMATION.FIRMWARE, firmwareState);
+ this.updateState(HueSyncConstants.CHANNELS.DEVICE.INFORMATION.FIRMWARE_AVAILABLE, firmwareAvailableState);
+ }
+
+ private void updateExecutionInformation(HueSyncExecution executionStatus) {
+ this.updateState(HueSyncConstants.CHANNELS.COMMANDS.MODE, new StringType(executionStatus.getMode()));
+ this.updateState(HueSyncConstants.CHANNELS.COMMANDS.SYNC,
+ executionStatus.syncActive ? OnOffType.ON : OnOffType.OFF);
+ this.updateState(HueSyncConstants.CHANNELS.COMMANDS.HDMI,
+ executionStatus.hdmiActive ? OnOffType.ON : OnOffType.OFF);
+ this.updateState(HueSyncConstants.CHANNELS.COMMANDS.SOURCE, new StringType(executionStatus.hdmiSource));
+ this.updateState(HueSyncConstants.CHANNELS.COMMANDS.BRIGHTNESS, new DecimalType(executionStatus.brightness));
+ }
+
+ private void handleRegistration(HueSyncRegistration registration) {
+ this.stopTasks();
+
+ setProperty(HueSyncConstants.REGISTRATION_ID, registration.registrationId);
+
+ Configuration configuration = this.editConfiguration();
+
+ configuration.put(HueSyncConstants.REGISTRATION_ID, registration.registrationId);
+ configuration.put(HueSyncConstants.API_TOKEN, registration.accessToken);
+
+ this.updateConfiguration(configuration);
+
+ this.startTasks();
+ }
+
+ private void checkCompatibility() throws HueSyncApiException {
+ try {
+ HueSyncDevice deviceInformation = this.deviceInfo.orElseThrow();
+
+ if (deviceInformation.apiLevel < HueSyncConstants.MINIMAL_API_VERSION) {
+ throw new HueSyncApiException("@text/api.minimal-version");
+ }
+ } catch (NoSuchElementException e) {
+ throw new HueSyncApiException("@text/api.communication-problem");
+ }
+ }
+
+ private void setProperty(String key, @Nullable String value) {
+ if (value != null) {
+ Map properties = this.editProperties();
+
+ if (properties.containsKey(key)) {
+ @Nullable
+ String currentValue = properties.get(key);
+ if (!(value.equals(currentValue))) {
+ saveProperty(key, value, properties);
+ }
+ } else {
+ saveProperty(key, value, properties);
+ }
+ }
+ }
+
+ private void saveProperty(String key, String value, Map properties) {
+ properties.put(key, value);
+ this.updateProperties(properties);
+ }
+
+ // #endregion
+
+ // #region Override
+ @Override
+ public void initialize() {
+ try {
+ updateStatus(ThingStatus.UNKNOWN);
+
+ this.stopTasks();
+
+ scheduler.execute(initializeConnection());
+ } catch (Exception e) {
+ this.logger.warn("{}", e.getMessage());
+
+ this.updateStatus(ThingStatus.OFFLINE);
+ }
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (thing.getStatus() != ThingStatus.ONLINE) {
+ this.logger.warn("Device status: {} - Command {} for chanel {} will be ignored",
+ thing.getStatus().toString(), command.toFullString(), channelUID.toString());
+ return;
+ }
+
+ Channel channel = thing.getChannel(channelUID);
+
+ if (channel == null) {
+ logger.error("Channel UID:{} does not exist - please report this as an issue", channelUID);
+ return;
+ }
+
+ this.connection.executeCommand(channel, command);
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ try {
+ this.stopTasks();
+ this.connection.dispose();
+ } catch (Exception e) {
+ this.logger.warn("{}", e.getMessage());
+ } finally {
+ this.logger.debug("Thing {} ({}) disposed.", this.thing.getLabel(), this.thing.getUID());
+ }
+ }
+
+ @Override
+ public void handleRemoval() {
+ super.handleRemoval();
+
+ this.connection.unregisterDevice();
+ }
+
+ // #endregion
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java
new file mode 100644
index 0000000000000..5b6d2c27bb75b
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncRegistrationTask.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.handler.tasks;
+
+import java.util.function.Consumer;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.huesync.internal.api.dto.device.HueSyncDevice;
+import org.openhab.binding.huesync.internal.api.dto.registration.HueSyncRegistration;
+import org.openhab.binding.huesync.internal.connection.HueSyncDeviceConnection;
+import org.openhab.binding.huesync.internal.exceptions.HueSyncConnectionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Task to handle device registration.
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+public class HueSyncRegistrationTask implements Runnable {
+ private final Logger logger = LoggerFactory.getLogger(HueSyncRegistrationTask.class);
+
+ private final HueSyncDeviceConnection connection;
+ private final HueSyncDevice deviceInfo;
+ private final Consumer action;
+
+ public HueSyncRegistrationTask(HueSyncDeviceConnection connection, HueSyncDevice deviceInfo,
+ Consumer action) {
+ this.connection = connection;
+ this.deviceInfo = deviceInfo;
+ this.action = action;
+ }
+
+ @Override
+ public void run() {
+ try {
+ String id = this.deviceInfo.uniqueId;
+
+ if (this.connection.isRegistered() || id == null) {
+ return;
+ }
+
+ this.logger.debug("Listening for device registration - {} {}:{}", this.deviceInfo.name,
+ this.deviceInfo.deviceType, id);
+
+ HueSyncRegistration registration = this.connection.registerDevice(id);
+
+ if (registration != null) {
+ this.logger.debug("API token for {} received", this.deviceInfo.name);
+
+ this.action.accept(registration);
+ }
+ } catch (HueSyncConnectionException e) {
+ this.logger.warn("{}", e.getMessage());
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTask.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTask.java
new file mode 100644
index 0000000000000..18067fa39f6ce
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTask.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.handler.tasks;
+
+import java.util.function.Consumer;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.huesync.internal.api.dto.device.HueSyncDevice;
+import org.openhab.binding.huesync.internal.connection.HueSyncDeviceConnection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Task to handle device information update.
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+public class HueSyncUpdateTask implements Runnable {
+
+ private final Logger logger = LoggerFactory.getLogger(HueSyncUpdateTask.class);
+
+ private final HueSyncDeviceConnection connection;
+ private final HueSyncDevice deviceInfo;
+
+ private final Consumer<@Nullable HueSyncUpdateTaskResult> action;
+
+ public HueSyncUpdateTask(HueSyncDeviceConnection connection, HueSyncDevice deviceInfo,
+ Consumer<@Nullable HueSyncUpdateTaskResult> action) {
+ this.connection = connection;
+ this.deviceInfo = deviceInfo;
+
+ this.action = action;
+ }
+
+ @Override
+ public void run() {
+ try {
+ this.logger.debug("Status update query for {} {}:{}", this.deviceInfo.name, this.deviceInfo.deviceType,
+ this.deviceInfo.uniqueId);
+
+ if (!this.connection.isRegistered()) {
+ this.action.accept(null);
+ }
+
+ HueSyncUpdateTaskResult updateInfo = new HueSyncUpdateTaskResult();
+
+ updateInfo.deviceStatus = this.connection.getDetailedDeviceInfo();
+ updateInfo.hdmiStatus = this.connection.getHdmiInfo();
+ updateInfo.execution = this.connection.getExecutionInfo();
+
+ this.action.accept(updateInfo);
+ } catch (Exception e) {
+ this.logger.debug("{}", e.getMessage());
+ this.action.accept(null);
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTaskResult.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTaskResult.java
new file mode 100644
index 0000000000000..7f44a86312747
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/handler/tasks/HueSyncUpdateTaskResult.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.handler.tasks;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.huesync.internal.api.dto.device.HueSyncDeviceDetailed;
+import org.openhab.binding.huesync.internal.api.dto.execution.HueSyncExecution;
+import org.openhab.binding.huesync.internal.api.dto.hdmi.HueSyncHdmi;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial contribution
+ */
+@NonNullByDefault
+public class HueSyncUpdateTaskResult {
+ public @Nullable HueSyncDeviceDetailed deviceStatus;
+ public @Nullable HueSyncHdmi hdmiStatus;
+ public @Nullable HueSyncExecution execution;
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/i18n/HueSyncLocalizer.java b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/i18n/HueSyncLocalizer.java
new file mode 100644
index 0000000000000..d6c2eebfd0680
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/java/org/openhab/binding/huesync/internal/i18n/HueSyncLocalizer.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2010-2024 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.huesync.internal.i18n;
+
+import java.util.Locale;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.i18n.TranslationProvider;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+
+/**
+ *
+ * @author Patrik Gfeller - Initial Contribution
+ */
+@NonNullByDefault
+public class HueSyncLocalizer {
+ private static final Locale LOCALE = Locale.ENGLISH;
+ private static final BundleContext BUNDLE_CONTEXT = FrameworkUtil.getBundle(HueSyncLocalizer.class)
+ .getBundleContext();
+ private static final ServiceReference SERVICE_REFERENCE = BUNDLE_CONTEXT
+ .getServiceReference(TranslationProvider.class);
+ private static final Bundle BUNDLE = BUNDLE_CONTEXT.getBundle();
+
+ public static String getResourceString(String key) {
+ String lookupKey = key.replace("@text/", "");
+
+ String missingKey = "Missing Translation: " + key;
+
+ String result = (BUNDLE_CONTEXT
+ .getService(SERVICE_REFERENCE) instanceof TranslationProvider translationProvider)
+ ? translationProvider.getText(BUNDLE, lookupKey, missingKey, LOCALE)
+ : missingKey;
+
+ return result == null ? missingKey : result;
+ }
+}
diff --git a/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/addon/addon.xml
new file mode 100644
index 0000000000000..b9afa2d7f4b11
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/addon/addon.xml
@@ -0,0 +1,23 @@
+
+
+
+ binding
+ Hue HDMI Sync Box Binding
+ Binding for the Hue HDMI Sync Box.
+ local
+
+
+
+ mdns
+
+
+ mdnsServiceType
+ _huesync._tcp.local.
+
+
+
+
+
+
diff --git a/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/config/config.xml
new file mode 100644
index 0000000000000..55c4017329927
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/config/config.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+ Connection
+
+
+
+ network-address
+ Address
+ Network address of the HDMI Sync Box.
+ true
+
+
+
+ Port
+ Port of the HDMI Sync Box.
+ true
+ 443
+ true
+
+
+
+ Registration Id
+ The id of the API registration.
+ true
+
+
+ password
+ Access Token
+ To enable the binding to communicate with the device, a registration is required. Once the registration
+ process is completed, the acquired token will authorize the binding to interact with the device. After initial
+ discovery and thing creation the device will stay offline. Press the registration button on the sync box for 3
+ seconds to grant the binding the required permissions.
+ true
+
+
+ Update Interval
+ Seconds between fetching values from the Hue Sync Box.
+ true
+ true
+ 10
+ Seconds
+
+
+
diff --git a/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/i18n/huesync.properties b/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/i18n/huesync.properties
new file mode 100644
index 0000000000000..8b4a0f04aabf1
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/i18n/huesync.properties
@@ -0,0 +1,110 @@
+# add-on
+
+addon.huesync.name = Hue HDMI Sync Box Binding
+addon.huesync.description = Binding for the Hue HDMI Sync Box.
+
+# thing types
+
+thing-type.huesync.box.label = HDMI Sync Box
+thing-type.huesync.box.description = Sync your smart lights to your on-screen TV content with the Philips Hue Play HDMI Sync Box. Four HDMI inputs allow you to connect your media devices to your Hue setup, resulting in a fast, seamless display of colorful smart light that responds to and reflects the content you watch or listen to.
+
+# thing types config
+
+thing-type.config.box.thing.apiAccessToken.label = Access Token
+thing-type.config.box.thing.apiAccessToken.description = To enable the binding to communicate with the device, a registration is required. Once the registration process is completed, the acquired token will authorize the binding to interact with the device. After initial discovery and thing creation the device will stay offline. Press the registration button on the sync box for 3 seconds to grant the binding the required permissions.
+thing-type.config.box.thing.group.connection.label = Connection
+thing-type.config.box.thing.host.label = Address
+thing-type.config.box.thing.host.description = Network address of the HDMI Sync Box.
+thing-type.config.box.thing.port.label = Port
+thing-type.config.box.thing.port.description = Port of the HDMI Sync Box.
+thing-type.config.box.thing.registrationId.label = Registration Id
+thing-type.config.box.thing.registrationId.description = The id of the API registration.
+thing-type.config.box.thing.statusUpdateInterval.label = Update Interval
+thing-type.config.box.thing.statusUpdateInterval.description = Seconds between fetching values from the Hue Sync Box.
+
+# channel group types
+
+channel-group-type.huesync.device-commands.label = Commands
+channel-group-type.huesync.device-commands.description = Commands are used to control the real-time behavior of the hue sync box. These commands allow you to influence how the lights react to your entertainment.
+channel-group-type.huesync.device-firmware.label = Firmware
+channel-group-type.huesync.device-firmware.description = Information about the installed device firmware and available updates.
+channel-group-type.huesync.device-hdmi-connection-in.label = HDMI Input
+channel-group-type.huesync.device-hdmi-connection-in.description = HDMI connection
+channel-group-type.huesync.device-hdmi-connection-out.label = HDMI Output
+channel-group-type.huesync.device-hdmi-connection-out.description = HDMI connection
+
+# channel types
+
+channel-type.huesync.connection-last-sync-mode.label = Last Mode
+channel-type.huesync.connection-last-sync-mode.description = Last sync mode used for this channel
+channel-type.huesync.connection-last-sync-mode.command.option.video = Video
+channel-type.huesync.connection-last-sync-mode.command.option.game = Game
+channel-type.huesync.connection-last-sync-mode.command.option.music = Music
+channel-type.huesync.connection-name.label = HDMI Name
+channel-type.huesync.connection-name.description = Friendly name of the HDMI connection
+channel-type.huesync.connection-status.label = HDMI Status
+channel-type.huesync.connection-status.description = Status of the HDMI input
+channel-type.huesync.connection-status.command.option.unplugged = Unplugged
+channel-type.huesync.connection-status.command.option.plugged = Plugged
+channel-type.huesync.connection-status.command.option.linked = Linked
+channel-type.huesync.connection-status.command.option.unknown = Unknown
+channel-type.huesync.connection-type.label = HDMI Type
+channel-type.huesync.connection-type.description = Type of the connected HDMI device
+channel-type.huesync.connection-type.command.option.generic = Generic
+channel-type.huesync.connection-type.command.option.video = Video
+channel-type.huesync.connection-type.command.option.game = Game
+channel-type.huesync.connection-type.command.option.music = Music
+channel-type.huesync.connection-type.command.option.xbox = XBox
+channel-type.huesync.connection-type.command.option.playstation = PlayStation
+channel-type.huesync.connection-type.command.option.nintendoswitch = Nintendo Switch
+channel-type.huesync.connection-type.command.option.phone = Phone
+channel-type.huesync.connection-type.command.option.desktop = Desktop
+channel-type.huesync.connection-type.command.option.laptop = Laptop
+channel-type.huesync.connection-type.command.option.appletv = Apple TV
+channel-type.huesync.connection-type.command.option.roku = Roku
+channel-type.huesync.connection-type.command.option.shield = Nvidia Shield
+channel-type.huesync.connection-type.command.option.chromecast = Chromecast
+channel-type.huesync.connection-type.command.option.firetv = Amazon Fire TV
+channel-type.huesync.connection-type.command.option.diskplayer = Disk Player
+channel-type.huesync.connection-type.command.option.settopbox = Set-top box
+channel-type.huesync.connection-type.command.option.satellite = Satellite
+channel-type.huesync.connection-type.command.option.avreceiver = AV receiver
+channel-type.huesync.connection-type.command.option.soundbar = Soundbar
+channel-type.huesync.connection-type.command.option.hdmiswitch = HDMI switch
+channel-type.huesync.device-info-firmware-available.label = Latest Firmware
+channel-type.huesync.device-info-firmware-available.description = Latest available firmware version
+channel-type.huesync.device-info-firmware.label = Firmware
+channel-type.huesync.device-info-firmware.description = Installed firmware version
+channel-type.huesync.execution-brightness.label = Brightness
+channel-type.huesync.execution-brightness.description = 0 ... 200
0 = max reduction 100 = no brightness reduction/boost compared to input 200 = max boost
+channel-type.huesync.execution-hdmi-active.label = HDMI Active
+channel-type.huesync.execution-hdmi-active.description = OFF in case of powersave mode and ON in case of passthrough , video , game or music mode.
When changed from OFF to ON , it will set passthrough mode. When changed from ON to OFF , will set powersave mode.
+channel-type.huesync.execution-hdmi-source.label = HDMI Input
+channel-type.huesync.execution-hdmi-source.description =
input1 input2 input3 input4
+channel-type.huesync.execution-mode.label = Mode
+channel-type.huesync.execution-mode.description =
"Video": Analyzes the on-screen visuals, translating colors and brightness into corresponding light effects for an immersive movie-watching experience.
"Music": Analyzes the rhythm and beat of your music, creating dynamic light along to your tunes.
"Game": Reacts to the action on your screen, intensifying the in-game atmosphere with bursts of light that correspond to explosions, gunfire, and other gameplay events.
"Passthrough" "Powersave"
+channel-type.huesync.execution-mode.command.option.powersave = Powersave
+channel-type.huesync.execution-mode.command.option.passthrough = Passthrough
+channel-type.huesync.execution-mode.command.option.video = Video
+channel-type.huesync.execution-mode.command.option.game = Game
+channel-type.huesync.execution-mode.command.option.music = Music
+channel-type.huesync.execution-sync-active.label = Synchronization Active
+channel-type.huesync.execution-sync-active.description = OFF in case of powersave or passthrough mode, and ON in case of video , game or music mode.
When changed from OFF to ON , it will start syncing in last used mode for current source. When changed from ON to OFF , will set passthrough mode.
+
+# *** exceptions ***
+
+exception.generic.connection = "Unable to connect to device."
+
+# api & connection exceptions
+
+api.minimal-version = Only devices with API level >= 7 are supported
+api.communication-problem = Communication problem with the device
+connection.invalid-login = Invalid or missing credentials
+
+# registration
+
+thing.config.huesync.box.registration = Device registration pending. Please press the HDMI Sync Box device button for 3 seconds.
+
+# logger (to keep text in sync with on-screen messages, log messages will always be in locale.english)
+
+logger.initialization-problem = Unable to initialize handler for {} ({}): {}
diff --git a/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/thing/channel-types.xml b/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/thing/channel-types.xml
new file mode 100644
index 0000000000000..0f423c075b680
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/thing/channel-types.xml
@@ -0,0 +1,225 @@
+
+
+
+
+ String
+ Firmware
+ Installed firmware version
+ text
+
+
+
+
+ String
+ Latest Firmware
+ Latest available firmware version
+ text
+
+
+
+
+ String
+ HDMI Name
+ Friendly name of the HDMI connection
+ text
+
+
+
+
+ String
+ HDMI Status
+ Status of the HDMI input
+ status
+
+
+
+ Unplugged
+ Plugged
+ Linked
+ Unknown
+
+
+
+
+
+ String
+ HDMI Type
+ Type of the connected HDMI device
+ text
+
+
+
+ Generic
+ Video
+ Game
+ Music
+ XBox
+ PlayStation
+ Nintendo Switch
+ Phone
+ Desktop
+ Laptop
+ Apple TV
+ Roku
+ Nvidia Shield
+ Chromecast
+ Amazon Fire TV
+ Disk Player
+ Set-top box
+ Satellite
+ AV receiver
+ Soundbar
+ HDMI switch
+
+
+
+
+
+ String
+ Last Mode
+ Last sync mode used for this channel
+ text
+
+
+
+ Video
+ Game
+ Music
+
+
+
+
+
+ String
+ Mode
+
+
+
+
+ "Video":
+
+ Analyzes the on-screen visuals, translating colors and brightness into corresponding light
+ effects for an immersive movie-watching experience.
+
+
+
+ "Music":
+
+ Analyzes the rhythm and beat of your music, creating
+ dynamic light along to your tunes.
+
+
+
+ "Game":
+
+ Reacts to the action on your screen, intensifying the in-game atmosphere
+ with bursts of light that correspond to explosions, gunfire, and other gameplay events.
+
+ "Passthrough"
+ "Powersave"
+
+
+ ]]>
+
+ text
+
+
+ Powersave
+ Passthrough
+ Video
+ Game
+ Music
+
+
+
+
+
+ Switch
+ Synchronization Active
+
+
+ OFF in case of powersave or passthrough mode, and ON in case of video , game or music mode.
+
+
+ When changed from OFF to ON , it will start syncing in last used mode for current source.
+ When changed from ON to OFF , will set passthrough mode.
+
+ ]]>
+
+ switch
+
+
+
+ Switch
+ HDMI Active
+
+
+ OFF in case of powersave mode and ON in case of passthrough , video , game or music mode.
+
+
+ When changed from OFF to ON , it will set passthrough mode.
+ When changed from ON to OFF , will set powersave mode.
+
+ ]]>
+
+ switch
+
+
+
+ String
+ HDMI Input
+
+
+
+ input1
+ input2
+ input3
+ input4
+
+
+ ]]>
+
+ receiver
+
+
+
+
+
+
+
+
+
+
+
+
+ Number:Dimensionless
+ Brightness
+
+
+ 0 ... 200
+
+ 0 = max reduction
+ 100 = no brightness reduction/boost compared to input
+ 200 = max boost
+
+
+ ]]>
+
+ slider
+
+
+
+
diff --git a/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644
index 0000000000000..043dc019c8f31
--- /dev/null
+++ b/bundles/org.openhab.binding.huesync/src/main/resources/OH-INF/thing/thing-types.xml
@@ -0,0 +1,84 @@
+
+
+
+
+ HDMI Sync Box
+
+ Sync your smart lights to your on-screen TV content with the Philips Hue Play HDMI Sync Box. Four HDMI
+ inputs allow you to connect your media devices to your Hue setup, resulting in a fast, seamless display of colorful
+ smart light that responds to and reflects the content you watch or listen to.
+
+
+ receiver
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Philips
+
+
+ host
+
+
+
+
+
+ Firmware
+ Information about the installed device firmware and available updates.
+ text
+
+
+
+
+
+
+ HDMI Input
+ HDMI connection
+ settings
+
+
+
+
+
+
+
+
+ HDMI Output
+ HDMI connection
+ settings
+
+
+
+
+
+
+
+
+ Commands
+ Commands are used to control the real-time behavior of the hue sync box. These
+ commands allow you to
+ influence how the lights react to your entertainment.
+ settings
+
+
+
+
+
+
+
+
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index a8d77c2c2fe73..0b81ed422be64 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -195,6 +195,7 @@
org.openhab.binding.hpprinter
org.openhab.binding.http
org.openhab.binding.hue
+ org.openhab.binding.huesync
org.openhab.binding.hydrawise
org.openhab.binding.hyperion
org.openhab.binding.iammeter