├── contrib ├── etc │ ├── modules-load.d │ │ └── nuc_led.conf │ └── modprobe.d │ │ └── nuc_led.conf ├── nuc_wmi │ ├── setup.cfg │ ├── python │ │ ├── test │ │ │ ├── __init__.py │ │ │ └── unit │ │ │ │ ├── nuc_wmi │ │ │ │ ├── __init__.py │ │ │ │ ├── cli │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── led_app_notification_test.py │ │ │ │ │ ├── switch_led_type_test.py │ │ │ │ │ ├── version_test.py │ │ │ │ │ ├── set_led_indictor_option_test.py │ │ │ │ │ ├── get_led_test.py │ │ │ │ │ ├── set_led_test.py │ │ │ │ │ └── query_led_test.py │ │ │ │ ├── utils_test.py │ │ │ │ ├── led_app_notification_test.py │ │ │ │ ├── switch_led_type_test.py │ │ │ │ ├── get_led_test.py │ │ │ │ ├── version_test.py │ │ │ │ ├── set_led_indicator_option_test.py │ │ │ │ ├── set_led_control_item_test.py │ │ │ │ ├── control_file_test.py │ │ │ │ ├── set_led_test.py │ │ │ │ ├── get_led_new_test.py │ │ │ │ └── query_led_test.py │ │ │ │ └── __init__.py │ │ └── nuc_wmi │ │ │ ├── cli │ │ │ ├── __init__.py │ │ │ ├── led_app_notification.py │ │ │ ├── version.py │ │ │ ├── switch_led_type.py │ │ │ ├── get_led.py │ │ │ ├── set_led_indicator_option.py │ │ │ ├── set_led.py │ │ │ ├── set_led_control_item.py │ │ │ ├── get_led_new.py │ │ │ └── query_led.py │ │ │ ├── utils.py │ │ │ ├── switch_led_type.py │ │ │ ├── led_app_notification.py │ │ │ ├── get_led.py │ │ │ ├── set_led_indicator_option.py │ │ │ ├── version.py │ │ │ ├── set_led_control_item.py │ │ │ ├── set_led.py │ │ │ ├── control_file.py │ │ │ ├── get_led_new.py │ │ │ ├── query_led.py │ │ │ └── __init__.py │ ├── .gitignore │ ├── Makefile │ ├── setup.py │ └── README.md └── reference │ ├── INTEL_WMI_LED_0.64.pdf │ ├── Intel_NUC7_WMI_webpage.pdf │ └── WMI-Spec-Intel-NUC-NUC10ixFNx.pdf ├── .gitignore ├── dkms.conf ├── .travis.yml ├── Makefile ├── PKGBUILD ├── README.md └── nuc_led.c /contrib/etc/modules-load.d/nuc_led.conf: -------------------------------------------------------------------------------- 1 | nuc_led -------------------------------------------------------------------------------- /contrib/nuc_wmi/setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal=1 -------------------------------------------------------------------------------- /contrib/etc/modprobe.d/nuc_led.conf: -------------------------------------------------------------------------------- 1 | options nuc_led nuc_led_perms=0664 nuc_led_gid=0 nuc_led_uid=0 -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python testing modules for unit testing and integration testing. 3 | """ 4 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi` module provides nose unit tests for `nuc_wmi`. 3 | """ 4 | -------------------------------------------------------------------------------- /contrib/reference/INTEL_WMI_LED_0.64.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uboslinux/intel-nuc-led/HEAD/contrib/reference/INTEL_WMI_LED_0.64.pdf -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python unit testing module targeting testing all nuc_wmi using unittest and nose. 3 | """ 4 | -------------------------------------------------------------------------------- /contrib/reference/Intel_NUC7_WMI_webpage.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uboslinux/intel-nuc-led/HEAD/contrib/reference/Intel_NUC7_WMI_webpage.pdf -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/cli/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.cli` provides the CLI interfaces for the exposed Intel NUC WMI functions. 3 | """ 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.ko 3 | *.order 4 | *.symvers 5 | *.cmd 6 | *.mod.c 7 | .tmp_versions/ 8 | *~ 9 | pkg/ 10 | src/ 11 | intel-nuc-led-*.pkg* 12 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/cli/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.cli` module provides nose unit tests for `nuc_wmi.cli`. 3 | """ 4 | -------------------------------------------------------------------------------- /dkms.conf: -------------------------------------------------------------------------------- 1 | AUTOINSTALL="yes" 2 | BUILT_MODULE_NAME[0]="nuc_led" 3 | DEST_MODULE_LOCATION[0]="/extra" 4 | PACKAGE_NAME="intel-nuc-led" 5 | PACKAGE_VERSION=1.0 -------------------------------------------------------------------------------- /contrib/reference/WMI-Spec-Intel-NUC-NUC10ixFNx.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uboslinux/intel-nuc-led/HEAD/contrib/reference/WMI-Spec-Intel-NUC-NUC10ixFNx.pdf -------------------------------------------------------------------------------- /contrib/nuc_wmi/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | deb_dist/ 4 | python/nuc_wmi.egg-info/ 5 | *~ 6 | *.pyc 7 | .coverage 8 | python/cover/ 9 | __pycache__/ 10 | nuc_wmi-*.tar.gz 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: c 3 | compiler: gcc 4 | 5 | dist: trusty 6 | 7 | before_install: 8 | - sudo apt-get update -q 9 | - sudo apt-get install -y linux-headers-$(uname -r) 10 | 11 | script: 12 | - make 13 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/Makefile: -------------------------------------------------------------------------------- 1 | clean: 2 | rm -rf .coverage build/ deb_dist/ dist/ python/cover python/nuc_wmi.egg-info nuc_wmi-*.tar.gz 3 | find . -type f -name "*~" -exec rm {} + 4 | find . -type f -name "*.pyc" -exec rm {} + 5 | find . -type d -name "__pycache__" -exec rmdir {} + 6 | deb: 7 | DEB_BUILD_OPTIONS=nocheck python setup.py --command-packages=stdeb.command bdist_deb 8 | nosetests: 9 | python setup.py nosetests --cover-branches --cover-html --cover-html-dir ./cover --cover-package nuc_wmi -d -s -v --with-coverage --py3where python/ 10 | test: 11 | python setup.py test 12 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.utils` provides utility functions for the WMI functions. 3 | """ 4 | 5 | def byte_list_to_bitmap(int_byte_list): 6 | """ 7 | Turns an list of integer bytes into a bitmap string. 8 | 9 | Args: 10 | int_byte_list: List of integers to be converted into a bitmap string. May 11 | be int strings. Integers must be 0-255. 12 | Exceptions: 13 | Raises `ValueError` for int conversion error. 14 | Returns: 15 | Bitmap string of the integer byte list. 16 | """ 17 | 18 | for int_byte in int_byte_list: 19 | if int(int_byte) < 0 or int(int_byte) > 255: 20 | raise ValueError('int byte values must be 0-255') 21 | 22 | return ''.join(["{0:b}".format(int(int_byte)).zfill(8) for int_byte in int_byte_list or [0]]) 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | obj-m := nuc_led.o 2 | 3 | KVERSION := $(shell uname -r) 4 | KDIR := /lib/modules/$(KVERSION)/build 5 | PWD := $(shell pwd) 6 | 7 | .PHONY: clean default dkms-add dkms-build dkms-deb dkms-install dkms-rpm dkms-uninstall install 8 | 9 | clean: 10 | $(MAKE) -C $(KDIR) M=$(PWD) clean 11 | 12 | default: 13 | $(MAKE) -C $(KDIR) M=$(PWD) modules 14 | 15 | dkms-add: 16 | dkms add --force $(PWD) 17 | 18 | dkms-build: dkms-add 19 | dkms build -m intel-nuc-led -v 1.0 20 | 21 | dkms-deb: dkms-add 22 | dkms mkdeb intel-nuc-led/1.0 --source-only 23 | 24 | dkms-install: dkms-build 25 | dkms install -m intel-nuc-led -v 1.0 26 | @depmod -a $(KVERSION) 27 | 28 | dkms-rpm: dkms-add 29 | dkms mkrpm intel-nuc-led/1.0 --source-only 30 | 31 | dkms-status: 32 | dkms status intel-nuc-led/1.0 33 | 34 | dkms-uninstall: 35 | dkms remove -m intel-nuc-led -v 1.0 --all 36 | rm -rf /usr/src/intel-nuc-led-1.0/ 37 | 38 | install: 39 | $(MAKE) -C $(KDIR) M=$(PWD) modules_install 40 | @depmod -a $(KVERSION) 41 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/switch_led_type.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.switch_led_type` provides an interface to the WMI switch led type function. 3 | """ 4 | 5 | from nuc_wmi import NucWmiError, RETURN_ERROR 6 | from nuc_wmi.control_file import read_control_file, write_control_file 7 | 8 | LED_COLOR_GROUP = [ 9 | 'Single color LED', 10 | 'Multi color LED' 11 | ] 12 | 13 | METHOD_ID=0x08 14 | 15 | def switch_led_type(led_color_group, control_file=None): 16 | """ 17 | Switches the LED color group type. 18 | 19 | Args: 20 | led_color_group: The LED color group type to set. 21 | Exceptions: 22 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 23 | or if `read_control_file` or `write_control_file` raise an exception. 24 | """ 25 | 26 | switch_led_byte_list = [METHOD_ID, led_color_group] 27 | 28 | write_control_file(switch_led_byte_list, control_file=control_file) 29 | 30 | ( 31 | error_code, 32 | reserved_byte_1, 33 | reserved_byte_2, 34 | reserved_byte_3 35 | ) = read_control_file(control_file=control_file) 36 | 37 | if error_code > 0: 38 | raise NucWmiError(RETURN_ERROR.get(error_code, 'Error (Unknown NUC WMI error code)')) 39 | -------------------------------------------------------------------------------- /PKGBUILD: -------------------------------------------------------------------------------- 1 | developer="https://indiecomputing.com/" 2 | url="https://github.com/uboslinux/intel_nuc_led" 3 | maintainer=${developer} 4 | pkgname=$(basename $(pwd)) 5 | pkgver=0.11 6 | pkgrel=11 7 | pkgdesc='Intel NUC LED Control for Linux' 8 | arch=('x86_64') 9 | license=('GPL3') 10 | _kernelver=6.7.5.arch1-1 11 | _kernelpath=6.7.5-arch1-1 12 | # Note: not the same: hyphen vs dot 13 | depends=("linux=${_kernelver}") 14 | makedepends=( 15 | "linux-headers=${_kernelver}" 16 | "python-setuptools" 17 | ) 18 | source=( 19 | "Makefile" 20 | "nuc_led.c" 21 | ) 22 | sha512sums=( 23 | 'SKIP' 24 | 'SKIP' 25 | ) 26 | 27 | build() { 28 | # Build kernel module 29 | cd ${srcdir} 30 | make KVERSION="$(uname -r)" default 31 | } 32 | 33 | package() { 34 | # Package kernel module 35 | install -m 0644 -D ${srcdir}/nuc_led.ko -t ${pkgdir}/usr/lib/modules/${_kernelpath}/extramodules/ 36 | 37 | install -m 0644 -D ${startdir}/contrib/etc/modprobe.d/nuc_led.conf -t ${pkgdir}/usr/lib/modprobe.d/ 38 | install -m 0644 -D ${startdir}/contrib/etc/modules-load.d/nuc_led.conf -t ${pkgdir}/usr/lib/modules-load.d/ 39 | 40 | find "${pkgdir}" -name '*.ko' -exec gzip -n {} + 41 | 42 | # Build and package userspace -- currently not working 43 | # cd ${startdir}/contrib/nuc_wmi 44 | # python setup.py install --root ${pkgdir} 45 | } 46 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/led_app_notification.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.led_app_notification` provides an interface to the WMI notification functions. 3 | """ 4 | 5 | from nuc_wmi import NucWmiError, RETURN_ERROR 6 | from nuc_wmi.control_file import read_control_file, write_control_file 7 | 8 | METHOD_ID=0x07 9 | 10 | NOTIFICATION_TYPE = [ 11 | None, 12 | 'save_led_config' 13 | ] 14 | 15 | def save_led_config(control_file=None): 16 | """ 17 | Send a save LED configuration LED app notification. 18 | 19 | Args: 20 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 21 | Exceptions: 22 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 23 | or if `read_control_file` or `write_control_file` raise an exception. 24 | """ 25 | 26 | notification_byte_list = [ 27 | METHOD_ID, 28 | NOTIFICATION_TYPE.index('save_led_config') 29 | ] 30 | 31 | write_control_file(notification_byte_list, control_file=control_file) 32 | 33 | ( 34 | error_code, 35 | reserved_byte_1, 36 | reserved_byte_2, 37 | reserved_byte_3 38 | ) = read_control_file(control_file=control_file) 39 | 40 | if error_code > 0: 41 | raise NucWmiError(RETURN_ERROR.get(error_code, 'Error (Unknown NUC WMI error code)')) 42 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/get_led.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.get_led` provides an interface to the WMI get led state function. 3 | """ 4 | 5 | from nuc_wmi import NucWmiError, RETURN_ERROR 6 | from nuc_wmi.control_file import read_control_file, write_control_file 7 | 8 | METHOD_ID=0x01 9 | 10 | def get_led(led, control_file=None): 11 | """ 12 | Get legacy LED state with regard to brightness, frequency, and color. 13 | 14 | Args: 15 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 16 | led: Selects the legacy LED to get the state for. 17 | Exceptions: 18 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 19 | or if `read_control_file` or `write_control_file` raise an exception. 20 | Returns: 21 | Tuple of legacy brightness, frequency, and color of the select LED. 22 | """ 23 | 24 | get_led_byte_list = [METHOD_ID, led] 25 | 26 | write_control_file(get_led_byte_list, control_file=control_file) 27 | 28 | ( 29 | error_code, 30 | brightness, 31 | frequency, 32 | color 33 | ) = read_control_file(control_file=control_file) 34 | 35 | if error_code > 0: 36 | raise NucWmiError(RETURN_ERROR.get(error_code, 'Error (Unknown NUC WMI error code)')) 37 | 38 | return tuple([brightness, frequency, color]) 39 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/set_led_indicator_option.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.set_led_indicator_option` provides an interface to the WMI set indicator function. 3 | """ 4 | 5 | from nuc_wmi import NucWmiError, RETURN_ERROR 6 | from nuc_wmi.control_file import read_control_file, write_control_file 7 | 8 | METHOD_ID = 0x05 9 | 10 | def set_led_indicator_option(led_type, led_indicator_option, control_file=None): 11 | """ 12 | Set the LED indicator option for the specified LED type, 13 | 14 | Args: 15 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 16 | led_indicator_option: The LED indicator option to set for the LED type. 17 | led_type: The LED type for which to set the LED indicator option. 18 | Exceptions: 19 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 20 | or if `read_control_file` or `write_control_file` raise an exception. 21 | """ 22 | 23 | set_led_indicator_option_byte_list = [METHOD_ID, led_type, led_indicator_option] 24 | 25 | write_control_file(set_led_indicator_option_byte_list, control_file=control_file) 26 | 27 | ( 28 | error_code, 29 | reserved_byte_1, 30 | reserved_byte_2, 31 | reserved_byte_3 32 | ) = read_control_file(control_file=control_file) 33 | 34 | if error_code > 0: 35 | raise NucWmiError(RETURN_ERROR.get(error_code, 'Error (Unknown NUC WMI error code)')) 36 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/version.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.version` provides an interface to the WMI version functions. 3 | """ 4 | 5 | from nuc_wmi import NucWmiError, RETURN_ERROR 6 | from nuc_wmi.control_file import read_control_file, write_control_file 7 | 8 | METHOD_ID=0x09 9 | 10 | VERSION_TYPE = [ 11 | 'wmi_interface_spec_compliance_version' 12 | ] 13 | 14 | def wmi_interface_spec_compliance_version(control_file=None): 15 | """ 16 | Returns the version for the WMI interface spec compliance. 17 | 18 | Args: 19 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 20 | Exceptions: 21 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 22 | or if `read_control_file` or `write_control_file` raise an exception. 23 | Returns: 24 | Tuple of two bytes representing the version number. 25 | """ 26 | 27 | wmi_version_byte_list = [ 28 | METHOD_ID, 29 | VERSION_TYPE.index('wmi_interface_spec_compliance_version') 30 | ] 31 | 32 | write_control_file(wmi_version_byte_list, control_file=control_file) 33 | 34 | ( 35 | error_code, 36 | version_byte_1, 37 | version_byte_2, 38 | reserved_byte 39 | ) = read_control_file(control_file=control_file) 40 | 41 | if error_code > 0: 42 | raise NucWmiError(RETURN_ERROR.get(error_code, 'Error (Unknown NUC WMI error code)')) 43 | 44 | return tuple([version_byte_2, version_byte_1]) 45 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/set_led_control_item.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.set_led_control_item` provides an interface to the WMI set control item function. 3 | """ 4 | 5 | from nuc_wmi import NucWmiError, RETURN_ERROR 6 | from nuc_wmi.control_file import read_control_file, write_control_file 7 | 8 | METHOD_ID = 0x06 9 | 10 | def set_led_control_item(led_type, led_indicator_option, control_item, control_item_value, control_file=None): 11 | """ 12 | Set the control item for the LED indicator option for the specified LED type, 13 | 14 | Args: 15 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 16 | control_item: The control item to set the value for. 17 | control_item_value: The value for the control item to set. 18 | led_indicator_option: The LED indicator option of the LED type for which to set the control item. 19 | led_type: The LED type for which to set the control item. 20 | Exceptions: 21 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 22 | or if `read_control_file` or `write_control_file` raise an exception. 23 | """ 24 | 25 | set_led_control_item_byte_list = [METHOD_ID, led_type, led_indicator_option, control_item, control_item_value] 26 | 27 | write_control_file(set_led_control_item_byte_list, control_file=control_file) 28 | 29 | ( 30 | error_code, 31 | reserved_byte_1, 32 | reserved_byte_2, 33 | reserved_byte_3 34 | ) = read_control_file(control_file=control_file) 35 | 36 | if error_code > 0: 37 | raise NucWmiError(RETURN_ERROR.get(error_code, 'Error (Unknown NUC WMI error code)')) 38 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/set_led.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.set_led` provides an interface to the WMI set led function. 3 | """ 4 | 5 | from nuc_wmi import NucWmiError, RETURN_ERROR 6 | from nuc_wmi.control_file import read_control_file, write_control_file 7 | 8 | METHOD_ID=0x02 9 | 10 | def set_led(led, brightness, frequency, color, control_file=None): 11 | """ 12 | Set LED state with regard to brightness, frequency, and color. 13 | 14 | Args: 15 | brightness: Controls the brightness level of the LED. 16 | color: Sets legacy RGB-color for LED. 17 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 18 | frequency: Sets the legacy LED frequency. 19 | led: Selects the legacy LED to set a state for. 20 | Exceptions: 21 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 22 | or if `read_control_file` or `write_control_file` raise an exception. 23 | """ 24 | 25 | set_led_byte_list = [METHOD_ID, led, brightness, frequency, color] 26 | 27 | write_control_file(set_led_byte_list, control_file=control_file) 28 | 29 | ( 30 | brightness_error, 31 | frequency_error, 32 | color_error, 33 | reserved_byte 34 | ) = read_control_file(control_file=control_file) 35 | 36 | if brightness_error > 0: 37 | raise NucWmiError(RETURN_ERROR.get(brightness_error, 'Error (Unknown NUC WMI error code)')) 38 | 39 | if frequency_error > 0: 40 | raise NucWmiError(RETURN_ERROR.get(frequency_error, 'Error (Unknown NUC WMI error code)')) 41 | 42 | if color_error > 0: 43 | raise NucWmiError(RETURN_ERROR.get(color_error, 'Error (Unknown NUC WMI error code)')) 44 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/cli/led_app_notification.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.cli.led_app_notification` provides a CLI interface to the WMI notification functions. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | from argparse import ArgumentParser 8 | from json import dumps 9 | from sys import exit 10 | 11 | from nuc_wmi import CONTROL_FILE 12 | from nuc_wmi.led_app_notification import save_led_config 13 | 14 | def save_led_config_cli(cli_args=None): 15 | """ 16 | Send a save LED configuration LED app notification. 17 | 18 | Args: 19 | cli_args: If provided, overrides the CLI args to use for `argparse`. 20 | CLI Options: 21 | --control_file : Sets the control file to use if provided, 22 | otherwise `nuc_wmi.CONTROL_FILE` is used. 23 | Outputs: 24 | stdout: JSON object with notification state or error message with 25 | failure error. 26 | Exit code: 27 | 0 on successfully sending the save led config notification or 1 on error. 28 | """ 29 | 30 | parser = ArgumentParser( 31 | description='Send a save LED configuration app notification.' 32 | ) 33 | 34 | parser.add_argument( 35 | '-c', 36 | '--control-file', 37 | default=None, 38 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 39 | ) 40 | 41 | try: 42 | args = parser.parse_args(args=cli_args) 43 | 44 | save_led_config(control_file=args.control_file) 45 | 46 | print( 47 | dumps( 48 | { 49 | 'led_app_notification': { 50 | 'type': 'save_led_config' 51 | } 52 | } 53 | ) 54 | ) 55 | except Exception as err: 56 | print(dumps({'error': str(err)})) 57 | 58 | exit(1) 59 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/cli/version.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.cli.version` provides a CLI interface to the WMI version functions. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | from argparse import ArgumentParser 8 | from json import dumps 9 | from sys import exit 10 | 11 | from nuc_wmi import CONTROL_FILE 12 | from nuc_wmi.version import wmi_interface_spec_compliance_version 13 | 14 | def wmi_interface_spec_compliance_version_cli(cli_args=None): 15 | """ 16 | Creates a CLI interface on top of the `nuc_wmi.version` `wmi_interface_spec_compliance_version` function. 17 | 18 | Args: 19 | cli_args: If provided, overrides the CLI args to use for `argparse`. 20 | CLI Options: 21 | --control_file : Sets the control file to use if provided, 22 | otherwise `nuc_wmi.CONTROL_FILE` is used. 23 | Outputs: 24 | stdout: JSON object with version and type or error message with 25 | failure error. 26 | Exit code: 27 | 0 on successfully retrieving the WMI interface spec compliance version or 1 on error. 28 | """ 29 | 30 | parser = ArgumentParser( 31 | description='Get the WMI interface spec compliance version.' 32 | ) 33 | 34 | parser.add_argument( 35 | '-c', 36 | '--control-file', 37 | default=None, 38 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 39 | ) 40 | 41 | try: 42 | args = parser.parse_args(args=cli_args) 43 | 44 | wmi_version = wmi_interface_spec_compliance_version(control_file=args.control_file) 45 | 46 | print( 47 | dumps( 48 | { 49 | 'version': { 50 | 'type': 'wmi_interface_spec_compliance', 51 | 'semver': '.'.join([str(semver_component) for semver_component in wmi_version]) 52 | } 53 | } 54 | ) 55 | ) 56 | except Exception as err: 57 | print(dumps({'error': str(err)})) 58 | 59 | exit(1) 60 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/cli/switch_led_type.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.cli.switch_led_type` provides a CLI interface to the WMI switch led type function. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | from argparse import ArgumentParser 8 | from json import dumps 9 | from sys import exit 10 | 11 | from nuc_wmi import CONTROL_FILE, LED_COLOR, LED_COLOR_TYPE, LED_BLINK_FREQUENCY, LED_TYPE 12 | from nuc_wmi.switch_led_type import LED_COLOR_GROUP, switch_led_type 13 | 14 | def switch_led_type_cli(cli_args=None): 15 | """ 16 | Creates a CLI interface on top of the `nuc_wmi.switch_led_type` `switch_led_type` function. 17 | 18 | Args: 19 | cli_args: If provided, overrides the CLI args to use for `argparse`. 20 | CLI Args: 21 | led_color_group: Selects the LED color group type to set.. 22 | CLI Options: 23 | --control_file : Sets the control file to use if provided, 24 | otherwise `nuc_wmi.CONTROL_FILE` is used. 25 | Outputs: 26 | stdout: JSON object with the selected LED color group type or error message with 27 | failure error. 28 | Exit code: 29 | 0 on successfully retrieving the selected LED state properties or 1 on error. 30 | """ 31 | 32 | parser = ArgumentParser( 33 | description='Switches the LED color group type.' 34 | ) 35 | 36 | parser.add_argument( 37 | '-c', 38 | '--control-file', 39 | default=None, 40 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 41 | ) 42 | parser.add_argument( 43 | 'led_color_group', 44 | choices=LED_COLOR_GROUP, 45 | help='The LED color group type to set the LEDs to.' 46 | ) 47 | 48 | try: 49 | args = parser.parse_args(args=cli_args) 50 | 51 | switch_led_type(LED_COLOR_GROUP.index(args.led_color_group), control_file=args.control_file) 52 | 53 | print( 54 | dumps( 55 | { 56 | 'led_color_group': { 57 | 'type': args.led_color_group 58 | } 59 | } 60 | ) 61 | ) 62 | except Exception as err: 63 | print(dumps({'error': str(err)})) 64 | 65 | exit(1) 66 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/cli/get_led.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.cli.get_led` provides a CLI interface to the WMI get led state function. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | from argparse import ArgumentParser 8 | from json import dumps 9 | from sys import exit 10 | 11 | from nuc_wmi import CONTROL_FILE, LED_COLOR, LED_COLOR_TYPE, LED_BLINK_FREQUENCY, LED_TYPE 12 | from nuc_wmi.get_led import get_led 13 | 14 | def get_led_cli(cli_args=None): 15 | """ 16 | Creates a CLI interface on top of the `nuc_wmi.get_led` `get_led` function. 17 | 18 | Args: 19 | cli_args: If provided, overrides the CLI args to use for `argparse`. 20 | CLI Args: 21 | led: Selects the legacy LED to get the state for. 22 | CLI Options: 23 | --control_file : Sets the control file to use if provided, 24 | otherwise `nuc_wmi.CONTROL_FILE` is used. 25 | Outputs: 26 | stdout: JSON object with brightness, frequency, and color of the selected LED or error message with 27 | failure error. 28 | Exit code: 29 | 0 on successfully retrieving the selected LED state properties or 1 on error. 30 | """ 31 | 32 | parser = ArgumentParser( 33 | description='Get legacy LED state with regard to brightness, frequency, and color.' 34 | ) 35 | 36 | parser.add_argument( 37 | '-c', 38 | '--control-file', 39 | default=None, 40 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 41 | ) 42 | parser.add_argument( 43 | 'led', 44 | choices=[led for led in LED_TYPE['legacy'] if led], 45 | help='The legacy LED for which to get the state.' 46 | ) 47 | 48 | try: 49 | args = parser.parse_args(args=cli_args) 50 | 51 | (brightness, frequency, color) = get_led(LED_TYPE['legacy'].index(args.led), control_file=args.control_file) 52 | 53 | print( 54 | dumps( 55 | { 56 | 'led': { 57 | 'type': args.led, 58 | 'brightness': str(brightness), 59 | 'frequency': LED_BLINK_FREQUENCY['legacy'][frequency], 60 | 'color': LED_COLOR['legacy'][LED_COLOR_TYPE['legacy'][args.led]][color] 61 | } 62 | } 63 | ) 64 | ) 65 | except Exception as err: 66 | print(dumps({'error': str(err)})) 67 | 68 | exit(1) 69 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/control_file.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.control_file` module provides interfaces for interacting with Intel NUC LED kernel module control file. 3 | """ 4 | 5 | from nuc_wmi import CONTROL_FILE, NucWmiError 6 | 7 | def read_control_file(control_file=None): 8 | """ 9 | Read the NUC LED control file hex bytes string. 10 | 11 | Args: 12 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 13 | Exceptions: 14 | Raises normal `IOError`/`OSError` on failure to read the control file, or `ValueError` for hex conversion error. 15 | Returns: 16 | Tuple of ints representing the hex numbers read in from the control file. 17 | """ 18 | 19 | with open(control_file or CONTROL_FILE, 'r') as fin: 20 | raw_hex_byte_string = fin.read() 21 | 22 | # Remove the new line and null char the driver leaves 23 | raw_hex_byte_string = raw_hex_byte_string.rstrip("\x00").rstrip("\n") 24 | 25 | byte_list = [int(hex_byte_str, 16) for hex_byte_str in raw_hex_byte_string.split(' ')] 26 | 27 | for hex_byte in byte_list: 28 | if hex_byte < 0 or hex_byte > 255: 29 | raise NucWmiError('NUC WMI returned hex byte outside of 0-255 range') 30 | 31 | if len(byte_list) != 4: 32 | raise NucWmiError('NUC WMI control file did not return an expected 4 bytes') 33 | 34 | return tuple(byte_list) 35 | 36 | 37 | def write_control_file(int_byte_list, control_file=None): 38 | """ 39 | Converts the integer byte list into a hex byte string and writes it to the NUC control file. 40 | 41 | Args: 42 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 43 | int_byte_list: List of integers bytes to be converted into a hex byte string to send to the NUC control file. May 44 | be int byte strings. Integers must be 0-255. 45 | Exceptions: 46 | Raises normal `IOError`/`OSError` on failure to read the control file, `ValueError` for hex conversion error, or 47 | `nuc_wmi.NucWmiError` for input value errors. 48 | """ 49 | 50 | for int_byte in int_byte_list: 51 | if int(int_byte) < 0 or int(int_byte) > 255: 52 | raise NucWmiError('Error (NUC LED byte values must be 0-255)') 53 | 54 | raw_hex_byte_string = ' '.join( 55 | ['{:02x}'.format(int(int_byte)) for int_byte in (int_byte_list + ([0] * (5 - len(int_byte_list))))] 56 | ) 57 | 58 | with open(control_file or CONTROL_FILE, 'w') as fout: 59 | fout.write(raw_hex_byte_string) 60 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/utils_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.utils_test` module provides unit tests for the functions in 3 | `nuc_wmi.utils`. 4 | 5 | Classes: 6 | TestUtils: A unit test class for the functions in `nuc_wmi.utils`. 7 | """ 8 | 9 | import unittest 10 | 11 | from nuc_wmi import NucWmiError 12 | from nuc_wmi.utils import byte_list_to_bitmap 13 | 14 | 15 | class TestUtils(unittest.TestCase): 16 | """ 17 | A unit test class for the functions of `nuc_wmi.utils` 18 | 19 | Methods: 20 | setUp: Unit test initialization. 21 | test_byte_list_to_bitmap: Tests that `byte_list_to_bitmap` raises the expected exception when any of the ints in 22 | byte list are outside of the 0-255 range, tests that it returns the proper bitmap 23 | string when the byte list is int or str, tests that returned bitmaps are properly 24 | explicitly padded to 8 bits, tests that we get an exception of the inputs are not 25 | proper ints. 26 | """ 27 | 28 | def setUp(self): 29 | """ 30 | Initializes the unit tests. 31 | """ 32 | 33 | self.maxDiff = None 34 | 35 | 36 | def test_byte_list_to_bitmap(self): 37 | """ 38 | Tests that `byte_list_to_bitmap` returns the expected exceptions, return values, or outputs. 39 | """ 40 | 41 | # Branch 1: Test that `byte_list_to_bitmap` raises an exception when any of the ints provided is outside the 42 | # 0-255 range. 43 | with self.assertRaises(ValueError) as err: 44 | byte_list_to_bitmap([0xFFF]) 45 | 46 | self.assertEqual(str(err.exception), 'int byte values must be 0-255') 47 | 48 | # Branch 2: Test that `byte_list_to_bitmap` returns the proper bitmap string regardless of whether the inputs 49 | # are ints or str. 50 | self.assertEqual( 51 | byte_list_to_bitmap([0x0D, 0x0E, 0x0A, 0x0D]), 52 | '00001101000011100000101000001101' 53 | ) 54 | 55 | self.assertEqual( 56 | byte_list_to_bitmap(['13', '14', '10', '13']), 57 | '00001101000011100000101000001101' 58 | ) 59 | 60 | # Branch 3: Tests that `byte_list_to_bitmap` explicitly pads each byte to 8 bits. 61 | self.assertEqual( 62 | byte_list_to_bitmap([0x00]), 63 | '00000000' 64 | ) 65 | 66 | # Branch 4: Test that `byte_list_bitmap` raises an exception when the input value is not an int 67 | with self.assertRaises(ValueError) as err: 68 | byte_list_to_bitmap(["0xZ"]) 69 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/cli/set_led_indicator_option.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.cli.set_led_indicator_option` provides a CLI interface to the WMI set led indicator function. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | from argparse import ArgumentParser 8 | from json import dumps 9 | from sys import exit 10 | 11 | from nuc_wmi import CONTROL_FILE, LED_INDICATOR_OPTION, LED_TYPE 12 | from nuc_wmi.set_led_indicator_option import set_led_indicator_option 13 | 14 | def set_led_indicator_option_cli(cli_args=None): 15 | """ 16 | Creates a CLI interface on top of the `nuc_wmi.set_led_indicator_option` `set_led_indicator_option` function. 17 | 18 | Args: 19 | cli_args: If provided, overrides the CLI args to use for `argparse`. 20 | CLI Args: 21 | led_indicator_option: The indicator option for the specified LED type for which to set the indicator option. 22 | led: Selects the LED to set the indicator option for. 23 | CLI Options: 24 | --control_file : Sets the control file to use if provided, 25 | otherwise `nuc_wmi.CONTROL_FILE` is used. 26 | Outputs: 27 | stdout: JSON object with the set indicator option of the selected LED or error message with 28 | failure error. 29 | Exit code: 30 | 0 on successfully setting the selected LED's indicator option or 1 on error. 31 | """ 32 | 33 | parser = ArgumentParser( 34 | description='Set the LED indicator option for the specified LED type,' 35 | ) 36 | 37 | parser.add_argument( 38 | '-c', 39 | '--control-file', 40 | default=None, 41 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 42 | ) 43 | parser.add_argument( 44 | 'led', 45 | choices=LED_TYPE['new'], 46 | help='The LED for which to set the indicator option.' 47 | ) 48 | parser.add_argument( 49 | 'led_indicator_option', 50 | choices=LED_INDICATOR_OPTION, 51 | help='The LED indicator option to set for the LED.' 52 | ) 53 | 54 | try: 55 | args = parser.parse_args(args=cli_args) 56 | 57 | set_led_indicator_option( 58 | LED_TYPE['new'].index(args.led), 59 | LED_INDICATOR_OPTION.index(args.led_indicator_option), 60 | control_file=args.control_file 61 | ) 62 | 63 | print( 64 | dumps( 65 | { 66 | 'led': { 67 | 'type': args.led, 68 | 'indicator_option': args.led_indicator_option 69 | } 70 | } 71 | ) 72 | ) 73 | except Exception as err: 74 | print(dumps({'error': str(err)})) 75 | 76 | exit(1) 77 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/led_app_notification_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.led_app_notification_test` module provides unit tests for the functions in 3 | `nuc_wmi.led_app_notification`. 4 | 5 | Classes: 6 | TestLedAppNotification: A unit test class for the functions in `nuc_wmi.led_app_notification`. 7 | """ 8 | 9 | import unittest 10 | 11 | from mock import patch 12 | 13 | from nuc_wmi import NucWmiError 14 | from nuc_wmi.led_app_notification import NOTIFICATION_TYPE, METHOD_ID, save_led_config 15 | 16 | import nuc_wmi 17 | 18 | 19 | class TestLedAppNotification(unittest.TestCase): 20 | """ 21 | A unit test class for the functions of `nuc_wmi.led_app_notification` 22 | 23 | Methods: 24 | setUp: Unit test initialization. 25 | test_save_led_config: Tests that it sends the expected byte list to the control file, tests that the returned 26 | control file response is properly processed, tests that it raises an exception when the 27 | control file returns an error code. 28 | """ 29 | 30 | def setUp(self): 31 | """ 32 | Initializes the unit tests; 33 | """ 34 | 35 | self.maxDiff = None 36 | 37 | 38 | @patch('nuc_wmi.led_app_notification.read_control_file') 39 | @patch('nuc_wmi.led_app_notification.write_control_file') 40 | def test_save_led_config(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 41 | """ 42 | Tests that `save_led_config` returns the expected exceptions, return values, or outputs. 43 | """ 44 | 45 | self.assertTrue(nuc_wmi.led_app_notification.read_control_file is nuc_wmi_read_control_file) 46 | self.assertTrue(nuc_wmi.led_app_notification.write_control_file is nuc_wmi_write_control_file) 47 | 48 | # Branch 1: Test that save_led_config sends the expected byte string to the control file 49 | # and that the returned control file response is properly processed. 50 | expected_write_byte_list = [METHOD_ID, NOTIFICATION_TYPE.index('save_led_config')] 51 | read_byte_list = [0x00, 0x00, 0x00, 0x00] 52 | 53 | nuc_wmi_read_control_file.return_value = read_byte_list 54 | returned_save_led_config = save_led_config() 55 | 56 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 57 | 58 | self.assertEqual(returned_save_led_config, None) 59 | 60 | # Reset 61 | nuc_wmi_read_control_file.reset_mock() 62 | nuc_wmi_write_control_file.reset_mock() 63 | 64 | # Branch 2: Test that save_led_config raises an exception when the control file returns an 65 | # error code. 66 | expected_write_byte_list = [METHOD_ID, NOTIFICATION_TYPE.index('save_led_config')] 67 | read_byte_list = [0xE1, 0x00, 0x00, 0x00] # Return function not supported 68 | 69 | nuc_wmi_read_control_file.return_value = read_byte_list 70 | 71 | with self.assertRaises(NucWmiError) as err: 72 | returned_save_led_config = save_led_config() 73 | 74 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 75 | 76 | self.assertEqual(str(err.exception), 'Error (Function not supported)') 77 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/switch_led_type_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.switch_led_type_test` module provides unit tests for the functions in 3 | `nuc_wmi.switch_led_type`. 4 | 5 | Classes: 6 | TestSwitchLedType: A unit test class for the functions in `nuc_wmi.switch_led_type`. 7 | """ 8 | 9 | import unittest 10 | 11 | from mock import patch 12 | 13 | from nuc_wmi import NucWmiError 14 | from nuc_wmi.switch_led_type import LED_COLOR_GROUP, METHOD_ID, switch_led_type 15 | 16 | import nuc_wmi 17 | 18 | 19 | class TestSwitchLedType(unittest.TestCase): 20 | """ 21 | A unit test class for the functions of `nuc_wmi.switch_led_type` 22 | 23 | Methods: 24 | setUp: Unit test initialization. 25 | test_switch_led_type: Tests that it sends the expected byte list to the control file, tests that the returned 26 | control file response is properly processed, tests that it raises an exception when the 27 | control file returns an error code. 28 | """ 29 | 30 | def setUp(self): 31 | """ 32 | Initializes the unit tests; 33 | """ 34 | 35 | self.maxDiff = None 36 | 37 | 38 | @patch('nuc_wmi.switch_led_type.read_control_file') 39 | @patch('nuc_wmi.switch_led_type.write_control_file') 40 | def test_switch_led_type(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 41 | """ 42 | Tests that `switch_led_type` returns the expected exceptions, return values, or outputs. 43 | """ 44 | 45 | self.assertTrue(nuc_wmi.switch_led_type.read_control_file is nuc_wmi_read_control_file) 46 | self.assertTrue(nuc_wmi.switch_led_type.write_control_file is nuc_wmi_write_control_file) 47 | 48 | # Branch 1: Test that switch_led_type sends the expected byte string to the control file 49 | # and that the returned control file response is properly processed. 50 | expected_write_byte_list = [METHOD_ID, LED_COLOR_GROUP.index('Single color LED')] 51 | read_byte_list = [0x00, 0x00, 0x00, 0x00] 52 | 53 | nuc_wmi_read_control_file.return_value = read_byte_list 54 | returned_switch_led_type = switch_led_type(LED_COLOR_GROUP.index('Single color LED')) 55 | 56 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 57 | 58 | self.assertEqual(returned_switch_led_type, None) 59 | 60 | # Reset 61 | nuc_wmi_read_control_file.reset_mock() 62 | nuc_wmi_write_control_file.reset_mock() 63 | 64 | # Branch 2: Test that switch_led_type raises an exception when the control file returns an 65 | # error code. 66 | expected_write_byte_list = [METHOD_ID, len(LED_COLOR_GROUP)] # Send incorrect LED_COLOR_GROUP index 67 | read_byte_list = [0xE4, 0x00, 0x00, 0x00] # Return invalid parameter 68 | 69 | nuc_wmi_read_control_file.return_value = read_byte_list 70 | 71 | with self.assertRaises(NucWmiError) as err: 72 | returned_switch_led_type = switch_led_type(len(LED_COLOR_GROUP)) 73 | 74 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 75 | 76 | self.assertEqual(str(err.exception), 'Error (Invalid Parameter)') 77 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/get_led_new.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.get_led_new` provides an interface to the new WMI get led set of functions. 3 | """ 4 | 5 | from nuc_wmi import NucWmiError, RETURN_ERROR 6 | from nuc_wmi.control_file import read_control_file, write_control_file 7 | 8 | GET_LED_TYPE = [ 9 | 'get_led_indicator_option', 10 | 'get_led_control_item' 11 | ] 12 | METHOD_ID=0x04 13 | 14 | def get_led_control_item(led_type, led_indicator_option, control_item, control_file=None): 15 | """ 16 | Get the current control item value for the control item of the indicator option for the specified LED type. 17 | 18 | Args: 19 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 20 | control_item: The control item of the specified LED type indicator option for which to retrieve the value. 21 | led_indicator_option: The indicator option for the specified LED type for which to retrieve the current control 22 | item value. 23 | led_type: The LED type for which to retrieve the current control item. 24 | Exceptions: 25 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 26 | or if `read_control_file` or `write_control_file` raise an exception. 27 | Returns: 28 | `nuc_wmi.CONTROL_ITEM` value for the control item of the indicator option for the specified LED type. 29 | """ 30 | 31 | get_led_control_item_byte_list = [ 32 | METHOD_ID, 33 | GET_LED_TYPE.index('get_led_control_item'), 34 | led_type, 35 | led_indicator_option, 36 | control_item 37 | ] 38 | 39 | write_control_file(get_led_control_item_byte_list, control_file=control_file) 40 | 41 | ( 42 | error_code, 43 | control_item_value, 44 | reserved_byte_1, 45 | reserved_byte_2 46 | ) = read_control_file(control_file=control_file) 47 | 48 | if error_code > 0: 49 | raise NucWmiError(RETURN_ERROR.get(error_code, 'Error (Unknown NUC WMI error code)')) 50 | 51 | return control_item_value 52 | 53 | 54 | def get_led_indicator_option(led_type, control_file=None): 55 | """ 56 | Get the current indicator option for the LED type. 57 | 58 | Args: 59 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 60 | led_type: The LED type for which to retrieve the current indicator option. 61 | Exceptions: 62 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 63 | or if `read_control_file` or `write_control_file` raise an exception. 64 | Returns: 65 | `nuc_wmi.LED_INDICATOR_OPTION` index of the current LED indicator option. 66 | """ 67 | 68 | get_led_indicator_option_byte_list = [ 69 | METHOD_ID, 70 | GET_LED_TYPE.index('get_led_indicator_option'), 71 | led_type 72 | ] 73 | 74 | write_control_file(get_led_indicator_option_byte_list, control_file=control_file) 75 | 76 | ( 77 | error_code, 78 | led_indicator_option, 79 | reserved_byte_1, 80 | reserved_byte_2 81 | ) = read_control_file(control_file=control_file) 82 | 83 | if error_code > 0: 84 | raise NucWmiError(RETURN_ERROR[error_code]) 85 | 86 | return led_indicator_option 87 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/cli/set_led.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.cli.set_led` provides a CLI interface to the WMI set led state function. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | from argparse import ArgumentParser 8 | from json import dumps 9 | from sys import exit 10 | 11 | from nuc_wmi import CONTROL_FILE, LED_BRIGHTNESS, LED_COLOR, LED_COLOR_TYPE, LED_BLINK_FREQUENCY, LED_TYPE 12 | from nuc_wmi.set_led import set_led 13 | 14 | def set_led_cli(cli_args=None): 15 | """ 16 | Creates a CLI interface on top of the `nuc_wmi.set_led` `set_led` function. 17 | 18 | Args: 19 | cli_args: If provided, overrides the CLI args to use for `argparse`. 20 | CLI Args: 21 | brightness: Controls the brightness level of the LED. 22 | color: Sets legacy RGB-color for LED. 23 | frequency: Sets the legacy LED frequency. 24 | led: Selects the legacy LED to set the state for. 25 | CLI Options: 26 | --control_file : Sets the control file to use if provided, 27 | otherwise `nuc_wmi.CONTROL_FILE` is used. 28 | Outputs: 29 | stdout: JSON object with brightness, frequency, and color of the selected LED or error message with 30 | failure error. 31 | Exit code: 32 | 0 on successfully setting the selected LED state properties or 1 on error. 33 | """ 34 | 35 | parser = ArgumentParser( 36 | description='Set legacy LED state with regard to brightness, frequency, and color.' 37 | ) 38 | 39 | parser.add_argument( 40 | '-c', 41 | '--control-file', 42 | default=None, 43 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 44 | ) 45 | parser.add_argument( 46 | 'led', 47 | choices=[led for led in LED_TYPE['legacy'] if led], 48 | help='The legacy LED for which to set the state.' 49 | ) 50 | parser.add_argument( 51 | 'brightness', 52 | choices=LED_BRIGHTNESS['legacy'], 53 | help='The brightness to set for the LED.' 54 | ) 55 | parser.add_argument( 56 | 'frequency', 57 | choices=[freq for freq in LED_BLINK_FREQUENCY['legacy'] if freq], 58 | help='The legacy frequency to set for the LED.' 59 | ) 60 | parser.add_argument( 61 | 'color', 62 | choices=set(LED_COLOR['legacy']['Dual-color Blue / Amber'] + LED_COLOR['legacy']['RGB-color']), 63 | help='The legacy color to set for the LED.' 64 | ) 65 | 66 | try: 67 | args = parser.parse_args(args=cli_args) 68 | 69 | led_type = LED_TYPE['legacy'].index(args.led) 70 | frequency = LED_BLINK_FREQUENCY['legacy'].index(args.frequency) 71 | 72 | try: 73 | color = LED_COLOR['legacy'][LED_COLOR_TYPE['legacy'][args.led]].index(args.color) 74 | except ValueError as err: 75 | raise ValueError('Invalid color for the specified legacy LED') 76 | 77 | set_led(led_type, args.brightness, frequency, color, control_file=args.control_file) 78 | 79 | print( 80 | dumps( 81 | { 82 | 'led': { 83 | 'type': args.led, 84 | 'brightness': str(args.brightness), 85 | 'frequency': args.frequency, 86 | 'color': args.color 87 | } 88 | } 89 | ) 90 | ) 91 | except Exception as err: 92 | print(dumps({'error': str(err)})) 93 | 94 | exit(1) 95 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/cli/led_app_notification_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.cli.led_app_notification_test` module provides unit tests for the functions in 3 | `nuc_wmi.cli.led_app_notification`. 4 | 5 | Classes: 6 | TestCliLedAppNotification: A unit test class for the functions in `nuc_wmi.cli.led_app_notification`. 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | import json 12 | import unittest 13 | 14 | from mock import patch 15 | 16 | from nuc_wmi import NucWmiError 17 | from nuc_wmi.cli.led_app_notification import save_led_config_cli 18 | 19 | import nuc_wmi 20 | 21 | 22 | class TestCliLedAppNotification(unittest.TestCase): 23 | """ 24 | A unit test class for the functions of `nuc_wmi.cli.led_app_notification` 25 | 26 | Methods: 27 | setUp: Unit test initialization. 28 | test_save_led_config_cli: Tests that it returns the proper JSON response and exit code for 29 | valid cli args, tests that it captures raised errors and returns 30 | the proper JSON error response and exit code. 31 | """ 32 | 33 | def setUp(self): 34 | """ 35 | Initializes the unit tests. 36 | """ 37 | 38 | self.maxDiff = None 39 | 40 | @patch('nuc_wmi.cli.led_app_notification.print') 41 | @patch('nuc_wmi.cli.led_app_notification.exit') 42 | @patch('nuc_wmi.cli.led_app_notification.save_led_config') 43 | def test_save_led_config_cli( 44 | self, 45 | nuc_wmi_cli_save_led_config, 46 | nuc_wmi_sys_exit, 47 | nuc_wmi_print 48 | ): 49 | """ 50 | Tests that `save_led_config_cli` returns the expected exceptions, return values, or outputs. 51 | """ 52 | 53 | self.assertTrue(nuc_wmi.cli.led_app_notification.save_led_config is nuc_wmi_cli_save_led_config) 54 | self.assertTrue(nuc_wmi.cli.led_app_notification.exit is nuc_wmi_sys_exit) 55 | self.assertTrue(nuc_wmi.cli.led_app_notification.print is nuc_wmi_print) 56 | 57 | # Branch 1: Test that save_led_config_cli returns the proper JSON response and exit 58 | # code for valid cli args 59 | returned_save_led_config_cli = save_led_config_cli([]) 60 | 61 | nuc_wmi_cli_save_led_config.assert_called_with( 62 | control_file=None 63 | ) 64 | nuc_wmi_print.assert_called() 65 | self.assertEqual( 66 | json.loads(nuc_wmi_print.call_args.args[0]), 67 | { 68 | 'led_app_notification': { 69 | 'type': 'save_led_config' 70 | } 71 | } 72 | ) 73 | 74 | self.assertEqual(returned_save_led_config_cli, None) 75 | 76 | # Reset 77 | nuc_wmi_cli_save_led_config.reset_mock() 78 | nuc_wmi_sys_exit.reset_mock() 79 | nuc_wmi_print.reset_mock() 80 | 81 | # Branch 2: Test that save_led_config_cli captures raised errors and returns 82 | # the proper JSON error response and exit code. 83 | nuc_wmi_cli_save_led_config.side_effect = NucWmiError('Error (Function not supported)') 84 | 85 | returned_save_led_config_cli = save_led_config_cli([]) 86 | 87 | nuc_wmi_cli_save_led_config.assert_called_with( 88 | control_file=None 89 | ) 90 | nuc_wmi_print.assert_called_with('{"error": "Error (Function not supported)"}') 91 | nuc_wmi_sys_exit.assert_called_with(1) 92 | 93 | self.assertEqual(returned_save_led_config_cli, None) 94 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/cli/switch_led_type_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.cli.switch_led_type_test` module provides unit tests for the functions in 3 | `nuc_wmi.cli.switch_led_type`. 4 | 5 | Classes: 6 | TestCliSwitchLedType: A unit test class for the functions in `nuc_wmi.cli.switch_led_type`. 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | import json 12 | import unittest 13 | 14 | from mock import patch 15 | 16 | from nuc_wmi import NucWmiError 17 | from nuc_wmi.switch_led_type import LED_COLOR_GROUP 18 | from nuc_wmi.cli.switch_led_type import switch_led_type_cli 19 | 20 | import nuc_wmi 21 | 22 | 23 | class TestCliSwitchLedType(unittest.TestCase): 24 | """ 25 | A unit test class for the functions of `nuc_wmi.cli.switch_led_type` 26 | 27 | Methods: 28 | setUp: Unit test initialization. 29 | test_switch_led_type_cli: Tests that it returns the proper JSON response and exit code for 30 | valid cli args, tests that it captures raised errors and returns 31 | the proper JSON error response and exit code. 32 | """ 33 | 34 | def setUp(self): 35 | """ 36 | Initializes the unit tests. 37 | """ 38 | 39 | self.maxDiff = None 40 | 41 | @patch('nuc_wmi.cli.switch_led_type.print') 42 | @patch('nuc_wmi.cli.switch_led_type.exit') 43 | @patch('nuc_wmi.cli.switch_led_type.switch_led_type') 44 | def test_switch_led_type_cli( 45 | self, 46 | nuc_wmi_switch_led_type, 47 | nuc_wmi_sys_exit, 48 | nuc_wmi_print 49 | ): 50 | """ 51 | Tests that `switch_led_type_cli` returns the expected exceptions, return values, or outputs. 52 | """ 53 | 54 | self.assertTrue(nuc_wmi.cli.switch_led_type.switch_led_type is \ 55 | nuc_wmi_switch_led_type) 56 | self.assertTrue(nuc_wmi.cli.switch_led_type.exit is nuc_wmi_sys_exit) 57 | self.assertTrue(nuc_wmi.cli.switch_led_type.print is nuc_wmi_print) 58 | 59 | # Branch 1: Test that switch_led_type_cli returns the proper JSON response and exit 60 | # code for valid cli args 61 | returned_switch_led_type_cli = switch_led_type_cli([LED_COLOR_GROUP[0]]) 62 | 63 | nuc_wmi_switch_led_type.assert_called_with( 64 | LED_COLOR_GROUP.index('Single color LED'), control_file=None 65 | ) 66 | nuc_wmi_print.assert_called() 67 | self.assertEqual( 68 | json.loads(nuc_wmi_print.call_args.args[0]), 69 | {"led_color_group": {"type": LED_COLOR_GROUP[0]}} 70 | ) 71 | 72 | self.assertEqual(returned_switch_led_type_cli, None) 73 | 74 | # Reset 75 | nuc_wmi_switch_led_type.reset_mock() 76 | nuc_wmi_sys_exit.reset_mock() 77 | nuc_wmi_print.reset_mock() 78 | 79 | # Branch 2: Test that switch_led_type_cli captures raised errors and returns 80 | # the proper JSON error response and exit code. 81 | nuc_wmi_switch_led_type.side_effect = NucWmiError('Error (Function not supported)') 82 | 83 | returned_switch_led_type_cli = switch_led_type_cli([LED_COLOR_GROUP[0]]) 84 | 85 | nuc_wmi_switch_led_type.assert_called_with( 86 | LED_COLOR_GROUP.index('Single color LED'), control_file=None 87 | ) 88 | nuc_wmi_print.assert_called_with('{"error": "Error (Function not supported)"}') 89 | nuc_wmi_sys_exit.assert_called_with(1) 90 | 91 | self.assertEqual(returned_switch_led_type_cli, None) 92 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/get_led_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.get_led_test` module provides unit tests for the functions in 3 | `nuc_wmi.get_led`. 4 | 5 | Classes: 6 | TestGetLed: A unit test class for the functions in `nuc_wmi.get_led`. 7 | """ 8 | 9 | import unittest 10 | 11 | from mock import patch 12 | 13 | from nuc_wmi import LED_BLINK_FREQUENCY, LED_BRIGHTNESS, LED_COLOR, LED_COLOR_TYPE, LED_TYPE, NucWmiError 14 | from nuc_wmi.get_led import METHOD_ID, get_led 15 | 16 | import nuc_wmi 17 | 18 | 19 | class TestGetLed(unittest.TestCase): 20 | """ 21 | A unit test class for the functions of `nuc_wmi.get_led` 22 | 23 | Methods: 24 | setUp: Unit test initialization. 25 | test_get_led: Tests that it sends the expected byte list to the control file, tests that the returned 26 | control file response is properly processed, tests that it raises an exception when the 27 | control file returns an error code. 28 | """ 29 | 30 | def setUp(self): 31 | """ 32 | Initializes the unit tests; 33 | """ 34 | 35 | self.maxDiff = None 36 | 37 | 38 | @patch('nuc_wmi.get_led.read_control_file') 39 | @patch('nuc_wmi.get_led.write_control_file') 40 | def test_get_led(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 41 | """ 42 | Tests that `get_led` returns the expected exceptions, return values, or outputs. 43 | """ 44 | 45 | self.assertTrue(nuc_wmi.get_led.read_control_file is nuc_wmi_read_control_file) 46 | self.assertTrue(nuc_wmi.get_led.write_control_file is nuc_wmi_write_control_file) 47 | 48 | # Branch 1: Test that get_led sends the expected byte string to the control file 49 | # and that the returned control file response is properly processed. 50 | 51 | # Get legacy S0 Ring LED which is set to Yellow, Always on, and 63% brightness 52 | expected_write_byte_list = [ 53 | METHOD_ID, 54 | LED_TYPE['legacy'].index('S0 Ring LED') 55 | ] 56 | read_byte_list = [ 57 | 0x00, 58 | LED_BRIGHTNESS['legacy'].index('63'), 59 | LED_BLINK_FREQUENCY['legacy'].index('Always on'), 60 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Yellow') 61 | ] 62 | 63 | nuc_wmi_read_control_file.return_value = read_byte_list 64 | returned_get_led = get_led(LED_TYPE['legacy'].index('S0 Ring LED')) 65 | 66 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 67 | 68 | self.assertEqual(returned_get_led, tuple(read_byte_list[1:])) 69 | 70 | # Reset 71 | nuc_wmi_read_control_file.reset_mock() 72 | nuc_wmi_write_control_file.reset_mock() 73 | 74 | # Branch 2: Test that get_led raises an exception when the control file returns an 75 | # error code. 76 | 77 | # Incorrect led 78 | expected_write_byte_list = [ 79 | METHOD_ID, 80 | len(LED_TYPE['legacy']), # Set incorrect led 81 | ] 82 | read_byte_list = [0xE2, 0x00, 0x00, 0x00] # Return undefined device 83 | 84 | nuc_wmi_read_control_file.return_value = read_byte_list 85 | 86 | with self.assertRaises(NucWmiError) as err: 87 | returned_get_led = get_led(len(LED_TYPE['legacy'])) # Set incorrect led 88 | 89 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 90 | 91 | self.assertEqual(str(err.exception), 'Error (Undefined device)') 92 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/version_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.version_test` module provides unit tests for the functions in 3 | `nuc_wmi.version`. 4 | 5 | Classes: 6 | TestVersion: A unit test class for the functions in `nuc_wmi.version`. 7 | """ 8 | 9 | import unittest 10 | 11 | from mock import patch 12 | 13 | from nuc_wmi import NucWmiError 14 | from nuc_wmi.version import METHOD_ID, VERSION_TYPE, wmi_interface_spec_compliance_version 15 | 16 | import nuc_wmi 17 | 18 | 19 | class TestVersion(unittest.TestCase): 20 | """ 21 | A unit test class for the functions of `nuc_wmi.version` 22 | 23 | Methods: 24 | setUp: Unit test initialization. 25 | test_wmi_interface_spec_compliance_version: Tests that it sends the expected byte list to the control file, 26 | tests that the returned control file response is properly processed, 27 | tests that it raises an exception when the control file returns an 28 | error code. 29 | """ 30 | 31 | def setUp(self): 32 | """ 33 | Initializes the unit tests. 34 | """ 35 | 36 | self.maxDiff = None 37 | 38 | 39 | @patch('nuc_wmi.version.read_control_file') 40 | @patch('nuc_wmi.version.write_control_file') 41 | def test_wmi_interface_spec_compliance_version(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 42 | """ 43 | Tests that `wmi_interface_spec_compliance_version` returns the expected exceptions, return values, or 44 | outputs. 45 | """ 46 | 47 | self.assertTrue(nuc_wmi.version.read_control_file is nuc_wmi_read_control_file) 48 | self.assertTrue(nuc_wmi.version.write_control_file is nuc_wmi_write_control_file) 49 | 50 | # Branch 1: Test that wmi_interface_spec_compliance_version send the expected byte string to the control file 51 | # and that the returned control file response is properly processed. 52 | expected_wmi_interface_spec_compliance_version = (0x01, 0x36) 53 | expected_write_byte_list = [METHOD_ID, VERSION_TYPE.index('wmi_interface_spec_compliance_version')] 54 | read_byte_list = [0x00, 0x36, 0x01, 0x00] 55 | 56 | nuc_wmi_read_control_file.return_value = read_byte_list 57 | returned_wmi_interface_spec_compliance_version = wmi_interface_spec_compliance_version() 58 | 59 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 60 | 61 | self.assertEqual(returned_wmi_interface_spec_compliance_version, expected_wmi_interface_spec_compliance_version) 62 | 63 | # Reset 64 | nuc_wmi_read_control_file.reset_mock() 65 | nuc_wmi_write_control_file.reset_mock() 66 | 67 | # Branch 2: Test that wmi_interface_spec_compliance_version raises an exception when the control file returns an 68 | # error code. 69 | expected_wmi_interface_spec_compliance_version = (0x01, 0x36) 70 | expected_write_byte_list = [METHOD_ID, VERSION_TYPE.index('wmi_interface_spec_compliance_version')] 71 | read_byte_list = [0xE1, 0x00, 0x00, 0x00] # Return function not supported 72 | 73 | nuc_wmi_read_control_file.return_value = read_byte_list 74 | 75 | with self.assertRaises(NucWmiError) as err: 76 | returned_wmi_interface_spec_compliance_version = wmi_interface_spec_compliance_version() 77 | 78 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 79 | 80 | self.assertEqual(str(err.exception), 'Error (Function not supported)') 81 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/cli/version_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.cli.version_test` module provides unit tests for the functions in 3 | `nuc_wmi.cli.version`. 4 | 5 | Classes: 6 | TestCliVersion: A unit test class for the functions in `nuc_wmi.cli.version`. 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | import json 12 | import unittest 13 | 14 | from mock import patch 15 | 16 | from nuc_wmi import NucWmiError 17 | from nuc_wmi.cli.version import wmi_interface_spec_compliance_version_cli 18 | 19 | import nuc_wmi 20 | 21 | 22 | class TestCliVersion(unittest.TestCase): 23 | """ 24 | A unit test class for the functions of `nuc_wmi.cli.version` 25 | 26 | Methods: 27 | setUp: Unit test initialization. 28 | test_wmi_interface_spec_compliance_version_cli: Tests that it returns the proper JSON response and exit code for 29 | valid cli args, tests that it captures raised errors and returns 30 | the proper JSON error response and exit code. 31 | """ 32 | 33 | def setUp(self): 34 | """ 35 | Initializes the unit tests. 36 | """ 37 | 38 | self.maxDiff = None 39 | 40 | @patch('nuc_wmi.cli.version.print') 41 | @patch('nuc_wmi.cli.version.exit') 42 | @patch('nuc_wmi.cli.version.wmi_interface_spec_compliance_version') 43 | def test_wmi_interface_spec_compliance_version_cli( 44 | self, 45 | nuc_wmi_cli_wmi_interface_spec_compliance_version, 46 | nuc_wmi_sys_exit, 47 | nuc_wmi_print 48 | ): 49 | """ 50 | Tests that `wmi_interface_spec_compliance_version_cli` returns the expected exceptions, return values, or 51 | outputs. 52 | """ 53 | 54 | self.assertTrue(nuc_wmi.cli.version.wmi_interface_spec_compliance_version is \ 55 | nuc_wmi_cli_wmi_interface_spec_compliance_version) 56 | self.assertTrue(nuc_wmi.cli.version.exit is nuc_wmi_sys_exit) 57 | self.assertTrue(nuc_wmi.cli.version.print is nuc_wmi_print) 58 | 59 | # Branch 1: Test that wmi_interface_spec_compliance_version_cli returns the proper JSON response and exit 60 | # code for valid cli args 61 | wmi_interface_spec_compliance_version = (0x01, 0x36) 62 | 63 | nuc_wmi_cli_wmi_interface_spec_compliance_version.return_value = wmi_interface_spec_compliance_version 64 | returned_wmi_interface_spec_compliance_version_cli = wmi_interface_spec_compliance_version_cli([]) 65 | 66 | nuc_wmi_cli_wmi_interface_spec_compliance_version.assert_called_with( 67 | control_file=None 68 | ) 69 | nuc_wmi_print.assert_called() 70 | self.assertEqual( 71 | json.loads(nuc_wmi_print.call_args.args[0]), 72 | {"version": {"semver": "1.54", "type": "wmi_interface_spec_compliance"}} 73 | ) 74 | 75 | self.assertEqual(returned_wmi_interface_spec_compliance_version_cli, None) 76 | 77 | # Reset 78 | nuc_wmi_cli_wmi_interface_spec_compliance_version.reset_mock() 79 | nuc_wmi_sys_exit.reset_mock() 80 | nuc_wmi_print.reset_mock() 81 | 82 | # Branch 2: Test that wmi_interface_spec_compliance_version_cli captures raised errors and returns 83 | # the proper JSON error response and exit code. 84 | nuc_wmi_cli_wmi_interface_spec_compliance_version.side_effect = NucWmiError('Error (Function not supported)') 85 | 86 | returned_wmi_interface_spec_compliance_version_cli = wmi_interface_spec_compliance_version_cli([]) 87 | 88 | nuc_wmi_cli_wmi_interface_spec_compliance_version.assert_called_with( 89 | control_file=None 90 | ) 91 | nuc_wmi_print.assert_called_with('{"error": "Error (Function not supported)"}') 92 | nuc_wmi_sys_exit.assert_called_with(1) 93 | 94 | self.assertEqual(returned_wmi_interface_spec_compliance_version_cli, None) 95 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/set_led_indicator_option_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.set_led_indicator_option_test` module provides unit tests for the functions in 3 | `nuc_wmi.set_led_indicator_option`. 4 | 5 | Classes: 6 | TestSetLedIndicatorOption: A unit test class for the functions in `nuc_wmi.set_led_indicator_option`. 7 | """ 8 | 9 | import unittest 10 | 11 | from mock import patch 12 | 13 | from nuc_wmi import LED_INDICATOR_OPTION, LED_TYPE, NucWmiError 14 | from nuc_wmi.set_led_indicator_option import METHOD_ID, set_led_indicator_option 15 | 16 | import nuc_wmi 17 | 18 | 19 | class TestSetLedIndicatorOption(unittest.TestCase): 20 | """ 21 | A unit test class for the functions of `nuc_wmi.set_led_indicator_option` 22 | 23 | Methods: 24 | setUp: Unit test initialization. 25 | test_set_led_indicator_option: Tests that it sends the expected byte list to the control file, tests that the 26 | returned control file response is properly processed, tests that it raises an 27 | exception when the control file returns an error code. 28 | """ 29 | 30 | def setUp(self): 31 | """ 32 | Initializes the unit tests; 33 | """ 34 | 35 | self.maxDiff = None 36 | 37 | 38 | @patch('nuc_wmi.set_led_indicator_option.read_control_file') 39 | @patch('nuc_wmi.set_led_indicator_option.write_control_file') 40 | def test_set_led_indicator_option(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 41 | """ 42 | Tests that `set_led_indicator_option` returns the expected exceptions, return values, or outputs. 43 | """ 44 | 45 | self.assertTrue(nuc_wmi.set_led_indicator_option.read_control_file is nuc_wmi_read_control_file) 46 | self.assertTrue(nuc_wmi.set_led_indicator_option.write_control_file is nuc_wmi_write_control_file) 47 | 48 | # Branch 1: Test that set_led_indicator_option sends the expected byte string to the control file 49 | # and that the returned control file response is properly processed. 50 | 51 | # Set HDD LED with HDD Activity Indicator 52 | expected_write_byte_list = [ 53 | METHOD_ID, 54 | LED_TYPE['new'].index('HDD LED'), 55 | LED_INDICATOR_OPTION.index('HDD Activity Indicator') 56 | ] 57 | read_byte_list = [ 58 | 0x00, 59 | 0x00, 60 | 0x00, 61 | 0x00 62 | ] 63 | 64 | nuc_wmi_read_control_file.return_value = read_byte_list 65 | returned_set_led_indicator_option = set_led_indicator_option( 66 | LED_TYPE['new'].index('HDD LED'), 67 | LED_INDICATOR_OPTION.index('HDD Activity Indicator') 68 | ) 69 | 70 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 71 | 72 | self.assertEqual(returned_set_led_indicator_option, None) 73 | 74 | # Reset 75 | nuc_wmi_read_control_file.reset_mock() 76 | nuc_wmi_write_control_file.reset_mock() 77 | 78 | # Branch 2: Test that set_led_indicator_option raises an exception when the control file returns an 79 | # error code. 80 | 81 | # Incorrect led 82 | expected_write_byte_list = [ 83 | METHOD_ID, 84 | len(LED_TYPE['new']), # Incorrect led 85 | LED_INDICATOR_OPTION.index('HDD Activity Indicator') 86 | ] 87 | read_byte_list = [ 88 | 0xE4, 89 | 0x00, 90 | 0x00, 91 | 0x00 92 | ] 93 | 94 | nuc_wmi_read_control_file.return_value = read_byte_list 95 | 96 | with self.assertRaises(NucWmiError) as err: 97 | returned_set_led_indicator_option = set_led_indicator_option( 98 | len(LED_TYPE['new']), # Incorrect led 99 | LED_INDICATOR_OPTION.index('HDD Activity Indicator') 100 | ) 101 | 102 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 103 | 104 | self.assertEqual(str(err.exception), 'Error (Invalid Parameter)') 105 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/cli/set_led_indictor_option_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.cli.set_led_indicator_option_test` module provides unit tests for the functions in 3 | `nuc_wmi.cli.set_led_indicator_option`. 4 | 5 | Classes: 6 | TestCliSetLedIndicatorOption: A unit test class for the functions in `nuc_wmi.cli.set_led_indicator_option`. 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | import json 12 | import unittest 13 | 14 | from mock import patch 15 | 16 | from nuc_wmi import LED_INDICATOR_OPTION, LED_TYPE, NucWmiError 17 | from nuc_wmi.cli.set_led_indicator_option import set_led_indicator_option_cli 18 | 19 | import nuc_wmi 20 | 21 | 22 | class TestCliSetLedIndicatorOption(unittest.TestCase): 23 | """ 24 | A unit test class for the functions of `nuc_wmi.cli.set_led_indicator_option` 25 | 26 | Methods: 27 | setUp: Unit test initialization. 28 | test_set_led_indicator_option_cli: Tests that it returns the proper JSON response and exit code for 29 | valid cli args, tests that it captures raised errors and returns 30 | the proper JSON error response and exit code. 31 | """ 32 | 33 | def setUp(self): 34 | """ 35 | Initializes the unit tests. 36 | """ 37 | 38 | self.maxDiff = None 39 | 40 | 41 | @patch('nuc_wmi.cli.set_led_indicator_option.print') 42 | @patch('nuc_wmi.cli.set_led_indicator_option.exit') 43 | @patch('nuc_wmi.cli.set_led_indicator_option.set_led_indicator_option') 44 | def test_set_led_indicator_option_cli( 45 | self, 46 | nuc_wmi_set_led_indicator_option, 47 | nuc_wmi_sys_exit, 48 | nuc_wmi_print 49 | ): 50 | """ 51 | Tests that `set_led_indicator_option_cli` returns the expected exceptions, return values, or outputs. 52 | """ 53 | 54 | self.assertTrue(nuc_wmi.cli.set_led_indicator_option.set_led_indicator_option is \ 55 | nuc_wmi_set_led_indicator_option) 56 | self.assertTrue(nuc_wmi.cli.set_led_indicator_option.exit is nuc_wmi_sys_exit) 57 | self.assertTrue(nuc_wmi.cli.set_led_indicator_option.print is nuc_wmi_print) 58 | 59 | # Branch 1: Test that set_led_indicator_option_cli returns the proper JSON response and exit 60 | # code for valid cli args 61 | 62 | # Set HDD LED indicator option to HDD Activity Indicator 63 | returned_set_led_indicator_option_cli = set_led_indicator_option_cli( 64 | [ 65 | LED_TYPE['new'][1], 66 | LED_INDICATOR_OPTION[1] 67 | ] 68 | ) 69 | 70 | nuc_wmi_set_led_indicator_option.assert_called_with( 71 | LED_TYPE['new'].index('HDD LED'), 72 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 73 | control_file=None 74 | ) 75 | nuc_wmi_print.assert_called() 76 | self.assertEqual( 77 | json.loads(nuc_wmi_print.call_args.args[0]), 78 | { 79 | 'led': { 80 | 'type': LED_TYPE['new'][1], 81 | 'indicator_option': LED_INDICATOR_OPTION[1] 82 | } 83 | } 84 | ) 85 | 86 | self.assertEqual(returned_set_led_indicator_option_cli, None) 87 | 88 | # Reset 89 | nuc_wmi_set_led_indicator_option.reset_mock() 90 | nuc_wmi_sys_exit.reset_mock() 91 | nuc_wmi_print.reset_mock() 92 | 93 | # Branch 2: Test that set_led_indicator_option_cli captures raised errors and returns 94 | # the proper JSON error response and exit code. 95 | nuc_wmi_set_led_indicator_option.side_effect = NucWmiError('Error (Function not supported)') 96 | 97 | returned_set_led_indicator_option_cli = set_led_indicator_option_cli( 98 | [ 99 | LED_TYPE['new'][1], 100 | LED_INDICATOR_OPTION[1] 101 | ] 102 | ) 103 | 104 | nuc_wmi_set_led_indicator_option.assert_called_with( 105 | LED_TYPE['new'].index('HDD LED'), 106 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 107 | control_file=None 108 | ) 109 | nuc_wmi_print.assert_called_with('{"error": "Error (Function not supported)"}') 110 | nuc_wmi_sys_exit.assert_called_with(1) 111 | 112 | self.assertEqual(returned_set_led_indicator_option_cli, None) 113 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/cli/get_led_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.cli.get_led_test` module provides unit tests for the functions in 3 | `nuc_wmi.cli.get_led`. 4 | 5 | Classes: 6 | TestCliGetLed: A unit test class for the functions in `nuc_wmi.cli.get_led`. 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | import json 12 | import unittest 13 | 14 | from mock import patch 15 | 16 | from nuc_wmi import LED_BLINK_FREQUENCY, LED_BRIGHTNESS, LED_COLOR, LED_COLOR_TYPE, LED_TYPE, NucWmiError 17 | from nuc_wmi.cli.get_led import get_led_cli 18 | 19 | import nuc_wmi 20 | 21 | 22 | class TestCliGetLed(unittest.TestCase): 23 | """ 24 | A unit test class for the functions of `nuc_wmi.cli.get_led` 25 | 26 | Methods: 27 | setUp: Unit test initialization. 28 | test_get_led_cli: Tests that it returns the proper JSON response and exit code for 29 | valid cli args, tests that it captures raised errors and returns 30 | the proper JSON error response and exit code. 31 | """ 32 | 33 | def setUp(self): 34 | """ 35 | Initializes the unit tests. 36 | """ 37 | 38 | self.maxDiff = None 39 | 40 | @patch('nuc_wmi.cli.get_led.print') 41 | @patch('nuc_wmi.cli.get_led.exit') 42 | @patch('nuc_wmi.cli.get_led.get_led') 43 | def test_get_led_cli( 44 | self, 45 | nuc_wmi_get_led, 46 | nuc_wmi_sys_exit, 47 | nuc_wmi_print 48 | ): 49 | """ 50 | Tests that `get_led_cli` returns the expected exceptions, return values, or outputs. 51 | """ 52 | 53 | self.assertTrue(nuc_wmi.cli.get_led.get_led is nuc_wmi_get_led) 54 | self.assertTrue(nuc_wmi.cli.get_led.exit is nuc_wmi_sys_exit) 55 | self.assertTrue(nuc_wmi.cli.get_led.print is nuc_wmi_print) 56 | 57 | # Branch 1: Test that get_led_cli returns the proper JSON response and exit 58 | # code for valid cli args 59 | 60 | # Get S0 Ring LED with a brightness of 47%, frequency of Always on, and color of Cyan 61 | expected_brightness = str(LED_BRIGHTNESS['legacy'].index('47')) 62 | expected_frequency = LED_BLINK_FREQUENCY['legacy'].index('Always on') 63 | expected_color = LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Cyan') 64 | nuc_wmi_get_led.return_value = (expected_brightness, expected_frequency, expected_color) 65 | returned_get_led_cli = get_led_cli( 66 | [ 67 | LED_TYPE['legacy'][2] 68 | ] 69 | ) 70 | 71 | nuc_wmi_get_led.assert_called_with( 72 | LED_TYPE['legacy'].index('S0 Ring LED'), 73 | control_file=None 74 | ) 75 | nuc_wmi_print.assert_called() 76 | self.assertEqual( 77 | json.loads(nuc_wmi_print.call_args.args[0]), 78 | { 79 | 'led': { 80 | 'type': LED_TYPE['legacy'][2], 81 | 'brightness': LED_BRIGHTNESS['legacy'][47], 82 | 'frequency': LED_BLINK_FREQUENCY['legacy'][4], 83 | 'color': LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']][1] 84 | } 85 | } 86 | ) 87 | 88 | self.assertEqual(returned_get_led_cli, None) 89 | 90 | # Reset 91 | nuc_wmi_get_led.reset_mock() 92 | nuc_wmi_sys_exit.reset_mock() 93 | nuc_wmi_print.reset_mock() 94 | 95 | # Branch 2: Test that get_led_cli captures raised errors and returns 96 | # the proper JSON error response and exit code. 97 | expected_brightness = str(LED_BRIGHTNESS['legacy'].index('47')) 98 | expected_frequency = LED_BLINK_FREQUENCY['legacy'].index('Always on') 99 | expected_color = LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Cyan') 100 | nuc_wmi_get_led.side_effect = NucWmiError('Error (Function not supported)') 101 | 102 | returned_get_led_cli = get_led_cli( 103 | [ 104 | LED_TYPE['legacy'][2] 105 | ] 106 | ) 107 | 108 | nuc_wmi_get_led.assert_called_with( 109 | LED_TYPE['legacy'].index('S0 Ring LED'), 110 | control_file=None 111 | ) 112 | nuc_wmi_print.assert_called_with('{"error": "Error (Function not supported)"}') 113 | nuc_wmi_sys_exit.assert_called_with(1) 114 | 115 | self.assertEqual(returned_get_led_cli, None) 116 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python setuptools wrapper for Python 2.6 or greater. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | import os 8 | import sys 9 | import unittest 10 | 11 | try: 12 | from pkg_resources import parse_version 13 | from setuptools import find_packages, setup 14 | 15 | import setuptools 16 | except ImportError as err: 17 | print('Python 2.6 or greater and setuptools is required for install:', file=sys.stderr) 18 | print('\thttps://packaging.python.org/en/latest/installing.html#requirements-for-installing-packages', 19 | file=sys.stderr) 20 | sys.exit(1) 21 | 22 | def read_file(file_name): 23 | """ 24 | File read wrapper for loading data unmodified from arbritrary file. 25 | """ 26 | 27 | file_data = None 28 | 29 | try: 30 | with open(file_name, 'r') as fin: 31 | file_data = fin.read() 32 | except Exception as err: # pylint: disable=broad-except 33 | print('Failed to read data from file \'%s\': %s' % (file_name, str(err)), file=sys.stderr) 34 | sys.exit(1) 35 | 36 | return file_data 37 | 38 | if issubclass(sys.version_info.__class__, tuple): 39 | PYTHON_VERSION = ".".join(map(str, sys.version_info[:3])) 40 | else: 41 | PYTHON_VERSION = '.'.join(map(str, [sys.version_info.major, sys.version_info.minor, sys.version_info.micro])) 42 | 43 | if parse_version(PYTHON_VERSION) < parse_version('2.6'): 44 | print('Python 2.6 or greater is required.') 45 | sys.exit(1) 46 | 47 | PYTHON_3_EXTRAS = {} 48 | 49 | if parse_version(PYTHON_VERSION) >= parse_version('3'): 50 | setuptools.use_2to3_on_doctests = True 51 | 52 | PYTHON_3_EXTRAS['convert_2to3_doctests'] = [] 53 | PYTHON_3_EXTRAS['use_2to3'] = True 54 | PYTHON_3_EXTRAS['use_2to3_exclude_fixers'] = [] 55 | PYTHON_3_EXTRAS['use_2to3_fixers'] = [] 56 | 57 | def test_suite(): 58 | test_loader = unittest.TestLoader() 59 | test_suite = test_loader.discover('python', pattern='*_test.py') 60 | 61 | return test_suite 62 | 63 | setup( # pylint: disable=star-args 64 | author='Julio Lajara', 65 | author_email='julio@tvisioninsights.com', 66 | classifiers=[ 67 | 'Development Status :: 5 - Production/Stable', 68 | 'Environment :: Console', 69 | 'Intended Audience :: Information Technology', 70 | 'Intended Audience :: System Administrators', 71 | 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', 72 | 'Natural Language :: English', 73 | 'Operating System :: POSIX :: Linux', 74 | 'Programming Language :: Python', 75 | 'Programming Language :: Python :: 2.7', 76 | 'Programming Language :: Python :: 3.6', 77 | 'Programming Language :: Unix Shell', 78 | 'Topic :: Software Development :: Libraries :: Python Modules', 79 | 'Topic :: System :: Systems Administration', 80 | 'Topic :: Utilities' 81 | ], 82 | description='NUC WMI CLI userland for intel_nuc_led kernel module', 83 | download_url='https://github.com/tvision-insights/intel_nuc_led', 84 | entry_points={ 85 | 'console_scripts': [ 86 | 'nuc_wmi-get_led = nuc_wmi.cli.get_led:get_led_cli', 87 | 'nuc_wmi-get_led_control_item = nuc_wmi.cli.get_led_new:get_led_control_item_cli', 88 | 'nuc_wmi-get_led_indicator_option = nuc_wmi.cli.get_led_new:get_led_indicator_option_cli', 89 | 'nuc_wmi-query_led_control_items = nuc_wmi.cli.query_led:query_led_control_items_cli', 90 | 'nuc_wmi-query_led_color_type = nuc_wmi.cli.query_led:query_led_color_type_cli', 91 | 'nuc_wmi-query_led_indicator_options = nuc_wmi.cli.query_led:query_led_indicator_options_cli', 92 | 'nuc_wmi-query_leds = nuc_wmi.cli.query_led:query_leds_cli', 93 | 'nuc_wmi-save_led_config = nuc_wmi.cli.led_app_notification:save_led_config_cli', 94 | 'nuc_wmi-set_led = nuc_wmi.cli.set_led:set_led_cli', 95 | 'nuc_wmi-set_led_control_item = nuc_wmi.cli.set_led_control_item:set_led_control_item_cli', 96 | 'nuc_wmi-set_led_indicator_option = nuc_wmi.cli.set_led_indicator_option:set_led_indicator_option_cli', 97 | 'nuc_wmi-switch_led_type = nuc_wmi.cli.switch_led_type:switch_led_type_cli', 98 | 'nuc_wmi-wmi_interface_spec_compliance_version = nuc_wmi.cli.version:wmi_interface_spec_compliance_version_cli' 99 | ] 100 | }, 101 | include_package_data=True, 102 | install_requires=[ 103 | 'setuptools' 104 | ], 105 | license='GPLv2', 106 | long_description=read_file('README.md'), 107 | keywords='cli intel kernel led nuc wmi', 108 | maintainer='Julio Lajara', 109 | maintainer_email='julio@tvisioninsights.com', 110 | name='nuc_wmi', 111 | package_dir={ 112 | '': 'python' 113 | }, 114 | packages=find_packages('python', exclude=['test', 'test.*']), 115 | setup_requires=[ 116 | 'setuptools' 117 | ], 118 | test_loader='setuptools.command.test:ScanningLoader', 119 | tests_require=[ 120 | 'coverage', 121 | 'mock', 122 | 'nose', 123 | 'nose-cov', 124 | 'setuptools' 125 | ], 126 | test_suite='setup.test_suite', 127 | url='https://github.com/tvision-insights/intel_nuc_led', 128 | version='1.0.0', 129 | zip_safe=True, 130 | **PYTHON_3_EXTRAS 131 | ) 132 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/set_led_control_item_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.set_led_control_item_test` module provides unit tests for the functions in 3 | `nuc_wmi.set_led_control_item`. 4 | 5 | Classes: 6 | TestSetLedControlItem: A unit test class for the functions in `nuc_wmi.set_led_control_item`. 7 | """ 8 | 9 | import unittest 10 | 11 | from mock import patch 12 | 13 | from nuc_wmi import CONTROL_ITEM, CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR, LED_BRIGHTNESS_MULTI_COLOR 14 | from nuc_wmi import LED_INDICATOR_OPTION, LED_TYPE, NucWmiError 15 | from nuc_wmi.set_led_control_item import METHOD_ID, set_led_control_item 16 | 17 | import nuc_wmi 18 | 19 | 20 | class TestSetLedControlItem(unittest.TestCase): 21 | """ 22 | A unit test class for the functions of `nuc_wmi.set_led_control_item` 23 | 24 | Methods: 25 | setUp: Unit test initialization. 26 | test_set_led_control_item: Tests that it sends the expected byte list to the control file, tests that the 27 | returned control file response is properly processed, tests that it raises an 28 | exception when the control file returns an error code. 29 | """ 30 | 31 | def setUp(self): 32 | """ 33 | Initializes the unit tests; 34 | """ 35 | 36 | self.maxDiff = None 37 | 38 | 39 | @patch('nuc_wmi.set_led_control_item.read_control_file') 40 | @patch('nuc_wmi.set_led_control_item.write_control_file') 41 | def test_Set_led_control_item(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 42 | """ 43 | Tests that `Set_led_control_item` returns the expected exceptions, return values, or outputs. 44 | """ 45 | 46 | self.assertTrue(nuc_wmi.set_led_control_item.read_control_file is nuc_wmi_read_control_file) 47 | self.assertTrue(nuc_wmi.set_led_control_item.write_control_file is nuc_wmi_write_control_file) 48 | 49 | # Branch 1: Test that set_led_control_item sends the expected byte string to the control file 50 | # and that the returned control file response is properly processed. 51 | 52 | # Set HDD LED HDD Activity Indicator Brightness of 30% for multi color led 53 | expected_write_byte_list = [ 54 | METHOD_ID, 55 | LED_TYPE['new'].index('HDD LED'), 56 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 57 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR.index( 58 | { 59 | 'Control Item': 'Brightness', 60 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 61 | } 62 | ), 63 | LED_BRIGHTNESS_MULTI_COLOR.index('30') 64 | ] 65 | read_byte_list = [ 66 | 0x00, 67 | 0x00, 68 | 0x00, 69 | 0x00 70 | ] 71 | 72 | nuc_wmi_read_control_file.return_value = read_byte_list 73 | returned_set_led_control_item = set_led_control_item( 74 | LED_TYPE['new'].index('HDD LED'), 75 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 76 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR.index( 77 | { 78 | 'Control Item': 'Brightness', 79 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 80 | } 81 | ), 82 | LED_BRIGHTNESS_MULTI_COLOR.index('30') 83 | ) 84 | 85 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 86 | 87 | self.assertEqual(returned_set_led_control_item, None) 88 | 89 | # Reset 90 | nuc_wmi_read_control_file.reset_mock() 91 | nuc_wmi_write_control_file.reset_mock() 92 | 93 | # Branch 2: Test that set_led_control_item raises an exception when the control file returns an 94 | # error code. 95 | 96 | # Incorrect led 97 | expected_write_byte_list = [ 98 | METHOD_ID, 99 | len(LED_TYPE['new']), # Incorrect led 100 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 101 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR.index( 102 | { 103 | 'Control Item': 'Brightness', 104 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 105 | } 106 | ), 107 | LED_BRIGHTNESS_MULTI_COLOR.index('30') 108 | ] 109 | read_byte_list = [ 110 | 0xE4, 111 | 0x00, 112 | 0x00, 113 | 0x00 114 | ] 115 | 116 | nuc_wmi_read_control_file.return_value = read_byte_list 117 | 118 | with self.assertRaises(NucWmiError) as err: 119 | returned_set_led_control_item = set_led_control_item( 120 | len(LED_TYPE['new']), # Incorrect led 121 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 122 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR.index( 123 | { 124 | 'Control Item': 'Brightness', 125 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 126 | } 127 | ), 128 | LED_BRIGHTNESS_MULTI_COLOR.index('30') 129 | ) 130 | 131 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 132 | 133 | self.assertEqual(str(err.exception), 'Error (Invalid Parameter)') 134 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/cli/set_led_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.cli.set_led_test` module provides unit tests for the functions in 3 | `nuc_wmi.cli.set_led`. 4 | 5 | Classes: 6 | TestCliSetLed: A unit test class for the functions in `nuc_wmi.cli.set_led`. 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | import json 12 | import unittest 13 | 14 | from mock import patch 15 | 16 | from nuc_wmi import LED_BRIGHTNESS, LED_COLOR, LED_COLOR_TYPE, LED_BLINK_FREQUENCY, LED_TYPE, NucWmiError 17 | from nuc_wmi.cli.set_led import set_led_cli 18 | 19 | import nuc_wmi 20 | 21 | 22 | class TestCliSetLed(unittest.TestCase): 23 | """ 24 | A unit test class for the functions of `nuc_wmi.cli.set_led` 25 | 26 | Methods: 27 | setUp: Unit test initialization. 28 | test_set_led_cli: Tests that it returns the proper JSON response and exit code for 29 | valid cli args, tests that it captures raised errors and returns 30 | the proper JSON error response and exit code, tests that invalid 31 | LED color raises appropriate error. 32 | """ 33 | 34 | def setUp(self): 35 | """ 36 | Initializes the unit tests. 37 | """ 38 | 39 | self.maxDiff = None 40 | 41 | @patch('nuc_wmi.cli.set_led.print') 42 | @patch('nuc_wmi.cli.set_led.exit') 43 | @patch('nuc_wmi.cli.set_led.set_led') 44 | def test_set_led_cli( 45 | self, 46 | nuc_wmi_set_led, 47 | nuc_wmi_sys_exit, 48 | nuc_wmi_print 49 | ): 50 | """ 51 | Tests that `set_led_cli` returns the expected exceptions, return values, or outputs. 52 | """ 53 | 54 | self.assertTrue(nuc_wmi.cli.set_led.set_led is \ 55 | nuc_wmi_set_led) 56 | self.assertTrue(nuc_wmi.cli.set_led.exit is nuc_wmi_sys_exit) 57 | self.assertTrue(nuc_wmi.cli.set_led.print is nuc_wmi_print) 58 | 59 | # Branch 1: Test that set_led_cli returns the proper JSON response and exit 60 | # code for valid cli args 61 | 62 | # Set S0 Ring LED to brightness of 47%, frequency of Always on, and color of Cyan 63 | returned_set_led_cli = set_led_cli( 64 | [ 65 | LED_TYPE['legacy'][2], 66 | LED_BRIGHTNESS['legacy'][47], 67 | LED_BLINK_FREQUENCY['legacy'][4], 68 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']][1] 69 | ] 70 | ) 71 | 72 | nuc_wmi_set_led.assert_called_with( 73 | LED_TYPE['legacy'].index('S0 Ring LED'), 74 | str(LED_BRIGHTNESS['legacy'].index('47')), 75 | LED_BLINK_FREQUENCY['legacy'].index('Always on'), 76 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Cyan'), 77 | control_file=None 78 | ) 79 | nuc_wmi_print.assert_called() 80 | self.assertEqual( 81 | json.loads(nuc_wmi_print.call_args.args[0]), 82 | { 83 | 'led': { 84 | 'type': LED_TYPE['legacy'][2], 85 | 'brightness': str(LED_BRIGHTNESS['legacy'][47]), 86 | 'frequency': LED_BLINK_FREQUENCY['legacy'][4], 87 | 'color': LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']][1] 88 | } 89 | } 90 | ) 91 | 92 | self.assertEqual(returned_set_led_cli, None) 93 | 94 | # Reset 95 | nuc_wmi_set_led.reset_mock() 96 | nuc_wmi_sys_exit.reset_mock() 97 | nuc_wmi_print.reset_mock() 98 | 99 | # Branch 2: Test that set_led_cli captures raised errors and returns 100 | # the proper JSON error response and exit code. 101 | nuc_wmi_set_led.side_effect = NucWmiError('Error (Function not supported)') 102 | 103 | returned_set_led_cli = set_led_cli( 104 | [ 105 | LED_TYPE['legacy'][2], 106 | LED_BRIGHTNESS['legacy'][47], 107 | LED_BLINK_FREQUENCY['legacy'][4], 108 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']][1] 109 | ] 110 | ) 111 | 112 | nuc_wmi_set_led.assert_called_with( 113 | LED_TYPE['legacy'].index('S0 Ring LED'), 114 | str(LED_BRIGHTNESS['legacy'].index('47')), 115 | LED_BLINK_FREQUENCY['legacy'].index('Always on'), 116 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Cyan'), 117 | control_file=None 118 | ) 119 | nuc_wmi_print.assert_called_with('{"error": "Error (Function not supported)"}') 120 | nuc_wmi_sys_exit.assert_called_with(1) 121 | 122 | self.assertEqual(returned_set_led_cli, None) 123 | 124 | # Reset 125 | nuc_wmi_set_led.reset_mock() 126 | nuc_wmi_sys_exit.reset_mock() 127 | nuc_wmi_print.reset_mock() 128 | 129 | # Branch 3: Tests that invalid LED color raises appropriate error. 130 | returned_set_led_cli = set_led_cli( 131 | [ 132 | LED_TYPE['legacy'][1], 133 | LED_BRIGHTNESS['legacy'][47], 134 | LED_BLINK_FREQUENCY['legacy'][4], 135 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']][1] 136 | ] 137 | ) 138 | 139 | nuc_wmi_set_led.assert_not_called() 140 | nuc_wmi_print.assert_called_with('{"error": "Invalid color for the specified legacy LED"}') 141 | nuc_wmi_sys_exit.assert_called_with(1) 142 | 143 | self.assertEqual(returned_set_led_cli, None) 144 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/cli/set_led_control_item.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.cli.set_led_control_item` provides a CLI interface to the WMI set control item function. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | from argparse import ArgumentParser 8 | from json import dumps 9 | from sys import exit 10 | 11 | from nuc_wmi import CONTROL_ITEM, CONTROL_FILE, LED_COLOR, LED_COLOR_TYPE, LED_INDICATOR_OPTION, LED_TYPE 12 | from nuc_wmi.query_led import query_led_color_type, query_led_indicator_options 13 | from nuc_wmi.set_led_control_item import set_led_control_item 14 | 15 | def set_led_control_item_cli(cli_args=None): 16 | """ 17 | Creates a CLI interface on top of the `nuc_wmi.set_led_control` `set_led_control_item` function. 18 | 19 | Args: 20 | cli_args: If provided, overrides the CLI args to use for `argparse`. 21 | CLI Args: 22 | control_item: The control item of the specified LED type indicator option for which to set the value. 23 | control_item_value: The value for the control item to set. 24 | led_indicator_option: The indicator option for the specified LED type for which to set the control 25 | item value. 26 | led: Selects the LED to set the control item for. 27 | CLI Options: 28 | --control_file : Sets the control file to use if provided, 29 | otherwise `nuc_wmi.CONTROL_FILE` is used. 30 | Outputs: 31 | stdout: JSON object with control item value for the control item of the indicator option for the selected LED or 32 | error message with failure error. 33 | Exit code: 34 | 0 on successfully setting the control item value or 1 on error. 35 | """ 36 | 37 | control_item_labels = list() 38 | control_item_values = list() 39 | 40 | for indicator_option in CONTROL_ITEM: 41 | if indicator_option is None: 42 | continue 43 | 44 | for control_items in indicator_option: 45 | if control_items is None: 46 | continue 47 | 48 | for control_item in control_items: 49 | control_item_labels.append(control_item['Control Item']) 50 | 51 | if control_item['Options'] is not None and control_item['Options'] != LED_COLOR['new']: 52 | control_item_values.extend(control_item['Options']) 53 | 54 | control_item_values.extend(LED_COLOR['new']['Dual-color Blue / Amber']) 55 | control_item_values.extend(LED_COLOR['new']['Dual-color Blue / White']) 56 | 57 | parser = ArgumentParser( 58 | description='Set the control item value for the control item of the indicator option ' + \ 59 | 'for the specified LED type.' 60 | ) 61 | 62 | parser.add_argument( 63 | '-c', 64 | '--control-file', 65 | default=None, 66 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 67 | ) 68 | parser.add_argument( 69 | 'led', 70 | choices=LED_TYPE['new'], 71 | help='The LED for which to set the control item value.' 72 | ) 73 | parser.add_argument( 74 | 'led_indicator_option', 75 | choices=LED_INDICATOR_OPTION, 76 | help='The LED indicator option for the LED.' 77 | ) 78 | parser.add_argument( 79 | 'control_item', 80 | choices=set(control_item_labels), 81 | help='The control item for the LED indicator option that is being set.' 82 | ) 83 | parser.add_argument( 84 | 'control_item_value', 85 | choices=set(control_item_values), 86 | help='The control item value for the control item for the LED indicator option that is being set.' 87 | ) 88 | 89 | try: 90 | args = parser.parse_args(args=cli_args) 91 | 92 | available_indicator_options = query_led_indicator_options( 93 | LED_TYPE['new'].index(args.led), 94 | control_file=args.control_file 95 | ) 96 | 97 | led_color_type = query_led_color_type( 98 | LED_TYPE['new'].index(args.led), 99 | control_file=args.control_file 100 | ) 101 | 102 | indicator = LED_INDICATOR_OPTION.index(args.led_indicator_option) 103 | 104 | if indicator not in available_indicator_options: 105 | raise ValueError('Invalid indicator option for the selected LED') 106 | 107 | control_items = CONTROL_ITEM[indicator][led_color_type] 108 | 109 | if control_items is None: 110 | raise ValueError('No control items are available for the selected LED and indicator option') 111 | 112 | control_item_index = None 113 | 114 | for index, control_item in enumerate(control_items): 115 | if control_item['Control Item'] == args.control_item: 116 | control_item_index = index 117 | 118 | if control_item_index is None: 119 | raise ValueError('Invalid control item specified for the selected LED and indicator option') 120 | 121 | try: 122 | # Convert the control item value into its index 123 | if control_items[control_item_index]['Options'] == LED_COLOR['new']: 124 | control_item_value_index = control_items[control_item_index]['Options'][LED_COLOR_TYPE['new'][led_color_type]].index(args.control_item_value) 125 | else: 126 | control_item_value_index = control_items[control_item_index]['Options'].index(args.control_item_value) 127 | except ValueError as err: 128 | raise ValueError('Invalid control item value for the specified control item') 129 | 130 | set_led_control_item( 131 | LED_TYPE['new'].index(args.led), 132 | indicator, 133 | control_item_index, 134 | control_item_value_index, 135 | control_file=args.control_file 136 | ) 137 | 138 | print( 139 | dumps( 140 | { 141 | 'led': { 142 | 'type': args.led, 143 | 'indicator_option': args.led_indicator_option, 144 | 'control_item': args.control_item, 145 | 'control_item_value': args.control_item_value 146 | } 147 | } 148 | ) 149 | ) 150 | except Exception as err: 151 | print(dumps({'error': str(err)})) 152 | 153 | exit(1) 154 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/control_file_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.control_file_test` module provides unit tests for the functions in 3 | `nuc_wmi.control_file`. 4 | 5 | Classes: 6 | TestControlFile: A unit test class for the functions in `nuc_wmi.control_file`. 7 | """ 8 | 9 | import os 10 | import unittest 11 | 12 | from mock import patch 13 | from tempfile import NamedTemporaryFile 14 | 15 | from nuc_wmi import NucWmiError 16 | from nuc_wmi.control_file import read_control_file, write_control_file 17 | 18 | 19 | class TestControlFile(unittest.TestCase): 20 | """ 21 | A unit test class for the functions of `nuc_wmi.control_file` 22 | 23 | Methods: 24 | setUp: Unit test initialization. 25 | tearDown: Unit test cleanup. 26 | test_read_control_file: Tests that `read_control_file` raises the expected exception when nuc_wmi.CONTROL_FILE 27 | doesnt exist, tests that exception is raised if less than 4 bytes are returned, tests 28 | that overriding control_file with existing file works, tests that overriding 29 | control_file with non existing file raises exception, and tests that exception is raised 30 | if NUC WMI provides a hex byte value outside of the 0-255 range. 31 | test_write_control_file: Tests that `write_control_file` raises the expected exception when nuc_wmi.CONTROL_FILE 32 | doesnt exist, tests that number of bytes written to control file are padded to 5 bytes 33 | if less than 5 bytes are passed in, tests that both integer and string bytes are 34 | accepted, tests that byte strings outside of 0-255 value raise an exception, tests that 35 | overriding control_file with different file works. 36 | """ 37 | 38 | def setUp(self): 39 | """ 40 | Initializes the unit tests. 41 | """ 42 | 43 | self.control_file = NamedTemporaryFile(delete=False) 44 | self.maxDiff = None 45 | 46 | self.control_file.close() 47 | 48 | 49 | def tearDown(self): 50 | """ 51 | Cleans up the unit tests. 52 | """ 53 | 54 | self.control_file.close() 55 | 56 | os.unlink(self.control_file.name) 57 | 58 | 59 | def test_read_control_file(self): 60 | """ 61 | Tests that `read_control_file` returns the expected exceptions, return values, or outputs. 62 | """ 63 | 64 | # Branch 1: Test that `read_control_file` raises exception when `nuc_wmi.CONTROL_FILE` doesnt exist 65 | # Assumes we are testing on a system without the driver installed. 66 | with self.assertRaises((IOError, OSError)) as err: 67 | read_control_file() 68 | 69 | # Reset 70 | with open(self.control_file.name, 'w') as fout: 71 | fout.truncate() 72 | 73 | # Branch 2: Test that `read_control_file` raise exception if less than 4 bytes are read 74 | with open(self.control_file.name, 'w') as fout: 75 | fout.write("00 00 00\n\x00") 76 | 77 | with self.assertRaises(NucWmiError) as byte_list_len_err: 78 | read_control_file(control_file=self.control_file.name) 79 | 80 | self.assertEqual(str(byte_list_len_err.exception), 81 | 'NUC WMI control file did not return an expected 4 bytes') 82 | 83 | # Reset 84 | with open(self.control_file.name, 'w') as fout: 85 | fout.truncate() 86 | 87 | # Branch 3: Test that overriding control file with existing file works 88 | with open(self.control_file.name, 'w') as fout: 89 | fout.write("0D 0E 0A 0D\n\x00") 90 | 91 | byte_list = (0x0D, 0x0E, 0x0A, 0x0D) 92 | 93 | self.assertEqual(read_control_file(control_file=self.control_file.name), byte_list) 94 | 95 | # Reset 96 | with open(self.control_file.name, 'w') as fout: 97 | fout.truncate() 98 | 99 | # Branch 4: Test that overriding control file with non existing file raises exception 100 | non_existent_file = NamedTemporaryFile() 101 | 102 | non_existent_file.close() 103 | 104 | with self.assertRaises((IOError, OSError)) as err: 105 | read_control_file(control_file=non_existent_file.name) 106 | 107 | # Branch 5: Test that exception is raised if NUC WMI returns a hex byte outside 0-255 range 108 | with open(self.control_file.name, 'w') as fout: 109 | fout.write("FFF 0E 0A 0D\n\x00") 110 | 111 | with self.assertRaises(NucWmiError) as err: 112 | read_control_file(control_file=self.control_file.name) 113 | 114 | self.assertEqual(str(err.exception), 'NUC WMI returned hex byte outside of 0-255 range') 115 | 116 | 117 | def test_write_control_file(self): 118 | """ 119 | Tests that `write_control_file` returns the expected exceptions, return values, or outputs. 120 | """ 121 | 122 | # Branch 1: Tests that `write_control_file` raises the expected exception when `nuc_wmi.CONTROL_FILE` doesnt 123 | # exist. Assumes we are testing on a system without the driver installed. 124 | with self.assertRaises((IOError, OSError)) as err: 125 | read_control_file() 126 | 127 | # Reset 128 | with open(self.control_file.name, 'w') as fout: 129 | fout.truncate() 130 | 131 | # Branch 2, 3: Tests that the number of bytes written to the control file are padded to 5 bytes, and that 132 | # integer byte list is properly written to the control file. 133 | byte_list = [0x0D, 0x0E, 0x0A, 0x0D] 134 | expected_byte_string='0d 0e 0a 0d 00' 135 | 136 | write_control_file(byte_list, control_file=self.control_file.name) 137 | 138 | with open(self.control_file.name, 'r') as fin: 139 | written_byte_string = fin.read() 140 | 141 | self.assertEqual(expected_byte_string, written_byte_string) 142 | 143 | # Reset 144 | with open(self.control_file.name, 'w') as fout: 145 | fout.truncate() 146 | 147 | # Branch 4: Tests that an string byte list is properly written to the control file 148 | byte_list = [str(0x0D), str(0x0E), str(0x0A), str(0x0D)] 149 | expected_byte_string='0d 0e 0a 0d 00' 150 | 151 | write_control_file(byte_list, control_file=self.control_file.name) 152 | 153 | with open(self.control_file.name, 'r') as fin: 154 | written_byte_string = fin.read() 155 | 156 | self.assertEqual(expected_byte_string, written_byte_string) 157 | 158 | # Reset 159 | with open(self.control_file.name, 'w') as fout: 160 | fout.truncate() 161 | 162 | # Branch 5: Test that byte strings outside of the 0-255 value raise an exception 163 | byte_list = [0xFFF] 164 | 165 | with self.assertRaises(NucWmiError) as err: 166 | write_control_file(byte_list, control_file=self.control_file.name) 167 | 168 | self.assertEqual(str(err.exception), 'Error (NUC LED byte values must be 0-255)') 169 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/query_led.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.query_led` provides an interface to the WMI query led set of functions. 3 | """ 4 | 5 | from nuc_wmi import CONTROL_ITEM, LED_COLOR_TYPE, LED_INDICATOR_OPTION, LED_TYPE, NucWmiError, RETURN_ERROR 6 | from nuc_wmi.control_file import read_control_file, write_control_file 7 | from nuc_wmi.utils import byte_list_to_bitmap 8 | 9 | LED_INDICATOR_OPTION_DISABLED = 0x06 10 | METHOD_ID=0x03 11 | 12 | QUERY_TYPE = [ 13 | 'query_leds', 14 | 'query_led_color_type', 15 | 'query_led_indicator_options', 16 | 'query_led_control_items' 17 | ] 18 | 19 | def query_led_color_type(led_type, control_file=None): 20 | """ 21 | Query the LED color type for the LED type. 22 | 23 | Args: 24 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 25 | led_type: The LED type for which to query the LED color type. 26 | Exceptions: 27 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 28 | or if `read_control_file` or `write_control_file` raise an exception. 29 | Returns: 30 | `nuc_wmi.LED_COLOR_TYPE` index of current LED color type. 31 | """ 32 | 33 | query_led_color_byte_list = [METHOD_ID, QUERY_TYPE.index('query_led_color_type'), led_type] 34 | 35 | write_control_file(query_led_color_byte_list, control_file=control_file) 36 | 37 | # Bitmap [0:7], [8:15], [16:23] 38 | ( 39 | error_code, 40 | led_color_type_bitmap_1, 41 | led_color_type_bitmap_2, 42 | led_color_type_bitmap_3 43 | ) = read_control_file(control_file=control_file) 44 | 45 | if error_code > 0: 46 | raise NucWmiError(RETURN_ERROR.get(error_code, 'Error (Unknown NUC WMI error code)')) 47 | 48 | led_color_type_bitmaps = [ 49 | led_color_type_bitmap_3, 50 | led_color_type_bitmap_2, 51 | led_color_type_bitmap_1 52 | ] 53 | led_color_type_bitmap = byte_list_to_bitmap(led_color_type_bitmaps) 54 | 55 | return int(led_color_type_bitmap, 2) 56 | 57 | 58 | def query_led_control_items(led_type, led_indicator_option, control_file=None): 59 | """ 60 | Query the LED control items for the LED indicator option of the LED type. 61 | 62 | Args: 63 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 64 | led_indicator_option: The LED indicator option to use for the LED type when querying for the LED control items. 65 | led_type: The LED type for which to query the LED control items. 66 | Exceptions: 67 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 68 | or if `read_control_file` or `write_control_file` raise an exception. 69 | Returns: 70 | List of available `nuc_wmi.CONTROL_ITEM` indexes for the specified LED type and LED indicator option. 71 | """ 72 | 73 | led_color_type = query_led_color_type(led_type, control_file=control_file) 74 | 75 | query_led_control_item_byte_list = [ 76 | METHOD_ID, 77 | QUERY_TYPE.index('query_led_control_items'), 78 | led_type, 79 | led_indicator_option 80 | ] 81 | 82 | write_control_file(query_led_control_item_byte_list, control_file=control_file) 83 | 84 | # Bitmap [0:7], [8:15], [16:23] 85 | ( 86 | error_code, 87 | led_control_item_bitmap_1, 88 | led_control_item_bitmap_2, 89 | led_control_item_bitmap_3 90 | ) = read_control_file(control_file=control_file) 91 | 92 | if error_code > 0: 93 | raise NucWmiError(RETURN_ERROR.get(error_code, 'Error (Unknown NUC WMI error code)')) 94 | 95 | led_control_item_bitmaps = [ 96 | led_control_item_bitmap_3, 97 | led_control_item_bitmap_2, 98 | led_control_item_bitmap_1 99 | ] 100 | led_control_item_bitmap = byte_list_to_bitmap(led_control_item_bitmaps)[::-1] 101 | 102 | if led_indicator_option == LED_INDICATOR_OPTION_DISABLED or \ 103 | CONTROL_ITEM[led_indicator_option][led_color_type] is None: 104 | return [] 105 | 106 | return [index for index, bit in enumerate(led_control_item_bitmap) if int(bit) and 107 | index < len(CONTROL_ITEM[led_indicator_option][led_color_type])] 108 | 109 | 110 | def query_led_indicator_options(led_type, control_file=None): 111 | """ 112 | Query the LED indicator options available for the LED type. 113 | 114 | Args: 115 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 116 | led_type: The LED type for which to query the LED indicator options. 117 | Exceptions: 118 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 119 | or if `read_control_file` or `write_control_file` raise an exception. 120 | Returns: 121 | List of available `nuc_wmi.LED_INDICATOR_OPTION` indexes. 122 | """ 123 | 124 | query_led_indicator_options_byte_list = [ 125 | METHOD_ID, 126 | QUERY_TYPE.index('query_led_indicator_options'), 127 | led_type 128 | ] 129 | 130 | write_control_file(query_led_indicator_options_byte_list, control_file=control_file) 131 | 132 | # Bitmap [0:7], [8:15], [16:23] 133 | ( 134 | error_code, 135 | led_indicator_option_bitmap_1, 136 | led_indicator_option_bitmap_2, 137 | led_indicator_option_bitmap_3 138 | ) = read_control_file(control_file=control_file) 139 | 140 | if error_code > 0: 141 | raise NucWmiError(RETURN_ERROR.get(error_code, 'Error (Unknown NUC WMI error code)')) 142 | 143 | led_indicator_option_bitmaps = [ 144 | led_indicator_option_bitmap_3, 145 | led_indicator_option_bitmap_2, 146 | led_indicator_option_bitmap_1 147 | ] 148 | led_indicator_option_bitmap = byte_list_to_bitmap(led_indicator_option_bitmaps)[::-1] 149 | 150 | return [index for index, bit in enumerate(led_indicator_option_bitmap) if int(bit) and 151 | index < len(LED_INDICATOR_OPTION)] 152 | 153 | 154 | def query_leds(control_file=None): 155 | """ 156 | List all LED types supported. 157 | 158 | Args: 159 | control_file: Sets the control file to use if provided, otherwise `nuc_wmi.CONTROL_FILE` is used. 160 | Exceptions: 161 | Raises `nuc_wmi.NucWmiError` exception if kernel module returns an error code, 162 | or if `read_control_file` or `write_control_file` raise an exception. 163 | Returns: 164 | List of available `nuc_wmi.LED_TYPE` indexes. 165 | """ 166 | 167 | query_leds_byte_list = [METHOD_ID, QUERY_TYPE.index('query_leds')] 168 | 169 | write_control_file(query_leds_byte_list, control_file=control_file) 170 | 171 | # Bitmap [0:7], [8:15], [16:23] 172 | ( 173 | error_code, 174 | led_type_bitmap_1, 175 | led_type_bitmap_2, 176 | led_type_bitmap_3 177 | ) = read_control_file(control_file=control_file) 178 | 179 | if error_code > 0: 180 | raise NucWmiError(RETURN_ERROR.get(error_code, 'Error (Unknown NUC WMI error code)')) 181 | 182 | led_type_bitmaps = [ 183 | led_type_bitmap_3, 184 | led_type_bitmap_2, 185 | led_type_bitmap_1 186 | ] 187 | led_type_bitmap = byte_list_to_bitmap(led_type_bitmaps)[::-1] 188 | 189 | return [index for index, bit in enumerate(led_type_bitmap) if int(bit) and index < len(LED_TYPE['new'])] 190 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/cli/get_led_new.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.cli.get_led_new` provides a CLI interface to the WMI get led set of functions. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | from argparse import ArgumentParser 8 | from json import dumps 9 | from sys import exit 10 | 11 | from nuc_wmi import CONTROL_ITEM, CONTROL_FILE, LED_COLOR, LED_COLOR_TYPE, LED_INDICATOR_OPTION, LED_TYPE 12 | from nuc_wmi.get_led_new import get_led_control_item, get_led_indicator_option 13 | from nuc_wmi.query_led import query_led_color_type, query_led_indicator_options 14 | 15 | def get_led_control_item_cli(cli_args=None): 16 | """ 17 | Creates a CLI interface on top of the `nuc_wmi.get_led_new` `get_led_control_item` function. 18 | 19 | Args: 20 | cli_args: If provided, overrides the CLI args to use for `argparse`. 21 | CLI Args: 22 | control_item: The control item of the specified LED type indicator option for which to retrieve the value. 23 | led_indicator_option: The indicator option for the specified LED type for which to retrieve the current control 24 | item value. 25 | led: Selects the LED to get the control item for. 26 | CLI Options: 27 | --control_file : Sets the control file to use if provided, 28 | otherwise `nuc_wmi.CONTROL_FILE` is used. 29 | Outputs: 30 | stdout: JSON object with control item value for the control item of the indicator option for the selected LED or 31 | error message with failure error. 32 | Exit code: 33 | 0 on successfully retrieving the control item value or 1 on error. 34 | """ 35 | 36 | control_item_labels = list() 37 | 38 | for indicator_option in CONTROL_ITEM: 39 | if indicator_option is None: 40 | continue 41 | 42 | for control_items in indicator_option: 43 | if control_items is None: 44 | continue 45 | 46 | for control_item in control_items: 47 | control_item_labels.append(control_item['Control Item']) 48 | 49 | parser = ArgumentParser( 50 | description='Get the current control item value for the control item of the indicator option ' + \ 51 | 'for the specified LED type.' 52 | ) 53 | 54 | parser.add_argument( 55 | '-c', 56 | '--control-file', 57 | default=None, 58 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 59 | ) 60 | parser.add_argument( 61 | 'led', 62 | choices=LED_TYPE['new'], 63 | help='The LED for which to get the control item value.' 64 | ) 65 | parser.add_argument( 66 | 'led_indicator_option', 67 | choices=LED_INDICATOR_OPTION, 68 | help='The LED indicator option for the current LED.' 69 | ) 70 | parser.add_argument( 71 | 'control_item', 72 | choices=set(control_item_labels), 73 | help='The control item for the current LED indicator option that is being retrieved.' 74 | ) 75 | 76 | try: 77 | args = parser.parse_args(args=cli_args) 78 | 79 | available_indicator_options = query_led_indicator_options( 80 | LED_TYPE['new'].index(args.led), 81 | control_file=args.control_file 82 | ) 83 | 84 | led_color_type = query_led_color_type( 85 | LED_TYPE['new'].index(args.led), 86 | control_file=args.control_file 87 | ) 88 | 89 | indicator = LED_INDICATOR_OPTION.index(args.led_indicator_option) 90 | 91 | if indicator not in available_indicator_options: 92 | raise ValueError('Invalid indicator option for the selected LED') 93 | 94 | control_items = CONTROL_ITEM[indicator][led_color_type] 95 | 96 | if control_items is None: 97 | raise ValueError('No control items are available for the selected LED and indicator option') 98 | 99 | control_item_index = None 100 | 101 | for index, control_item in enumerate(control_items): 102 | if control_item['Control Item'] == args.control_item: 103 | control_item_index = index 104 | 105 | if control_item_index is None: 106 | raise ValueError('Invalid control item specified for the selected LED and indicator option') 107 | 108 | control_item_value = get_led_control_item( 109 | LED_TYPE['new'].index(args.led), 110 | indicator, 111 | control_item_index, 112 | control_file=args.control_file 113 | ) 114 | 115 | # Convert the control item value index into its value 116 | if control_items[control_item_index]['Options'] == LED_COLOR['new']: 117 | control_item_value = control_items[control_item_index]['Options'][LED_COLOR_TYPE['new'][led_color_type]][control_item_value] 118 | else: 119 | control_item_value = control_items[control_item_index]['Options'][control_item_value] 120 | 121 | print( 122 | dumps( 123 | { 124 | 'led': { 125 | 'type': args.led, 126 | 'indicator_option': args.led_indicator_option, 127 | 'control_item': args.control_item, 128 | 'control_item_value': control_item_value 129 | } 130 | } 131 | ) 132 | ) 133 | except Exception as err: 134 | print(dumps({'error': str(err)})) 135 | 136 | exit(1) 137 | 138 | 139 | def get_led_indicator_option_cli(cli_args=None): 140 | """ 141 | Creates a CLI interface on top of the `nuc_wmi.get_led_new` `get_led_indicator_option` function. 142 | 143 | Args: 144 | cli_args: If provided, overrides the CLI args to use for `argparse`. 145 | CLI Args: 146 | led: Selects the LED to get the indicator option for. 147 | CLI Options: 148 | --control_file : Sets the control file to use if provided, 149 | otherwise `nuc_wmi.CONTROL_FILE` is used. 150 | Outputs: 151 | stdout: JSON object with indicator option of the selected LED or error message with 152 | failure error. 153 | Exit code: 154 | 0 on successfully retrieving the selected LED's indicator option or 1 on error. 155 | """ 156 | 157 | parser = ArgumentParser( 158 | description='Get the current indicator option for the LED type.' 159 | ) 160 | 161 | parser.add_argument( 162 | '-c', 163 | '--control-file', 164 | default=None, 165 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 166 | ) 167 | parser.add_argument( 168 | 'led', 169 | choices=LED_TYPE['new'], 170 | help='The LED for which to get the indicator option.' 171 | ) 172 | 173 | try: 174 | args = parser.parse_args(args=cli_args) 175 | 176 | indicator_option = get_led_indicator_option(LED_TYPE['new'].index(args.led), control_file=args.control_file) 177 | 178 | print( 179 | dumps( 180 | { 181 | 'led': { 182 | 'type': args.led, 183 | 'indicator_option': LED_INDICATOR_OPTION[indicator_option] 184 | } 185 | } 186 | ) 187 | ) 188 | except Exception as err: 189 | print(dumps({'error': str(err)})) 190 | 191 | exit(1) 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intel NUC LED Control: NUC6CAY, NUC7i[x]BN and NUC10i3FNH, maybe more 2 | 3 | This is a simple kernel module to control LEDs on Intel NUCs, with 4 | optional high-level userspace tools. 5 | 6 | It is based on the previous work at 7 | [github.com/milesp20/intel_nuc_led](https://github.com/milesp20/intel_nuc_led/), but 8 | with significant changes: 9 | 10 | * it can be used with the more recent NUC10 as well 11 | * it tracks the more recent kernel APIs 12 | * the kernel API is very low-level, and you'll be sending and receiving bytes 13 | * higher-level commands like "turn the power LED to flashing red" are not 14 | implemented in the kernel module itself, but in user space. 15 | 16 | This was primarily created for [UBOS](https://ubos.net/), a Linux distro for 17 | self-hosting (based on Arch) and is mostly tested there. But chances are you 18 | can run it on other distros as well. 19 | 20 | Pull requests appreciated. And reports if you could (or could not) get it 21 | running on other NUCs with software-controllable LEDs, and other distros. 22 | 23 | ## Requirements 24 | 25 | Requirements: 26 | 27 | * Intel NUC6CAY, or NUC7i[x]BN or NUC10i3FNH, maybe more 28 | * BIOS AY0038 or BN0043 or later 29 | * ACPI/WMI support in kernel 30 | * LED(s) set to `SW Control` in BIOS for those (older) NUCs where this can 31 | only be changed in the BIOS, not via API. 32 | 33 | ## Building the kernel module 34 | 35 | The `nuc_led` kernel module supports building and installing "from source" directly or using `dkms`. 36 | 37 | ### Installing Build Dependencies 38 | 39 | UBOS: you don't need to, it's in the repos: ``pacman -S intel-nuc-led`` 40 | 41 | If you want to build it anyway, you need: 42 | 43 | ``` 44 | pacman -S linux-headers base-develop 45 | ``` 46 | 47 | Ubuntu (not verified): 48 | 49 | ``` 50 | apt-get install build-essential linux-headers-$(uname -r) 51 | 52 | # DKMS dependencies 53 | apt-get install debhelper dkms 54 | ``` 55 | 56 | Redhat (not verified): 57 | 58 | ``` 59 | yum groupinstall "Development Tools" 60 | yum install kernel-devel-$(uname -r) 61 | 62 | # Install appropriate EPEL for DKMS if needed by your RHEL variant 63 | yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm 64 | 65 | yum install dkms 66 | ``` 67 | 68 | ### Building and Installing "from source" 69 | 70 | ``` 71 | make clean 72 | make install 73 | ``` 74 | 75 | ### Building and Installing Using DKMS 76 | 77 | Build and install without system packaging: 78 | 79 | ``` 80 | make dkms-install 81 | ``` 82 | 83 | Uninstall without system packaging: 84 | 85 | ``` 86 | make dkms-uninstall 87 | ``` 88 | 89 | Build and install using system packaging: 90 | 91 | ``` 92 | # UBOS 93 | makepkg -i 94 | 95 | # Ubuntu (not verified) 96 | make dkms-deb 97 | 98 | # RHEL (not verified) 99 | make dkms-rpm 100 | 101 | # Install generated DEB/RPM from the folder specified in the output using system package manager 102 | ``` 103 | 104 | ## Low-level vs high-level interface 105 | 106 | In the previous, NUC6/NUC7-only version by milesp20, you would use something like: 107 | 108 | ``` 109 | echo 'ring,80,blink_medium,green' > /proc/acpi/nuc_led 110 | ``` 111 | 112 | to turn the ring LED to a blinking green. But with the greater hardware 113 | variety now supported by this module, and a substantially extended number 114 | of API calls, this interface doesn't make so much sense any more. In 115 | addition, some settings now require several system calls. 116 | 117 | So instead, the kernel module now simply exposes the input and outputs of 118 | the WMI system call, and leaves it to userspace to send in the right bytes, 119 | and interpret the resulting bytes. 120 | 121 | ~~Maybe somebody wants to design some higher-level tools to make this easier? 122 | As a bonus, those tools could run in userspace.~~ 123 | Thanks to [Julio Lajara](https://github.com/ju2wheels), who built exactly 124 | that. See documentation in [contrib/nuc_wmi](contrib/nuc_wmi). 125 | 126 | ## Usage (Kernel device) 127 | 128 | NOTE: this works differently from the previous version by milesp20. 129 | 130 | ``` 131 | echo xx xx xx xx xx > /proc/acpi/nuc_led 132 | ``` 133 | where the ``xx`` are 1-byte hex numbers: 134 | 135 | * first byte: the Method ID of the WMI call 136 | * bytes 2-5: the four bytes of arguments passed into the WMI call 137 | 138 | This will invoke the specified method with the provided arguments, 139 | and save the return results. 140 | 141 | And then: 142 | 143 | ``` 144 | cat /proc/acpi/nuc_led 145 | ``` 146 | will emit 4 hex numbers, which are the bytes returned by the last 147 | invocation of the WMI system call. 148 | 149 | ## The bytes and their values 150 | 151 | The following Intel documents describe the available Method IDs and 152 | parameters: 153 | 154 | * for the NUC6CAY, or NUC7i[x]BN: 155 | [Use WMI Explorer* to Program the Ring LED and Button LED](https://www.intel.com/content/www/us/en/support/articles/000023426/intel-nuc/intel-nuc-kits.html). 156 | 157 | * for the NUC10i3FNH: 158 | [WMI Interface for Intel NUC Products / WMI Specification / Frost Canyon / July2020 Revision 1.0](https://www.intel.com/content/dam/support/us/en/documents/intel-nuc/WMI-Spec-Intel-NUC-NUC10ixFNx.pdf) 159 | 160 | There are copies of these documents here in [contrib/reference](contrib/reference/). 161 | 162 | Note that the WMI APIs have changed significantly. E.g. the Method IDs 163 | for the older models are 1 and 2, while the they are 3 to 9 for the newer 164 | model. 165 | 166 | ## Errors 167 | 168 | Errors will appear as warnings in dmesg or journalctl -k. WMI call 169 | error codes are part of the return value of the WMI call, and shown 170 | through ``cat /proc/acpi/nuc_led``. 171 | 172 | Once the device has been read, the value there will be reset to 173 | ``ff ff ff ff`` (something not used by the WMI call). This is also the 174 | initial value. 175 | 176 | ## Examples 177 | 178 | ### NUC6CAY 179 | 180 | Make sure you have enabled LED software control in the BIOS, as there 181 | is no API call to change that setting on this device. 182 | 183 | To set the Ring LED to brightness 80, blink at medium speed, and green: 184 | 185 | ``` 186 | echo 02 02 50 05 06 > /proc/acpi/nuc_led 187 | ``` 188 | 189 | where: 190 | * `02`: method ID: "Set LED function" 191 | * `02`: Ring LED command mode 192 | * `50`: 80% brightness (in hex) 193 | * `05`: 0.5 Hz 194 | * `06`: green 195 | 196 | ### NUC10i3FNH 197 | 198 | Make sure you have enabled LED software control in the BIOS, or have 199 | previously executed the API call to turn on software control. 200 | 201 | To set the Power Button LED to brightness 80, blink at medium speed, and color amber: 202 | 203 | ``` 204 | echo 06 00 04 00 50 > /proc/acpi/nuc_led # brightness 205 | echo 06 00 04 01 02 > /proc/acpi/nuc_led # blinking behavior 206 | echo 06 00 04 02 05 > /proc/acpi/nuc_led # blinking frequency 207 | echo 06 00 04 03 01 > /proc/acpi/nuc_led # color 208 | 209 | ``` 210 | 211 | where: 212 | 213 | * brightness: 214 | 215 | * `06`: method ID: "Set the value to the control item of the indicator option and the LED type" 216 | * `00`: power button LED 217 | * `04`: software indicator 218 | * `00`: brightness control item 219 | * `50`: brightness value (in hex) 220 | 221 | * blinking behavior (same, then): 222 | 223 | * `01`: blinking behavior control item 224 | * `02`: pulsing 225 | 226 | * blinking frequency (same, then): 227 | 228 | * `02`: blinking behavior control item 229 | * `05`: 5 times 0.1Hz = 0.5Hz 230 | 231 | * color (same, then): 232 | 233 | * `03`: color control item 234 | * `01`: amber color 235 | 236 | ## Permissions 237 | 238 | You can change the owner, group and permissions of `/proc/acpi/nuc_led` by 239 | passing parameters to the kernel module. Use: 240 | 241 | * `nuc_led_uid` to set the owner (default is 0, root) 242 | * `nuc_led_gid` to set the owning group (default is 0, root) 243 | * `nuc_led_perms` to set the file permissions (default is r+w for 244 | group and user and r for others) 245 | 246 | Note: Once an LED has been set to `SW Control` in the BIOS, it will 247 | remain off initially until a color is explicitly set, after which the set 248 | color is retained across reboots. 249 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/set_led_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.set_led_test` module provides unit tests for the functions in 3 | `nuc_wmi.set_led`. 4 | 5 | Classes: 6 | TestSetLed: A unit test class for the functions in `nuc_wmi.set_led`. 7 | """ 8 | 9 | import unittest 10 | 11 | from mock import patch 12 | 13 | from nuc_wmi import LED_BLINK_FREQUENCY, LED_BRIGHTNESS, LED_COLOR, LED_COLOR_TYPE, LED_TYPE, NucWmiError 14 | from nuc_wmi.set_led import METHOD_ID, set_led 15 | 16 | import nuc_wmi 17 | 18 | 19 | class TestSetLed(unittest.TestCase): 20 | """ 21 | A unit test class for the functions of `nuc_wmi.set_led` 22 | 23 | Methods: 24 | setUp: Unit test initialization. 25 | test_set_led: Tests that it sends the expected byte list to the control file, tests that the returned 26 | control file response is properly processed, tests that it raises an exception when the 27 | control file returns an error code. 28 | """ 29 | 30 | def setUp(self): 31 | """ 32 | Initializes the unit tests; 33 | """ 34 | 35 | self.maxDiff = None 36 | 37 | 38 | @patch('nuc_wmi.set_led.read_control_file') 39 | @patch('nuc_wmi.set_led.write_control_file') 40 | def test_set_led(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 41 | """ 42 | Tests that `set_led` returns the expected exceptions, return values, or outputs. 43 | """ 44 | 45 | self.assertTrue(nuc_wmi.set_led.read_control_file is nuc_wmi_read_control_file) 46 | self.assertTrue(nuc_wmi.set_led.write_control_file is nuc_wmi_write_control_file) 47 | 48 | # Branch 1: Test that set_led sends the expected byte string to the control file 49 | # and that the returned control file response is properly processed. 50 | 51 | # Set legacy S0 Ring LED to Yellow, Always on, and 63% brightness 52 | expected_write_byte_list = [ 53 | METHOD_ID, 54 | LED_TYPE['legacy'].index('S0 Ring LED'), 55 | LED_BRIGHTNESS['legacy'].index('63'), 56 | LED_BLINK_FREQUENCY['legacy'].index('Always on'), 57 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Yellow') 58 | ] 59 | read_byte_list = [0x00, 0x00, 0x00, 0x00] 60 | 61 | nuc_wmi_read_control_file.return_value = read_byte_list 62 | returned_set_led = set_led( 63 | LED_TYPE['legacy'].index('S0 Ring LED'), 64 | LED_BRIGHTNESS['legacy'].index('63'), 65 | LED_BLINK_FREQUENCY['legacy'].index('Always on'), 66 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Yellow') 67 | ) 68 | 69 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 70 | 71 | self.assertEqual(returned_set_led, None) 72 | 73 | # Reset 74 | nuc_wmi_read_control_file.reset_mock() 75 | nuc_wmi_write_control_file.reset_mock() 76 | 77 | # Branch 2: Test that set_led raises an exception when the control file returns an 78 | # error code. 79 | 80 | # Incorrect led 81 | expected_write_byte_list = [ 82 | METHOD_ID, 83 | len(LED_TYPE['legacy']), # Set incorrect led 84 | LED_BRIGHTNESS['legacy'].index('63'), 85 | LED_BLINK_FREQUENCY['legacy'].index('Always on'), 86 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Yellow') 87 | ] 88 | read_byte_list = [0xE1, 0x00, 0x00, 0x00] # Return function not supported 89 | 90 | nuc_wmi_read_control_file.return_value = read_byte_list 91 | 92 | with self.assertRaises(NucWmiError) as err: 93 | returned_set_led = set_led( 94 | len(LED_TYPE['legacy']), # Set incorrect led 95 | LED_BRIGHTNESS['legacy'].index('63'), 96 | LED_BLINK_FREQUENCY['legacy'].index('Always on'), 97 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Yellow') 98 | ) 99 | 100 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 101 | 102 | self.assertEqual(str(err.exception), 'Error (Function not supported)') 103 | 104 | # Reset 105 | nuc_wmi_read_control_file.reset_mock() 106 | nuc_wmi_write_control_file.reset_mock() 107 | 108 | # Incorrect brightness 109 | expected_write_byte_list = [ 110 | METHOD_ID, 111 | LED_TYPE['legacy'].index('S0 Ring LED'), 112 | len(LED_BRIGHTNESS['legacy']), # Set incorrect brightness 113 | LED_BLINK_FREQUENCY['legacy'].index('Always on'), 114 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Yellow') 115 | ] 116 | read_byte_list = [0xE4, 0x00, 0x00, 0x00] # Return invalid parameter 117 | 118 | nuc_wmi_read_control_file.return_value = read_byte_list 119 | 120 | with self.assertRaises(NucWmiError) as err: 121 | returned_set_led = set_led( 122 | LED_TYPE['legacy'].index('S0 Ring LED'), 123 | len(LED_BRIGHTNESS['legacy']), # Set incorrect brightness 124 | LED_BLINK_FREQUENCY['legacy'].index('Always on'), 125 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Yellow') 126 | ) 127 | 128 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 129 | 130 | self.assertEqual(str(err.exception), 'Error (Invalid Parameter)') 131 | 132 | # Reset 133 | nuc_wmi_read_control_file.reset_mock() 134 | nuc_wmi_write_control_file.reset_mock() 135 | 136 | # Incorrect frequency 137 | expected_write_byte_list = [ 138 | METHOD_ID, 139 | LED_TYPE['legacy'].index('S0 Ring LED'), 140 | LED_BRIGHTNESS['legacy'].index('63'), 141 | len(LED_BLINK_FREQUENCY['legacy']), # Set incorrect frequency 142 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Yellow') 143 | ] 144 | read_byte_list = [0x00, 0xE4, 0x00, 0x00] # Return invalid parameter 145 | 146 | nuc_wmi_read_control_file.return_value = read_byte_list 147 | 148 | with self.assertRaises(NucWmiError) as err: 149 | returned_set_led = set_led( 150 | LED_TYPE['legacy'].index('S0 Ring LED'), 151 | LED_BRIGHTNESS['legacy'].index('63'), 152 | len(LED_BLINK_FREQUENCY['legacy']), # Set incorrect frequency 153 | LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']].index('Yellow') 154 | ) 155 | 156 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 157 | 158 | self.assertEqual(str(err.exception), 'Error (Invalid Parameter)') 159 | 160 | # Reset 161 | nuc_wmi_read_control_file.reset_mock() 162 | nuc_wmi_write_control_file.reset_mock() 163 | 164 | # Incorrect color 165 | expected_write_byte_list = [ 166 | METHOD_ID, 167 | LED_TYPE['legacy'].index('S0 Ring LED'), 168 | LED_BRIGHTNESS['legacy'].index('63'), 169 | LED_BLINK_FREQUENCY['legacy'].index('Always on'), 170 | len(LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']]) # Set incorrect color 171 | ] 172 | read_byte_list = [0x00, 0x00, 0xE4, 0x00] # Return invalid parameter 173 | 174 | nuc_wmi_read_control_file.return_value = read_byte_list 175 | 176 | with self.assertRaises(NucWmiError) as err: 177 | returned_set_led = set_led( 178 | LED_TYPE['legacy'].index('S0 Ring LED'), 179 | LED_BRIGHTNESS['legacy'].index('63'), 180 | LED_BLINK_FREQUENCY['legacy'].index('Always on'), 181 | len(LED_COLOR['legacy'][LED_COLOR_TYPE['legacy']['S0 Ring LED']]) # Set incorrect color 182 | ) 183 | 184 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 185 | 186 | self.assertEqual(str(err.exception), 'Error (Invalid Parameter)') 187 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/get_led_new_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.get_led_new_test` module provides unit tests for the functions in 3 | `nuc_wmi.get_led_new`. 4 | 5 | Classes: 6 | TestGetLed: A unit test class for the functions in `nuc_wmi.get_led_new`. 7 | """ 8 | 9 | import unittest 10 | 11 | from mock import patch 12 | 13 | from nuc_wmi import CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR, LED_BRIGHTNESS_MULTI_COLOR, LED_INDICATOR_OPTION 14 | from nuc_wmi import LED_TYPE, NucWmiError 15 | from nuc_wmi.get_led_new import GET_LED_TYPE, METHOD_ID, get_led_control_item, get_led_indicator_option 16 | 17 | import nuc_wmi 18 | 19 | 20 | class TestGetLedNew(unittest.TestCase): 21 | """ 22 | A unit test class for the functions of `nuc_wmi.get_led_new` 23 | 24 | Methods: 25 | setUp: Unit test initialization. 26 | test_get_led_control_item: Tests that it sends the expected byte list to the control file, tests that the 27 | returned control file response is properly processed, tests that it raises an 28 | exception when the control file returns an error code. 29 | test_get_led_indicator_option: Tests that it sends the expected byte list to the control file, tests that the 30 | returned control file response is properly processed, tests that it raises an 31 | exception when the control file returns an error code. 32 | """ 33 | 34 | def setUp(self): 35 | """ 36 | Initializes the unit tests; 37 | """ 38 | 39 | self.maxDiff = None 40 | 41 | 42 | @patch('nuc_wmi.get_led_new.read_control_file') 43 | @patch('nuc_wmi.get_led_new.write_control_file') 44 | def test_get_led_control_item(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 45 | """ 46 | Tests that `get_led_control_item` returns the expected exceptions, return values, or outputs. 47 | """ 48 | 49 | self.assertTrue(nuc_wmi.get_led_new.read_control_file is nuc_wmi_read_control_file) 50 | self.assertTrue(nuc_wmi.get_led_new.write_control_file is nuc_wmi_write_control_file) 51 | 52 | # Branch 1: Test that get_led_control_item sends the expected byte string to the control file 53 | # and that the returned control file response is properly processed. 54 | 55 | # Get HDD LED HDD Activity Indicator Brightness of 30% for multi color led 56 | expected_write_byte_list = [ 57 | METHOD_ID, 58 | GET_LED_TYPE.index('get_led_control_item'), 59 | LED_TYPE['new'].index('HDD LED'), 60 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 61 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR.index( 62 | { 63 | 'Control Item': 'Brightness', 64 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 65 | } 66 | ), 67 | ] 68 | read_byte_list = [ 69 | 0x00, 70 | LED_BRIGHTNESS_MULTI_COLOR.index('30'), 71 | 0x00, 72 | 0x00 73 | ] 74 | 75 | nuc_wmi_read_control_file.return_value = read_byte_list 76 | returned_get_led_control_item = get_led_control_item( 77 | LED_TYPE['new'].index('HDD LED'), 78 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 79 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR.index( 80 | { 81 | 'Control Item': 'Brightness', 82 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 83 | } 84 | ) 85 | ) 86 | 87 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 88 | 89 | self.assertEqual(returned_get_led_control_item, read_byte_list[1]) 90 | 91 | # Reset 92 | nuc_wmi_read_control_file.reset_mock() 93 | nuc_wmi_write_control_file.reset_mock() 94 | 95 | # Branch 2: Test that get_led_control_item raises an exception when the control file returns an 96 | # error code. 97 | 98 | # Incorrect led 99 | expected_write_byte_list = [ 100 | METHOD_ID, 101 | GET_LED_TYPE.index('get_led_control_item'), 102 | len(LED_TYPE['new']), # Incorrect led 103 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 104 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR.index( 105 | { 106 | 'Control Item': 'Brightness', 107 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 108 | } 109 | ) 110 | ] 111 | read_byte_list = [ 112 | 0xE4, 113 | 0x00, 114 | 0x00, 115 | 0x00 116 | ] 117 | 118 | nuc_wmi_read_control_file.return_value = read_byte_list 119 | 120 | with self.assertRaises(NucWmiError) as err: 121 | returned_get_led_control_item = get_led_control_item( 122 | len(LED_TYPE['new']), # Incorrect led 123 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 124 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR.index( 125 | { 126 | 'Control Item': 'Brightness', 127 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 128 | } 129 | ) 130 | ) 131 | 132 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 133 | 134 | self.assertEqual(str(err.exception), 'Error (Invalid Parameter)') 135 | 136 | 137 | @patch('nuc_wmi.get_led_new.read_control_file') 138 | @patch('nuc_wmi.get_led_new.write_control_file') 139 | def test_get_led_indicator_option(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 140 | """ 141 | Tests that `get_led_indicator_option` returns the expected exceptions, return values, or outputs. 142 | """ 143 | 144 | self.assertTrue(nuc_wmi.get_led_new.read_control_file is nuc_wmi_read_control_file) 145 | self.assertTrue(nuc_wmi.get_led_new.write_control_file is nuc_wmi_write_control_file) 146 | 147 | # Branch 1: Test that get_led_indicator_option sends the expected byte string to the control file 148 | # and that the returned control file response is properly processed. 149 | 150 | # Get HDD LED with HDD Activity Indicator 151 | expected_write_byte_list = [ 152 | METHOD_ID, 153 | GET_LED_TYPE.index('get_led_indicator_option'), 154 | LED_TYPE['new'].index('HDD LED') 155 | ] 156 | read_byte_list = [ 157 | 0x00, 158 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 159 | 0x00, 160 | 0x00 161 | ] 162 | 163 | nuc_wmi_read_control_file.return_value = read_byte_list 164 | returned_get_led_indicator_option = get_led_indicator_option( 165 | LED_TYPE['new'].index('HDD LED') 166 | ) 167 | 168 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 169 | 170 | self.assertEqual(returned_get_led_indicator_option, read_byte_list[1]) 171 | 172 | # Reset 173 | nuc_wmi_read_control_file.reset_mock() 174 | nuc_wmi_write_control_file.reset_mock() 175 | 176 | # Branch 2: Test that get_led_indicator_option raises an exception when the control file returns an 177 | # error code. 178 | 179 | # Incorrect led 180 | expected_write_byte_list = [ 181 | METHOD_ID, 182 | GET_LED_TYPE.index('get_led_indicator_option'), 183 | len(LED_TYPE['new']) # Incorrect led 184 | ] 185 | read_byte_list = [ 186 | 0xE4, 187 | 0x00, 188 | 0x00, 189 | 0x00 190 | ] 191 | 192 | nuc_wmi_read_control_file.return_value = read_byte_list 193 | 194 | with self.assertRaises(NucWmiError) as err: 195 | returned_get_led_indicator_option = get_led_indicator_option( 196 | len(LED_TYPE['new']) # Incorrect led 197 | ) 198 | 199 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 200 | 201 | self.assertEqual(str(err.exception), 'Error (Invalid Parameter)') 202 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/cli/query_led.py: -------------------------------------------------------------------------------- 1 | """ 2 | `nuc_wmi.cli.query_led` provides a CLI interface to the WMI query led set of functions. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | from argparse import ArgumentParser 8 | from json import dumps 9 | from sys import exit 10 | 11 | from nuc_wmi import CONTROL_ITEM, CONTROL_FILE, LED_COLOR_TYPE, LED_INDICATOR_OPTION, LED_TYPE 12 | from nuc_wmi.query_led import query_led_color_type, query_led_control_items, query_led_indicator_options, query_leds 13 | 14 | def query_led_color_type_cli(cli_args=None): 15 | """ 16 | Creates a CLI interface on top of the `nuc_wmi.query_led` `query_led_color_type` function. 17 | 18 | Args: 19 | cli_args: If provided, overrides the CLI args to use for `argparse`. 20 | CLI Args: 21 | led: Selects the LED to get the color type for. 22 | CLI Options: 23 | --control_file : Sets the control file to use if provided, 24 | otherwise `nuc_wmi.CONTROL_FILE` is used. 25 | Outputs: 26 | stdout: JSON object with color type of the selected LED or error message with 27 | failure error. 28 | Exit code: 29 | 0 on successfully retrieving the selected LED's color type or 1 on error. 30 | """ 31 | 32 | parser = ArgumentParser( 33 | description='Query the LED color type for the LED type.' 34 | ) 35 | 36 | parser.add_argument( 37 | '-c', 38 | '--control-file', 39 | default=None, 40 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 41 | ) 42 | parser.add_argument( 43 | 'led', 44 | choices=LED_TYPE['new'], 45 | help='The LED for which to get the color type.' 46 | ) 47 | 48 | try: 49 | args = parser.parse_args(args=cli_args) 50 | 51 | led_color_type = query_led_color_type(LED_TYPE['new'].index(args.led), control_file=args.control_file) 52 | 53 | print( 54 | dumps( 55 | { 56 | 'led': { 57 | 'type': args.led, 58 | 'color_type': LED_COLOR_TYPE['new'][led_color_type] 59 | } 60 | } 61 | ) 62 | ) 63 | except Exception as err: 64 | print(dumps({'error': str(err)})) 65 | 66 | exit(1) 67 | 68 | 69 | def query_led_control_items_cli(cli_args=None): 70 | """ 71 | Creates a CLI interface on top of the `nuc_wmi.query_led` `query_led_control_items` function. 72 | 73 | Args: 74 | cli_args: If provided, overrides the CLI args to use for `argparse`. 75 | CLI Args: 76 | led_indicator_option: The indicator option for the specified LED type for which to retrieve the available control 77 | items. 78 | led: Selects the LED to get the available control items for. 79 | CLI Options: 80 | --control_file : Sets the control file to use if provided, 81 | otherwise `nuc_wmi.CONTROL_FILE` is used. 82 | Outputs: 83 | stdout: JSON object with control items for the selected LED indicator option or 84 | error message with failure error. 85 | Exit code: 86 | 0 on successfully retrieving the control items or 1 on error. 87 | """ 88 | 89 | parser = ArgumentParser( 90 | description='Query the LED control items for the LED indicator option of the LED type.' 91 | ) 92 | 93 | parser.add_argument( 94 | '-c', 95 | '--control-file', 96 | default=None, 97 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 98 | ) 99 | parser.add_argument( 100 | 'led', 101 | choices=LED_TYPE['new'], 102 | help='The LED for which to get the control items.' 103 | ) 104 | parser.add_argument( 105 | 'led_indicator_option', 106 | choices=LED_INDICATOR_OPTION, 107 | help='The LED indicator option for the LED for which to get control items.' 108 | ) 109 | 110 | try: 111 | args = parser.parse_args(args=cli_args) 112 | 113 | led_color_type = query_led_color_type( 114 | LED_TYPE['new'].index(args.led), 115 | control_file=args.control_file 116 | ) 117 | 118 | available_indicator_options = query_led_indicator_options( 119 | LED_TYPE['new'].index(args.led), 120 | control_file=args.control_file 121 | ) 122 | 123 | indicator = LED_INDICATOR_OPTION.index(args.led_indicator_option) 124 | 125 | if indicator not in available_indicator_options: 126 | raise ValueError('Invalid indicator option for the selected LED') 127 | 128 | control_items = query_led_control_items( 129 | LED_TYPE['new'].index(args.led), 130 | indicator, 131 | control_file=args.control_file 132 | ) 133 | 134 | print( 135 | dumps( 136 | { 137 | 'led': { 138 | 'type': args.led, 139 | 'indicator_option': args.led_indicator_option, 140 | 'control_items': [CONTROL_ITEM[indicator][led_color_type][control_item]['Control Item'] \ 141 | for control_item in control_items] 142 | } 143 | } 144 | ) 145 | ) 146 | except Exception as err: 147 | print(dumps({'error': str(err)})) 148 | 149 | exit(1) 150 | 151 | 152 | def query_led_indicator_options_cli(cli_args=None): 153 | """ 154 | Creates a CLI interface on top of the `nuc_wmi.query_led` `query_led_indicator_options` function. 155 | 156 | Args: 157 | cli_args: If provided, overrides the CLI args to use for `argparse`. 158 | CLI Args: 159 | led: Selects the LED to get the indicator options for. 160 | CLI Options: 161 | --control_file : Sets the control file to use if provided, 162 | otherwise `nuc_wmi.CONTROL_FILE` is used. 163 | Outputs: 164 | stdout: JSON object with indicator options of the selected LED or error message with 165 | failure error. 166 | Exit code: 167 | 0 on successfully retrieving the selected LED's indicator options or 1 on error. 168 | """ 169 | 170 | parser = ArgumentParser( 171 | description='Query the LED indicator options available for the LED type.' 172 | ) 173 | 174 | parser.add_argument( 175 | '-c', 176 | '--control-file', 177 | default=None, 178 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 179 | ) 180 | parser.add_argument( 181 | 'led', 182 | choices=LED_TYPE['new'], 183 | help='The LED for which to get the indicator options.' 184 | ) 185 | 186 | try: 187 | args = parser.parse_args(args=cli_args) 188 | 189 | led_indicator_options = query_led_indicator_options( 190 | LED_TYPE['new'].index(args.led), 191 | control_file=args.control_file 192 | ) 193 | 194 | print( 195 | dumps( 196 | { 197 | 'led': { 198 | 'type': args.led, 199 | 'indicator_options': [LED_INDICATOR_OPTION[indicator] for indicator in led_indicator_options] 200 | } 201 | } 202 | ) 203 | ) 204 | except Exception as err: 205 | print(dumps({'error': str(err)})) 206 | 207 | exit(1) 208 | 209 | 210 | def query_leds_cli(cli_args=None): 211 | """ 212 | Creates a CLI interface on top of the `nuc_wmi.query_led` `query_leds` function. 213 | 214 | Args: 215 | cli_args: If provided, overrides the CLI args to use for `argparse`. 216 | CLI Options: 217 | --control_file : Sets the control file to use if provided, 218 | otherwise `nuc_wmi.CONTROL_FILE` is used. 219 | Outputs: 220 | stdout: JSON object with list of available LEDs or error message with 221 | failure error. 222 | Exit code: 223 | 0 on successfully retrieving the list of available LEDs or 1 on error. 224 | """ 225 | 226 | parser = ArgumentParser( 227 | description='List all LED types supported.' 228 | ) 229 | 230 | parser.add_argument( 231 | '-c', 232 | '--control-file', 233 | default=None, 234 | help='The path to the NUC WMI control file. Defaults to ' + CONTROL_FILE + ' if not specified.' 235 | ) 236 | 237 | try: 238 | args = parser.parse_args(args=cli_args) 239 | 240 | leds = query_leds(control_file=args.control_file) 241 | 242 | print( 243 | dumps( 244 | { 245 | 'leds': [LED_TYPE['new'][led] for led in leds] 246 | } 247 | ) 248 | ) 249 | except Exception as err: 250 | print(dumps({'error': str(err)})) 251 | 252 | exit(1) 253 | -------------------------------------------------------------------------------- /nuc_led.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Low-level Intel NUC LED Control Driver 3 | * 4 | * Copyright (C) 2020 Johannes Ernst 5 | * 6 | * Portions based on nuc_led.c for NUC 6, 7: 7 | * Copyright (C) 2017 Miles Peterson 8 | * 9 | * Portions based on asus-wmi.c: 10 | * Copyright (C) 2010 Intel Corporation. 11 | * Copyright (C) 2010-2011 Corentin Chary 12 | * 13 | * Portions based on acpi_call.c: 14 | * Copyright (C) 2010: Michal Kottman 15 | * 16 | * Based on Intel Article ID 000023426 17 | * http://www.intel.com/content/www/us/en/support/boards-and-kits/intel-nuc-kits/000023426.html 18 | * and Intel Article "WMI Interface for Intel NUC Products" 19 | * https://www.intel.com/content/dam/support/us/en/documents/intel-nuc/WMI-Spec-Intel-NUC-NUC10ixFNx.pdf 20 | * 21 | * This program is free software; you can redistribute it and/or modify 22 | * it under the terms of the GNU General Public License as published by 23 | * the Free Software Foundation; either version 2 of the License, or 24 | * (at your option) any later version. 25 | * 26 | * This program is distributed in the hope that it will be useful, 27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | * GNU General Public License for more details. 30 | * 31 | * You should have received a copy of the GNU General Public License 32 | * along with this program; if not, write to the Free Software 33 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 34 | */ 35 | 36 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | MODULE_AUTHOR("Johannes Ernst"); 48 | MODULE_DESCRIPTION("Intel NUC LED Control WMI Driver"); 49 | MODULE_LICENSE("GPL"); 50 | ACPI_MODULE_NAME("NUC_LED"); 51 | 52 | static unsigned int nuc_led_perms __read_mostly = S_IRUGO | S_IWUSR | S_IWGRP; 53 | static unsigned int nuc_led_uid __read_mostly; 54 | static unsigned int nuc_led_gid __read_mostly; 55 | 56 | module_param(nuc_led_perms, uint, S_IRUGO | S_IWUSR | S_IWGRP); 57 | module_param(nuc_led_uid, uint, 0); 58 | module_param(nuc_led_gid, uint, 0); 59 | 60 | MODULE_PARM_DESC(nuc_led_perms, "permissions on /proc/acpi/nuc_led"); 61 | MODULE_PARM_DESC(nuc_led_uid, "default owner of /proc/acpi/nuc_led"); 62 | MODULE_PARM_DESC(nuc_led_gid, "default owning group of /proc/acpi/nuc_led"); 63 | 64 | /* Intel NUC WMI GUID */ 65 | #define NUCLED_WMI_MGMT_GUID "8C5DA44C-CDC3-46b3-8619-4E26D34390B7" 66 | MODULE_ALIAS("wmi:" NUCLED_WMI_MGMT_GUID); 67 | 68 | extern struct proc_dir_entry *acpi_root_dir; 69 | 70 | #define INPUT_BUFFER_SIZE 5 71 | static unsigned char input_buffer[INPUT_BUFFER_SIZE]; 72 | 73 | #define OUTPUT_BUFFER_SIZE 4 74 | static unsigned char output_buffer[OUTPUT_BUFFER_SIZE]; 75 | 76 | /* 77 | * Invoked by the kernel when the device is written. 78 | */ 79 | static ssize_t acpi_proc_write(struct file *filp, const char __user *buff, 80 | size_t len, loff_t *data) 81 | { 82 | char * kernelBuff; 83 | unsigned int kernelBuffI; 84 | unsigned int inputBufferI; 85 | unsigned int currentByte; 86 | unsigned int currentNibble; 87 | unsigned char c; 88 | int inWhite; // boolean 89 | 90 | struct acpi_buffer acpiInput; 91 | struct acpi_buffer acpiOutput = { ACPI_ALLOCATE_BUFFER, NULL }; 92 | acpi_status acpiStatus; 93 | union acpi_object * acpiObjectP; 94 | int i; 95 | 96 | // Move buffer from user space to kernel space -- cannot access used space 97 | kernelBuff = vmalloc(len); 98 | if (!kernelBuff) { 99 | return -ENOMEM; 100 | } 101 | 102 | if (copy_from_user(kernelBuff, buff, len)) { 103 | return -EFAULT; 104 | } 105 | 106 | // parse hex 107 | currentByte = 0; 108 | inWhite = 1; 109 | 110 | for( kernelBuffI = 0, inputBufferI = 0 ; 111 | kernelBuffI < len && inputBufferI < INPUT_BUFFER_SIZE ; 112 | ++kernelBuffI ) 113 | { 114 | c = kernelBuff[kernelBuffI]; 115 | 116 | if( c >= '0' && c <= '9' ) { 117 | if( inWhite ) { 118 | inWhite = 0; 119 | currentByte = 0; 120 | } 121 | currentNibble = c - '0'; 122 | currentByte = ( currentByte << 4 ) + currentNibble; 123 | 124 | } else if( c >= 'a' && c <= 'f' ) { 125 | if( inWhite ) { 126 | inWhite = 0; 127 | currentByte = 0; 128 | } 129 | currentNibble = c - 'a' + 10; 130 | currentByte = ( currentByte << 4 ) + currentNibble; 131 | 132 | } else if( c >= 'A' && c <= 'F' ) { 133 | if( inWhite ) { 134 | inWhite = 0; 135 | currentByte = 0; 136 | } 137 | currentNibble = c - 'A' + 10; 138 | currentByte = ( currentByte << 4 ) + currentNibble; 139 | 140 | } else if( c == ' ' || c == '\t' || c == '\n' ) { 141 | if( !inWhite ) { 142 | inWhite = 1; 143 | input_buffer[ inputBufferI++ ] = currentByte; 144 | currentByte = 0; 145 | } 146 | 147 | } else { 148 | pr_warn("NUC LED invalid character: %c\n", c ); 149 | return -EIO; 150 | } 151 | } 152 | if( !inWhite && inputBufferI < INPUT_BUFFER_SIZE ) { 153 | input_buffer[ inputBufferI++ ] = currentByte; 154 | } 155 | if( inputBufferI != 5 ) { 156 | pr_warn("NUC LED received wrong number of bytes (5 needed): %d\n", inputBufferI ); 157 | return -EIO; 158 | } 159 | 160 | vfree(kernelBuff); 161 | 162 | // prepare wmi call 163 | 164 | acpiInput.length = (acpi_size) inputBufferI-1; 165 | acpiInput.pointer = input_buffer+1; 166 | 167 | //pr_info( "NUC LED wmi_evaluate_method method=%02x, data: %02x %02x %02x %02x\n", 168 | // (unsigned char) input_buffer[0], 169 | // ((unsigned char *) acpiInput.pointer)[0], 170 | // ((unsigned char *) acpiInput.pointer)[1], 171 | // ((unsigned char *) acpiInput.pointer)[2], 172 | // ((unsigned char *) acpiInput.pointer)[3] ); 173 | 174 | acpiStatus = wmi_evaluate_method(NUCLED_WMI_MGMT_GUID, 0, input_buffer[0], 175 | &acpiInput, &acpiOutput); 176 | 177 | if (ACPI_FAILURE(acpiStatus)) { 178 | ACPI_EXCEPTION((AE_INFO, acpiStatus, "wmi_evaluate_method")); 179 | return -EIO; 180 | } 181 | 182 | // unpack wmi output 183 | 184 | acpiObjectP = (union acpi_object *)acpiOutput.pointer; 185 | 186 | // pr_info( "NUC LED WMI response has length: %d\n", acpiObjectP->buffer.length ); 187 | 188 | for( i = 0; 189 | i < acpiObjectP->buffer.length && i < OUTPUT_BUFFER_SIZE; 190 | ++i ) 191 | { 192 | output_buffer[i] = ((const char *)acpiObjectP->buffer.pointer)[i]; 193 | } 194 | 195 | kfree( acpiOutput.pointer ); 196 | 197 | return len; 198 | } 199 | 200 | /* 201 | * Invoked by the kernel when the device is read. 202 | */ 203 | static ssize_t acpi_proc_read(struct file *filp, char __user *buff, 204 | size_t count, loff_t *off) 205 | { 206 | int len; 207 | char line[16]; 208 | ssize_t ret; 209 | 210 | sprintf( line, "%02x %02x %02x %02x\n", output_buffer[0], output_buffer[1], output_buffer[2], output_buffer[3] ); 211 | 212 | len = strlen(line); 213 | ret = simple_read_from_buffer(buff, count, off, line, len + 1); 214 | 215 | // Clear buffer 216 | memset(output_buffer, 0xff, OUTPUT_BUFFER_SIZE); 217 | 218 | return ret; 219 | } 220 | 221 | /* 222 | * Table of ACPI device operations 223 | */ 224 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) 225 | static struct proc_ops proc_acpi_operations = { 226 | .proc_read = acpi_proc_read, 227 | .proc_write = acpi_proc_write, 228 | }; 229 | #else 230 | static struct file_operations proc_acpi_operations = { 231 | .owner = THIS_MODULE, 232 | .read = acpi_proc_read, 233 | .write = acpi_proc_write, 234 | }; 235 | #endif 236 | 237 | /* 238 | * Kernel module initialization 239 | */ 240 | static int __init init_nuc_led(void) 241 | { 242 | struct proc_dir_entry *acpi_entry; 243 | kuid_t uid; 244 | kgid_t gid; 245 | 246 | // Make sure LED control WMI GUID exists 247 | if (!wmi_has_guid(NUCLED_WMI_MGMT_GUID)) { 248 | pr_warn("Intel NUC LED WMI GUID not found\n"); 249 | return -ENODEV; 250 | } 251 | 252 | // Verify the user parameters 253 | uid = make_kuid(&init_user_ns, nuc_led_uid); 254 | gid = make_kgid(&init_user_ns, nuc_led_gid); 255 | 256 | if (!uid_valid(uid) || !gid_valid(gid)) { 257 | pr_warn("Intel NUC LED control driver got an invalid UID or GID\n"); 258 | return -EINVAL; 259 | } 260 | 261 | // Clear buffer 262 | memset(output_buffer, 0xff, OUTPUT_BUFFER_SIZE); 263 | 264 | // Create nuc_led ACPI proc entry 265 | acpi_entry = proc_create("nuc_led", nuc_led_perms, acpi_root_dir, &proc_acpi_operations); 266 | 267 | if (acpi_entry == NULL) { 268 | pr_warn("Intel NUC LED control driver could not create proc entry\n"); 269 | return -ENOMEM; 270 | } 271 | 272 | proc_set_user(acpi_entry, uid, gid); 273 | 274 | pr_info("Intel NUC LED control driver loaded\n"); 275 | 276 | return 0; 277 | } 278 | 279 | /* 280 | * Kernel module exit 281 | */ 282 | static void __exit unload_nuc_led(void) 283 | { 284 | remove_proc_entry("nuc_led", acpi_root_dir); 285 | pr_info("Intel NUC LED control driver unloaded\n"); 286 | } 287 | 288 | /* 289 | * Register module functions 290 | */ 291 | module_init(init_nuc_led); 292 | module_exit(unload_nuc_led); 293 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/README.md: -------------------------------------------------------------------------------- 1 | # nuc_wmi Python userland for Intel NUC LED kernel module 2 | 3 | ## Compatibility 4 | 5 | This `nuc_wmi` userland was written from the merger of available Intel NUC WMI guides for the NUC 7, 8, and 10 6 | (included in the [contrib/reference/](../reference) folder). 7 | 8 | It has been tested on NUC 6, NUC 7 and 10, but theoretically should work for all NUCS from 6 through 10. 9 | 10 | Although we followed the specification documents, we have found that compatibility varies by a number of factors: 11 | 12 | * Device generation (NUC 7 devices for example only support the legacy `get_led` and `set_led` WMI methods). 13 | * BIOS version (for some settings, the BIOS version may impact what options are available). 14 | * BIOS configuration (for some settings, the BIOS configuration for LEDs can affect whether they are usable and 15 | in what default state they are in). 16 | * BIOS bugs (we have found some device BIOS have bugs where LEDs which should support RGB are only capable of 17 | dual color mode through WMI, but are capable of RGB when manually configuring via BIOS). 18 | 19 | Aside from the above, command options can change based on the combination of what the BIOS allows and what 20 | indicator option mode LEDs are put in. 21 | 22 | ## Warnings 23 | 24 | The `nuc_led` kernel module only allows return values for the last command issued to be read once. If multiple 25 | commands are issued in rapid succession without reading the return code for each in between, then the return 26 | codes are lost. 27 | 28 | In the same light, there is an unresolved race condition in using the `nuc_wmi` userland as a result of this 29 | behavior. Some of the CLI commands issue multiple WMI calls in succession in order to provide better usability 30 | instead of just translating CLI options and blindly passing them into the WMI interface. Therefore, since we 31 | haven't implemented file locking to prevent two CLI commands from creating a race condition on the control file, 32 | we recommend not running CLI commands concurrently. 33 | 34 | ## Installing from package 35 | 36 | On UBOS, these userland tools are part of the ``intel-nuc-led`` package. Install with: 37 | 38 | ``` 39 | sudo pacman -S ubos-nuc-led 40 | ``` 41 | 42 | On other distros, install from source. 43 | 44 | ## Installing from source 45 | 46 | The tool conforms to standard Python `pip` packaging and can be installed using `pip` or `setuptools` using 47 | Python >= `2.7`. 48 | 49 | When installing from source using the instructions below, be sure to modify the `python` or `pip` executable 50 | commands based on how you have your system setup as they may require appending the version to the end such 51 | as `python2`, `python2.7`, `pip2`, or `pip2.7`. 52 | 53 | ### Installing from source using system `python` 54 | 55 | #### Install using setuptools 56 | 57 | 1. Use your system's pacakge manager to install your choice of `python` version and `setuptools` Python package. 58 | 2. Install the `nuc-wmi` package using setuptools: 59 | ``` 60 | python setup.py install 61 | ``` 62 | 3. Run `nuc_wmi-*` commands available in `/usr/bin`. 63 | 64 | #### Install using pip 65 | 66 | 1. Use your system's pacakge manager to install your choice of `python` version and `setuptools` and `pip` Python 67 | packages. 68 | 2. Install the package using setuptools or pip: 69 | ``` 70 | # Install into the system Python library path 71 | python -m pip install ./ 72 | 73 | # Install into the per user Python library path at `~/.local/` 74 | python -m pip install --user ./ 75 | ``` 76 | 3. Run `nuc_wmi-*` commands available in `/usr/bin` or `~/.local/bin/` (this may not automatically be in 77 | your `PATH`) if installed per user. 78 | 79 | ### Installing from source using `virtualenv` 80 | 81 | You can install your own per user `python` and associated virtual library path using normal `virtualenv` tools or 82 | wrappers like [pyenv](https://github.com/pyenv/pyenv) and [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv). 83 | 84 | Once you have your `virtualenv` setup, activate it and then follow the normal install steps above. When using a 85 | `virtualenv` however, the library path and `bin` paths will be relative to the `virtualenv` parent directory. 86 | 87 | ## Packaging 88 | 89 | The tool conforms to standard Python `pip` packaging using `setuptools` and can be turned into normal PyPi compatible 90 | package in the form of a `wheel`, `egg`, or distro specific package using `setuptools` helpers. 91 | 92 | ### Python egg 93 | 94 | 1. Use your system's pacakge manager to install your choice of `python` version and `setuptools` Python package. 95 | 2. Build the `egg`: 96 | ``` 97 | python setup.py build 98 | ``` 99 | 3. The `egg` package is available in the `dist/` folder. 100 | 101 | ### Python wheel 102 | 103 | 1. Use your system's pacakge manager to install your choice of `python` version and `setuptools` and `wheel` Python 104 | packages. 105 | 2. Build the `wheel`: 106 | ``` 107 | python setup.py build bdist_wheel 108 | ``` 109 | 3. The `wheel` package is available in the `dist/` folder. 110 | 111 | ### Debian/Ubuntu deb 112 | 113 | 1. Use `apt` to install your choice of `python` version and `setuptools` and `stdeb` Python packages. 114 | 2. Use `apt` to install `debhelper` and `fakeroot`. 115 | 2. Build the `deb`: 116 | ``` 117 | DEB_BUILD_OPTIONS=nocheck python setup.py --command-packages=stdeb.command bdist_deb 118 | ``` 119 | 3. The `deb` package is available in the `dist/` folder. 120 | 121 | ## Testing 122 | 123 | Use your system's package manager to install your choice of `python` version and `coverage`, `mock`, `nose`, `nose-cov`, 124 | and `setuptools` Python packages. 125 | 126 | Clean directory: 127 | 128 | ``` 129 | rm -rf .coverage build/ deb_dist/ dist/ python/cover python/nuc_wmi.egg-info nuc_wmi-*.tar.gz 130 | find . -type f -name "*~" -exec rm {} + 131 | find . -type f -name "*.pyc" -exec rm {} + 132 | find . -type d -name "__pycache__" -exec rmdir {} + 133 | ``` 134 | 135 | Run tests: 136 | 137 | ``` 138 | python setup.py test 139 | ``` 140 | 141 | Run detailed code coverage report (HTML report available in `python/cover/`): 142 | 143 | ``` 144 | python setup.py nosetests --cover-branches --cover-html --cover-html-dir ./cover --cover-package nuc_wmi -d -s -v --with-coverage --py3where python/ 145 | ``` 146 | 147 | ## Example Usage 148 | 149 | All `nuc_wmi-*` CLI commands provided by `nuc_wmi` Python module have builtin help via `-h` or `--help` and will 150 | show allowed argument values. Some commands allow a large number of combinations in terms of accepted input values, 151 | so please be sure to reference the WMI spec for the device you are using to see what is actually supported. 152 | 153 | ### NUC 7: 154 | 155 | ``` 156 | # Note: When a legacy device (NUC 7 or older) has disabled software control in BIOS, we can't change it 157 | # via WMI like we can on newer models. Trying to use a LED that hasnt had software control enabled will return 158 | # this error. 159 | $ nuc_wmi-get_led 'S0 Power LED' 160 | {"error": "Error (Undefined device)"} 161 | 162 | $ nuc_wmi-get_led 'S0 Ring LED' 163 | {"led": {"color": "White", "frequency": "Always on", "type": "S0 Ring LED", "brightness": "100"}} 164 | 165 | $ nuc_wmi-set_led 'S0 Ring LED' 100 'Always on' 'White' 166 | {"led": {"color": "White", "frequency": "Always on", "type": "S0 Ring LED", "brightness": "100"}} 167 | ``` 168 | 169 | ### NUC 10: 170 | 171 | ``` 172 | $ nuc_wmi-get_led_control_item 'HDD LED' 'Software Indicator' 'Brightness' 173 | {"led": {"control_item": "Brightness", "type": "HDD LED", "indicator_option": "Software Indicator", "control_item_value": "100"}} 174 | $ nuc_wmi-get_led_control_item 'HDD LED' 'Software Indicator' 'Color' 175 | {"led": {"control_item": "Color", "type": "HDD LED", "indicator_option": "Software Indicator", "control_item_value": "White"}} 176 | $ nuc_wmi-get_led_control_item 'Power Button LED' 'Power State Indicator' 'S0 Indicator Color' 177 | {"led": {"control_item": "S0 Indicator Color", "type": "Power Button LED", "indicator_option": "Power State Indicator", "control_item_value": "Blue"}} 178 | $ nuc_wmi-get_led_control_item 'Power Button LED' 'Power State Indicator' 'S0 Indicator Brightness' 179 | {"led": {"control_item": "S0 Indicator Brightness", "type": "Power Button LED", "indicator_option": "Power State Indicator", "control_item_value": "50"}} 180 | 181 | $ nuc_wmi-get_led_indicator_option 'HDD LED' 182 | {"led": {"type": "HDD LED", "indicator_option": "Software Indicator"}} 183 | $ nuc_wmi-get_led_indicator_option 'Power Button LED' 184 | {"led": {"type": "Power Button LED", "indicator_option": "Power State Indicator"}} 185 | 186 | $ nuc_wmi-query_led_color_type 'HDD LED' 187 | {"led": {"color_type": "Dual-color Blue / White", "type": "HDD LED"}} 188 | $ nuc_wmi-query_led_color_type 'Power Button LED' 189 | {"led": {"color_type": "Dual-color Blue / Amber", "type": "Power Button LED"}} 190 | 191 | $ nuc_wmi-query_led_control_items 'Power Button LED' 'Power State Indicator' 192 | {"led": {"control_items": ["S0 Indicator Brightness", "S0 Indicator Blinking Behavior", "S0 Indicator Blinking Frequency", "S0 Indicator Color"], "type": "Power Button LED", "indicator_option": "Power State Indicator"}} 193 | $ nuc_wmi-query_led_control_items 'Power Button LED' 'Software Indicator' 194 | {"led": {"control_items": ["Brightness", "Blinking Behavior", "Blinking Frequency", "Color"], "type": "Power Button LED", "indicator_option": "Software Indicator"}} 195 | $ nuc_wmi-query_led_control_items 'HDD LED' 'Software Indicator' 196 | {"led": {"control_items": ["Brightness", "Blinking Behavior", "Blinking Frequency", "Color"], "type": "HDD LED", "indicator_option": "Software Indicator"}} 197 | $ nuc_wmi-query_led_control_items 'HDD LED' 'HDD Activity Indicator' 198 | {"led": {"control_items": ["Brightness", "Color", "Color 2", "Color 3"], "type": "HDD LED", "indicator_option": "HDD Activity Indicator"}} 199 | 200 | $ nuc_wmi-query_led_indicator_options 'HDD LED' 201 | {"led": {"type": "HDD LED", "indicator_options": ["HDD Activity Indicator", "Software Indicator"]}} 202 | $ nuc_wmi-query_led_indicator_options 'Power Button LED' 203 | {"led": {"type": "Power Button LED", "indicator_options": ["Power State Indicator", "HDD Activity Indicator", "Software Indicator"]}} 204 | 205 | $ nuc_wmi-query_leds 206 | {"leds": ["Power Button LED", "HDD LED"]} 207 | 208 | $ nuc_wmi-save_led_config 209 | {"led_app_notification": {"type": "save_led_config"}} 210 | 211 | $ nuc_wmi-set_led_control_item 'HDD LED' 'Software Indicator' 'Brightness' 100 212 | {"led": {"control_item": "Brightness", "type": "HDD LED", "indicator_option": "Software Indicator", "control_item_value": "100"}} 213 | $ nuc_wmi-set_led_control_item 'Power Button LED' 'Power State Indicator' 'S0 Indicator Color' Blue 214 | {"led": {"control_item": "S0 Indicator Color", "type": "Power Button LED", "indicator_option": "Power State Indicator", "control_item_value": "Blue"}} 215 | 216 | $ nuc_wmi-set_led_indicator_option 'HDD LED' 'Software Indicator' 217 | {"led": {"type": "HDD LED", "indicator_option": "Software Indicator"}} 218 | $ nuc_wmi-set_led_indicator_option 'Power Button LED' 'Power State Indicator' 219 | {"led": {"type": "Power Button LED", "indicator_option": "Power State Indicator"}} 220 | 221 | # No idea what this WMI function does, I just implemented it according to spec. It doesnt work on NUC 10. 222 | $ nuc_wmi-switch_led_type 'Single color LED' 223 | $ nuc_wmi-switch_led_type 'Multi color LED' 224 | 225 | $ nuc_wmi-wmi_interface_spec_compliance_version 226 | {"version": {"semver": "1.32", "type": "wmi_interface_spec_compliance"}} 227 | ``` 228 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/nuc_wmi/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | nuc_wmi CLI userland for the Intel NUC LED kernel module. 3 | """ 4 | 5 | CONTROL_FILE = '/proc/acpi/nuc_led' 6 | 7 | LED_BLINK_BEHAVIOR_MULTI_COLOR = [ 8 | 'Solid', 9 | 'Breathing', 10 | 'Pulsing', 11 | 'Strobing' 12 | ] 13 | 14 | LED_BLINK_BEHAVIOR_SINGLE_COLOR = [ 15 | '1Hz', 16 | '0.25Hz', 17 | '1Hz Fade', 18 | '0.25Hz Fade', 19 | 'Always On' 20 | ] 21 | 22 | LED_BLINK_BEHAVIOR = [ 23 | # Index = LED Color Type 24 | 25 | # Dual-color Blue / Amber 26 | LED_BLINK_BEHAVIOR_MULTI_COLOR, 27 | 28 | # Dual-color Blue / White 29 | LED_BLINK_BEHAVIOR_MULTI_COLOR, 30 | 31 | # RGB-color 32 | LED_BLINK_BEHAVIOR_MULTI_COLOR, 33 | 34 | # Single-color LED 35 | LED_BLINK_BEHAVIOR_SINGLE_COLOR 36 | ] 37 | 38 | LED_BLINK_FREQUENCY = { 39 | # NUC 7: 40 | # - Using BIOS AY0029 or BN0042, only 0x01-0x04 are available. 41 | # - Using BIOS AY0038 or BN0043, all frequencies are available. 42 | 'legacy': [ 43 | None, 44 | '1Hz', 45 | '0.25Hz', 46 | '1Hz fade', 47 | 'Always on', 48 | '0.5Hz', 49 | '0.25Hz fade', 50 | '0.5Hz fade' 51 | ], 52 | # 0x00 is not supported but we have to add it so its indexed right. 53 | 'new': [str(freq) for freq in range(0x00, 0x0A + 1)] 54 | } 55 | 56 | LED_BRIGHTNESS_MULTI_COLOR = [str(brightness) for brightness in range(0x00, 0x64 + 1)] 57 | 58 | LED_BRIGHTNESS_SINGLE_COLOR = [ 59 | 'OFF', 60 | '50%', 61 | '100%' 62 | ] 63 | 64 | LED_BRIGHTNESS = { 65 | 'legacy': LED_BRIGHTNESS_MULTI_COLOR, 66 | 'new': [ 67 | # Index = LED color type 68 | 69 | # Dual-color Blue / Amber 70 | LED_BRIGHTNESS_MULTI_COLOR, 71 | 72 | # Dual-color Blue / White 73 | LED_BRIGHTNESS_MULTI_COLOR, 74 | 75 | # RGB-color 76 | LED_BRIGHTNESS_MULTI_COLOR, 77 | 78 | # Single-color LED 79 | LED_BRIGHTNESS_SINGLE_COLOR 80 | ] 81 | } 82 | 83 | LED_COLOR = { 84 | 'legacy': { 85 | 'Dual-color Blue / Amber': [ 86 | 'Disable', 87 | 'Blue', 88 | 'Amber' 89 | ], 90 | 'RGB-color': [ 91 | 'Disable', 92 | 'Cyan', 93 | 'Pink', 94 | 'Yellow', 95 | 'Blue', 96 | 'Red', 97 | 'Green', 98 | 'White' 99 | ] 100 | }, 101 | 'new': { 102 | 'Dual-color Blue / Amber': [ 103 | 'Blue', 104 | 'Amber' 105 | ], 106 | 'Dual-color Blue / White': [ 107 | 'Blue', 108 | 'White' 109 | ], 110 | 'RGB-color': [str(rgb) for rgb in range(0x00, 0xFF + 1)] 111 | } 112 | } 113 | 114 | LED_COLOR_TYPE = { 115 | 'legacy': { 116 | 'S0 Power LED': 'Dual-color Blue / Amber', 117 | 'S0 Ring LED': 'RGB-color' 118 | }, 119 | 'new': [ 120 | 'Dual-color Blue / Amber', 121 | 'Dual-color Blue / White', 122 | 'RGB-color', 123 | 'Single-color LED' 124 | ] 125 | } 126 | 127 | LED_INDICATOR_OPTION = [ 128 | 'Power State Indicator', 129 | 'HDD Activity Indicator', 130 | 'Ethernet Indicator', 131 | 'WiFi Indicator', 132 | 'Software Indicator', 133 | 'Power Limit Indicator', 134 | 'Disable' 135 | ] 136 | 137 | LED_TYPE = { 138 | 'legacy': [ 139 | None, 140 | 'S0 Power LED', 141 | 'S0 Ring LED' 142 | ], 143 | 'new': [ 144 | 'Power Button LED', 145 | 'HDD LED', 146 | 'Skull LED', 147 | 'Eyes LED', 148 | 'Front LED1', 149 | 'Front LED2', 150 | 'Front LED3', 151 | ] 152 | } 153 | 154 | CONTROL_ITEM_ETHERNET_INDICATOR_TYPE = [ 155 | 'LAN1', 156 | 'LAN2', 157 | 'LAN1 + LAN2' 158 | ] 159 | 160 | CONTROL_ITEM_ETHERNET_INDICATOR_MULTI_COLOR = [ 161 | { 162 | 'Control Item': 'Type', 163 | 'Options': CONTROL_ITEM_ETHERNET_INDICATOR_TYPE 164 | }, 165 | { 166 | 'Control Item': 'Brightness', 167 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 168 | }, 169 | { 170 | 'Control Item': 'Color', 171 | 'Options': LED_COLOR['new'] 172 | }, 173 | { 174 | 'Control Item': 'Color 2', 175 | 'Options': LED_COLOR['new']['RGB-color'] 176 | }, 177 | { 178 | 'Control Item': 'Color 3', 179 | 'Options': LED_COLOR['new']['RGB-color'] 180 | } 181 | ] 182 | 183 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_BEHAVIOR = [ 184 | 'Normally OFF, ON when active', 185 | 'Normally ON, OFF when active' 186 | ] 187 | 188 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR = [ 189 | { 190 | 'Control Item': 'Brightness', 191 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 192 | }, 193 | { 194 | 'Control Item': 'Color', 195 | 'Options': LED_COLOR['new'] 196 | }, 197 | { 198 | 'Control Item': 'Color 2', 199 | 'Options': LED_COLOR['new']['RGB-color'] 200 | }, 201 | { 202 | 'Control Item': 'Color 3', 203 | 'Options': LED_COLOR['new']['RGB-color'] 204 | }, 205 | { 206 | 'Control Item': 'Behavior', 207 | 'Options': CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_BEHAVIOR 208 | } 209 | ] 210 | 211 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_SINGLE_COLOR = [ 212 | { 213 | 'Control Item': 'Brightness', 214 | 'Options': LED_BRIGHTNESS_SINGLE_COLOR 215 | }, 216 | { 217 | 'Control Item': 'Behavior', 218 | 'Options': CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_BEHAVIOR 219 | } 220 | ] 221 | 222 | CONTROL_ITEM_POWER_LIMIT_INDICATOR_INDICATION_SCHEME = [ 223 | 'Green to Red', 224 | 'Single Color' 225 | ] 226 | 227 | CONTROL_ITEM_POWER_LIMIT_INDICATOR_MULTI_COLOR = [ 228 | { 229 | 'Control Item': 'Indication Scheme', 230 | 'Options': CONTROL_ITEM_POWER_LIMIT_INDICATOR_INDICATION_SCHEME 231 | }, 232 | { 233 | 'Control Item': 'Brightness', 234 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 235 | }, 236 | { 237 | 'Control Item': 'Color', 238 | 'Options': LED_COLOR['new'] 239 | }, 240 | { 241 | 'Control Item': 'Color 2', 242 | 'Options': LED_COLOR['new']['RGB-color'] 243 | }, 244 | { 245 | 'Control Item': 'Color 3', 246 | 'Options': LED_COLOR['new']['RGB-color'] 247 | } 248 | ] 249 | 250 | CONTROL_ITEM_POWER_STATE_INDICATOR_MULTI_COLOR = [ 251 | { 252 | 'Control Item': 'S0 Indicator Brightness', 253 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 254 | }, 255 | { 256 | 'Control Item': 'S0 Indicator Blinking Behavior', 257 | 'Options': LED_BLINK_BEHAVIOR_MULTI_COLOR 258 | }, 259 | { 260 | 'Control Item': 'S0 Indicator Blinking Frequency', 261 | 'Options': LED_BLINK_FREQUENCY['new'] 262 | }, 263 | { 264 | 'Control Item': 'S0 Indicator Color', 265 | 'Options': LED_COLOR['new'] 266 | }, 267 | { 268 | 'Control Item': 'S0 Indicator Color 2', 269 | 'Options': LED_COLOR['new']['RGB-color'] 270 | }, 271 | { 272 | 'Control Item': 'S0 Indicator Color 3', 273 | 'Options': LED_COLOR['new']['RGB-color'] 274 | }, 275 | { 276 | 'Control Item': 'S3 Indicator Brightness', 277 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 278 | }, 279 | { 280 | 'Control Item': 'S3 Indicator Blinking Behavior', 281 | 'Options': LED_BLINK_BEHAVIOR_MULTI_COLOR 282 | }, 283 | { 284 | 'Control Item': 'S3 Indicator Blinking Frequency', 285 | 'Options': LED_BLINK_FREQUENCY['new'] 286 | }, 287 | { 288 | 'Control Item': 'S3 Indicator Color', 289 | 'Options': LED_COLOR['new'] 290 | }, 291 | { 292 | 'Control Item': 'S3 Indicator Color 2', 293 | 'Options': LED_COLOR['new']['RGB-color'] 294 | }, 295 | { 296 | 'Control Item': 'S3 Indicator Color 3', 297 | 'Options': LED_COLOR['new']['RGB-color'] 298 | }, 299 | { 300 | 'Control Item': 'Modern Standby Indicator Brightness', 301 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 302 | }, 303 | { 304 | 'Control Item': 'Modern Standby Indicator Blinking Behavior', 305 | 'Options': LED_BLINK_BEHAVIOR_MULTI_COLOR 306 | }, 307 | { 308 | 'Control Item': 'Modern Standby Indicator Blinking Frequency', 309 | 'Options': LED_BLINK_FREQUENCY['new'] 310 | }, 311 | { 312 | 'Control Item': 'Modern Standby Indicator Color', 313 | 'Options': LED_COLOR['new'] 314 | }, 315 | { 316 | 'Control Item': 'Modern Standby Indicator Color 2', 317 | 'Options': LED_COLOR['new']['RGB-color'] 318 | }, 319 | { 320 | 'Control Item': 'Modern Standby Indicator Color 3', 321 | 'Options': LED_COLOR['new']['RGB-color'] 322 | }, 323 | { 324 | 'Control Item': 'S5 Indicator Brightness', 325 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 326 | }, 327 | { 328 | 'Control Item': 'S5 Indicator Blinking Behavior', 329 | 'Options': LED_BLINK_BEHAVIOR_MULTI_COLOR 330 | }, 331 | { 332 | 'Control Item': 'S5 Indicator Blinking Frequency', 333 | 'Options': LED_BLINK_FREQUENCY['new'] 334 | }, 335 | { 336 | 'Control Item': 'S5 Indicator Color', 337 | 'Options': LED_COLOR['new'] 338 | }, 339 | { 340 | 'Control Item': 'S5 Indicator Color 2', 341 | 'Options': LED_COLOR['new']['RGB-color'] 342 | }, 343 | { 344 | 'Control Item': 'S5 Indicator Color 3', 345 | 'Options': LED_COLOR['new']['RGB-color'] 346 | } 347 | ] 348 | 349 | CONTROL_ITEM_POWER_STATE_INDICATOR_SINGLE_COLOR = [ 350 | { 351 | 'Control Item': 'S0 Indicator Brightness', 352 | 'Options': LED_BRIGHTNESS_SINGLE_COLOR 353 | }, 354 | { 355 | 'Control Item': 'S0 Indicator Blinking Behavior', 356 | 'Options': LED_BLINK_BEHAVIOR_SINGLE_COLOR 357 | }, 358 | { 359 | 'Control Item': 'S3 Indicator Brightness', 360 | 'Options': LED_BRIGHTNESS_SINGLE_COLOR 361 | }, 362 | { 363 | 'Control Item': 'S3 Indicator Blinking Behavior', 364 | 'Options': LED_BLINK_BEHAVIOR_SINGLE_COLOR 365 | } 366 | ] 367 | 368 | CONTROL_ITEM_SOFTWARE_INDICATOR_MULTI_COLOR = [ 369 | { 370 | 'Control Item': 'Brightness', 371 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 372 | }, 373 | { 374 | 'Control Item': 'Blinking Behavior', 375 | 'Options': LED_BLINK_BEHAVIOR_MULTI_COLOR 376 | }, 377 | { 378 | 'Control Item': 'Blinking Frequency', 379 | 'Options': LED_BLINK_FREQUENCY['new'] 380 | }, 381 | { 382 | 'Control Item': 'Color', 383 | 'Options': LED_COLOR['new'] 384 | }, 385 | { 386 | 'Control Item': 'Color 2', 387 | 'Options': LED_COLOR['new']['RGB-color'] 388 | }, 389 | { 390 | 'Control Item': 'Color 3', 391 | 'Options': LED_COLOR['new']['RGB-color'] 392 | } 393 | ] 394 | 395 | CONTROL_ITEM_SOFTWARE_INDICATOR_SINGLE_COLOR = [ 396 | { 397 | 'Control Item': 'Brightness', 398 | 'Options': LED_BRIGHTNESS_SINGLE_COLOR 399 | }, 400 | { 401 | 'Control Item': 'Blinking Behavior', 402 | 'Options': LED_BLINK_BEHAVIOR_SINGLE_COLOR 403 | } 404 | ] 405 | 406 | CONTROL_ITEM_WIFI_INDICATOR_MULTI_COLOR = [ 407 | { 408 | 'Control Item': 'Brightness', 409 | 'Options': LED_BRIGHTNESS_MULTI_COLOR 410 | }, 411 | { 412 | 'Control Item': 'Color', 413 | 'Options': LED_COLOR['new'] 414 | }, 415 | { 416 | 'Control Item': 'Color 2', 417 | 'Options': LED_COLOR['new']['RGB-color'] 418 | }, 419 | { 420 | 'Control Item': 'Color 3', 421 | 'Options': LED_COLOR['new']['RGB-color'] 422 | } 423 | ] 424 | 425 | CONTROL_ITEM = [ 426 | # Index = LED Indicator Option 427 | 428 | # Power State Indicator 429 | [ 430 | # Index = LED color type 431 | 432 | # Dual-color Blue / Amber 433 | CONTROL_ITEM_POWER_STATE_INDICATOR_MULTI_COLOR, 434 | 435 | # Dual-color Blue / White 436 | CONTROL_ITEM_POWER_STATE_INDICATOR_MULTI_COLOR, 437 | 438 | # RGB-color 439 | CONTROL_ITEM_POWER_STATE_INDICATOR_MULTI_COLOR, 440 | 441 | # Single-color LED 442 | CONTROL_ITEM_POWER_STATE_INDICATOR_SINGLE_COLOR 443 | ], 444 | 445 | # HDD Activity Indicator 446 | [ 447 | # Index = LED color type 448 | 449 | # Dual-color Blue / Amber 450 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR, 451 | 452 | # Dual-color Blue / White 453 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR, 454 | 455 | # RGB-color 456 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR, 457 | 458 | # Single-color LED 459 | CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_SINGLE_COLOR 460 | ], 461 | 462 | # Ethernet Indicator 463 | [ 464 | # Index = LED color type 465 | 466 | # Dual-color Blue / Amber 467 | CONTROL_ITEM_ETHERNET_INDICATOR_MULTI_COLOR, 468 | 469 | # Dual-color Blue / White 470 | CONTROL_ITEM_ETHERNET_INDICATOR_MULTI_COLOR, 471 | 472 | # RGB-color 473 | CONTROL_ITEM_ETHERNET_INDICATOR_MULTI_COLOR, 474 | 475 | # Single-color LED 476 | None 477 | ], 478 | 479 | # Wifi Indicator 480 | [ 481 | # Index = LED color type 482 | 483 | # Dual-color Blue / Amber 484 | CONTROL_ITEM_WIFI_INDICATOR_MULTI_COLOR, 485 | 486 | # Dual-color Blue / White 487 | CONTROL_ITEM_WIFI_INDICATOR_MULTI_COLOR, 488 | 489 | # RGB-color 490 | CONTROL_ITEM_WIFI_INDICATOR_MULTI_COLOR, 491 | 492 | # Single-color LED 493 | None 494 | ], 495 | 496 | # Software Indicator 497 | [ 498 | # Index = LED color type 499 | 500 | # Dual-color Blue / Amber 501 | CONTROL_ITEM_SOFTWARE_INDICATOR_MULTI_COLOR, 502 | 503 | # Dual-color Blue / White 504 | CONTROL_ITEM_SOFTWARE_INDICATOR_MULTI_COLOR, 505 | 506 | # RGB-color 507 | CONTROL_ITEM_SOFTWARE_INDICATOR_MULTI_COLOR, 508 | 509 | # Single-color LED 510 | CONTROL_ITEM_SOFTWARE_INDICATOR_SINGLE_COLOR 511 | ], 512 | 513 | # Power Limit Indicator 514 | [ 515 | # Index = LED color type 516 | 517 | # Dual-color Blue / Amber 518 | CONTROL_ITEM_POWER_LIMIT_INDICATOR_MULTI_COLOR, 519 | 520 | # Dual-color Blue / White 521 | CONTROL_ITEM_POWER_LIMIT_INDICATOR_MULTI_COLOR, 522 | 523 | # RGB-color 524 | CONTROL_ITEM_POWER_LIMIT_INDICATOR_MULTI_COLOR, 525 | 526 | # Single-color LED 527 | None 528 | ], 529 | 530 | # Disable 531 | None 532 | ] 533 | 534 | # Return value of FF FF FF FF is specific to the driver, not the actual WMI implementation. 535 | # Some of these return errors are the generic NUC WMI errors, not all are specific to the NUC LEDs. 536 | RETURN_ERROR = { 537 | 0xE1: 'Error (Function not supported)', 538 | 0xE2: 'Error (Undefined device)', 539 | 0xE3: 'Error (EC doesn\'t respond)', 540 | 0xE4: 'Error (Invalid Parameter)', 541 | 0xE5: 'Error (Node busy. Command could not be executed because ' + 542 | 'command processing resources are temporarily unavailable.)', 543 | 0xE6: 'Error (Command execution failure. ' + 544 | 'Parameter is illegal because destination device has been disabled or is unavailable)', 545 | 0xE7: 'Error (Invalid CEC Opcode)', 546 | 0xE8: 'Error (Data Buffer size is not enough)', 547 | 0xEF: 'Error (Unexpected error)', 548 | 0xFF: 'Error (Return value has already been read and reset)' 549 | } 550 | 551 | class NucWmiError(Exception): 552 | pass 553 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/query_led_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.query_led_test` module provides unit tests for the functions in 3 | `nuc_wmi.query_led`. 4 | 5 | Classes: 6 | TestQueryLed: A unit test class for the functions in `nuc_wmi.query_led`. 7 | """ 8 | 9 | import unittest 10 | 11 | from mock import patch 12 | 13 | from nuc_wmi import LED_COLOR_TYPE, LED_INDICATOR_OPTION, LED_TYPE, NucWmiError 14 | from nuc_wmi.query_led import LED_INDICATOR_OPTION_DISABLED, METHOD_ID, QUERY_TYPE, query_led_color_type 15 | from nuc_wmi.query_led import query_led_control_items, query_led_indicator_options, query_leds 16 | 17 | import nuc_wmi 18 | 19 | 20 | class TestQueryLed(unittest.TestCase): 21 | """ 22 | A unit test class for the functions of `nuc_wmi.query_led` 23 | 24 | Methods: 25 | setUp: Unit test initialization. 26 | test_query_led_color_type: Tests that it sends the expected byte list to the control file, 27 | tests that the returned control file response is properly processed, 28 | tests that it raises an exception when the control file returns an 29 | error code. 30 | test_query_led_control_items: Tests that it sends the expected byte list to the control file, 31 | tests that the returned control file response is properly processed, 32 | tests that it raises an exception when the control file returns an 33 | error code. 34 | test_query_led_indicator_options: Tests that it sends the expected byte list to the control file, 35 | tests that the returned control file response is properly processed, 36 | tests that it raises an exception when the control file returns an 37 | error code. 38 | test_query_leds: Tests that it sends the expected byte list to the control file, 39 | tests that the returned control file response is properly processed, 40 | tests that it raises an exception when the control file returns an 41 | error code. 42 | """ 43 | 44 | def setUp(self): 45 | """ 46 | Initializes the unit tests. 47 | """ 48 | 49 | self.maxDiff = None 50 | 51 | 52 | @patch('nuc_wmi.query_led.read_control_file') 53 | @patch('nuc_wmi.query_led.write_control_file') 54 | def test_query_led_color_type(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 55 | """ 56 | Tests that `query_led_color_type` returns the expected exceptions, return values, or 57 | outputs. 58 | """ 59 | 60 | self.assertTrue(nuc_wmi.query_led.read_control_file is nuc_wmi_read_control_file) 61 | self.assertTrue(nuc_wmi.query_led.write_control_file is nuc_wmi_write_control_file) 62 | 63 | # Branch 1: Test that query_led_color_type send the expected byte string to the control file 64 | # and that the returned control file response is properly processed. 65 | 66 | # Query HDD LED that returns a color type of Dual-color Blue / White 67 | expected_query_led_color_type = LED_COLOR_TYPE['new'].index('Dual-color Blue / White') 68 | expected_write_byte_list = [ 69 | METHOD_ID, 70 | QUERY_TYPE.index('query_led_color_type'), 71 | LED_TYPE['new'].index('HDD LED') 72 | ] 73 | read_byte_list = [ 74 | 0x00, 75 | LED_COLOR_TYPE['new'].index('Dual-color Blue / White'), 76 | 0x00, 77 | 0x00 78 | ] 79 | 80 | nuc_wmi_read_control_file.return_value = read_byte_list 81 | returned_query_led_color_type = query_led_color_type( 82 | LED_TYPE['new'].index('HDD LED') 83 | ) 84 | 85 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 86 | 87 | self.assertEqual(returned_query_led_color_type, expected_query_led_color_type) 88 | 89 | # Reset 90 | nuc_wmi_read_control_file.reset_mock() 91 | nuc_wmi_write_control_file.reset_mock() 92 | 93 | # Branch 2: Test that query_led_color_type raises an exception when the control file returns an 94 | # error code. 95 | 96 | # Incorrect led 97 | expected_query_led_color_type = LED_COLOR_TYPE['new'].index('Dual-color Blue / White') 98 | expected_write_byte_list = [ 99 | METHOD_ID, 100 | QUERY_TYPE.index('query_led_color_type'), 101 | len(LED_TYPE['new']) # Incorrect led index 102 | ] 103 | read_byte_list = [ 104 | 0xE4, 105 | 0x00, 106 | 0x00, 107 | 0x00 108 | ] 109 | 110 | nuc_wmi_read_control_file.return_value = read_byte_list 111 | 112 | with self.assertRaises(NucWmiError) as err: 113 | returned_query_led_color_type = query_led_color_type( 114 | len(LED_TYPE['new']) # Incorrect led index 115 | ) 116 | 117 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 118 | 119 | self.assertEqual(str(err.exception), 'Error (Invalid Parameter)') 120 | 121 | 122 | @patch('nuc_wmi.query_led.query_led_color_type') 123 | @patch('nuc_wmi.query_led.read_control_file') 124 | @patch('nuc_wmi.query_led.write_control_file') 125 | def test_query_led_control_items( 126 | self, 127 | nuc_wmi_write_control_file, 128 | nuc_wmi_read_control_file, 129 | nuc_wmi_query_led_color_type 130 | ): 131 | """ 132 | Tests that `query_led_control_items` returns the expected exceptions, return values, or 133 | outputs. 134 | """ 135 | 136 | self.assertTrue(nuc_wmi.query_led.read_control_file is nuc_wmi_read_control_file) 137 | self.assertTrue(nuc_wmi.query_led.write_control_file is nuc_wmi_write_control_file) 138 | self.assertTrue(nuc_wmi.query_led.query_led_color_type is nuc_wmi_query_led_color_type) 139 | 140 | # Branch 1: Test that query_led_control_items send the expected byte string to the control file 141 | # and that the returned control file response is properly processed. 142 | 143 | # Query control items of HDD LED with HDD Activity Indicator 144 | expected_query_led_control_items = [0x00, 0x01, 0x02, 0x03] 145 | expected_write_byte_list = [ 146 | METHOD_ID, 147 | QUERY_TYPE.index('query_led_control_items'), 148 | LED_TYPE['new'].index('HDD LED'), 149 | LED_INDICATOR_OPTION.index('HDD Activity Indicator') 150 | ] 151 | read_byte_list = [ 152 | 0x00, 153 | 0x0F, 154 | 0x00, 155 | 0x00 156 | ] 157 | 158 | nuc_wmi_read_control_file.return_value = read_byte_list 159 | nuc_wmi_query_led_color_type.return_value = LED_COLOR_TYPE['new'].index('Dual-color Blue / White') 160 | returned_query_led_control_items = query_led_control_items( 161 | LED_TYPE['new'].index('HDD LED'), 162 | LED_INDICATOR_OPTION.index('HDD Activity Indicator') 163 | ) 164 | 165 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 166 | nuc_wmi_query_led_color_type.assert_called_with(LED_TYPE['new'].index('HDD LED'), control_file=None) 167 | 168 | self.assertEqual(returned_query_led_control_items, expected_query_led_control_items) 169 | 170 | # Reset 171 | nuc_wmi_read_control_file.reset_mock() 172 | nuc_wmi_write_control_file.reset_mock() 173 | nuc_wmi_query_led_color_type.reset_mock() 174 | 175 | # Query control items of HDD LED with Disabled Indicator 176 | expected_query_led_control_items = [] 177 | expected_write_byte_list = [ 178 | METHOD_ID, 179 | QUERY_TYPE.index('query_led_control_items'), 180 | LED_TYPE['new'].index('HDD LED'), 181 | LED_INDICATOR_OPTION_DISABLED 182 | ] 183 | read_byte_list = [ 184 | 0x00, 185 | 0x00, 186 | 0x00, 187 | 0x00 188 | ] 189 | 190 | nuc_wmi_read_control_file.return_value = read_byte_list 191 | nuc_wmi_query_led_color_type.return_value = LED_COLOR_TYPE['new'].index('Dual-color Blue / White') 192 | returned_query_led_control_items = query_led_control_items( 193 | LED_TYPE['new'].index('HDD LED'), 194 | LED_INDICATOR_OPTION_DISABLED 195 | ) 196 | 197 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 198 | nuc_wmi_query_led_color_type.assert_called_with(LED_TYPE['new'].index('HDD LED'), control_file=None) 199 | 200 | self.assertEqual(returned_query_led_control_items, expected_query_led_control_items) 201 | 202 | # Reset 203 | nuc_wmi_read_control_file.reset_mock() 204 | nuc_wmi_write_control_file.reset_mock() 205 | nuc_wmi_query_led_color_type.reset_mock() 206 | 207 | # Branch 2: Test that query_led_control_items raises an exception when the control file returns an 208 | # error code. 209 | 210 | # Incorrect led 211 | expected_query_led_control_items = [0x00, 0x01, 0x02, 0x03] 212 | expected_write_byte_list = [ 213 | METHOD_ID, 214 | QUERY_TYPE.index('query_led_control_items'), 215 | len(LED_TYPE['new']), # Invalid led index 216 | LED_INDICATOR_OPTION.index('HDD Activity Indicator') 217 | ] 218 | read_byte_list = [ 219 | 0xE4, 220 | 0x00, 221 | 0x00, 222 | 0x00 223 | ] 224 | 225 | nuc_wmi_read_control_file.return_value = read_byte_list 226 | nuc_wmi_query_led_color_type.return_value = LED_COLOR_TYPE['new'].index('Dual-color Blue / White') 227 | 228 | with self.assertRaises(NucWmiError) as err: 229 | returned_query_led_control_items = query_led_control_items( 230 | len(LED_TYPE['new']), # Invalid led index 231 | LED_INDICATOR_OPTION.index('HDD Activity Indicator') 232 | ) 233 | 234 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 235 | nuc_wmi_query_led_color_type.assert_called_with(len(LED_TYPE['new']), control_file=None) 236 | 237 | self.assertEqual(str(err.exception), 'Error (Invalid Parameter)') 238 | 239 | 240 | @patch('nuc_wmi.query_led.read_control_file') 241 | @patch('nuc_wmi.query_led.write_control_file') 242 | def test_query_led_indicator_options(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 243 | """ 244 | Tests that `query_led_indicator_options` returns the expected exceptions, return values, or 245 | outputs. 246 | """ 247 | 248 | self.assertTrue(nuc_wmi.query_led.read_control_file is nuc_wmi_read_control_file) 249 | self.assertTrue(nuc_wmi.query_led.write_control_file is nuc_wmi_write_control_file) 250 | 251 | # Branch 1: Test that query_led_indicator_options send the expected byte string to the control file 252 | # and that the returned control file response is properly processed. 253 | 254 | # Query HDD LED indicator options 255 | expected_query_led_indicator_options = [0x01, 0x04] 256 | expected_write_byte_list = [ 257 | METHOD_ID, 258 | QUERY_TYPE.index('query_led_indicator_options'), 259 | LED_TYPE['new'].index('HDD LED') 260 | ] 261 | read_byte_list = [ 262 | 0x00, 263 | 0x12, 264 | 0x00, 265 | 0x00 266 | ] 267 | 268 | nuc_wmi_read_control_file.return_value = read_byte_list 269 | returned_query_led_indicator_options = query_led_indicator_options( 270 | LED_TYPE['new'].index('HDD LED') 271 | ) 272 | 273 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 274 | 275 | self.assertEqual(returned_query_led_indicator_options, expected_query_led_indicator_options) 276 | 277 | # Reset 278 | nuc_wmi_read_control_file.reset_mock() 279 | nuc_wmi_write_control_file.reset_mock() 280 | 281 | # Branch 2: Test that query_led_indicator_options raises an exception when the control file returns an 282 | # error code. 283 | 284 | # Incorrect led 285 | expected_query_led_indicator_options = [0x01, 0x04] 286 | expected_write_byte_list = [ 287 | METHOD_ID, 288 | QUERY_TYPE.index('query_led_indicator_options'), 289 | len(LED_TYPE['new']) # Incorrect led index 290 | ] 291 | read_byte_list = [ 292 | 0xE4, 293 | 0x00, 294 | 0x00, 295 | 0x00 296 | ] 297 | 298 | nuc_wmi_read_control_file.return_value = read_byte_list 299 | 300 | with self.assertRaises(NucWmiError) as err: 301 | returned_query_led_indicator_options = query_led_indicator_options( 302 | len(LED_TYPE['new']) # Incorrect led index 303 | ) 304 | 305 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 306 | 307 | self.assertEqual(str(err.exception), 'Error (Invalid Parameter)') 308 | 309 | 310 | @patch('nuc_wmi.query_led.read_control_file') 311 | @patch('nuc_wmi.query_led.write_control_file') 312 | def test_query_leds(self, nuc_wmi_write_control_file, nuc_wmi_read_control_file): 313 | """ 314 | Tests that `query_leds` returns the expected exceptions, return values, or outputs. 315 | """ 316 | 317 | self.assertTrue(nuc_wmi.query_led.read_control_file is nuc_wmi_read_control_file) 318 | self.assertTrue(nuc_wmi.query_led.write_control_file is nuc_wmi_write_control_file) 319 | 320 | # Branch 1: Test that query_leds send the expected byte string to the control file 321 | # and that the returned control file response is properly processed. 322 | 323 | # Query HDD LED indicator options 324 | expected_query_leds = [0x00 , 0x01] 325 | expected_write_byte_list = [ 326 | METHOD_ID, 327 | QUERY_TYPE.index('query_leds') 328 | ] 329 | read_byte_list = [ 330 | 0x00, 331 | 0x03, 332 | 0x00, 333 | 0x00 334 | ] 335 | 336 | nuc_wmi_read_control_file.return_value = read_byte_list 337 | returned_query_leds = query_leds() 338 | 339 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 340 | 341 | self.assertEqual(returned_query_leds, expected_query_leds) 342 | 343 | # Reset 344 | nuc_wmi_read_control_file.reset_mock() 345 | nuc_wmi_write_control_file.reset_mock() 346 | 347 | # Branch 2: Test that query_leds raises an exception when the control file returns an 348 | # error code. 349 | 350 | # Incorrect led 351 | expected_query_leds = [0x00, 0x01] 352 | expected_write_byte_list = [ 353 | METHOD_ID, 354 | QUERY_TYPE.index('query_leds') 355 | ] 356 | read_byte_list = [ 357 | 0xE1, 358 | 0x00, 359 | 0x00, 360 | 0x00 361 | ] 362 | 363 | nuc_wmi_read_control_file.return_value = read_byte_list 364 | 365 | with self.assertRaises(NucWmiError) as err: 366 | returned_query_leds = query_leds() 367 | 368 | nuc_wmi_write_control_file.assert_called_with(expected_write_byte_list, control_file=None) 369 | 370 | self.assertEqual(str(err.exception), 'Error (Function not supported)') 371 | -------------------------------------------------------------------------------- /contrib/nuc_wmi/python/test/unit/nuc_wmi/cli/query_led_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `test.unit.nuc_wmi.cli.query_led_test` module provides unit tests for the functions in 3 | `nuc_wmi.cli.query_led`. 4 | 5 | Classes: 6 | TestCliQueryLed: A unit test class for the functions in `nuc_wmi.cli.query_led`. 7 | """ 8 | 9 | from __future__ import print_function 10 | 11 | import json 12 | import unittest 13 | 14 | from mock import patch 15 | 16 | from nuc_wmi import CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR, LED_COLOR_TYPE, LED_INDICATOR_OPTION 17 | from nuc_wmi import LED_TYPE, NucWmiError 18 | from nuc_wmi.cli.query_led import query_led_color_type_cli, query_led_control_items_cli 19 | from nuc_wmi.cli.query_led import query_led_indicator_options_cli, query_leds_cli 20 | 21 | import nuc_wmi 22 | 23 | 24 | class TestCliQueryLed(unittest.TestCase): 25 | """ 26 | A unit test class for the functions of `nuc_wmi.cli.query_led` 27 | 28 | Methods: 29 | setUp: Unit test initialization. 30 | test_query_led_color_type_cli: Tests that it returns the proper JSON response and exit code for 31 | valid cli args, tests that it captures raised errors and returns 32 | the proper JSON error response and exit code, tests that invalid 33 | LED color raises appropriate error. 34 | test_query_led_control_items_cli: Tests that it returns the proper JSON response and exit code for 35 | valid cli args, tests that it captures raised errors and returns 36 | the proper JSON error response and exit code, tests that invalid 37 | LED color raises appropriate error. 38 | test_query_led_indicator_options_cli: Tests that it returns the proper JSON response and exit code for 39 | valid cli args, tests that it captures raised errors and returns 40 | the proper JSON error response and exit code, tests that invalid 41 | LED color raises appropriate error. 42 | test_query_leds_cli: Tests that it returns the proper JSON response and exit code for 43 | valid cli args, tests that it captures raised errors and returns 44 | the proper JSON error response and exit code, tests that invalid 45 | LED color raises appropriate error. 46 | """ 47 | 48 | def setUp(self): 49 | """ 50 | Initializes the unit tests. 51 | """ 52 | 53 | self.maxDiff = None 54 | 55 | @patch('nuc_wmi.cli.query_led.print') 56 | @patch('nuc_wmi.cli.query_led.exit') 57 | @patch('nuc_wmi.cli.query_led.query_led_color_type') 58 | def test_query_led_color_type_cli( 59 | self, 60 | nuc_wmi_query_led_color_type, 61 | nuc_wmi_sys_exit, 62 | nuc_wmi_print 63 | ): 64 | """ 65 | Tests that `query_led_color_type_cli` returns the expected exceptions, return values, or outputs. 66 | """ 67 | 68 | self.assertTrue(nuc_wmi.cli.query_led.query_led_color_type is nuc_wmi_query_led_color_type) 69 | self.assertTrue(nuc_wmi.cli.query_led.exit is nuc_wmi_sys_exit) 70 | self.assertTrue(nuc_wmi.cli.query_led.print is nuc_wmi_print) 71 | 72 | # Branch 1: Test that query_led_color_type_cli returns the proper JSON response and exit 73 | # code for valid cli args 74 | 75 | # Return HDD LED color type of Dual-color Blue / White 76 | nuc_wmi_query_led_color_type.return_value = LED_COLOR_TYPE['new'].index('Dual-color Blue / White') 77 | returned_query_led_color_type_cli = query_led_color_type_cli( 78 | [ 79 | LED_TYPE['new'][1] 80 | ] 81 | ) 82 | 83 | nuc_wmi_query_led_color_type.assert_called_with( 84 | LED_TYPE['new'].index('HDD LED'), 85 | control_file=None 86 | ) 87 | nuc_wmi_print.assert_called() 88 | self.assertEqual( 89 | json.loads(nuc_wmi_print.call_args.args[0]), 90 | { 91 | 'led': { 92 | 'type': LED_TYPE['new'][1], 93 | 'color_type': LED_COLOR_TYPE['new'][1] 94 | } 95 | } 96 | ) 97 | 98 | self.assertEqual(returned_query_led_color_type_cli, None) 99 | 100 | # Reset 101 | nuc_wmi_query_led_color_type.reset_mock() 102 | nuc_wmi_sys_exit.reset_mock() 103 | nuc_wmi_print.reset_mock() 104 | 105 | # Branch 2: Test that query_led_color_type_cli captures raised errors and returns 106 | # the proper JSON error response and exit code. 107 | nuc_wmi_query_led_color_type.side_effect = NucWmiError('Error (Function not supported)') 108 | 109 | returned_query_led_color_type_cli = query_led_color_type_cli( 110 | [ 111 | LED_TYPE['new'][1] 112 | ] 113 | ) 114 | 115 | nuc_wmi_query_led_color_type.assert_called_with( 116 | LED_TYPE['new'].index('HDD LED'), 117 | control_file=None 118 | ) 119 | nuc_wmi_print.assert_called_with('{"error": "Error (Function not supported)"}') 120 | nuc_wmi_sys_exit.assert_called_with(1) 121 | 122 | self.assertEqual(returned_query_led_color_type_cli, None) 123 | 124 | 125 | @patch('nuc_wmi.cli.query_led.print') 126 | @patch('nuc_wmi.cli.query_led.exit') 127 | @patch('nuc_wmi.cli.query_led.query_led_color_type') 128 | @patch('nuc_wmi.cli.query_led.query_led_indicator_options') 129 | @patch('nuc_wmi.cli.query_led.query_led_control_items') 130 | def test_query_led_control_items_cli( 131 | self, 132 | nuc_wmi_query_led_control_items, 133 | nuc_wmi_query_led_indicator_options, 134 | nuc_wmi_query_led_color_type, 135 | nuc_wmi_sys_exit, 136 | nuc_wmi_print 137 | ): 138 | """ 139 | Tests that `query_led_control_items_cli` returns the expected exceptions, return values, or outputs. 140 | """ 141 | 142 | self.assertTrue(nuc_wmi.cli.query_led.query_led_control_items is nuc_wmi_query_led_control_items) 143 | self.assertTrue(nuc_wmi.cli.query_led.query_led_indicator_options is nuc_wmi_query_led_indicator_options) 144 | self.assertTrue(nuc_wmi.cli.query_led.query_led_color_type is nuc_wmi_query_led_color_type) 145 | self.assertTrue(nuc_wmi.cli.query_led.exit is nuc_wmi_sys_exit) 146 | self.assertTrue(nuc_wmi.cli.query_led.print is nuc_wmi_print) 147 | 148 | # Branch 1: Test that query_led_control_items_cli returns the proper JSON response and exit 149 | # code for valid cli args 150 | 151 | # Get control items for HDD LED set to HDD Activity Indicator 152 | expected_control_items = [0x00, 0x01, 0x02, 0x03] 153 | nuc_wmi_query_led_color_type.return_value = LED_COLOR_TYPE['new'].index('Dual-color Blue / White') 154 | nuc_wmi_query_led_indicator_options.return_value = [0x01, 0x04] 155 | nuc_wmi_query_led_control_items.return_value = expected_control_items 156 | returned_query_led_control_items_cli = query_led_control_items_cli( 157 | [ 158 | LED_TYPE['new'][1], 159 | LED_INDICATOR_OPTION[1] 160 | ] 161 | ) 162 | 163 | nuc_wmi_query_led_color_type.assert_called_with( 164 | LED_TYPE['new'].index('HDD LED'), 165 | control_file=None 166 | ) 167 | nuc_wmi_query_led_indicator_options.assert_called_with( 168 | LED_TYPE['new'].index('HDD LED'), 169 | control_file=None 170 | ) 171 | nuc_wmi_query_led_control_items.assert_called_with( 172 | LED_TYPE['new'].index('HDD LED'), 173 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 174 | control_file=None 175 | ) 176 | nuc_wmi_print.assert_called() 177 | self.assertEqual( 178 | json.loads(nuc_wmi_print.call_args.args[0]), 179 | { 180 | 'led': { 181 | 'type': LED_TYPE['new'][1], 182 | 'indicator_option': LED_INDICATOR_OPTION[1], 183 | 'control_items': [CONTROL_ITEM_HDD_ACTIVITY_INDICATOR_MULTI_COLOR[control_item]['Control Item'] \ 184 | for control_item in expected_control_items] 185 | } 186 | } 187 | ) 188 | 189 | self.assertEqual(returned_query_led_control_items_cli, None) 190 | 191 | # Reset 192 | nuc_wmi_query_led_color_type.reset_mock() 193 | nuc_wmi_query_led_indicator_options.reset_mock() 194 | nuc_wmi_query_led_control_items.reset_mock() 195 | nuc_wmi_sys_exit.reset_mock() 196 | nuc_wmi_print.reset_mock() 197 | 198 | # Branch 2: Test that query_led_control_items_cli captures raised errors and returns 199 | # the proper JSON error response and exit code. 200 | expected_control_items = [0x00, 0x01, 0x02, 0x03] 201 | nuc_wmi_query_led_color_type.return_value = LED_COLOR_TYPE['new'].index('Dual-color Blue / White') 202 | nuc_wmi_query_led_indicator_options.return_value = [0x01, 0x04] 203 | nuc_wmi_query_led_control_items.side_effect = NucWmiError('Error (Function not supported)') 204 | returned_query_led_control_items_cli = query_led_control_items_cli( 205 | [ 206 | LED_TYPE['new'][1], 207 | LED_INDICATOR_OPTION[1] 208 | ] 209 | ) 210 | 211 | nuc_wmi_query_led_color_type.assert_called_with( 212 | LED_TYPE['new'].index('HDD LED'), 213 | control_file=None 214 | ) 215 | nuc_wmi_query_led_indicator_options.assert_called_with( 216 | LED_TYPE['new'].index('HDD LED'), 217 | control_file=None 218 | ) 219 | nuc_wmi_query_led_control_items.assert_called_with( 220 | LED_TYPE['new'].index('HDD LED'), 221 | LED_INDICATOR_OPTION.index('HDD Activity Indicator'), 222 | control_file=None 223 | ) 224 | nuc_wmi_print.assert_called_with('{"error": "Error (Function not supported)"}') 225 | nuc_wmi_sys_exit.assert_called_with(1) 226 | 227 | self.assertEqual(returned_query_led_control_items_cli, None) 228 | 229 | # Reset 230 | nuc_wmi_query_led_color_type.reset_mock() 231 | nuc_wmi_query_led_indicator_options.reset_mock() 232 | nuc_wmi_query_led_control_items.reset_mock() 233 | nuc_wmi_sys_exit.reset_mock() 234 | nuc_wmi_print.reset_mock() 235 | 236 | # Branch 3: Tests that invalid LED indicator raises appropriate error. 237 | expected_control_items = [0x00, 0x01, 0x02, 0x03] 238 | nuc_wmi_query_led_color_type.return_value = LED_COLOR_TYPE['new'].index('Dual-color Blue / White') 239 | nuc_wmi_query_led_indicator_options.return_value = [] 240 | nuc_wmi_query_led_control_items.return_value = expected_control_items 241 | returned_query_led_control_items_cli = query_led_control_items_cli( 242 | [ 243 | LED_TYPE['new'][1], 244 | LED_INDICATOR_OPTION[1] 245 | ] 246 | ) 247 | 248 | nuc_wmi_query_led_color_type.assert_called_with( 249 | LED_TYPE['new'].index('HDD LED'), 250 | control_file=None 251 | ) 252 | nuc_wmi_query_led_indicator_options.assert_called_with( 253 | LED_TYPE['new'].index('HDD LED'), 254 | control_file=None 255 | ) 256 | nuc_wmi_query_led_control_items.assert_not_called() 257 | nuc_wmi_print.assert_called_with('{"error": "Invalid indicator option for the selected LED"}') 258 | nuc_wmi_sys_exit.assert_called_with(1) 259 | 260 | self.assertEqual(returned_query_led_control_items_cli, None) 261 | 262 | 263 | @patch('nuc_wmi.cli.query_led.print') 264 | @patch('nuc_wmi.cli.query_led.exit') 265 | @patch('nuc_wmi.cli.query_led.query_led_indicator_options') 266 | def test_query_led_indicator_options_cli( 267 | self, 268 | nuc_wmi_query_led_indicator_options, 269 | nuc_wmi_sys_exit, 270 | nuc_wmi_print 271 | ): 272 | """ 273 | Tests that `query_led_indicator_options_cli` returns the expected exceptions, return values, or outputs. 274 | """ 275 | 276 | self.assertTrue(nuc_wmi.cli.query_led.query_led_indicator_options is nuc_wmi_query_led_indicator_options) 277 | self.assertTrue(nuc_wmi.cli.query_led.exit is nuc_wmi_sys_exit) 278 | self.assertTrue(nuc_wmi.cli.query_led.print is nuc_wmi_print) 279 | 280 | # Branch 1: Test that query_led_indicator_options_cli returns the proper JSON response and exit 281 | # code for valid cli args 282 | 283 | # Return HDD LED indicator options of HDD Activity Indicator and Software Indicator 284 | expected_indicator_options = [0x01, 0x04] 285 | nuc_wmi_query_led_indicator_options.return_value = expected_indicator_options 286 | returned_query_led_indicator_options_cli = query_led_indicator_options_cli( 287 | [ 288 | LED_TYPE['new'][1] 289 | ] 290 | ) 291 | 292 | nuc_wmi_query_led_indicator_options.assert_called_with( 293 | LED_TYPE['new'].index('HDD LED'), 294 | control_file=None 295 | ) 296 | nuc_wmi_print.assert_called() 297 | self.assertEqual( 298 | json.loads(nuc_wmi_print.call_args.args[0]), 299 | { 300 | 'led': { 301 | 'type': LED_TYPE['new'][1], 302 | 'indicator_options': [LED_INDICATOR_OPTION[indicator] for indicator in expected_indicator_options] 303 | } 304 | } 305 | ) 306 | 307 | self.assertEqual(returned_query_led_indicator_options_cli, None) 308 | 309 | # Reset 310 | nuc_wmi_query_led_indicator_options.reset_mock() 311 | nuc_wmi_sys_exit.reset_mock() 312 | nuc_wmi_print.reset_mock() 313 | 314 | # Branch 2: Test that query_led_indicator_options_cli captures raised errors and returns 315 | # the proper JSON error response and exit code. 316 | nuc_wmi_query_led_indicator_options.side_effect = NucWmiError('Error (Function not supported)') 317 | 318 | returned_query_led_indicator_options_cli = query_led_indicator_options_cli( 319 | [ 320 | LED_TYPE['new'][1] 321 | ] 322 | ) 323 | 324 | nuc_wmi_query_led_indicator_options.assert_called_with( 325 | LED_TYPE['new'].index('HDD LED'), 326 | control_file=None 327 | ) 328 | nuc_wmi_print.assert_called_with('{"error": "Error (Function not supported)"}') 329 | nuc_wmi_sys_exit.assert_called_with(1) 330 | 331 | self.assertEqual(returned_query_led_indicator_options_cli, None) 332 | 333 | 334 | @patch('nuc_wmi.cli.query_led.print') 335 | @patch('nuc_wmi.cli.query_led.exit') 336 | @patch('nuc_wmi.cli.query_led.query_leds') 337 | def test_query_leds_cli( 338 | self, 339 | nuc_wmi_query_leds, 340 | nuc_wmi_sys_exit, 341 | nuc_wmi_print 342 | ): 343 | """ 344 | Tests that `query_leds_cli` returns the expected exceptions, return values, or outputs. 345 | """ 346 | 347 | self.assertTrue(nuc_wmi.cli.query_led.query_leds is nuc_wmi_query_leds) 348 | self.assertTrue(nuc_wmi.cli.query_led.exit is nuc_wmi_sys_exit) 349 | self.assertTrue(nuc_wmi.cli.query_led.print is nuc_wmi_print) 350 | 351 | # Branch 1: Test that query_leds_cli returns the proper JSON response and exit 352 | # code for valid cli args 353 | expected_leds = [0x00, 0x01] 354 | nuc_wmi_query_leds.return_value = expected_leds 355 | returned_query_leds_cli = query_leds_cli([]) 356 | 357 | nuc_wmi_query_leds.assert_called_with( 358 | control_file=None 359 | ) 360 | nuc_wmi_print.assert_called() 361 | self.assertEqual( 362 | json.loads(nuc_wmi_print.call_args.args[0]), 363 | { 364 | 'leds': [LED_TYPE['new'][led] for led in expected_leds] 365 | } 366 | ) 367 | 368 | self.assertEqual(returned_query_leds_cli, None) 369 | 370 | # Reset 371 | nuc_wmi_query_leds.reset_mock() 372 | nuc_wmi_sys_exit.reset_mock() 373 | nuc_wmi_print.reset_mock() 374 | 375 | # Branch 2: Test that query_leds_cli captures raised errors and returns 376 | # the proper JSON error response and exit code. 377 | nuc_wmi_query_leds.side_effect = NucWmiError('Error (Function not supported)') 378 | 379 | returned_query_leds_cli = query_leds_cli([]) 380 | 381 | nuc_wmi_query_leds.assert_called_with( 382 | control_file=None 383 | ) 384 | nuc_wmi_print.assert_called_with('{"error": "Error (Function not supported)"}') 385 | nuc_wmi_sys_exit.assert_called_with(1) 386 | 387 | self.assertEqual(returned_query_leds_cli, None) 388 | --------------------------------------------------------------------------------