diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js
index 356e42e0..aa468cd1 100755
--- a/assets/webconfig/js/content_leds.js
+++ b/assets/webconfig/js/content_leds.js
@@ -18,7 +18,7 @@ var bottomRight2bottomLeft = null;
var bottomLeft2topLeft = null;
var toggleKeystoneCorrectionArea = false;
-var devSPI = ['apa102', 'apa104', 'ws2801', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'sk9822', 'ws2812spi'];
+var devSPI = ['apa102', 'apa104', 'hd108', 'lpd6803', 'lpd8806', 'p9813', 'sk6812spi', 'sk6822spi', 'sk9822', 'ws2801', 'ws2812spi'];
var devFTDI = ['apa102_ftdi', 'sk6812_ftdi', 'ws2812_ftdi'];
var devRPiPWM = ['ws281x'];
var devRPiGPIO = ['piblaster'];
@@ -1115,6 +1115,7 @@ $(document).ready(function () {
case "ws2812spi":
case "piblaster":
case "ws281x":
+ case "hd108":
//Serial devices
case "adalight":
@@ -1480,6 +1481,7 @@ $(document).ready(function () {
case "apa102_ftdi":
case "sk6812_ftdi":
case "ws2812_ftdi":
+ case "hd108":
default:
}
@@ -1962,6 +1964,7 @@ function saveLedConfig(genDefLayout = false) {
case "apa102_ftdi":
case "sk6812_ftdi":
case "ws2812_ftdi":
+ case "hd108":
default:
if (genDefLayout === true) {
ledConfig = {
@@ -2219,6 +2222,7 @@ var updateOutputSelectList = function (ledType, discoveryInfo) {
case "sk6822spi":
case "sk9822":
case "ws2812spi":
+ case "hd108":
case "piblaster":
for (const device of discoveryInfo.devices) {
enumVals.push(device.systemLocation);
diff --git a/libsrc/leddevice/LedDeviceSchemas.qrc b/libsrc/leddevice/LedDeviceSchemas.qrc
index 7cd6a235..b63d805c 100644
--- a/libsrc/leddevice/LedDeviceSchemas.qrc
+++ b/libsrc/leddevice/LedDeviceSchemas.qrc
@@ -42,6 +42,7 @@
schemas/schema-ws2812_ftdi.json
schemas/schema-apa102_ftdi.json
schemas/schema-sk6812_ftdi.json
- schemas/schema-skydimo.json
+ schemas/schema-skydimo.json
+ schemas/schema-hd108.json
diff --git a/libsrc/leddevice/dev_spi/LedDeviceHD108.cpp b/libsrc/leddevice/dev_spi/LedDeviceHD108.cpp
new file mode 100644
index 00000000..964c9b5d
--- /dev/null
+++ b/libsrc/leddevice/dev_spi/LedDeviceHD108.cpp
@@ -0,0 +1,133 @@
+#include "LedDeviceHD108.h"
+
+/**
+ * @brief Constructor for the HD108 LED device.
+ *
+ * @param deviceConfig JSON configuration object for this device.
+ */
+LedDeviceHD108::LedDeviceHD108(const QJsonObject &deviceConfig)
+ : ProviderSpi(deviceConfig)
+{
+ // By default, set the global brightness register to full (16-bit max)
+ _global_brightness = 0xFFFF;
+}
+
+/**
+ * @brief Factory method: creates an instance of LedDeviceHD108.
+ *
+ * @param deviceConfig The JSON configuration for the device.
+ * @return A pointer to the newly constructed LedDeviceHD108 instance.
+ */
+LedDevice* LedDeviceHD108::construct(const QJsonObject &deviceConfig)
+{
+ return new LedDeviceHD108(deviceConfig);
+}
+
+/**
+ * @brief Initializes the HD108 device using the given JSON configuration.
+ *
+ * This reads certain device-specific parameters, such as the maximum brightness
+ * level, and configures the global brightness register accordingly.
+ *
+ * @param deviceConfig The JSON object containing device parameters.
+ * @return True if initialization succeeded, false otherwise.
+ */
+bool LedDeviceHD108::init(const QJsonObject &deviceConfig)
+{
+ bool isInitOK = false;
+
+ // First, let the base SPI provider perform its initialization
+ if (ProviderSpi::init(deviceConfig))
+ {
+ // Read brightnessControlMaxLevel from the config, falling back to a default if absent
+ _brightnessControlMaxLevel = deviceConfig["brightnessControlMaxLevel"].toInt(HD108_BRIGHTNESS_MAX_LEVEL);
+
+ // Log the brightness info
+ Info(_log,
+ "[%s] Setting maximum brightness to [%d] = %d%%",
+ QSTRING_CSTR(_activeDeviceType),
+ _brightnessControlMaxLevel,
+ _brightnessControlMaxLevel * 100 / HD108_BRIGHTNESS_MAX_LEVEL);
+
+ // Combine the brightness levels into the HD108's 16-bit brightness field.
+ // According to the HD108 spec, this is composed of a control bit plus
+ // the brightness level split into three segments for R, G, B.
+ _global_brightness = (1 << 15)
+ | (_brightnessControlMaxLevel << 10)
+ | (_brightnessControlMaxLevel << 5)
+ | _brightnessControlMaxLevel;
+
+ isInitOK = true;
+ }
+
+ return isInitOK;
+}
+
+/**
+ * @brief Writes a vector of RGB colors to the HD108 LEDs.
+ *
+ * The HD108 protocol requires:
+ * - A start frame of 64 bits (8 bytes) all set to 0x00.
+ * - For each LED, 64 bits:
+ * - 16 bits of global brightness
+ * - 16 bits for red
+ * - 16 bits for green
+ * - 16 bits for blue
+ * - An end frame of at least (ledCount / 16 + 1) bytes of 0xFF.
+ *
+ * Each 8-bit color value is expanded to 16 bits by copying it into both the high
+ * and low byte (e.g. 0x7F -> 0x7F7F). This ensures a correct mapping to the HD108's
+ * internal 16-bit color resolution and allows for a true "off" state at 0x0000.
+ *
+ * @param ledValues A vector of ColorRgb (red, green, blue) structures.
+ * @return The result of the SPI write operation (0 for success, or an error code).
+ */
+int LedDeviceHD108::write(const std::vector & ledValues)
+{
+ // Calculate how much space we need in total:
+ // - 8 bytes for the start frame
+ // - 8 bytes per LED (16 bits global brightness + 16 bits R + G + B)
+ // - end frame: ledCount / 16 + 1 bytes of 0xFF
+ const size_t ledCount = ledValues.size();
+ const size_t totalSize = 8 // start frame
+ + (ledCount * 8) // LED data (8 bytes each)
+ + (ledCount / 16 + 1); // end frame bytes
+
+ // Reserve enough space to avoid multiple allocations
+ std::vector hd108Data;
+ hd108Data.reserve(totalSize);
+
+ // 1) Start frame: 64 bits of 0x00
+ hd108Data.insert(hd108Data.end(), 8, 0x00);
+
+ // 2) For each LED, insert 8 bytes: 16 bits brightness, 16 bits R, 16 bits G, 16 bits B
+ for (const ColorRgb &color : ledValues)
+ {
+ // Expand 8-bit color components to 16 bits each
+ uint16_t red16 = (static_cast(color.red) << 8) | color.red;
+ uint16_t green16 = (static_cast(color.green) << 8) | color.green;
+ uint16_t blue16 = (static_cast(color.blue) << 8) | color.blue;
+
+ // Global brightness (16 bits)
+ hd108Data.push_back(_global_brightness >> 8);
+ hd108Data.push_back(_global_brightness & 0xFF);
+
+ // Red (16 bits)
+ hd108Data.push_back(red16 >> 8);
+ hd108Data.push_back(red16 & 0xFF);
+
+ // Green (16 bits)
+ hd108Data.push_back(green16 >> 8);
+ hd108Data.push_back(green16 & 0xFF);
+
+ // Blue (16 bits)
+ hd108Data.push_back(blue16 >> 8);
+ hd108Data.push_back(blue16 & 0xFF);
+ }
+
+ // 3) End frame: at least (ledCount / 16 + 1) bytes of 0xFF
+ hd108Data.insert(hd108Data.end(), (ledCount / 16) + 1, 0xFF);
+
+ // Finally, transmit the assembled data via SPI
+ return writeBytes(hd108Data.size(), hd108Data.data());
+}
diff --git a/libsrc/leddevice/dev_spi/LedDeviceHD108.h b/libsrc/leddevice/dev_spi/LedDeviceHD108.h
new file mode 100644
index 00000000..effed023
--- /dev/null
+++ b/libsrc/leddevice/dev_spi/LedDeviceHD108.h
@@ -0,0 +1,53 @@
+#ifndef LEDDEVICEHD108_H
+#define LEDDEVICEHD108_H
+
+#include "ProviderSpi.h"
+
+/// The maximal level supported by the HD108 brightness control field, 31
+const int HD108_BRIGHTNESS_MAX_LEVEL = 31;
+
+
+class LedDeviceHD108 : public ProviderSpi
+{
+
+public:
+
+ ///
+ /// @brief Constructs an HD108 LED-device
+ ///
+ /// @param deviceConfig Device's configuration as JSON-Object
+ ///
+ explicit LedDeviceHD108(const QJsonObject &deviceConfig);
+
+ ///
+ /// @brief Constructs the LED-device
+ ///
+ /// @param[in] deviceConfig Device's configuration as JSON-Object
+ /// @return LedDevice constructed
+ ///
+ static LedDevice* construct(const QJsonObject &deviceConfig);
+
+private:
+
+ ///
+ /// @brief Initialise the device's configuration
+ ///
+ /// @param[in] deviceConfig the JSON device configuration
+ /// @return True, if success
+ ///
+ bool init(const QJsonObject &deviceConfig) override;
+
+ ///
+ /// @brief Writes the RGB-Color values to the LEDs.
+ ///
+ /// @param[in] ledValues The RGB-color per LED
+ /// @return Zero on success, else negative
+ ///
+ int write(const std::vector & ledValues) override;
+
+ /// The brighness level. Possibile values 1 .. 31.
+ int _brightnessControlMaxLevel;
+ uint16_t _global_brightness;
+};
+
+#endif // LEDDEVICEHD108_H
diff --git a/libsrc/leddevice/schemas/schema-hd108.json b/libsrc/leddevice/schemas/schema-hd108.json
new file mode 100644
index 00000000..8f3c30af
--- /dev/null
+++ b/libsrc/leddevice/schemas/schema-hd108.json
@@ -0,0 +1,35 @@
+{
+ "type":"object",
+ "required":true,
+ "properties":{
+ "output": {
+ "type": "string",
+ "title":"edt_dev_spec_spipath_title",
+ "propertyOrder" : 1
+ },
+ "rate": {
+ "type": "integer",
+ "title":"edt_dev_spec_baudrate_title",
+ "default": 3000000,
+ "propertyOrder" : 2
+ },
+ "brightnessControlMaxLevel": {
+ "type": "integer",
+ "title":"edt_conf_color_brightness_title",
+ "default": 31,
+ "minimum": 1,
+ "maximum": 31,
+ "propertyOrder" : 4
+ },
+ "rewriteTime": {
+ "type": "integer",
+ "title":"edt_dev_general_rewriteTime_title",
+ "default": 0,
+ "append" : "edt_append_ms",
+ "minimum": 0,
+ "access" : "expert",
+ "propertyOrder" : 5
+ }
+ },
+ "additionalProperties": true
+}