aura_led: add driver for Asus Aura LED USB controllers (#456)
This commit is contained in:
parent
71aca8703c
commit
622d83c428
|
@ -5,3 +5,4 @@
|
|||
/dist/
|
||||
__pycache__/
|
||||
liquidctl/_version.py
|
||||
.DS_Store
|
14
README.md
14
README.md
|
@ -78,7 +78,7 @@ Corsair Vengeance RGB DIMM4
|
|||
1. [Automation and running at boot]
|
||||
1. [Set up Linux using systemd]
|
||||
1. [Set up Windows using Task Scheduler]
|
||||
1. [Set up macOS using launchd]
|
||||
1. [Set up macOS using various methods]
|
||||
1. [Troubleshooting]
|
||||
1. [Additional documentation]
|
||||
1. [License]
|
||||
|
@ -139,7 +139,8 @@ The notes are sorted alphabetically, major (upper case) notes before minor
|
|||
| Graphics card RGB | [ASUS Strix RTX 2080 Ti OC](docs/nvidia-guide.md) | I²C | <sup>_Ux_</sup> |
|
||||
| Graphics card RGB | [Additional ASUS GTX and RTX cards](docs/nvidia-guide.md) | I²C | <sup>_Uex_</sup> |
|
||||
| Graphics card RGB | [EVGA GTX 1080 FTW](docs/nvidia-guide.md) | I²C | <sup>_Ux_</sup> |
|
||||
| Graphics card RGB | [Additional EVGA GTX 1070 and 1070 Ti cards](docs/nvidia-guide.md) | I²C | <sup>_Uex_</sup> |
|
||||
| Graphics card RGB | [Additional EVGA GTX 1070 and 1070 Ti cards](docs/nvidia-guide.md) | I²C | <sup>_Uenx_</sup> |
|
||||
| Motherboard | [Asus Aura LED motherboards](docs/asus-aura-led-guide.md) | USB HID | <sup>_e_</sup> |
|
||||
| Motherboard | [Gigabyte RGB Fusion 2.0 motherboards](docs/gigabyte-rgb-fusion2-guide.md) | USB HID | |
|
||||
| Power supply | [Corsair HX750i, HX850i, HX1000i, HX1200i](docs/corsair-hxi-rmi-psu-guide.md) | USB HID | <sup>_h_</sup> |
|
||||
| Power supply | [Corsair RM650i, RM750i, RM850i, RM1000i](docs/corsair-hxi-rmi-psu-guide.md) | USB HID | <sup>_h_</sup> |
|
||||
|
@ -705,10 +706,13 @@ A slightly more complex example can be seen in [issue #14](https://github.com/li
|
|||
|
||||
As an alternative to using Task Scheduler, the batch file can simply be placed in the startup folder; you can run `shell:startup` to [find out where that is](https://support.microsoft.com/en-us/help/4026268/windows-10-change-startup-apps).
|
||||
|
||||
### Set up macOS using launchd
|
||||
[Set up macOS using launchd]: #set-up-macos-using-launchd
|
||||
### Set up macOS using various methods
|
||||
[Set up macOS using various methods]: #set-up-macos-using-various-methods
|
||||
|
||||
You can use a shell script and launchd to automatically configure your devices during login or after waking from sleep. A [detailed guide](https://www.tonymacx86.com/threads/gigabyte-z490-vision-d-thunderbolt-3-i5-10400-amd-rx-580.298642/page-24#post-2138475) is available on tonymacx86.
|
||||
You can follow either or both of the guides below to automatically configure your devices during login or after waking from sleep. The guides are hosted on tonymacx86:
|
||||
|
||||
- [This guide](https://www.tonymacx86.com/threads/gigabyte-z490-vision-d-thunderbolt-3-i5-10400-amd-rx-580.298642/post-2138475) is for controllers that lose their state during sleep (e.g. Gigabyte RGB Fusion 2.0) and need to be reinitialized after wake-from-sleep. This guide uses _Automator_ to initialize supported devices at login, and _sleepwatcher_ to initialize supported devices after wake-from-sleep.
|
||||
- [This guide](https://www.tonymacx86.com/threads/asus-z690-proart-creator-wifi-thunderbolt-4-i7-12700k-amd-rx-6800-xt.318311/post-2306524) is for controllers that do not lose their state during sleep (e.g. Asus Aura LED). This driver uses the _launchctl_ method to initialize supported devices at login.
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
# Asus Aura LED (USB-based) controllers
|
||||
_Driver API and source code available in [`liquidctl.driver.aura_led`](../liquidctl/driver/aura_led.py)._
|
||||
|
||||
__NOTE:__
|
||||
This driver is marked `experimental` because of the need for additional testing and feedback by the community. The API is unstable and may be changed in the future.
|
||||
|
||||
|
||||
This driver supports Asus Aura USB-based lighting controllers that appear in various Asus Z490, Z590, and Z690 motherboards. These controllers operate in either (a) direct mode or (b) effect mode. _Direct_ mode is employed by Aura Crate in Windows. It requires the application to send a continuous stream of commands to the controller in order to modulate lighting effects on each addressable LED. The other mode is _effect_ mode in which the controller itself modulates lighting effects on each addressable LED. Effect mode requires the application to issue a single set of command codes to the controller in order to initiate the given effect. The controller continues to process that effect until the application sends a different command.
|
||||
|
||||
This driver employs the _effect_ mode (fire and forget). The selected lighting mode remains in effect until it is explicitly changed. This means the selected lighting mode remains in effect (a) on cold boot, (b) on warm boot, (c) after wake-from-sleep.
|
||||
|
||||
The disadvantage, however, is the inability to set different lighting modes to different lighting channels. All channels remain synchronized.
|
||||
|
||||
There are three known variants of the Aura LED USB-based controller:
|
||||
|
||||
- Device `0x19AF`: found in Asus ProArt Z690-Creator WiFi
|
||||
- Device `0x1939` [^1]
|
||||
- Device `0x18F3`[^1]: found in Asus ROG Maximus Z690 Formula
|
||||
|
||||
[^1]: Support for devices `0x1939` and `0x18F3` may not be sufficiently developed so users are asked to experiment and provide feedback. [Wireshark USB traffic capture](./developer/capturing-usb-traffic.md), in particular, will be very helpful.
|
||||
|
||||
|
||||
## Initialization
|
||||
|
||||
Asus Aura LED controller does not need to be initialized before use. Initialization is optional.
|
||||
|
||||
```
|
||||
# liquidctl -m Aura initialize
|
||||
AsusTek Aura LED Controller
|
||||
└── Firmware version AULA3-AR32-0207
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
The `status` function returns the number of ARGB and RGB channels detected by the controller. If the command is invoked with `--debug` flag, the entire reply from the controller will be displayed in groups of 6 bytes. This information has not been fully decoded, but is provided in the event that someone is able to decipher it.
|
||||
|
||||
On Asus ProArt Z690-Creator WiFi the following is returned:
|
||||
```
|
||||
% liquidctl -m Aura status
|
||||
AsusTek Aura LED Controller (experimental)
|
||||
├── ARGB channels: 2
|
||||
└── RGB channels: 1
|
||||
```
|
||||
|
||||
To display the set of 6-byte status values, use `--debug` on the command line. The following will be returned:
|
||||
|
||||
```
|
||||
% liquidctl --debug -m Aura status
|
||||
AsusTek Aura LED Controller (experimental)
|
||||
├── ARGB channels: 2
|
||||
├── RGB channels: 1
|
||||
├── Device Config: 1 0x1e, 0x9f, 0x02, 0x01, 0x00, 0x00
|
||||
├── Device Config: 2 0x78, 0x3c, 0x00, 0x01, 0x00, 0x00
|
||||
├── Device Config: 3 0x78, 0x3c, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 4 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 5 0x00, 0x00, 0x00, 0x01, 0x03, 0x02
|
||||
├── Device Config: 6 0x01, 0xf4, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 7 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 8 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 9 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
└── Device Config: 10 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
```
|
||||
|
||||
On Asus ROG Strix Z690-i Gaming WiFi (mini-ITX) the following is returned:
|
||||
```
|
||||
% liquidctl --debug -m Aura status
|
||||
AsusTek Aura LED Controller (experimental)
|
||||
├── ARGB channels: 2
|
||||
├── RGB channels: 1
|
||||
├── Device Config: 1 0x1e, 0x9f, 0x02, 0x01, 0x00, 0x00
|
||||
├── Device Config: 2 0x78, 0x3c, 0x00, 0x01, 0x00, 0x00
|
||||
├── Device Config: 3 0x78, 0x3c, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 4 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 5 0x00, 0x00, 0x00, 0x01, 0x03, 0x02
|
||||
├── Device Config: 6 0x01, 0xf4, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 7 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 8 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 9 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
└── Device Config: 10 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
```
|
||||
|
||||
On some Asus Z490 boards (controller ID 0x18F3) the following is returned:
|
||||
```
|
||||
% liquidctl --debug -m Aura status
|
||||
AsusTek Aura LED Controller (experimental)
|
||||
├── ARGB channels: 1
|
||||
├── RGB channels: 1
|
||||
├── Device Config: 1 0x1e, 0x9f, 0x01, 0x01, 0x00, 0x00
|
||||
├── Device Config: 2 0x78, 0x3c, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 3 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 4 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 5 0x00, 0x00, 0x00, 0x06, 0x07, 0x02
|
||||
├── Device Config: 6 0x01, 0xf4, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 7 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 8 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
├── Device Config: 9 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
└── Device Config: 10 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
```
|
||||
|
||||
## RGB lighting
|
||||
|
||||
The driver supports one 12V RGB channel named `led1` and three 5V Addressable RGB channels named `led2`, `led3`, and `led4`. Because the driver uses `effect` mode, all channels are synchronized. It is not possible at this time to set different color modes to different channels (`direct` mode is used for that). Nevertheless, independent channel names are provided in case a future BIOS update provides more flexibility in `effect` mode.
|
||||
|
||||
```
|
||||
# liquidctl -m Aura set led1 color static af5a2f
|
||||
# liquidctl -m Aura set led2 color breathing 350017
|
||||
# liquidctl -m Aura set led3 color rainbow
|
||||
# liquidctl -m Aura set led4 color spectrum-cycle
|
||||
# liquidctl -m Aura set sync color gentle-transition
|
||||
```
|
||||
|
||||
Colors can be specified in RGB, HSV or HSL (see [Supported color specification formats](../README.md#supported-color-specification-formats)), and each animation mode supports zero or one color.
|
||||
|
||||
|
||||
| Mode | Colors | Notes |
|
||||
| --- | --- | --- |
|
||||
| `off` | None |
|
||||
| `static` | One |
|
||||
| `breathing` | One |
|
||||
| `flashing` | One |
|
||||
| `spectrum_cycle` | None |
|
||||
| `rainbow` | None |
|
||||
| `spectrum_cycle_breathing` | None |
|
||||
| `chase_fade` | One |
|
||||
| `spectrum_cycle_chase_fade` | None |
|
||||
| `chase` | One |
|
||||
| `spectrum_cycle_chase` | None |
|
||||
| `spectrum_cycle_wave` | None |
|
||||
| `chase_rainbow_pulse` | None |
|
||||
| `rainbow_flicker` | None |
|
||||
| `gentle_transition` | None | name given by us |
|
||||
| `wave_propagation` | None | name given by us |
|
||||
| `wave_propagation_pause` | None | name given by us |
|
||||
| `red_pulse` | None | name given by us |
|
||||
|
||||
In addition to these, it is also possible to use the `sync` pseudo-channel to apply a setting to all lighting channels.
|
|
@ -329,6 +329,15 @@ KERNEL=="i2c-*", ATTR{name}=="NVIDIA i2c adapter 1 *", ATTRS{vendor}=="0x10de",
|
|||
# Asetek 690LC (assuming NZXT Kraken X)
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2433", ATTRS{idProduct}=="b200", TAG+="uaccess"
|
||||
|
||||
# Asus Aura LED Controller
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0b05", ATTRS{idProduct}=="19af", TAG+="uaccess"
|
||||
|
||||
# Asus Aura LED Controller
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0b05", ATTRS{idProduct}=="1939", TAG+="uaccess"
|
||||
|
||||
# Asus Aura LED Controller
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0b05", ATTRS{idProduct}=="18f3", TAG+="uaccess"
|
||||
|
||||
# Corsair Commander Core
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b1c", ATTRS{idProduct}=="0c1c", TAG+="uaccess"
|
||||
|
||||
|
|
31
liquidctl.8
31
liquidctl.8
|
@ -579,6 +579,37 @@ When applicable the animation speed can be set with
|
|||
where the allowed values are: \fIslowest\fR, \fIslower\fR, \fInormal\fR,
|
||||
\fIfaster\fR, \fIfastest\fR.
|
||||
.
|
||||
.SS ASUS Aura USB LED 0x19AF Controller
|
||||
.SS ASUS Aura USB LED 0x1939 Controller
|
||||
.SS ASUS Aura USB LED 0x18F3 Controller
|
||||
Fan channels: none.
|
||||
.PP
|
||||
Lighting channels: \fIled[1\(en4]\fR, \fIsync\fR.
|
||||
.TS
|
||||
l c
|
||||
----
|
||||
l c .
|
||||
Mode #colors
|
||||
\fIoff\fR 0
|
||||
\fIstatic\fR 1
|
||||
\fIbreathing\fR 1
|
||||
\fIflashing\fR 1
|
||||
\fIspectrum_cycle\fR 0
|
||||
\fIrainbow\fR 0
|
||||
\fIspectrum_cycle_breathing\fR 0
|
||||
\fIchase_fade\fR 1
|
||||
\fIspectrum_cycle_chase_fade\fR 0
|
||||
\fIchase\fR 1
|
||||
\fIspectrum_cycle_chase\fR 0
|
||||
\fIspectrum_cycle_wave\fR 0
|
||||
\fIchase_rainbow_pulse\fR 0
|
||||
\fIrainbow_flicker\fR 0
|
||||
\fIgentle_transition\fR 0
|
||||
\fIwave_propagation\fR 0
|
||||
\fIwave_propagation_pause\fR 0
|
||||
\fIred_pulse\fR 0
|
||||
.TE
|
||||
.
|
||||
.SS Gigabyte RGB Fusion 2.0 5702 Controller
|
||||
.SS Gigabyte RGB Fusion 2.0 8297 Controller
|
||||
Fan channels: none.
|
||||
|
|
|
@ -25,6 +25,7 @@ from liquidctl.driver.base import BaseBus, find_all_subclasses
|
|||
# automatically enabled drivers
|
||||
from liquidctl.driver import asetek
|
||||
from liquidctl.driver import asetek_pro
|
||||
from liquidctl.driver import aura_led
|
||||
from liquidctl.driver import commander_core
|
||||
from liquidctl.driver import commander_pro
|
||||
from liquidctl.driver import corsair_hid_psu
|
||||
|
|
|
@ -0,0 +1,345 @@
|
|||
"""liquidctl driver for Asus Aura LED USB controllers.
|
||||
|
||||
Supported controllers:
|
||||
|
||||
- AsusTek Aura LED Controller: idVendor=0x0b05, idProduct=0x19af
|
||||
|
||||
This controller is found on Asus ProArt Z690-Creator WiFi and other boards
|
||||
|
||||
Additional controllers (requires user testing and feedback):
|
||||
|
||||
- idVendor=0x0B05, idProduct=0x1939
|
||||
- idVendor=0x0B05, idProduct=0x18F3
|
||||
|
||||
These controllers have not been sufficiently tested. Users are asked to test and
|
||||
report issues.
|
||||
|
||||
Limitations:
|
||||
|
||||
This controller only supports 'effect' modes that are managed entirely by the
|
||||
controller. The other mode is 'direct' that is managed by software, but this
|
||||
requires the continuous transmission of command codes to control dynamic lighting
|
||||
effects (i.e. all lighting modes in which colors either change or the LED
|
||||
blinks/fades/pulses).
|
||||
|
||||
In 'effect' mode, the software simply issues a 'fire-and-forget' command to the
|
||||
controller, which subsequently manages the lighting effect on its own. There are
|
||||
some limitations in 'effect' mode, as follows:
|
||||
|
||||
- Dynamic color modes (spectrum, rainbow, etc.) cannot be applied to individual
|
||||
color channels, but apply to all channels
|
||||
|
||||
- Static color modes (static) can be applied to individual color channels
|
||||
|
||||
- Off mode turns all channels off regardless of which channel is selected. However,
|
||||
individual channels can subsequently be enabled, while others will remain off.
|
||||
This applies only to static modes.
|
||||
|
||||
|
||||
Acknowledgements:
|
||||
|
||||
- Aura Addressable Header Controller (for list of color mode names)
|
||||
https://gitlab.com/cneil02/aura-addressable-header-controller
|
||||
|
||||
- OpenRGB Project (for list of color mode names)
|
||||
https://github.com/CalcProgrammer1/OpenRGB
|
||||
|
||||
- @dehjomz for discovering color modes 0x10, 0x11, 0x12, 0x13
|
||||
|
||||
Aura LED control codes were independently obtained from USB traffic captured using
|
||||
Wireshark on Windows. This Aura LED controller uses very different control codes
|
||||
from previous Aura LED controllers.
|
||||
|
||||
Copyright (C) 2022 CaseySJ
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
|
||||
from liquidctl.driver.usb import UsbHidDriver
|
||||
from liquidctl.error import NotSupportedByDevice
|
||||
from liquidctl.util import clamp
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
_READ_LENGTH = 65
|
||||
_WRITE_LENGTH = 65
|
||||
|
||||
_RESET_CHANNEL_ITERATIONS = 5 # Aura Crate software uses 38
|
||||
|
||||
_CMD_CODE = 0xEC
|
||||
|
||||
_FUNCTION_CODE = {
|
||||
"direct": [_CMD_CODE, 0x40],
|
||||
"end_direct": [_CMD_CODE, 0x35, 0x00, 0x00, 0x00, 0x00],
|
||||
"end_effect": [_CMD_CODE, 0x35, 0x00, 0x00, 0x00, 0xFF],
|
||||
"start_seq1": [_CMD_CODE, 0x35],
|
||||
"start_seq2": [_CMD_CODE, 0x36, 0x00],
|
||||
"end_seq1": [_CMD_CODE, 0x35, 0x00, 0x00, 0x01, 0x05],
|
||||
"end_seq2": [_CMD_CODE, 0x3F, 0x55, 0x00, 0x00],
|
||||
"reset_seq1": [_CMD_CODE, 0x38, 0x01, 0x01],
|
||||
"reset_seq2": [_CMD_CODE, 0x38, 0x00, 0x01],
|
||||
"channel_off_pre1": [_CMD_CODE, 0x38, 0x01, 0x00],
|
||||
"channel_off_pre2": [_CMD_CODE, 0x38, 0x00, 0x00],
|
||||
"channel_off_prefix": [_CMD_CODE, 0x35, 0x01, 0x00, 0x00, 0xFF],
|
||||
"firmware": [_CMD_CODE, 0x82],
|
||||
"config": [_CMD_CODE, 0xB0],
|
||||
}
|
||||
|
||||
# channel_type 0 designates RGB bus
|
||||
# channel_type 1 designates ARGB bus
|
||||
# "effect" mode channel IDs are different from "direct" mode channel IDs
|
||||
_ColorChannel = namedtuple(
|
||||
"_ColorChannel", ["name", "channel_id", "direct_channel_id", "channel_type", "rgb_offset"]
|
||||
)
|
||||
|
||||
_COLOR_CHANNELS = {
|
||||
channel.name: channel
|
||||
for channel in [
|
||||
_ColorChannel("led1", 0x01, 0x00, 0x00, 0x0), # rgb channel
|
||||
_ColorChannel("led2", 0x02, 0x00, 0x01, 0x01), # argb channel
|
||||
_ColorChannel("led3", 0x04, 0x01, 0x01, 0x02), # argb channel
|
||||
_ColorChannel("led4", 0x08, 0x02, 0x01, 0x03), # argb channel
|
||||
]
|
||||
}
|
||||
|
||||
_ColorMode = namedtuple("_ColorMode", ["name", "value", "takes_color"])
|
||||
|
||||
_COLOR_MODES = {
|
||||
mode.name: mode
|
||||
for mode in [
|
||||
_ColorMode("off", 0x00, takes_color=False),
|
||||
_ColorMode("static", 0x01, takes_color=True),
|
||||
_ColorMode("breathing", 0x02, takes_color=True),
|
||||
_ColorMode("flashing", 0x03, takes_color=True),
|
||||
_ColorMode("spectrum_cycle", 0x04, takes_color=False),
|
||||
_ColorMode("rainbow", 0x05, takes_color=False),
|
||||
_ColorMode("spectrum_cycle_breathing", 0x06, takes_color=False),
|
||||
_ColorMode("chase_fade", 0x07, takes_color=True),
|
||||
_ColorMode("spectrum_cycle_chase_fade", 0x08, takes_color=False),
|
||||
_ColorMode("chase", 0x09, takes_color=True),
|
||||
_ColorMode("spectrum_cycle_chase", 0x0A, takes_color=False),
|
||||
_ColorMode("spectrum_cycle_wave", 0x0B, takes_color=False),
|
||||
_ColorMode("chase_rainbow_pulse", 0x0C, takes_color=False),
|
||||
_ColorMode("rainbow_flicker", 0x0D, takes_color=False),
|
||||
_ColorMode("gentle_transition", 0x10, takes_color=False),
|
||||
_ColorMode("wave_propagation", 0x11, takes_color=False),
|
||||
_ColorMode("wave_propagation_pause", 0x12, takes_color=False),
|
||||
_ColorMode("red_pulse", 0x13, takes_color=False),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
class AuraLed(UsbHidDriver):
|
||||
"""
|
||||
liquidctl driver for Asus Aura LED USB controllers.
|
||||
This driver only supports 'effect' mode, hence no speed/color channels
|
||||
|
||||
Devices 0x1939 and 0x18F3 are not fully supported at this time; users are asked
|
||||
to experiment with this driver and provide feedback
|
||||
"""
|
||||
|
||||
SUPPORTED_DEVICES = [
|
||||
(0x0B05, 0x19AF, None, "AsusTek Aura LED Controller (experimental)", {}),
|
||||
(0x0B05, 0x1939, None, "AsusTek Aura LED Controller (experimental)", {}),
|
||||
(0x0B05, 0x18F3, None, "AsusTek Aura LED Controller (experimental)", {}),
|
||||
]
|
||||
|
||||
def initialize(self, **kwargs):
|
||||
"""Initialize the device.
|
||||
Returns a list of `(property, value, unit)` tuples, containing the
|
||||
firmware version and other useful information provided by the hardware.
|
||||
"""
|
||||
# Get firmware version
|
||||
self._write(_FUNCTION_CODE["firmware"])
|
||||
|
||||
# Build reply string
|
||||
status = []
|
||||
data = self.device.read(_READ_LENGTH)
|
||||
if data[1] == 0x02:
|
||||
status.append(("Firmware version", "".join(map(chr, data[2:17])), ""))
|
||||
else:
|
||||
status.append("Unexpected reply for firmware", "", "")
|
||||
return status
|
||||
|
||||
# This stops Direct mode if it was previously applied
|
||||
self._write(_FUNCTION_CODE["end_direct"])
|
||||
|
||||
"""
|
||||
Extra operations during initialization
|
||||
This is experimental and may not be necessary
|
||||
self._write([0xec, 0x31, 0x0d, 0x00]);
|
||||
self._write([0xec, 0xb1, 0x00, 0x00]);
|
||||
self.device.read(_READ_LENGTH)
|
||||
self._write([0xec, 0x31, 0x0e, 0x00]);
|
||||
self._write([0xec, 0xb1, 0x00, 0x00]);
|
||||
self.device.read(_READ_LENGTH)
|
||||
self._write([0xec, 0x31, 0x0f, 0x00]);
|
||||
self._write([0xec, 0xb1, 0x00, 0x00]);
|
||||
self.device.read(_READ_LENGTH)
|
||||
self._write([0xec, 0x83, 0x00, 0x00]);
|
||||
self.device.read(_READ_LENGTH)
|
||||
"""
|
||||
return status
|
||||
|
||||
def get_status(self, **kwargs):
|
||||
"""Get a status report."""
|
||||
status = []
|
||||
# Get config table
|
||||
self._write(_FUNCTION_CODE["config"])
|
||||
data = self.device.read(_READ_LENGTH)
|
||||
if data[1] == 0x30:
|
||||
start_index = 4 # index of first record
|
||||
|
||||
argb_channels = data[start_index + 2]
|
||||
rgb_channels = data[start_index + 3]
|
||||
status.append(("ARGB channels: " + str(argb_channels), "", ""))
|
||||
status.append((" RGB channels: " + str(rgb_channels), "", ""))
|
||||
|
||||
if "debug" in kwargs and kwargs["debug"] == True:
|
||||
num = 6 # number of bytes per record
|
||||
count = 1
|
||||
while start_index + num < _READ_LENGTH:
|
||||
status.append(
|
||||
(
|
||||
"Device Config: " + str(count),
|
||||
", ".join(
|
||||
"0x{:02x}".format(x) for x in data[start_index : start_index + num]
|
||||
),
|
||||
"",
|
||||
)
|
||||
)
|
||||
start_index += num
|
||||
count += 1
|
||||
else:
|
||||
status.append("Unexpected reply for config", "", "")
|
||||
return status
|
||||
|
||||
def set_color(self, channel, mode, colors, speed="normal", **kwargs):
|
||||
"""Set the color mode for a specific channel.
|
||||
|
||||
`colors` should be an iterable of zero or one `[red, green, blue]`
|
||||
triples, where each red/green/blue component is a value in the range
|
||||
0–255.
|
||||
"""
|
||||
colors = iter(colors)
|
||||
if _COLOR_MODES[mode].takes_color:
|
||||
try:
|
||||
r, g, b = next(colors)
|
||||
single_color = (r, g, b)
|
||||
except:
|
||||
raise ValueError(f"one color required for this mode") from None
|
||||
else:
|
||||
single_color = (0, 0, 0)
|
||||
|
||||
if channel != "sync" and channel not in _COLOR_CHANNELS:
|
||||
message = "valid channels are "
|
||||
for chan in _COLOR_CHANNELS:
|
||||
message += chan + " "
|
||||
raise KeyError(message) from None
|
||||
return
|
||||
|
||||
"""
|
||||
This is experimental (it's an example of direct mode)
|
||||
if mode == 'off':
|
||||
self.channel_off(channel)
|
||||
self.reset_all_channels()
|
||||
return
|
||||
"""
|
||||
|
||||
if channel == "sync":
|
||||
selected_channels = _COLOR_CHANNELS.values()
|
||||
else:
|
||||
selected_channels = (_COLOR_CHANNELS[channel],)
|
||||
full_cmd_seq = [] # entire series of commands are added to this list
|
||||
|
||||
"""
|
||||
Experimental code for treating RGB channel differently from others
|
||||
if channel == "led1":
|
||||
cmd_tuple=self.construct_color_commands(channel, mode, single_color)
|
||||
self._write(cmd_tuple[0])
|
||||
self._write(cmd_tuple[1])
|
||||
self._write(_FUNCTION_CODE["end_seq2"])
|
||||
self.end_color_sequence()
|
||||
self._write(cmd_tuple[0])
|
||||
self._write(cmd_tuple[1])
|
||||
self._write(_FUNCTION_CODE["end_seq2"])
|
||||
else:
|
||||
"""
|
||||
|
||||
for chan in selected_channels:
|
||||
cmd_tuple = self.construct_color_commands(chan.name, mode, single_color)
|
||||
full_cmd_seq.append(cmd_tuple[0])
|
||||
full_cmd_seq.append(cmd_tuple[1])
|
||||
full_cmd_seq.append(_FUNCTION_CODE["end_seq2"])
|
||||
|
||||
"""
|
||||
Asus Aura Crate sends command sequence twice, but our tests show
|
||||
that this may be redundant. Nevertheless, let's keep this code here
|
||||
in case we need to send commands twice as well
|
||||
#full_cmd_seq.append(cmd_tuple[0])
|
||||
#full_cmd_seq.append(cmd_tuple[1])
|
||||
#full_cmd_seq.append(_FUNCTION_CODE["end_seq2"])
|
||||
"""
|
||||
|
||||
for cmd_seq in full_cmd_seq:
|
||||
self._write(cmd_seq)
|
||||
self.end_color_sequence()
|
||||
|
||||
def set_speed_profile(self, channel, profile, **kwargs):
|
||||
"""Not supported by this device."""
|
||||
raise NotSupportedByDevice()
|
||||
|
||||
def set_fixed_speed(self, channel, duty, **kwargs):
|
||||
"""Not supported by this device."""
|
||||
raise NotSupportedByDevice()
|
||||
|
||||
def reset_all_channels(self):
|
||||
"""Reset all LED channels."""
|
||||
for i in range(_RESET_CHANNEL_ITERATIONS):
|
||||
self._write(_FUNCTION_CODE["reset_seq1"])
|
||||
self._write(_FUNCTION_CODE["reset_seq2"])
|
||||
|
||||
def channel_off(self, channel):
|
||||
"""
|
||||
Uses direct mode to disable a specific channel
|
||||
"""
|
||||
self._write(_FUNCTION_CODE["end_effect"])
|
||||
|
||||
for i in range(_RESET_CHANNEL_ITERATIONS):
|
||||
self._write(_FUNCTION_CODE["channel_off_pre1"])
|
||||
self._write(_FUNCTION_CODE["channel_off_pre2"])
|
||||
|
||||
self._write(_FUNCTION_CODE["channel_off_prefix"])
|
||||
|
||||
# set all LEDs to off, 20 at a time
|
||||
for i in (0, 20, 40, 60, 80, 100):
|
||||
self._write(
|
||||
_FUNCTION_CODE["direct"]
|
||||
+ [_COLOR_CHANNELS[channel].direct_channel_id | (0x80 * (i == 100)), i, 20]
|
||||
)
|
||||
self.end_color_sequence()
|
||||
self._write(_FUNCTION_CODE["end_direct"])
|
||||
|
||||
def construct_color_commands(self, channel, mode, single_color):
|
||||
"""
|
||||
Create command strings for specified color channel
|
||||
"""
|
||||
mode = _COLOR_MODES[mode]
|
||||
channel_type = _COLOR_CHANNELS[channel].channel_type # 0=RGB, 1=ARGB
|
||||
channel_id = _COLOR_CHANNELS[channel].channel_id
|
||||
rgb_offset = _COLOR_CHANNELS[channel].rgb_offset
|
||||
data1 = _FUNCTION_CODE["start_seq1"] + [channel_type, 0x00, 0x00, mode.value]
|
||||
data2 = _FUNCTION_CODE["start_seq2"] + [channel_id, 0x00] + [0, 0, 0] * rgb_offset
|
||||
data2 += single_color
|
||||
return (data1, data2)
|
||||
|
||||
def end_color_sequence(self):
|
||||
self._write(_FUNCTION_CODE["end_seq1"])
|
||||
self._write(_FUNCTION_CODE["end_seq2"])
|
||||
|
||||
def _write(self, data):
|
||||
padding = [0x0] * (_WRITE_LENGTH - len(data))
|
||||
self.device.write(data + padding)
|
|
@ -0,0 +1,115 @@
|
|||
import pytest
|
||||
from _testutils import MockHidapiDevice, Report
|
||||
|
||||
from collections import deque
|
||||
|
||||
from liquidctl.driver.aura_led import AuraLed
|
||||
|
||||
# Sample data for Aura LED controller from Asus ProArt Z690-Creator WiFi
|
||||
_INIT_19AF_FIRMWARE_DATA = bytes.fromhex(
|
||||
"ec0241554c41332d415233322d30323037000000000000000000000000000000"
|
||||
"000000000000000000000000000000000000000000000000000000000000000000"
|
||||
)
|
||||
_INIT_19AF_FIRMWARE = Report(_INIT_19AF_FIRMWARE_DATA[0], _INIT_19AF_FIRMWARE_DATA[1:])
|
||||
|
||||
_INIT_19AF_CONFIG_DATA = bytes.fromhex(
|
||||
"ec3000001e9f03010000783c00010000783c00010000783c0000000000000001"
|
||||
"040201f40000000000000000000000000000000000000000000000000000000000"
|
||||
)
|
||||
_INIT_19AF_CONFIG = Report(_INIT_19AF_CONFIG_DATA[0], _INIT_19AF_CONFIG_DATA[1:])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mockAuraLed_19AFDevice():
|
||||
device = MockHidapiDevice(vendor_id=0x0B05, product_id=0x19AF, address="addr")
|
||||
dev = AuraLed(device, "mock Aura LED Controller")
|
||||
dev.connect()
|
||||
return dev
|
||||
|
||||
|
||||
def test_aura_led_19AF_device_command_format(mockAuraLed_19AFDevice):
|
||||
mockAuraLed_19AFDevice.device.preload_read(_INIT_19AF_FIRMWARE)
|
||||
mockAuraLed_19AFDevice.device.preload_read(_INIT_19AF_CONFIG)
|
||||
mockAuraLed_19AFDevice.initialize() # should perform 3 writes
|
||||
mockAuraLed_19AFDevice.set_color(
|
||||
channel="sync", mode="off", colors=[]
|
||||
) # should perform 14 writes
|
||||
assert len(mockAuraLed_19AFDevice.device.sent) == 2 + 14
|
||||
for i, (report, data) in enumerate(mockAuraLed_19AFDevice.device.sent):
|
||||
assert report == 0xEC
|
||||
assert len(data) == 64
|
||||
|
||||
|
||||
def test_aura_led_19AF_device_get_status(mockAuraLed_19AFDevice):
|
||||
mockAuraLed_19AFDevice.device.preload_read(_INIT_19AF_CONFIG)
|
||||
assert mockAuraLed_19AFDevice.get_status() != []
|
||||
|
||||
|
||||
def test_aura_led_19AF_device_initialize_status(mockAuraLed_19AFDevice):
|
||||
mockAuraLed_19AFDevice.device.preload_read(_INIT_19AF_FIRMWARE)
|
||||
mockAuraLed_19AFDevice.device.preload_read(_INIT_19AF_CONFIG)
|
||||
status_list = mockAuraLed_19AFDevice.initialize()
|
||||
firmware_tuple = status_list[0]
|
||||
assert firmware_tuple[1] == "AULA3-AR32-0207"
|
||||
|
||||
|
||||
def test_aura_led_19AF_device_off_with_some_channel(mockAuraLed_19AFDevice):
|
||||
colors = [[0xFF, 0, 0x80]] # should be ignored
|
||||
mockAuraLed_19AFDevice.set_color(channel="led2", mode="off", colors=iter(colors))
|
||||
assert len(mockAuraLed_19AFDevice.device.sent) == 5
|
||||
data1 = mockAuraLed_19AFDevice.device.sent[0].data
|
||||
data2 = mockAuraLed_19AFDevice.device.sent[1].data
|
||||
assert data1[1] == 0x01 # key for led2
|
||||
assert data1[4] == 0x00 # off
|
||||
assert data2[2] == 0x02 # channel led2
|
||||
assert data2[7:10] == [0x00, 0x00, 0x00]
|
||||
|
||||
|
||||
def test_aura_led_19AF_static_with_some_channel(mockAuraLed_19AFDevice):
|
||||
colors = [[0xFF, 0, 0x80], [0x30, 0x30, 0x30]] # second color should be ignored
|
||||
mockAuraLed_19AFDevice.set_color(channel="led2", mode="static", colors=iter(colors))
|
||||
assert len(mockAuraLed_19AFDevice.device.sent) == 5
|
||||
data1 = mockAuraLed_19AFDevice.device.sent[0].data
|
||||
data2 = mockAuraLed_19AFDevice.device.sent[1].data
|
||||
assert data1[1] == 0x01 # key for led2
|
||||
assert data1[4] == 0x01 # static mode
|
||||
assert data2[2] == 0x02 # channel led2
|
||||
assert data2[7:10] == [0xFF, 0x00, 0x80]
|
||||
|
||||
|
||||
def test_aura_led_19AF_spectrum_cycle_with_some_channel(mockAuraLed_19AFDevice):
|
||||
colors = [[0xFF, 0, 0x80], [0x30, 0x30, 0x30]] # second color should be ignored
|
||||
mockAuraLed_19AFDevice.set_color(channel="led3", mode="spectrum_cycle", colors=iter(colors))
|
||||
assert len(mockAuraLed_19AFDevice.device.sent) == 5
|
||||
data1 = mockAuraLed_19AFDevice.device.sent[0].data
|
||||
data2 = mockAuraLed_19AFDevice.device.sent[1].data
|
||||
assert data1[1] == 0x01 # key for led3
|
||||
assert data1[4] == 0x04 # spectrum cycle
|
||||
assert data2[2] == 0x04 # channel led3
|
||||
assert data2[7:10] == [0x00, 0x00, 0x00]
|
||||
|
||||
|
||||
def test_aura_led_19AF_device_sync_channel(mockAuraLed_19AFDevice):
|
||||
colors = [[0xFF, 0, 0x80]]
|
||||
mockAuraLed_19AFDevice.set_color(channel="sync", mode="static", colors=iter(colors))
|
||||
assert len(mockAuraLed_19AFDevice.device.sent) == 14 # 14 writes
|
||||
|
||||
|
||||
def test_aura_led_19AF_device_invalid_set_color_arguments(mockAuraLed_19AFDevice):
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
mockAuraLed_19AFDevice.set_color("invalid", "off", [])
|
||||
|
||||
with pytest.raises(KeyError):
|
||||
mockAuraLed_19AFDevice.set_color("led2", "invalid", [])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
mockAuraLed_19AFDevice.set_color("led3", "static", [])
|
||||
|
||||
|
||||
def test_aura_led_19AF_device_initialize_status(mockAuraLed_19AFDevice):
|
||||
mockAuraLed_19AFDevice.device.preload_read(_INIT_19AF_FIRMWARE)
|
||||
mockAuraLed_19AFDevice.device.preload_read(_INIT_19AF_CONFIG)
|
||||
status_list = mockAuraLed_19AFDevice.initialize()
|
||||
firmware_tuple = status_list[0]
|
||||
assert firmware_tuple[1] == "AULA3-AR32-0207"
|
Loading…
Reference in New Issue