11 Commits
v0.2 ... master

Author SHA1 Message Date
Berthold Höllmann
c33c05ae67 Add support for setting power LED via systemctl (#60)
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
* Add support for setting power LED via systemctl

* Add documentation for rudimentary ugreen-power-led.service

* Add "Type=oneshot" and "RemainAfterExit=true" as suggested
2025-06-12 10:14:48 +08:00
Thomas
dc04db484c Don't mark drives in standby as failed (#59)
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
The standby return code of smartctl is updated to 0 to avoid detecting it as a failure.
2025-06-07 11:31:44 +08:00
Thomas
276ae34cd1 Update ugreen-diskiomon to ignore smartctl non-critical return codes (#57)
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
Smartctl will also return non-zero if it detected past errors even when currently no other errors are observed. It will set up to 8 bits to indicate the differen types of observed results, where bit 5 does not indicate a _current_ error: `SMART status check returned "DISK OK" but some attributes have been at or below threshold in the past.`

The change ignores bit 5 (= 32) when checking the exit code.

Future improvements could detect other bits to color the leds differently depending on the type of error (f.e. bit 4 might indicate a future problem and could turn the led orange instead of red)
2025-06-04 17:33:44 +08:00
Yuhao Zhou
af2b3c327a add license (#54)
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
2025-04-12 22:49:48 +08:00
Yuhao Zhou
ab0db369fa update the debian build scripts
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
2025-03-20 15:05:05 +08:00
Yuhao Zhou
730f395251 Update README.md
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
2025-01-19 17:51:59 +08:00
Yuhao Zhou
e8e5a28c3f Update README.md (#13)
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
Add a link to an install script for TrueNAS.
2025-01-13 11:24:53 +08:00
docgalaxyblock
15dbae0f8f fix systemd services (#51)
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
* move systemd scripts to their own subfolder

* move probe-leds to its own oneshot service

* README improvements
2025-01-11 21:50:36 +08:00
tooomm
b789a67793 Readme: small tweaks (#46)
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
* small tweaks

* Clarify chassis LED ID
2025-01-03 13:04:18 +08:00
a4d9e2e73e Add verbose mode in the kernel driver (#47)
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
- Error messages are displayed only when verbose mode is enabled
2024-12-15 23:17:14 +08:00
tooomm
39511eb639 Readme: Add DXP4800 to list of supported devices (#44)
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
2024-12-01 20:01:53 +08:00
13 changed files with 158 additions and 66 deletions

101
README.md
View File

@@ -1,13 +1,14 @@
LED Controller for UGREEN's DX/DXP NAS Series
==
UGREEN's DX/DXP NAS Series covers 2 to 8 bay NAS devices with a built-in system based on OpenWRT called `UGOS`.
UGREEN's DX/DXP NAS Series covers 2 to 8 bay NAS devices with a built-in system based on OpenWRT called `UGOS` or Debian called `UGOS-Pro`.
Debian Linux or dedicated NAS operating systems and appliances are compatible with the hardware, but do not have drivers for the LED lights on the front panel to indicate power, network and hard drive activity.
Instead, when using a 3rd party OS with DX 4600 Pro, only the power indicator light blinks, and the other LEDs are off by default.
Instead, when using a 3rd party OS with e.g. DX 4600 Pro, only the power indicator light blinks, and the other LEDs are off by default.
For the DXP series, all LEDs blink in rolling sequence when non-UGOS systems are running.
This repository
- describes the control logic of UGOS for these LED lights
- provides a command-line tool and a kernel module to control them
- Describes the control logic of UGOS for the LED lights on the device front
- Provides a command-line tool and a kernel module to control them
For the process of understanding this control logic, please refer to [my blog (in Chinese)](https://blog.miskcoo.com/2024/05/ugreen-dx4600-pro-led-controller).
@@ -16,6 +17,7 @@ For the process of understanding this control logic, please refer to [my blog (i
> - [x] UGREEN DX4600 Pro
> - [x] UGREEN DX4700+
> - [x] UGREEN DXP2800 (reported in [#19](https://github.com/miskcoo/ugreen_leds_controller/issues/19))
> - [x] UGREEN DXP4800 (confirmed in [#41](https://github.com/miskcoo/ugreen_leds_controller/issues/41))
> - [x] UGREEN DXP4800 Plus (reported [here](https://gist.github.com/Kerryliu/c380bb6b3b69be5671105fc23e19b7e8))
> - [x] UGREEN DXP6800 Pro (reported in [#7](https://github.com/miskcoo/ugreen_leds_controller/issues/7))
> - [x] UGREEN DXP8800 Plus (see [this repo](https://github.com/meyergru/ugreen_dxp8800_leds_controller) and [#1](https://github.com/miskcoo/ugreen_leds_controller/issues/1))
@@ -28,7 +30,7 @@ For the process of understanding this control logic, please refer to [my blog (i
For third-party systems, I am using Debian 12 "Bookworm", but you can find some manuals for other systems:
- **DSM**: see [#8](https://github.com/miskcoo/ugreen_leds_controller/issues/8)
- **TrueNAS**: see [#13](https://github.com/miskcoo/ugreen_leds_controller/issues/13) (and maybe [here](https://github.com/miskcoo/ugreen_leds_controller/tree/truenas-build/build-scripts/truenas)) for how to build the module, and [here](https://gist.github.com/Kerryliu/c380bb6b3b69be5671105fc23e19b7e8) for a script using the cli tool; [here](https://github.com/miskcoo/ugreen_leds_controller/tree/gh-actions/build-scripts/truenas/build) for pre-build drivers
- **TrueNAS**: see [#13](https://github.com/miskcoo/ugreen_leds_controller/issues/13) and [this repo](https://github.com/0x556c79/install-ugreen-leds-controller) (and maybe [here](https://github.com/miskcoo/ugreen_leds_controller/tree/truenas-build/build-scripts/truenas)) for how to build the module, and [here](https://gist.github.com/Kerryliu/c380bb6b3b69be5671105fc23e19b7e8) for a script using the cli tool; [here](https://github.com/miskcoo/ugreen_leds_controller/tree/gh-actions/build-scripts/truenas/build) for pre-build drivers
- **unRAID**: there is a [plugin](https://forums.unraid.net/topic/168423-ugreen-nas-led-control/); see also [this repo](https://github.com/ich777/unraid-ugreenleds-driver/tree/master/source/usr/bin)
- **Proxmox**: you need to use the cli tool in Proxmox, not in a VM
- **Debian**: see [the section below](#start-at-boot-for-debian-12)
@@ -54,7 +56,8 @@ ugreen_leds_cli disk4 -color 0 0 255 -blink 400 600
## Preparation
We communicate with the control chip of the LED via I2C, corresponding to the device with address `0x3a` on *SMBus I801 adapter*. Before proceeding, we need to load the `i2c-dev` module and install the `i2c-tools` tool.
We communicate with the control chip of the LED via I2C, corresponding to the device with address `0x3a` on *SMBus I801 adapter*.
Before proceeding, we need to load the `i2c-dev` module and install the `i2c-tools` tool.
```
$ apt install -y i2c-tools
@@ -129,9 +132,9 @@ ugreen_leds_cli power -on -color 0 0 255 -brightness 128 -status
There are three methods to install the module:
- Run `cd kmod && make` to build the kernel module, and then load it with `sudo insmod led-ugreen.ko`.
- **A)** Run `cd kmod && make` to build the kernel module, and then load it with `sudo insmod led-ugreen.ko`.
- Alternatively, you can install it with dkms:
- **B)** Alternatively, you can install it with dkms:
```bash
cp -r kmod /usr/src/led-ugreen-0.1
@@ -139,7 +142,7 @@ There are three methods to install the module:
dkms build -m led-ugreen -v 0.1 && dkms install -m led-ugreen -v 0.1
```
- You can also directly install the package [here](https://github.com/miskcoo/ugreen_leds_controller/releases).
- **C)** You can also directly install the `.deb` package [here](https://github.com/miskcoo/ugreen_leds_controller/releases).
After loading the `led-ugreen` module, you need to run `scripts/ugreen-probe-leds`, and you can see LEDs in `/sys/class/leds`.
@@ -172,17 +175,20 @@ To see how to map the disk LEDs to correct disk slots, please read the [Disk Map
The configure file of `ugreen-diskiomon` and `ugreen-netdevmon` is `/etc/ugreen-led.conf`.
Please see `scripts/ugreen-leds.conf` for an example.
- Edit `/etc/modules-load.d/ugreen-led.conf` and add the following lines:
- Add the following lines to `/etc/modules-load.d/ugreen-led.conf`
```
cat > /etc/modules-load.d/ugreen-led.conf << EOF
i2c-dev
led-ugreen
ledtrig-oneshot
ledtrig-netdev
EOF
```
- Install the `smartctl` tool: `apt install smartmontools`
- Install the kernel module by one of the three methods mentioned above. For example, directly install [the deb package](https://github.com/miskcoo/ugreen_leds_controller/releases).
- Install the kernel module by one of the three methods mentioned above.
For example, directly install [the deb package](https://github.com/miskcoo/ugreen_leds_controller/releases).
- Copy files in the `scripts` directory:
```bash
@@ -197,18 +203,20 @@ Please see `scripts/ugreen-leds.conf` for an example.
cp scripts/ugreen-leds.conf /etc/ugreen-leds.conf
# copy the systemd services
cp scripts/*.service /etc/systemd/system/
cp scripts/systemd/*.service /etc/systemd/system/
systemctl daemon-reload
# change enp2s0 to the network device you want to monitor
systemctl start ugreen-netdevmon@enp2s0
systemctl start ugreen-diskiomon
systemctl start ugreen-power-led
# if you confirm that everything works well,
# run the command below to make the service start at boot
systemctl enable ugreen-netdevmon@enp2s0
systemctl enable ugreen-diskiomon
systemctl enable ugreen-power-led
```
- (_Optional_) To reduce the CPU usage of blinking LEDs when disks are active, you can enter the `scripts` directory and do the following things:
@@ -261,13 +269,13 @@ sdh 7:0:0:0 XXJDB1XX
## Communication Protocols
The IDs for the six LED lights on the front panel of the DX4600 Pro chassis are as follows:
The IDs for the LED lights on the front panel of the NAS chassis are as follows:
| ID | LED |
|------|--------------------------------|
| 0 | power indicator |
| 1 | network device indicator |
| 2-5 | hard drive indicator 1-4 |
| ID | LED |
|-------------|--------------------------------|
| `0` | Power indicator |
| `1` | Network device indicator |
| `2`,`3`,... | Hard drive indicator "disk1", "disk2" etc. |
### Query Status
@@ -275,17 +283,17 @@ Reading 11 bytes from the address `0x81 + LED_ID` allows us to obtain the curren
| Address | Meaning of Corresponding Data |
|---------|--------------------------------|
| 0x00 | LED status: 0 - off, 1 - on, 2 - blink, 3 - breath |
| 0x01 | LED brightness |
| 0x02 | LED color (Red component in RGB) |
| 0x03 | LED color (Green component in RGB) |
| 0x04 | LED color (Blue component in RGB) |
| 0x05 | Milliseconds needed to complete one blink / breath cycle (high 8 bits) |
| 0x06 | Milliseconds needed to complete one blink / breath cycle (low 8 bits) |
| 0x07 | Milliseconds the LED is on during one blink / breath cycle (high 8 bits) |
| 0x08 | Milliseconds the LED is on during one blink / breath cycle (low 8 bits) |
| 0x09 | Checksum of data in the range 0x00 - 0x08 (high 8 bits) |
| 0x0a | Checksum of data in the range 0x00 - 0x08 (low 8 bits) |
| `0x00` | LED status: 0 (off), 1 (on), 2 (blink), 3 (breath) |
| `0x01` | LED brightness |
| `0x02` | LED color (Red component in RGB) |
| `0x03` | LED color (Green component in RGB) |
| `0x04` | LED color (Blue component in RGB) |
| `0x05` | Milliseconds needed to complete one blink/breath cycle (high 8 bits) |
| `0x06` | Milliseconds needed to complete one blink/breath cycle (low 8 bits) |
| `0x07` | Milliseconds the LED is on during one blink/breath cycle (high 8 bits) |
| `0x08` | Milliseconds the LED is on during one blink/breath cycle (low 8 bits) |
| `0x09` | Checksum of data in the range 0x00 - 0x08 (high 8 bits) |
| `0x0a` | Checksum of data in the range 0x00 - 0x08 (low 8 bits) |
The checksum is a 16-bit value obtained by summing all the data at the corresponding positions as unsigned integers.
@@ -301,25 +309,24 @@ By writing 12 bytes to the address `0x00 + LED_ID`, we can modify the current st
| Address | Meaning of Corresponding Data |
|---------|--------------------------------|
| 0x00 | LED ID |
| 0x01 | Constant: 0xa0 |
| 0x02 | Constant: 0x01 |
| 0x03 | Constant: 0x00 |
| 0x04 | Constant: 0x00 |
| 0x05 | If the value is 1, it indicates modifying brightness. <br/>If the value is 2, it indicates modifying color. <br/>If the value is 3, it indicates setting the on/off state.<br/>If the value is 4 / 5, it indicates setting the blink / breath state. |
| 0x06 | First parameter |
| 0x07 | Second parameter |
| 0x08 | Third parameter |
| 0x09 | Fourth parameter |
| 0x0a | Checksum of data in the range 0x01 - 0x09 (high 8 bits) |
| 0x0b | Checksum of data in the range 0x01 - 0x09 (low 8 bits) |
| `0x00` | LED ID |
| `0x01` | Constant: 0xa0 |
| `0x02` | Constant: 0x01 |
| `0x03` | Constant: 0x00 |
| `0x04` | Constant: 0x00 |
| `0x05` | If the value is 1, it indicates modifying brightness<br/> If the value is 2, it indicates modifying color<br/> If the value is 3, it indicates setting the on/off state<br/> If the value is 4, it indicates setting the blink state<br/> If the value is 5, it indicates setting the breath state |
| `0x06` | First parameter |
| `0x07` | Second parameter |
| `0x08` | Third parameter |
| `0x09` | Fourth parameter |
| `0x0a` | Checksum of data in the range 0x01 - 0x09 (high 8 bits) |
| `0x0b` | Checksum of data in the range 0x01 - 0x09 (low 8 bits) |
For the four different modification types at address 0x05:
- If we need to modify brightness, the first parameter contains brightness information.
- If we need to modify color, the first three parameters represent RGB information.
- If we need to toggle the on/off state, the first parameter is either 0 or 1, representing off or on, respectively.
- If we need to set the blink / breath state, the first two parameters together form a 16-bit unsigned integer in big-endian order, representing the number of milliseconds needed to complete one blink / breath cycle. The next two parameters, also in big-endian order, represent the number of milliseconds the LED is on during one blink / breath cycle.
For the four different modification types at address `0x05`:
- If we need to modify **brightness**, the first parameter contains brightness information.
- If we need to modify **color**, the first three parameters represent RGB information.
- If we need to toggle the **on/off state**, the first parameter is either 0 (off) or 1 (on).
- If we need to set the **blink/breath state**, the first two parameters together form a 16-bit unsigned integer in big-endian order, representing the number of milliseconds needed to complete one blink/breath cycle. The next two parameters, also in big-endian order, represent the time in milliseconds the LED is on during one blink/breath cycle.
Below is an example for turning off and on the power indicator light using `i2cset`:

View File

@@ -3,7 +3,7 @@
set -e
set -x
pkgver="0.2"
pkgver="0.3"
pkgname="led-ugreen-dkms"
drivername="led-ugreen"

View File

@@ -3,7 +3,7 @@
set -e
set -x
pkgver="0.2"
pkgver="0.3"
pkgname="led-ugreen-utils"
drivername="led-ugreen"
@@ -33,7 +33,7 @@ done
# systemd file
mkdir -p $pkgname/etc/systemd/system
cp scripts/*.service $pkgname/etc/systemd/system/
cp scripts/systemd/*.service $pkgname/etc/systemd/system/
# cp scripts/ugreen-ledmon@.service $pkgname/etc/systemd/system/
# example config file
@@ -44,8 +44,8 @@ g++ -std=c++17 -O2 scripts/blink-disk.cpp -o ugreen-blink-disk
cp ugreen-blink-disk $pkgname/usr/bin
# compile the disk standby monitor
g++ -std=c++17 -O2 scripts/check-standby.cpp -o ugreen-check-standby
cp ugreen-check-standby $pkgname/usr/bin
#g++ -std=c++17 -O2 scripts/check-standby.cpp -o ugreen-check-standby
#cp ugreen-check-standby $pkgname/usr/bin
# change to root
chown -R root:root $pkgname/

View File

@@ -1,2 +1,14 @@
Run `bash build.sh TrueNAS-SCALE-Dragonfish/24.04.1`
### Important Note
When you find errors like this:
```
--2025-01-19 09:03:41-- https://download.truenas.com/TrueNAS-SCALE-ElectricEel/24.10.1/packages/Packages.gz
Resolving download.truenas.com (download.truenas.com)... 185.244.226.2
Connecting to download.truenas.com (download.truenas.com)|185.244.226.2|:443... connected.
HTTP request sent, awaiting response... 404 Not Found
2025-01-19 09:03:42 ERROR 404: Not Found.
```
You can try to use previous versions. See Issue #28.

View File

@@ -24,6 +24,10 @@
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
static bool verbose = false;
module_param(verbose, bool, 0644);
MODULE_PARM_DESC(verbose, "Enable verbose output");
static struct ugreen_led_state *lcdev_to_ugreen_led_state(struct led_classdev *led_cdev) {
return container_of(led_cdev, struct ugreen_led_state, cdev);
}
@@ -183,7 +187,7 @@ static void ugreen_led_turn_on_or_off_unlock(struct ugreen_led_array *priv, u8 l
int rc = ugreen_led_change_state_robust(priv->client, led_id, 0x03, on ? 1 : 0, 0, 0, 0);
if (rc == 0) {
priv->state[led_id].status = on ? UGREEN_LED_STATE_ON : UGREEN_LED_STATE_OFF;
} else {
} else if (verbose) {
pr_err("failed to turn %d %s", led_id, on ? "on" : "off");
}
}
@@ -199,7 +203,7 @@ static void ugreen_led_set_brightness_unlock(struct ugreen_led_array *priv, u8 l
int rc = ugreen_led_change_state_robust(priv->client, led_id, 0x01, brightness, 0, 0, 0);
if (rc == 0) {
state->brightness = brightness;
} else {
} else if (verbose) {
pr_err("failed to set brightness of %d to %d", led_id, brightness);
}
}
@@ -223,7 +227,7 @@ static void ugreen_led_set_color_unlock(struct ugreen_led_array *priv, u8 led_id
state->r = r;
state->g = g;
state->b = b;
} else {
} else if (verbose) {
pr_err("failed to set color of %d to 0x%02x%02x%02x", led_id, r, g, b);
}
}
@@ -247,7 +251,7 @@ static void ugreen_led_set_blink_or_breath_unlock(struct ugreen_led_array *priv,
state->t_on = t_on;
state->t_cycle = t_cycle;
state->status = led_status;
} else {
} else if (verbose) {
pr_err("failed to set %s of %d to %d %d", is_blink ? "blink" : "breath", led_id, t_on, t_cycle);
}
}

View File

@@ -1,13 +1,11 @@
[Unit]
Description=UGREEN LEDs daemon for monitoring diskio and blinking corresponding LEDs
After=systemd-modules-load.service
Requires=systemd-modules-load.service
After=ugreen-probe-leds.service
Requires=ugreen-probe-leds.service
[Service]
ExecStartPre=/usr/bin/ugreen-probe-leds
ExecStart=/usr/bin/ugreen-diskiomon
StandardOutput=journal
[Install]
WantedBy=multi-user.target

View File

@@ -1,13 +1,11 @@
[Unit]
Description=UGREEN LEDs daemon for monitoring netio (of %i) and blinking corresponding LEDs
After=systemd-modules-load.service
Requires=systemd-modules-load.service
After=ugreen-probe-leds.service
Requires=ugreen-probe-leds.service
[Service]
ExecStartPre=/usr/bin/ugreen-probe-leds
ExecStart=/usr/bin/ugreen-netdevmon %i
StandardOutput=journal
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,13 @@
[Unit]
Description=UGREEN LEDs daemon for configuring power LED
After=ugreen-probe-leds.service
Requires=ugreen-probe-leds.service
[Service]
Type=oneshot
ExecStart=/usr/bin/ugreen-power-led
RemainAfterExit=true
StandardOutput=journal
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,13 @@
[Unit]
Description=UGREEN LED initial hardware probing service
After=systemd-modules-load.service
Requires=systemd-modules-load.service
[Service]
Type=oneshot
ExecStart=/usr/bin/ugreen-probe-leds
RemainAfterExit=true
StandardOutput=journal
[Install]
WantedBy=multi-user.target

View File

@@ -274,10 +274,13 @@ if [ "$CHECK_SMART" = true ]; then
dev=${devices[$led]}
/usr/sbin/smartctl -H /dev/${dev} -n standby,1 &> /dev/null
# read the smart status return code, but ignore if the drive is on standby
/usr/sbin/smartctl -H /dev/${dev} -n standby,0 &> /dev/null
RET=$?
if [[ $RET -gt 1 ]]; then
# check return code for critical errors (any bit set except bit 5)
# possible return bits set: https://invent.kde.org/system/kpmcore/-/merge_requests/28
if (( $RET & ~32 )); then
echo "$COLOR_SMART_FAIL" > /sys/class/leds/$led/color
echo Disk failure detected on /dev/$dev at $(date +%Y-%m-%d' '%H:%M:%S)
continue

View File

@@ -110,3 +110,17 @@ COLOR_NETDEV_LINK_10000="255 255 255"
# color of the netdev when unable to ping the gateway
COLOR_NETDEV_GATEWAY_UNREACHABLE="255 0 0"
# =========== parameters for power LED ===========
# Blink settings for the power LED
# * none: no blinking (default)
# * breath <delay_on> <delay_off>: breathing blink
# * blink <delay_on> <delay_off>: blinking
BLINK_TYPE_POWER="none"
# brighness of the power LED (default: 255)
BRIGHTNESS_POWER=255
# color of the power LED (default: 255 255 255)
COLOR_POWER="255 255 255"

30
scripts/ugreen-power-led Normal file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/bash
# use variables from config file (unRAID specific)
if [[ -f /boot/config/plugins/ugreenleds-driver/settings.cfg ]]; then
source /boot/config/plugins/ugreenleds-driver/settings.cfg
fi
# load environment variables
if [[ -f /etc/ugreen-leds.conf ]]; then
source /etc/ugreen-leds.conf
fi
# Blink settings for the power LED
# * none: no blinking
# * breath <delay_on> <delay_off>: breathing blink
# * blink <delay_on> <delay_off>: blinking
BLINK_TYPE_POWER=${BLINK_TYPE_POWER:="none"}
# brighness of the power LED
BRIGHTNESS_POWER=${BRIGHTNESS_POWER:=255}
# color of the power LED
COLOR_POWER=${COLOR_POWER:="255 255 255"}
# initialize LEDs
if [[ -d /sys/class/leds/power ]]; then
echo "$BLINK_TYPE_POWER" > /sys/class/leds/power/blink_type
echo "$BRIGHTNESS_POWER" > /sys/class/leds/power/brightness
echo "$COLOR_POWER" > /sys/class/leds/power/color
fi