From e8b054c4c3ef938bfecf84bce3bf5bad68c5ead3 Mon Sep 17 00:00:00 2001 From: damachine Date: Mon, 9 Mar 2026 00:53:45 +0100 Subject: [PATCH 1/5] VERSION: bump to 3.0.1 --- .SRCINFO | 5 +- PKGBUILD | 4 +- VERSION | 2 +- aur/PKGBUILD | 2 +- .../plugins/coolerdash/config.json | 5 +- .../plugins/coolerdash/ui/index.html | 101 +++++++++++++----- src/device/config.c | 28 +++++ src/device/config.h | 4 + src/srv/cc_conf.c | 21 +++- src/srv/cc_main.c | 97 +++++++++++++++-- src/srv/cc_main.h | 20 ++++ src/srv/cc_sensor.c | 21 +++- 12 files changed, 257 insertions(+), 53 deletions(-) diff --git a/.SRCINFO b/.SRCINFO index bcc031c..d1f1988 100644 --- a/.SRCINFO +++ b/.SRCINFO @@ -1,6 +1,6 @@ pkgbase = coolerdash - pkgdesc = Monitor telemetry data on an AIO liquid cooler with an integrated LCD display - pkgver = 2.2.6 + pkgdesc = Plug-in for CoolerControl that extends the LCD functionality with additional features + pkgver = 3.0.1 pkgrel = 1 url = https://github.com/damachine/coolerdash install = coolerdash.install @@ -11,7 +11,6 @@ pkgbase = coolerdash makedepends = pkg-config makedepends = git depends = cairo - depends = coolercontrol depends = jansson depends = libcurl-gnutls depends = ttf-roboto diff --git a/PKGBUILD b/PKGBUILD index 2381770..7313cc0 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -10,11 +10,11 @@ pkgrel=1 provides=('coolerdash-git') replaces=('coolerdash-git') conflicts=('coolerdash-git') -pkgdesc="Monitor telemetry data on an AIO liquid cooler with an integrated LCD display" +pkgdesc="Plug-in for CoolerControl that extends the LCD functionality with additional features" arch=('x86_64') url="https://github.com/damachine/coolerdash" license=('MIT') -depends=('cairo' 'coolercontrol' 'jansson' 'libcurl-gnutls' 'ttf-roboto') +depends=('cairo' 'jansson' 'libcurl-gnutls' 'ttf-roboto') makedepends=('gcc' 'make' 'pkg-config' 'git') optdepends=() backup=('etc/coolercontrol/plugins/coolerdash/config.json') diff --git a/VERSION b/VERSION index bda8fbe..cb2b00e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.6 +3.0.1 diff --git a/aur/PKGBUILD b/aur/PKGBUILD index a08dfc4..2e0b33c 100644 --- a/aur/PKGBUILD +++ b/aur/PKGBUILD @@ -7,7 +7,7 @@ pkgrel=1 provides=('coolerdash') replaces=('coolerdash') conflicts=('coolerdash') -pkgdesc="Monitor telemetry data on an AIO liquid cooler with an integrated LCD display" +pkgdesc="Plug-in for CoolerControl that extends the LCD functionality with additional features" arch=('x86_64') url="https://github.com/damachine/coolerdash" license=('MIT') diff --git a/etc/coolercontrol/plugins/coolerdash/config.json b/etc/coolercontrol/plugins/coolerdash/config.json index 62195fc..cb754f5 100644 --- a/etc/coolercontrol/plugins/coolerdash/config.json +++ b/etc/coolercontrol/plugins/coolerdash/config.json @@ -3,7 +3,10 @@ "daemon": { "address": "http://localhost:11987", - "password": "coolAdmin" + "access_token": "", + "_comment_token": "CC4: Create token in UI → Access Protection. Format: cc_. Preferred over password.", + "password": "coolAdmin", + "_comment_password": "Legacy CC3 / fallback. Ignored when access_token is set." }, "paths": { diff --git a/etc/coolercontrol/plugins/coolerdash/ui/index.html b/etc/coolercontrol/plugins/coolerdash/ui/index.html index b1cdc73..6d2c95d 100644 --- a/etc/coolercontrol/plugins/coolerdash/ui/index.html +++ b/etc/coolercontrol/plugins/coolerdash/ui/index.html @@ -577,7 +577,7 @@

CoolerDash Configuration

CoolerControl API - Make sure CoolerControl is running and the API is reachable. + Make sure CoolerControl is running and the API address below is reachable.
@@ -585,11 +585,46 @@

CoolerDash Configuration

HTTP or HTTPS supported (Default: http://localhost:11987)
+ +

Authentication — CC4 (Bearer Token)

+
+ CoolerControl 4 recommends Bearer Tokens for plugins. + Create an access token under CoolerControl → Access Protection. + Format: cc_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx — takes priority over the password. +
+
+ + Enter a CC4 Bearer token (cc_…). Leave empty to fall back to password auth (Legacy / CC3). + +
+ +

Authentication — Legacy / CC3 (Password)

- Leave empty if authentication is disabled (Default: coolAdmin) + Only used when no Access Token is set. Default: coolAdmin
+ +

TLS / HTTPS (Optional)

+
+
+ + Path to a CA certificate for self-signed HTTPS. Example: /etc/coolercontrol/coolercontrol.crt + +
+
+ + Insecure — only use when no CA certificate is available. + +
+
+

+ Note: TLS options only affect the CoolerDash daemon, not this browser UI. + Localhost connections always use plain HTTP regardless of TLS settings. +

@@ -1084,7 +1119,10 @@

System Environment

const FACTORY_DEFAULTS = { daemon: { address: "http://localhost:11987", - password: "coolAdmin" + access_token: "", + password: "coolAdmin", + tls_ca_cert_path: "", + tls_skip_verify: false }, paths: { image_shutdown: "/etc/coolercontrol/plugins/coolerdash/shutdown.png" @@ -1253,16 +1291,23 @@

System Environment

window.runPluginScript = function(mainFunction) { mainFunction(); }; } + // ===== AUTH HEADER HELPER ===== + function makeAuthHeaders(token, password) { + var t = (token || '').trim(); + var p = (password || '').trim(); + if (t) return { 'Authorization': 'Bearer ' + t }; + if (p) return { 'Authorization': 'Basic ' + btoa('CCAdmin:' + p) }; + return {}; + } + // ===== SENSOR DISCOVERY ===== - async function discoverSensors(apiAddress, password) { + async function discoverSensors(apiAddress, token, password) { try { - const headers = { 'Content-Type': 'application/json' }; - if (password) { - headers['Authorization'] = 'Basic ' + btoa('admin:' + password); - } + const authHeaders = makeAuthHeaders(token, password); + const headers = Object.assign({ 'Content-Type': 'application/json' }, authHeaders); // GET /devices for device names - const devRes = await fetch(apiAddress + '/devices', { headers: password ? { 'Authorization': headers['Authorization'] } : {} }); + const devRes = await fetch(apiAddress + '/devices', { headers: authHeaders }); const devData = devRes.ok ? await devRes.json() : { devices: [] }; const deviceNames = {}; for (const dev of (devData.devices || [])) { @@ -1715,10 +1760,12 @@

System Environment

// ===== DEVICE DETECTION ===== var lastApiAddress = ''; + var lastApiToken = ''; var lastApiPassword = ''; - async function fetchDeviceInfo(apiAddress, password) { + async function fetchDeviceInfo(apiAddress, token, password) { lastApiAddress = apiAddress; + lastApiToken = token; lastApiPassword = password; var loadingEl = document.getElementById('device-loading'); @@ -1730,10 +1777,7 @@

System Environment

errorEl.style.display = 'none'; try { - var headers = {}; - if (password) { - headers['Authorization'] = 'Basic ' + btoa('admin:' + password); - } + var headers = makeAuthHeaders(token, password); var response = await fetch(apiAddress + '/devices', { headers: headers }); if (!response.ok) throw new Error('HTTP ' + response.status); var data = await response.json(); @@ -1816,15 +1860,12 @@

System Environment

} function refreshDeviceInfo() { - fetchDeviceInfo(lastApiAddress, lastApiPassword); + fetchDeviceInfo(lastApiAddress, lastApiToken, lastApiPassword); } - async function fetchSystemInfo(apiAddress, password) { + async function fetchSystemInfo(apiAddress, token, password) { try { - var headers = {}; - if (password) { - headers['Authorization'] = 'Basic ' + btoa('admin:' + password); - } + var headers = makeAuthHeaders(token, password); var healthRes = await fetch(apiAddress + '/health', { headers: headers }); if (healthRes.ok) { @@ -1996,6 +2037,9 @@

System Environment

// Sensor configs from dynamic form sections config.sensors = collectSensorConfigsFromDOM(); + // Convert tls_skip_verify to boolean for JSON compatibility with C daemon + if (config.daemon) config.daemon.tls_skip_verify = !!config.daemon.tls_skip_verify; + return config; } @@ -2109,9 +2153,9 @@

System Environment

async function restartPluginDaemon(config) { try { var apiAddress = (config.daemon && config.daemon.address) || 'http://localhost:11987'; + var token = (config.daemon && config.daemon.access_token) || ''; var password = (config.daemon && config.daemon.password) || ''; - var headers = { 'Content-Type': 'application/json' }; - if (password) headers['Authorization'] = 'Basic ' + btoa('admin:' + password); + var headers = Object.assign({ 'Content-Type': 'application/json' }, makeAuthHeaders(token, password)); var response = await fetch(apiAddress + '/plugins/coolerdash/restart', { method: 'POST', headers: headers }); if (!response.ok) console.warn("Could not restart plugin automatically"); @@ -2145,12 +2189,14 @@

System Environment

async function writeConfigToFile(config) { try { var apiAddress = (config.daemon && config.daemon.address) || 'http://localhost:11987'; + var token = (config.daemon && config.daemon.access_token) || ''; var password = (config.daemon && config.daemon.password) || ''; + var authHdr = makeAuthHeaders(token, password); - if (password) { + if (token || password) { var response = await fetch(apiAddress + '/plugins/coolerdash/config', { method: 'PUT', - headers: { 'Content-Type': 'application/json', 'Authorization': 'Basic ' + btoa('admin:' + password) }, + headers: Object.assign({ 'Content-Type': 'application/json' }, authHdr), body: JSON.stringify(config, null, 2) }); if (response.ok) return true; @@ -2285,10 +2331,11 @@

System Environment

// Detect device and discover sensors from CoolerControl API var apiAddr = (config.daemon && config.daemon.address) || 'http://localhost:11987'; + var apiToken = (config.daemon && config.daemon.access_token) || ''; var apiPass = (config.daemon && config.daemon.password) || ''; - fetchDeviceInfo(apiAddr, apiPass); - fetchSystemInfo(apiAddr, apiPass); - discoverSensors(apiAddr, apiPass); + fetchDeviceInfo(apiAddr, apiToken, apiPass); + fetchSystemInfo(apiAddr, apiToken, apiPass); + discoverSensors(apiAddr, apiToken, apiPass); } catch (error) { console.error("Init failed:", error); if (DEFAULT_CONFIG) populateForm(DEFAULT_CONFIG); diff --git a/src/device/config.c b/src/device/config.c index 464615d..9753fec 100644 --- a/src/device/config.c +++ b/src/device/config.c @@ -74,6 +74,8 @@ static void set_daemon_defaults(Config *config) { SAFE_STRCPY(config->daemon_password, ""); } + /* access_token, tls_ca_cert_path default to empty string; tls_skip_verify defaults to 0 */ + /* (already zeroed by memset in load_plugin_config) */ } /** @@ -654,6 +656,32 @@ static void load_daemon_from_json(json_t *root, Config *config) SAFE_STRCPY(config->daemon_password, value); } } + + json_t *token = json_object_get(daemon, "access_token"); + if (token && json_is_string(token) && json_string_length(token) > 0) + { + const char *value = json_string_value(token); + if (value) + { + SAFE_STRCPY(config->access_token, value); + } + } + + json_t *ca_cert = json_object_get(daemon, "tls_ca_cert_path"); + if (ca_cert && json_is_string(ca_cert) && json_string_length(ca_cert) > 0) + { + const char *value = json_string_value(ca_cert); + if (value) + { + SAFE_STRCPY(config->tls_ca_cert_path, value); + } + } + + json_t *skip_verify = json_object_get(daemon, "tls_skip_verify"); + if (skip_verify && json_is_boolean(skip_verify)) + { + config->tls_skip_verify = json_is_true(skip_verify) ? 1 : 0; + } } /** diff --git a/src/device/config.h b/src/device/config.h index 0f8e6b7..e1bca7a 100644 --- a/src/device/config.h +++ b/src/device/config.h @@ -23,6 +23,7 @@ // Configuration constants #define CONFIG_MAX_STRING_LEN 256 #define CONFIG_MAX_PASSWORD_LEN 128 +#define CONFIG_MAX_TOKEN_LEN 64 #define CONFIG_MAX_PATH_LEN 512 #define CONFIG_MAX_FONT_NAME_LEN 64 #define CONFIG_MAX_SENSOR_SLOT_LEN 256 @@ -93,6 +94,9 @@ typedef struct Config // Daemon configuration char daemon_address[CONFIG_MAX_STRING_LEN]; char daemon_password[CONFIG_MAX_PASSWORD_LEN]; + char access_token[CONFIG_MAX_TOKEN_LEN]; + char tls_ca_cert_path[CONFIG_MAX_PATH_LEN]; + int tls_skip_verify; // Paths configuration char paths_images[CONFIG_MAX_PATH_LEN]; diff --git a/src/srv/cc_conf.c b/src/srv/cc_conf.c index 7ff25ec..23c6fa3 100644 --- a/src/srv/cc_conf.c +++ b/src/srv/cc_conf.c @@ -388,7 +388,8 @@ static int parse_liquidctl_data(const char *json, char *lcd_uid, /** * @brief Configure CURL options for device cache request. */ -static void configure_device_cache_curl(CURL *curl, const char *url, +static void configure_device_cache_curl(CURL *curl, const Config *config, + const char *url, http_response *chunk, struct curl_slist **headers) { @@ -400,7 +401,23 @@ static void configure_device_cache_curl(CURL *curl, const char *url, curl_easy_setopt(curl, CURLOPT_TIMEOUT, 2L); *headers = curl_slist_append(NULL, "accept: application/json"); + + /* Attach auth: Bearer token preferred, otherwise share session cookie */ + const char *bearer = get_session_access_token(); + if (bearer && bearer[0] != '\0') + { + *headers = curl_slist_append(*headers, bearer); + } + else + { + const char *cookie_jar = get_session_cookie_jar(); + if (cookie_jar && cookie_jar[0] != '\0') + curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar); + } + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, *headers); + + apply_ssl_options(curl, config); } /** @@ -533,7 +550,7 @@ static int initialize_device_cache(const Config *config) chunk.capacity = 4096; struct curl_slist *headers = NULL; - configure_device_cache_curl(curl, url, &chunk, &headers); + configure_device_cache_curl(curl, config, url, &chunk, &headers); int success = 0; if (curl_easy_perform(curl) == CURLE_OK) diff --git a/src/srv/cc_main.c b/src/srv/cc_main.c index c39930e..532073f 100644 --- a/src/srv/cc_main.c +++ b/src/srv/cc_main.c @@ -85,6 +85,7 @@ typedef struct { CURL *curl_handle; char cookie_jar[CC_COOKIE_SIZE]; + char access_token[CC_BEARER_HEADER_SIZE]; int session_initialized; } CoolerControlSession; @@ -94,7 +95,7 @@ typedef struct * handle and session initialization status. */ static CoolerControlSession cc_session = { - .curl_handle = NULL, .cookie_jar = {0}, .session_initialized = 0}; + .curl_handle = NULL, .cookie_jar = {0}, .access_token = {0}, .session_initialized = 0}; /** * @brief Reallocate response buffer if needed. @@ -152,6 +153,9 @@ size_t write_callback(const void *contents, size_t size, size_t nmemb, return realsize; } +/* Forward declaration — definition is after cleanup_coolercontrol_session() */ +void apply_ssl_options(void *curl_raw, const struct Config *config); + /** * @brief Validate snprintf result for buffer overflow. * @details Checks if snprintf truncated output. @@ -210,11 +214,7 @@ static struct curl_slist *configure_login_curl(CURL *curl, const Config *config, curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // Enable SSL verification for HTTPS - if (strncmp(config->daemon_address, "https://", 8) == 0) - { - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); - } + apply_ssl_options(curl, config); return headers; } @@ -236,6 +236,26 @@ static int is_login_successful(CURLcode res, long response_code) */ int init_coolercontrol_session(const Config *config) { + /* --- Bearer Token path: skip login entirely --- */ + if (config->access_token[0] != '\0') + { + int written = snprintf(cc_session.access_token, sizeof(cc_session.access_token), + "Authorization: Bearer %s", config->access_token); + if (written < 0 || (size_t)written >= sizeof(cc_session.access_token)) + cc_session.access_token[sizeof(cc_session.access_token) - 1] = '\0'; + + /* We still need a CURL handle for LCD uploads */ + curl_global_init(CURL_GLOBAL_DEFAULT); + cc_session.curl_handle = curl_easy_init(); + if (!cc_session.curl_handle) + return 0; + + cc_session.session_initialized = 1; + log_message(LOG_STATUS, "Session initialized using Bearer token (CC4)"); + return 1; + } + + /* --- Basic Auth / Cookie path (CC3 / fallback) --- */ curl_global_init(CURL_GLOBAL_DEFAULT); cc_session.curl_handle = curl_easy_init(); if (!cc_session.curl_handle) @@ -331,6 +351,58 @@ void cleanup_coolercontrol_session(void) } } +/** + * @brief Returns the active Bearer auth header string (or empty string). + */ +const char *get_session_access_token(void) +{ + return cc_session.access_token; +} + +/** + * @brief Returns the session cookie jar path. + */ +const char *get_session_cookie_jar(void) +{ + return cc_session.cookie_jar; +} + +/** + * @brief Apply TLS/SSL options to a CURL handle based on config. + * @details Localhost always uses plain HTTP; for HTTPS addresses this sets + * peer/host verification and optional custom CA cert or skip flag. + */ +void apply_ssl_options(void *curl_raw, const struct Config *config) +{ + if (!curl_raw || !config) + return; + + CURL *curl = (CURL *)curl_raw; + + /* Plain HTTP → nothing to do */ + if (strncmp(config->daemon_address, "https://", 8) != 0) + return; + + if (config->tls_skip_verify) + { + log_message(LOG_WARNING, + "TLS peer verification disabled (tls_skip_verify=true) — insecure!"); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + return; + } + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); + + if (config->tls_ca_cert_path[0] != '\0') + { + curl_easy_setopt(curl, CURLOPT_CAINFO, config->tls_ca_cert_path); + log_message(LOG_INFO, "TLS: using custom CA cert: %s", + config->tls_ca_cert_path); + } +} + /** * @brief Add a string field to multipart form with error checking. * @details Helper function to add a named string field to curl_mime form. @@ -474,11 +546,7 @@ static struct curl_slist *configure_lcd_upload_curl(const Config *config, curl_easy_setopt(cc_session.curl_handle, CURLOPT_CUSTOMREQUEST, "PUT"); // Enable SSL verification for HTTPS - if (strncmp(config->daemon_address, "https://", 8) == 0) - { - curl_easy_setopt(cc_session.curl_handle, CURLOPT_SSL_VERIFYPEER, 1L); - curl_easy_setopt(cc_session.curl_handle, CURLOPT_SSL_VERIFYHOST, 2L); - } + apply_ssl_options(cc_session.curl_handle, config); // Set write callback curl_easy_setopt( @@ -490,6 +558,13 @@ static struct curl_slist *configure_lcd_upload_curl(const Config *config, struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "User-Agent: CoolerDash/1.0"); headers = curl_slist_append(headers, "Accept: application/json"); + + // Attach auth: Bearer token preferred, otherwise rely on session cookie + if (cc_session.access_token[0] != '\0') + { + headers = curl_slist_append(headers, cc_session.access_token); + } + curl_easy_setopt(cc_session.curl_handle, CURLOPT_HTTPHEADER, headers); return headers; diff --git a/src/srv/cc_main.h b/src/srv/cc_main.h index bf86801..a7e4ba6 100644 --- a/src/srv/cc_main.h +++ b/src/srv/cc_main.h @@ -29,6 +29,7 @@ #define CC_UID_SIZE 128 #define CC_URL_SIZE 512 #define CC_USERPWD_SIZE 128 +#define CC_BEARER_HEADER_SIZE (CONFIG_MAX_TOKEN_LEN + 32) // Maximum safe allocation size to prevent overflow #define CC_MAX_SAFE_ALLOC_SIZE (SIZE_MAX / 2) @@ -91,6 +92,25 @@ int is_session_initialized(void); */ void cleanup_coolercontrol_session(void); +/** + * @brief Returns the active Bearer access token (empty string if not set). + * @details Used by cc_conf and cc_sensor to attach auth headers. + */ +const char *get_session_access_token(void); + +/** + * @brief Returns the cookie jar path for session cookie sharing. + * @details Used by cc_conf and cc_sensor when password-auth is active. + */ +const char *get_session_cookie_jar(void); + +/** + * @brief Apply TLS/SSL options to a CURL handle based on config. + * @details Handles VERIFYPEER/VERIFYHOST/CAINFO/skip-verify logic. + * Call this for every CURL handle that may use HTTPS. + */ +void apply_ssl_options(void *curl, const struct Config *config); + /** * @brief Sends an image directly to the LCD of the CoolerControl device. * @details Uploads an image to the LCD display using a multipart HTTP PUT diff --git a/src/srv/cc_sensor.c b/src/srv/cc_sensor.c index e9f9603..340dd38 100644 --- a/src/srv/cc_sensor.c +++ b/src/srv/cc_sensor.c @@ -360,15 +360,26 @@ static int get_sensor_data_from_api(const Config *config, configure_status_request(curl, url, &response); - if (strncmp(config->daemon_address, "https://", 8) == 0) - { - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); - } + /* SSL options (apply_ssl_options handles http:// as no-op) */ + apply_ssl_options(curl, config); struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "accept: application/json"); headers = curl_slist_append(headers, "content-type: application/json"); + + /* Attach auth: Bearer token preferred, otherwise share session cookie */ + const char *bearer = get_session_access_token(); + if (bearer && bearer[0] != '\0') + { + headers = curl_slist_append(headers, bearer); + } + else + { + const char *cookie_jar = get_session_cookie_jar(); + if (cookie_jar && cookie_jar[0] != '\0') + curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar); + } + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); int result = 0; From 039a764731432d3e893074e612c50db617c4fe1f Mon Sep 17 00:00:00 2001 From: damachine Date: Mon, 9 Mar 2026 17:56:59 +0100 Subject: [PATCH 2/5] sync openapi spec --- openapi-spec.json | 4600 +-------------------------------------------- 1 file changed, 1 insertion(+), 4599 deletions(-) diff --git a/openapi-spec.json b/openapi-spec.json index daac9c4..194fe90 100644 --- a/openapi-spec.json +++ b/openapi-spec.json @@ -1,4599 +1 @@ -{ - "openapi": "3.1.0", - "info": { - "title": "CoolerControl Daemon API", - "summary": "CoolerControl Rest Endpoints", - "description": "Basic OpenAPI documentation for the CoolerControl Daemon API", - "contact": { - "name": "CoolerControl", - "url": "https://coolercontrol.org" - }, - "license": { - "name": "GPL3+", - "identifier": "GPL3+" - }, - "version": "2.2.1" - }, - "paths": { - "/handshake": { - "get": { - "tags": [ - "base" - ], - "summary": "Handshake", - "description": "A simple endpoint to verify the connection", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - - } - } - } - } - } - } - }, - "/health": { - "get": { - "tags": [ - "base" - ], - "summary": "Health Check", - "description": "Returns a Health Check Status.", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HealthCheck" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/logs": { - "get": { - "tags": [ - "base" - ], - "summary": "Daemon Logs", - "description": "This returns all recent main daemon logs as raw text", - "responses": { - "200": { - "description": "plain text", - "content": { - "text/plain; charset=utf-8": { - - } - } - } - } - } - }, - "/acknowledge": { - "post": { - "tags": [ - "base" - ], - "summary": "Acknowledge Log Issues", - "description": "This acknowledges existing log warnings and errors, and sets a timestamp of when this occurred", - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/shutdown": { - "post": { - "tags": [ - "base" - ], - "summary": "Shutdown Daemon", - "description": "Sends a cancellation signal to shut the daemon down. When the daemon is running as a systemd or initrc service, it is automatically restarted.", - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/login": { - "post": { - "tags": [ - "auth" - ], - "summary": "Login", - "description": "The endpoint used to create a login session.", - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "BasicAuth": [] - } - ] - } - }, - "/verify-session": { - "post": { - "tags": [ - "auth" - ], - "summary": "Verify Session Auth", - "description": "Verifies that the current session is still authenticated", - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/set-passwd": { - "post": { - "tags": [ - "auth" - ], - "summary": "Set Admin Password", - "description": "Stores a new Admin password.", - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - }, - { - "BasicAuth": [] - } - ] - } - }, - "/logout": { - "post": { - "tags": [ - "auth" - ], - "summary": "Logout", - "description": "Logout and invalidate the current session.", - "responses": { - "200": { - "description": "no content" - } - } - } - }, - "/thinkpad-fan-control": { - "put": { - "tags": [ - "device" - ], - "summary": "ThinkPad Fan Control", - "description": "Enables/Disabled Fan Control for ThinkPads, if acpi driver is present.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ThinkPadFanControlRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/devices": { - "get": { - "tags": [ - "device" - ], - "summary": "All Devices", - "description": "Returns a list of all detected devices and their associated information.", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DevicesResponse" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/devices/{device_uid}/settings": { - "get": { - "tags": [ - "device" - ], - "summary": "All Device Settings", - "description": "Returns all the currently applied settings for the given device. It returns the Config Settings model, which includes all possibilities for each channel.", - "parameters": [ - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SettingsResponse" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/devices/{device_uid}/settings/{channel_name}/manual": { - "put": { - "tags": [ - "device" - ], - "summary": "Device Channel Manual", - "description": "Applies a fan duty to a specific device channel.", - "parameters": [ - { - "in": "path", - "name": "channel_name", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - }, - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SettingManualRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/devices/{device_uid}/settings/{channel_name}/profile": { - "put": { - "tags": [ - "device" - ], - "summary": "Device Channel Profile", - "description": "Applies a Profile to a specific device channel.", - "parameters": [ - { - "in": "path", - "name": "channel_name", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - }, - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SettingProfileUID" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/devices/{device_uid}/settings/{channel_name}/lcd": { - "put": { - "tags": [ - "device" - ], - "summary": "Device Channel LCD", - "description": "Applies LCD Settings to a specific device channel.", - "parameters": [ - { - "in": "path", - "name": "channel_name", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - }, - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LcdSettings" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/devices/{device_uid}/settings/{channel_name}/lcd/images": { - "get": { - "tags": [ - "device" - ], - "summary": "Retrieve Device Channel LCD", - "description": "Retrieves the currently applied LCD Image file.", - "parameters": [ - { - "in": "path", - "name": "channel_name", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - }, - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - }, - "put": { - "tags": [ - "device" - ], - "summary": "Update Device Channel LCD Settings", - "description": "Used to apply LCD settings that contain images.", - "parameters": [ - { - "in": "path", - "name": "channel_name", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - }, - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - }, - "post": { - "tags": [ - "device" - ], - "summary": "Process Device Channel LCD Image", - "description": "This takes and image file and processes it for optimal use by the specified device channel. This is useful for a UI Preview and is used internally before applying the image to the device.", - "parameters": [ - { - "in": "path", - "name": "channel_name", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - }, - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/devices/{device_uid}/settings/{channel_name}/lighting": { - "put": { - "tags": [ - "device" - ], - "summary": "Device Channel Lighting", - "description": "Applies Lighting Settings (RGB) to a specific device channel.", - "parameters": [ - { - "in": "path", - "name": "channel_name", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - }, - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LightingSettings" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/devices/{device_uid}/settings/{channel_name}/pwm": { - "put": { - "tags": [ - "device" - ], - "summary": "Device Channel PWM Mode", - "description": "Applies PWM Mode to a specific device channel.", - "parameters": [ - { - "in": "path", - "name": "channel_name", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - }, - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SettingPWMMode" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/devices/{device_uid}/settings/{channel_name}/reset": { - "put": { - "tags": [ - "device" - ], - "summary": "Device Channel Reset", - "description": "Resents the specific device channel settings to not-set/device default.", - "parameters": [ - { - "in": "path", - "name": "channel_name", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - }, - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/devices/{device_uid}/asetek690": { - "patch": { - "tags": [ - "device" - ], - "summary": "Device AseTek690", - "description": "Set the driver type for liquidctl AseTek cooler. This is needed to set Legacy690Lc or Modern690Lc device driver type.", - "parameters": [ - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AseTek690Request" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/status": { - "post": { - "tags": [ - "status" - ], - "summary": "Retrieve Status", - "description": "Returns the status of all devices with the selected filters from the request body", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/StatusRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/StatusResponse" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/profiles": { - "get": { - "tags": [ - "profile" - ], - "summary": "Retrieve Profile List", - "description": "Returns a list of all the persisted Profiles.", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProfilesDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - }, - "put": { - "tags": [ - "profile" - ], - "summary": "Update Profile", - "description": "Updates the Profile with the given properties. Dependent on the Profile UID.", - "requestBody": { - "description": "Profile Settings", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Profile" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - }, - "post": { - "tags": [ - "profile" - ], - "summary": "Create Profile", - "description": "Creates the given Profile", - "requestBody": { - "description": "Profile Settings", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Profile" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/profiles/{profile_uid}": { - "delete": { - "tags": [ - "profile" - ], - "summary": "Delete Profile", - "description": "Deletes the Profile with the given Profile UID", - "parameters": [ - { - "in": "path", - "name": "profile_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/profiles/order": { - "post": { - "tags": [ - "profile" - ], - "summary": "Save Profile Order", - "description": "Saves the order of Profiles as given.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProfilesDto" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/functions": { - "get": { - "tags": [ - "function" - ], - "summary": "Retrieve Function List", - "description": "Returns a list of all the persisted Functions.", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FunctionsDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - }, - "put": { - "tags": [ - "function" - ], - "summary": "Update Function", - "description": "Updates the Function with the given properties. Dependent on the Function UID.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Function" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - }, - "post": { - "tags": [ - "function" - ], - "summary": "Create Function", - "description": "Creates the given Function", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Function" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/functions/{function_uid}": { - "delete": { - "tags": [ - "function" - ], - "summary": "Delete Function", - "description": "Deletes the Function with the given Function UID", - "parameters": [ - { - "in": "path", - "name": "function_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/functions/order": { - "post": { - "tags": [ - "function" - ], - "summary": "Save Function Order", - "description": "Saves the order of the Functions as given.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FunctionsDto" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/custom-sensors": { - "get": { - "tags": [ - "custom-sensor" - ], - "summary": "Retrieve Custom Sensor List", - "description": "Returns a list of all the persisted Custom Sensors.", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomSensorsDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - }, - "put": { - "tags": [ - "custom-sensor" - ], - "summary": "Update Custom Sensor", - "description": "Updates the Custom Sensor with the given properties. Dependent on the Custom Sensor ID.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomSensor" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - }, - "post": { - "tags": [ - "custom-sensor" - ], - "summary": "Create Custom Sensor", - "description": "Creates the given Custom Sensor", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomSensor" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/custom-sensors/{custom_sensor_id}": { - "get": { - "tags": [ - "custom-sensor" - ], - "summary": "Retrieve Custom Sensor", - "description": "Retrieves the Custom Sensor with the given Custom Sensor ID", - "parameters": [ - { - "in": "path", - "name": "custom_sensor_id", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomSensor" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - }, - "delete": { - "tags": [ - "custom-sensor" - ], - "summary": "Delete Custom Sensor", - "description": "Deletes the Custom Sensor with the given Custom Sensor UID", - "parameters": [ - { - "in": "path", - "name": "custom_sensor_id", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/custom-sensors/order": { - "post": { - "tags": [ - "custom-sensor" - ], - "summary": "Save Custom Sensor Order", - "description": "Saves the order of the Custom Sensors as given.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CustomSensorsDto" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/modes": { - "get": { - "tags": [ - "mode" - ], - "summary": "Retrieve Mode List", - "description": "Returns a list of all the persisted Modes.", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ModesDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - }, - "put": { - "tags": [ - "mode" - ], - "summary": "Update Mode", - "description": "Updates the Mode with the given properties.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateModeDto" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - }, - "post": { - "tags": [ - "mode" - ], - "summary": "Create Mode", - "description": "Creates a Mode with the given name, based on the currently applied settings.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateModeDto" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ModeDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/modes/{mode_uid}": { - "get": { - "tags": [ - "mode" - ], - "summary": "Retrieve Mode", - "description": "Retrieves the Mode with the given Mode UID", - "parameters": [ - { - "in": "path", - "name": "mode_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ModeDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - }, - "delete": { - "tags": [ - "mode" - ], - "summary": "Delete Mode", - "description": "Deletes the Mode with the given Mode UID", - "parameters": [ - { - "in": "path", - "name": "mode_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/modes/{mode_uid}/duplicate": { - "post": { - "tags": [ - "mode" - ], - "summary": "Duplicate Mode", - "description": "Duplicates the Mode and it's settings from the given Mode UID and returns the new Mode.", - "parameters": [ - { - "in": "path", - "name": "mode_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ModeDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/modes/{mode_uid}/settings": { - "put": { - "tags": [ - "mode" - ], - "summary": "Update Mode Device Settings", - "description": "Updates the Mode with the given Mode UID device settings to what is currently applied, and returns the Mode with it's new settings.", - "parameters": [ - { - "in": "path", - "name": "mode_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ModeDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/modes-active": { - "get": { - "tags": [ - "mode" - ], - "summary": "Retrieve Active Modes", - "description": "Returns the active and previously active Mode UIDs.", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ActiveModesDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/modes-active/{mode_uid}": { - "post": { - "tags": [ - "mode" - ], - "summary": "Activate Mode", - "description": "Activates the Mode with the given Mode UID. This applies all of this Mode's device settings.", - "parameters": [ - { - "in": "path", - "name": "mode_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/modes/order": { - "post": { - "tags": [ - "mode" - ], - "summary": "Save Mode Order", - "description": "Saves the order of the Modes as given.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ModeOrderDto" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/settings": { - "get": { - "tags": [ - "setting" - ], - "summary": "CoolerControl Settings", - "description": "Returns the current CoolerControl settings.", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CoolerControlSettingsDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - }, - "patch": { - "tags": [ - "setting" - ], - "summary": "Update CoolerControl Settings", - "description": "Applies only the given properties.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CoolerControlSettingsDto" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/settings/devices": { - "get": { - "tags": [ - "setting" - ], - "summary": "CoolerControl All Device Settings", - "description": "Returns the current CoolerControl device settings for all devices.", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CoolerControlAllDeviceSettingsDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/settings/devices/{device_uid}": { - "get": { - "tags": [ - "setting" - ], - "summary": "CoolerControl Device Settings", - "description": "Returns the current CoolerControl device settings for the given device UID.", - "parameters": [ - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CoolerControlDeviceSettingsDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - }, - "put": { - "tags": [ - "setting" - ], - "summary": "Update CoolerControl Device Settings", - "description": "Updates the CoolerControl device settings for the given device UID.", - "parameters": [ - { - "in": "path", - "name": "device_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "requestBody": { - "description": "General Device Settings for `CoolerControl`", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CoolerControlDeviceSettings" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/settings/ui": { - "get": { - "tags": [ - "setting" - ], - "summary": "CoolerControl UI Settings", - "description": "Returns the current CoolerControl UI Settings.", - "responses": { - "200": { - "description": "plain text", - "content": { - "text/plain; charset=utf-8": { - - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - }, - "put": { - "tags": [ - "setting" - ], - "summary": "Update CoolerControl UI Settings", - "description": "Updates and persists the CoolerControl UI settings.", - "requestBody": { - "content": { - "text/plain; charset=utf-8": { - - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - } - }, - "/alerts": { - "get": { - "tags": [ - "alert" - ], - "summary": "Retrieve Alert List", - "description": "Returns a list of all the persisted Alerts.", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AlertsDto" - } - } - } - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - } - }, - "put": { - "tags": [ - "alert" - ], - "summary": "Update Alert", - "description": "Updates the Alert with the given properties. Dependent on the Alert UID.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AlertDto" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - }, - "post": { - "tags": [ - "alert" - ], - "summary": "Create Alert", - "description": "Creates the given Alert", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AlertDto" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/alerts/{alert_uid}": { - "delete": { - "tags": [ - "alert" - ], - "summary": "Delete Alert", - "description": "Deletes the Alert with the given Alert UID", - "parameters": [ - { - "in": "path", - "name": "alert_uid", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "description": "no content" - }, - "400": { - "description": "Bad Request. The request is invalid" - }, - "401": { - "description": "Unauthorized. Invalid credentials were provided." - }, - "403": { - "description": "Forbidden. Insufficient permissions." - }, - "404": { - "description": "Whatever you're looking for, it's not here." - }, - "500": { - "description": "An internal error has occurred." - }, - "502": { - "description": "Bad Gateway. An error has occurred with an external library." - } - }, - "security": [ - { - "CookieAuth": [] - } - ] - } - }, - "/sse/logs": { - "get": { - "tags": [ - "sse" - ], - "summary": "Log Server Sent Events", - "description": "Subscribes and returns the Server Sent Events for a Log stream" - } - }, - "/sse/status": { - "get": { - "tags": [ - "sse" - ], - "summary": "Recent Status Server Sent Events", - "description": "Subscribes and returns the Server Sent Events for a Status stream" - } - }, - "/sse/modes": { - "get": { - "tags": [ - "sse" - ], - "summary": "Activated Mode Events", - "description": "Subscribes and returns the Server Sent Events for a ModeActivated stream" - } - }, - "/sse/alerts": { - "get": { - "tags": [ - "sse" - ], - "summary": "Alert Events", - "description": "Subscribes and returns Events for when an Alert State has changed" - } - } - }, - "components": { - "securitySchemes": { - "CookieAuth": { - "type": "apiKey", - "in": "cookie", - "name": "cc", - "description": "The private session cookie used for authentication." - }, - "BasicAuth": { - "type": "http", - "scheme": "basic", - "bearerFormat": "", - "description": "HTTP Basic authentication, mostly used to generate a secure authentication cookie." - } - }, - "schemas": { - "ActiveModesDto": { - "type": "object", - "properties": { - "current_mode_uid": { - "type": [ - "string", - "null" - ] - }, - "previous_mode_uid": { - "type": [ - "string", - "null" - ] - } - } - }, - "AlertDto": { - "type": "object", - "required": [ - "channel_source", - "max", - "min", - "name", - "uid" - ], - "properties": { - "channel_source": { - "$ref": "#/components/schemas/ChannelSource" - }, - "max": { - "type": "number", - "format": "double" - }, - "min": { - "type": "number", - "format": "double" - }, - "name": { - "type": "string" - }, - "state": { - "anyOf": [ - { - "$ref": "#/components/schemas/AlertState" - }, - { - "type": "null" - } - ] - }, - "uid": { - "type": "string" - } - } - }, - "AlertLog": { - "type": "object", - "required": [ - "message", - "name", - "state", - "timestamp", - "uid" - ], - "properties": { - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "state": { - "$ref": "#/components/schemas/AlertState" - }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "uid": { - "type": "string" - } - } - }, - "AlertPath": { - "type": "object", - "required": [ - "alert_uid" - ], - "properties": { - "alert_uid": { - "type": "string" - } - } - }, - "AlertState": { - "type": "string", - "enum": [ - "Active", - "Inactive" - ] - }, - "AlertsDto": { - "type": "object", - "required": [ - "alerts", - "logs" - ], - "properties": { - "alerts": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AlertDto" - } - }, - "logs": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AlertLog" - } - } - } - }, - "AseTek690Request": { - "type": "object", - "required": [ - "is_legacy690" - ], - "properties": { - "is_legacy690": { - "type": "boolean" - } - } - }, - "BaseDriver": { - "type": "string", - "enum": [ - "Aquacomputer", - "Legacy690Lc", - "Modern690Lc", - "Hydro690Lc", - "HydroPro", - "AsusRyujin", - "AuraLed", - "CommanderCore", - "CommanderPro", - "Coolit", - "CorsairHidPsu", - "Ddr4Temperature", - "VengeanceRgb", - "HydroPlatinum", - "Kraken2", - "KrakenX3", - "KrakenZ3", - "MockKrakenZ3", - "MpgCooler", - "EvgaPascal", - "RogTuring", - "NzxtEPsu", - "RgbFusion2", - "SmartDevice", - "SmartDevice2", - "H1V2", - "MsiAcpiEc", - "NotSupported" - ] - }, - "CSPath": { - "type": "object", - "required": [ - "custom_sensor_id" - ], - "properties": { - "custom_sensor_id": { - "type": "string" - } - } - }, - "ChannelInfo": { - "type": "object", - "required": [ - "lcd_modes", - "lighting_modes" - ], - "properties": { - "label": { - "type": [ - "string", - "null" - ] - }, - "lcd_info": { - "anyOf": [ - { - "$ref": "#/components/schemas/LcdInfo" - }, - { - "type": "null" - } - ] - }, - "lcd_modes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LcdMode" - } - }, - "lighting_modes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LightingMode" - } - }, - "speed_options": { - "anyOf": [ - { - "$ref": "#/components/schemas/SpeedOptions" - }, - { - "type": "null" - } - ] - } - } - }, - "ChannelMetric": { - "type": "string", - "enum": [ - "Temp", - "Duty", - "Load", - "RPM", - "Freq" - ] - }, - "ChannelSource": { - "description": "A source for displaying sensor data that is related to a particular channel. This is like `TempSource` but not limited to temperature sensors. (Load, Duty, etc.)", - "type": "object", - "required": [ - "channel_metric", - "channel_name", - "device_uid" - ], - "properties": { - "channel_metric": { - "$ref": "#/components/schemas/ChannelMetric" - }, - "channel_name": { - "description": "The internal name for this channel source. NOT the Label.", - "type": "string" - }, - "device_uid": { - "description": "The associated device uid containing current values", - "type": "string" - } - } - }, - "ChannelStatus": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "duty": { - "type": [ - "number", - "null" - ], - "format": "double" - }, - "freq": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0 - }, - "name": { - "type": "string" - }, - "pwm_mode": { - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0 - }, - "rpm": { - "type": [ - "integer", - "null" - ], - "format": "uint32", - "minimum": 0 - }, - "watts": { - "type": [ - "number", - "null" - ], - "format": "double" - } - } - }, - "CoolerControlAllDeviceSettingsDto": { - "type": "object", - "required": [ - "devices" - ], - "properties": { - "devices": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CoolerControlDeviceSettingsDto" - } - } - } - }, - "CoolerControlDeviceSettings": { - "description": "General Device Settings for `CoolerControl`", - "type": "object", - "required": [ - "disable", - "disable_channels", - "name" - ], - "properties": { - "disable": { - "description": "All communication with this device will be avoided if disabled", - "type": "boolean" - }, - "disable_channels": { - "description": "A list of channels to disable communication with.", - "type": "array", - "items": { - "type": "string" - } - }, - "name": { - "description": "The device name for this setting. Helpful after blacklisting(disabling) devices.", - "type": "string" - } - } - }, - "CoolerControlDeviceSettingsDto": { - "type": "object", - "required": [ - "disable", - "disable_channels", - "name", - "uid" - ], - "properties": { - "disable": { - "type": "boolean" - }, - "disable_channels": { - "type": "array", - "items": { - "type": "string" - } - }, - "name": { - "type": "string" - }, - "uid": { - "type": "string" - } - } - }, - "CoolerControlSettingsDto": { - "type": "object", - "properties": { - "apply_on_boot": { - "type": [ - "boolean", - "null" - ] - }, - "compress": { - "type": [ - "boolean", - "null" - ] - }, - "drivetemp_suspend": { - "type": [ - "boolean", - "null" - ] - }, - "hide_duplicate_devices": { - "type": [ - "boolean", - "null" - ] - }, - "liquidctl_integration": { - "type": [ - "boolean", - "null" - ] - }, - "no_init": { - "type": [ - "boolean", - "null" - ] - }, - "poll_rate": { - "type": [ - "number", - "null" - ], - "format": "double" - }, - "startup_delay": { - "type": [ - "integer", - "null" - ], - "format": "uint16", - "minimum": 0 - }, - "thinkpad_full_speed": { - "type": [ - "boolean", - "null" - ] - } - } - }, - "CreateModeDto": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "name": { - "type": "string" - } - } - }, - "CustomSensor": { - "type": "object", - "required": [ - "cs_type", - "id", - "mix_function", - "sources" - ], - "properties": { - "cs_type": { - "$ref": "#/components/schemas/CustomSensorType" - }, - "file_path": { - "type": [ - "string", - "null" - ] - }, - "id": { - "description": "ID MUST be unique, as `temp_name` must be unique.", - "type": "string" - }, - "mix_function": { - "$ref": "#/components/schemas/CustomSensorMixFunctionType" - }, - "sources": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CustomTempSourceData" - } - } - } - }, - "CustomSensorMixFunctionType": { - "type": "string", - "enum": [ - "Min", - "Max", - "Delta", - "Avg", - "WeightedAvg" - ] - }, - "CustomSensorType": { - "type": "string", - "enum": [ - "Mix", - "File" - ] - }, - "CustomSensorsDto": { - "type": "object", - "required": [ - "custom_sensors" - ], - "properties": { - "custom_sensors": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CustomSensor" - } - } - } - }, - "CustomTempSourceData": { - "type": "object", - "required": [ - "temp_source", - "weight" - ], - "properties": { - "temp_source": { - "$ref": "#/components/schemas/TempSource" - }, - "weight": { - "type": "integer", - "format": "uint8", - "minimum": 0 - } - } - }, - "DeviceChannelPath": { - "type": "object", - "required": [ - "channel_name", - "device_uid" - ], - "properties": { - "channel_name": { - "type": "string" - }, - "device_uid": { - "type": "string" - } - } - }, - "DeviceDto": { - "type": "object", - "required": [ - "d_type", - "info", - "name", - "type_index", - "uid" - ], - "properties": { - "d_type": { - "$ref": "#/components/schemas/DeviceType" - }, - "info": { - "$ref": "#/components/schemas/DeviceInfo" - }, - "lc_info": { - "anyOf": [ - { - "$ref": "#/components/schemas/LcInfo" - }, - { - "type": "null" - } - ] - }, - "name": { - "type": "string" - }, - "type_index": { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "uid": { - "type": "string" - } - } - }, - "DeviceInfo": { - "description": "Needed Device info per device", - "type": "object", - "required": [ - "channels", - "driver_info", - "lighting_speeds", - "profile_max_length", - "profile_min_length", - "temp_max", - "temp_min", - "temps" - ], - "properties": { - "channels": { - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/ChannelInfo" - } - }, - "driver_info": { - "$ref": "#/components/schemas/DriverInfo" - }, - "lighting_speeds": { - "type": "array", - "items": { - "type": "string" - } - }, - "model": { - "type": [ - "string", - "null" - ] - }, - "profile_max_length": { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "profile_min_length": { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "temp_max": { - "description": "The maximum temp to use for Profiles for this device", - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "temp_min": { - "description": "The minimum temp to use for Profiles for this device", - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "temps": { - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/TempInfo" - } - }, - "thinkpad_fan_control": { - "description": "When present, then this is a `ThinkPad` device. True or False indicates whether Fan control is enabled for the kernel module and changing values is possible", - "type": [ - "boolean", - "null" - ] - } - } - }, - "DevicePath": { - "type": "object", - "required": [ - "device_uid" - ], - "properties": { - "device_uid": { - "type": "string" - } - } - }, - "DeviceStatusDto": { - "type": "object", - "required": [ - "d_type", - "status_history", - "type_index", - "uid" - ], - "properties": { - "d_type": { - "$ref": "#/components/schemas/DeviceType" - }, - "status_history": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Status" - } - }, - "type_index": { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "uid": { - "type": "string" - } - } - }, - "DeviceType": { - "type": "string", - "enum": [ - "CPU", - "GPU", - "Liquidctl", - "Hwmon", - "CustomSensors" - ] - }, - "DevicesResponse": { - "type": "object", - "required": [ - "devices" - ], - "properties": { - "devices": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DeviceDto" - } - } - } - }, - "DriverInfo": { - "description": "Device Driver Information", - "type": "object", - "required": [ - "drv_type", - "locations" - ], - "properties": { - "drv_type": { - "$ref": "#/components/schemas/DriverType" - }, - "locations": { - "description": "If available various paths used to access the device. This can include paths like the kernel device path, hwmon path, HID path, or PCI Bus ID", - "type": "array", - "items": { - "type": "string" - } - }, - "name": { - "description": "If available the kernel driver name or liquidctl driver class.", - "type": [ - "string", - "null" - ] - }, - "version": { - "description": "If available the driver's version. For kernel-based drivers this is the current kernel version. For liquidctl-based drivers this is the liquidctl version. For Nvidia-based drivers this is the version of the installed nvidia proprietary drivers.", - "type": [ - "string", - "null" - ] - } - } - }, - "DriverType": { - "description": "The Driver Type, or source of the driver actively being used for this device.", - "type": "string", - "enum": [ - "Kernel", - "Liquidctl", - "NVML", - "NvidiaCLI", - "CoolerControl" - ] - }, - "Function": { - "type": "object", - "required": [ - "duty_maximum", - "duty_minimum", - "f_type", - "name", - "uid" - ], - "properties": { - "deviance": { - "description": "The temperature deviance threshold in degrees", - "type": [ - "number", - "null" - ], - "format": "double" - }, - "duty_maximum": { - "description": "The maximum duty change to apply", - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "duty_minimum": { - "description": "The minimum duty change to apply", - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "f_type": { - "description": "The type of this function", - "$ref": "#/components/schemas/FunctionType" - }, - "name": { - "description": "The user given name for this function", - "type": "string" - }, - "only_downward": { - "description": "Whether to apply settings only on the way down", - "type": [ - "boolean", - "null" - ] - }, - "response_delay": { - "description": "The response delay in seconds", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0 - }, - "sample_window": { - "description": "The sample window this function should use, particularly applicable to moving averages", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0 - }, - "uid": { - "description": "The Unique identifier for this function", - "type": "string" - } - } - }, - "FunctionPath": { - "type": "object", - "required": [ - "function_uid" - ], - "properties": { - "function_uid": { - "type": "string" - } - } - }, - "FunctionType": { - "type": "string", - "enum": [ - "Identity", - "Standard", - "ExponentialMovingAvg" - ] - }, - "FunctionsDto": { - "type": "object", - "required": [ - "functions" - ], - "properties": { - "functions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Function" - } - } - } - }, - "HealthCheck": { - "type": "object", - "required": [ - "current_timestamp", - "description", - "details", - "links", - "status", - "system" - ], - "properties": { - "current_timestamp": { - "type": "string", - "format": "date-time" - }, - "description": { - "type": "string" - }, - "details": { - "$ref": "#/components/schemas/HealthDetails" - }, - "links": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "status": { - "type": "string" - }, - "system": { - "$ref": "#/components/schemas/SystemDetails" - } - } - }, - "HealthDetails": { - "type": "object", - "required": [ - "errors", - "liquidctl_connected", - "memory_mb", - "pid", - "uptime", - "version", - "warnings" - ], - "properties": { - "errors": { - "type": "integer", - "format": "uint", - "minimum": 0 - }, - "liquidctl_connected": { - "type": "boolean" - }, - "memory_mb": { - "type": "number", - "format": "double" - }, - "pid": { - "type": "integer", - "format": "uint32", - "minimum": 0 - }, - "uptime": { - "type": "string" - }, - "version": { - "type": "string" - }, - "warnings": { - "type": "integer", - "format": "uint", - "minimum": 0 - } - } - }, - "LcInfo": { - "description": "Specific Liquidctl device information", - "type": "object", - "required": [ - "driver_type", - "unknown_asetek" - ], - "properties": { - "driver_type": { - "description": "An Enum representation of the various Liquidctl driver classes", - "$ref": "#/components/schemas/BaseDriver" - }, - "firmware_version": { - "description": "The detected firmware version at initialization", - "type": [ - "string", - "null" - ] - }, - "unknown_asetek": { - "description": "An indicator for needed user input to determine actual asetek690lc device", - "type": "boolean" - } - } - }, - "LcdCarouselSettings": { - "description": "Settings for the LCD Carousel.\n\nThis can be used to have a carousel of images (static or gif), of sensor data, or a combination of both.", - "type": "object", - "required": [ - "interval" - ], - "properties": { - "images_path": { - "description": "The absolute path directory location for images for the carousel. All applicable images present are processed when the setting is applied.", - "type": [ - "string", - "null" - ] - }, - "interval": { - "description": "The interval in seconds (2-900) in which to change images in the carousel.", - "type": "integer", - "format": "uint64", - "minimum": 0 - } - } - }, - "LcdInfo": { - "description": "Specific LCD Screen info", - "type": "object", - "required": [ - "max_image_size_bytes", - "screen_height", - "screen_width" - ], - "properties": { - "max_image_size_bytes": { - "type": "integer", - "format": "uint32", - "minimum": 0 - }, - "screen_height": { - "type": "integer", - "format": "uint32", - "minimum": 0 - }, - "screen_width": { - "type": "integer", - "format": "uint32", - "minimum": 0 - } - } - }, - "LcdMode": { - "type": "object", - "required": [ - "brightness", - "colors_max", - "colors_min", - "frontend_name", - "image", - "name", - "orientation", - "type_" - ], - "properties": { - "brightness": { - "type": "boolean" - }, - "colors_max": { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "colors_min": { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "frontend_name": { - "type": "string" - }, - "image": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "orientation": { - "type": "boolean" - }, - "type_": { - "$ref": "#/components/schemas/LcdModeType" - } - } - }, - "LcdModeType": { - "type": "string", - "enum": [ - "None", - "Liquidctl", - "Custom" - ] - }, - "LcdSettings": { - "type": "object", - "required": [ - "colors", - "mode" - ], - "properties": { - "brightness": { - "description": "The LCD brightness (0-100%)", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0 - }, - "carousel": { - "anyOf": [ - { - "$ref": "#/components/schemas/LcdCarouselSettings" - }, - { - "type": "null" - } - ] - }, - "colors": { - "description": "a list of RGB tuple values, eg [(20,20,120), (0,0,255)]", - "type": "array", - "items": { - "type": "array", - "items": [ - { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - { - "type": "integer", - "format": "uint8", - "minimum": 0 - } - ], - "maxItems": 3, - "minItems": 3 - } - }, - "image_file_processed": { - "description": "The LCD Image processed file path location, where the preprocessed image is located.", - "type": [ - "string", - "null" - ] - }, - "mode": { - "description": "The Lcd mode name", - "type": "string" - }, - "orientation": { - "description": "The LCD Image orientation (0,90,180,270)", - "type": [ - "integer", - "null" - ], - "format": "uint16", - "minimum": 0 - }, - "temp_source": { - "description": "A temp source for displaying a temperature.", - "anyOf": [ - { - "$ref": "#/components/schemas/TempSource" - }, - { - "type": "null" - } - ] - } - } - }, - "LightingMode": { - "type": "object", - "required": [ - "backward_enabled", - "frontend_name", - "max_colors", - "min_colors", - "name", - "speed_enabled", - "type_" - ], - "properties": { - "backward_enabled": { - "type": "boolean" - }, - "frontend_name": { - "type": "string" - }, - "max_colors": { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "min_colors": { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "name": { - "type": "string" - }, - "speed_enabled": { - "type": "boolean" - }, - "type_": { - "$ref": "#/components/schemas/LightingModeType" - } - } - }, - "LightingModeType": { - "type": "string", - "enum": [ - "None", - "Liquidctl", - "Custom" - ] - }, - "LightingSettings": { - "type": "object", - "required": [ - "colors", - "mode" - ], - "properties": { - "backward": { - "description": "run backwards or not", - "type": [ - "boolean", - "null" - ] - }, - "colors": { - "description": "a list of RGB tuple values, eg [(20,20,120), (0,0,255)]", - "type": "array", - "items": { - "type": "array", - "items": [ - { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - { - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - { - "type": "integer", - "format": "uint8", - "minimum": 0 - } - ], - "maxItems": 3, - "minItems": 3 - } - }, - "mode": { - "description": "The lighting mode name", - "type": "string" - }, - "speed": { - "description": "The speed to set", - "type": [ - "string", - "null" - ] - } - } - }, - "ModeDto": { - "type": "object", - "required": [ - "device_settings", - "name", - "uid" - ], - "properties": { - "device_settings": { - "type": "array", - "items": { - "type": "array", - "items": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "$ref": "#/components/schemas/Setting" - } - } - ], - "maxItems": 2, - "minItems": 2 - } - }, - "name": { - "type": "string" - }, - "uid": { - "type": "string" - } - } - }, - "ModeOrderDto": { - "type": "object", - "required": [ - "mode_uids" - ], - "properties": { - "mode_uids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "ModePath": { - "type": "object", - "required": [ - "mode_uid" - ], - "properties": { - "mode_uid": { - "type": "string" - } - } - }, - "ModesDto": { - "type": "object", - "required": [ - "modes" - ], - "properties": { - "modes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ModeDto" - } - } - } - }, - "Profile": { - "description": "Profile Settings", - "type": "object", - "required": [ - "function_uid", - "member_profile_uids", - "name", - "p_type", - "uid" - ], - "properties": { - "function_uid": { - "description": "The function uid to apply to this profile", - "type": "string" - }, - "member_profile_uids": { - "description": "The profiles that make up the mix profile", - "type": "array", - "items": { - "type": "string" - } - }, - "mix_function_type": { - "description": "The function to mix the members with if this is a Mix Profile", - "anyOf": [ - { - "$ref": "#/components/schemas/ProfileMixFunctionType" - }, - { - "type": "null" - } - ] - }, - "name": { - "description": "The User given name for this Profile", - "type": "string" - }, - "p_type": { - "description": "The profile type", - "$ref": "#/components/schemas/ProfileType" - }, - "speed_fixed": { - "description": "The fixed duty speed to set. eg: 20 (%)", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0 - }, - "speed_profile": { - "description": "The profile temp/duty speeds to set. eg: [(20.0, 50), (25.7, 80)]", - "type": [ - "array", - "null" - ], - "items": { - "type": "array", - "items": [ - { - "type": "number", - "format": "double" - }, - { - "type": "integer", - "format": "uint8", - "minimum": 0 - } - ], - "maxItems": 2, - "minItems": 2 - } - }, - "temp_source": { - "description": "The associated temperature source", - "anyOf": [ - { - "$ref": "#/components/schemas/TempSource" - }, - { - "type": "null" - } - ] - }, - "uid": { - "description": "The Unique Identifier for this Profile", - "type": "string" - } - } - }, - "ProfileMixFunctionType": { - "type": "string", - "enum": [ - "Min", - "Max", - "Avg" - ] - }, - "ProfilePath": { - "type": "object", - "required": [ - "profile_uid" - ], - "properties": { - "profile_uid": { - "type": "string" - } - } - }, - "ProfileType": { - "type": "string", - "enum": [ - "Default", - "Fixed", - "Graph", - "Mix" - ] - }, - "ProfilesDto": { - "type": "object", - "required": [ - "profiles" - ], - "properties": { - "profiles": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Profile" - } - } - } - }, - "Setting": { - "description": "Setting is a passed struct used to store applied Settings to a device channel Usually only one specific lighting or speed setting is applied at a time.", - "type": "object", - "required": [ - "channel_name" - ], - "properties": { - "channel_name": { - "type": "string" - }, - "lcd": { - "description": "Settings for LCD screens", - "anyOf": [ - { - "$ref": "#/components/schemas/LcdSettings" - }, - { - "type": "null" - } - ] - }, - "lighting": { - "description": "Settings for lighting", - "anyOf": [ - { - "$ref": "#/components/schemas/LightingSettings" - }, - { - "type": "null" - } - ] - }, - "profile_uid": { - "description": "The Profile UID that applies to this device channel", - "type": [ - "string", - "null" - ] - }, - "pwm_mode": { - "description": "the current `pwm_mode` to set for hwmon devices, eg: 1", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0 - }, - "reset_to_default": { - "description": "Used to set hwmon & nvidia channels back to their default 'automatic' values.", - "type": [ - "boolean", - "null" - ] - }, - "speed_fixed": { - "description": "The fixed duty speed to set. eg: 20 (%)", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0 - } - } - }, - "SettingManualRequest": { - "type": "object", - "required": [ - "speed_fixed" - ], - "properties": { - "speed_fixed": { - "type": "integer", - "format": "uint8", - "minimum": 0 - } - } - }, - "SettingPWMMode": { - "type": "object", - "required": [ - "pwm_mode" - ], - "properties": { - "pwm_mode": { - "type": "integer", - "format": "uint8", - "minimum": 0 - } - } - }, - "SettingProfileUID": { - "type": "object", - "required": [ - "profile_uid" - ], - "properties": { - "profile_uid": { - "type": "string" - } - } - }, - "SettingsResponse": { - "type": "object", - "required": [ - "settings" - ], - "properties": { - "settings": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Setting" - } - } - } - }, - "SpeedOptions": { - "type": "object", - "required": [ - "fixed_enabled", - "manual_profiles_enabled", - "max_duty", - "min_duty", - "profiles_enabled" - ], - "properties": { - "fixed_enabled": { - "description": "If this is false, it means the fans are not controllable, but viewable.", - "type": "boolean" - }, - "manual_profiles_enabled": { - "description": "This enables software-profiles for device-internal temperatures External temperatures must always be software-profiles and are not handled by this property", - "type": "boolean" - }, - "max_duty": { - "description": "The maximum fan duty for this speed channel", - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "min_duty": { - "description": "The minimum fan duty for this speed channel", - "type": "integer", - "format": "uint8", - "minimum": 0 - }, - "profiles_enabled": { - "description": "If (temp, duty) profiles are supported by the device natively or not (device-internal temps)", - "type": "boolean" - } - } - }, - "Status": { - "description": "A Model which contains various applicable device statuses", - "type": "object", - "required": [ - "channels", - "temps", - "timestamp" - ], - "properties": { - "channels": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ChannelStatus" - } - }, - "temps": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TempStatus" - } - }, - "timestamp": { - "type": "string", - "format": "date-time" - } - } - }, - "StatusRequest": { - "type": "object", - "properties": { - "all": { - "type": [ - "boolean", - "null" - ] - }, - "since": { - "type": [ - "string", - "null" - ], - "format": "date-time" - } - } - }, - "StatusResponse": { - "type": "object", - "required": [ - "devices" - ], - "properties": { - "devices": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DeviceStatusDto" - } - } - } - }, - "SystemDetails": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "name": { - "type": "string" - } - } - }, - "TempInfo": { - "type": "object", - "required": [ - "label", - "number" - ], - "properties": { - "label": { - "type": "string" - }, - "number": { - "type": "integer", - "format": "uint8", - "minimum": 0 - } - } - }, - "TempSource": { - "type": "object", - "required": [ - "device_uid", - "temp_name" - ], - "properties": { - "device_uid": { - "description": "The associated device uid containing current temp values", - "type": "string" - }, - "temp_name": { - "description": "The internal name for this Temperature Source. NOT the `TempInfo` Label.", - "type": "string" - } - } - }, - "TempStatus": { - "type": "object", - "required": [ - "name", - "temp" - ], - "properties": { - "name": { - "type": "string" - }, - "temp": { - "type": "number", - "format": "double" - } - } - }, - "ThinkPadFanControlRequest": { - "type": "object", - "required": [ - "enable" - ], - "properties": { - "enable": { - "type": "boolean" - } - } - }, - "UpdateModeDto": { - "type": "object", - "required": [ - "name", - "uid" - ], - "properties": { - "name": { - "type": "string" - }, - "uid": { - "type": "string" - } - } - } - } - }, - "tags": [ - { - "name": "base", - "description": "Foundational endpoints for this API" - }, - { - "name": "auth", - "description": "Authentication" - }, - { - "name": "device", - "description": "Device Interaction" - }, - { - "name": "status", - "description": "Device Status" - }, - { - "name": "profile", - "description": "Profiles" - }, - { - "name": "function", - "description": "Functions" - }, - { - "name": "custom-sensor", - "description": "Custom Sensors" - }, - { - "name": "mode", - "description": "Modes" - }, - { - "name": "setting", - "description": "Settings" - }, - { - "name": "alert", - "description": "Alerts" - }, - { - "name": "sse", - "description": "Server Side Events" - } - ] -} +{"openapi":"3.1.0","info":{"title":"CoolerControl Daemon API","summary":"CoolerControl Rest Endpoints","description":"Basic OpenAPI documentation for the CoolerControl Daemon API","contact":{"name":"CoolerControl","url":"https://coolercontrol.org"},"license":{"name":"GPL3+","identifier":"GPL3+"},"version":"4.0.0"},"paths":{"/handshake":{"get":{"tags":["base"],"summary":"Handshake","description":"A simple endpoint to verify the connection"}},"/health":{"get":{"tags":["base"],"summary":"Health Check","description":"Returns a Health Check Status.","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthCheck"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/logs":{"get":{"tags":["base"],"summary":"Daemon Logs","description":"This returns all recent main daemon logs as raw text","responses":{"200":{"description":"plain text","content":{"text/plain; charset=utf-8":{}}}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/acknowledge":{"post":{"tags":["base"],"summary":"Acknowledge Log Issues","description":"This acknowledges existing log warnings and errors, and sets a timestamp of when this occurred","responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/shutdown":{"post":{"tags":["base"],"summary":"Shutdown Daemon","description":"Sends a cancellation signal to shut the daemon down. When the daemon is running as a systemd or initrc service, it is automatically restarted.","responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]}]}},"/login":{"post":{"tags":["auth"],"summary":"Login","description":"The endpoint used to create a login session.","responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"BasicAuth":[]}]}},"/verify-session":{"post":{"tags":["auth"],"summary":"Verify Session Auth","description":"Verifies that the current session is still authenticated","responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]}]}},"/set-passwd":{"post":{"tags":["auth"],"summary":"Set Admin Password","description":"Stores a new Admin password.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPasswdRequest"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[],"BasicAuth":[]}]}},"/logout":{"post":{"tags":["auth"],"summary":"Logout","description":"Logout and invalidate the current session.","responses":{"200":{"description":"no content"}},"security":[{"CookieAuth":[]}]}},"/tokens":{"get":{"tags":["auth"],"summary":"List Access Tokens","description":"Returns a list of all access tokens (without hashes).","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenListResponse"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]}]},"post":{"tags":["auth"],"summary":"Create Access Token","description":"Creates a new access token for external service authentication. The raw token is only returned once.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTokenRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTokenResponse"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]}]}},"/tokens/{token_id}":{"delete":{"tags":["auth"],"summary":"Delete Access Token","description":"Deletes the access token with the given ID.","parameters":[{"in":"path","name":"token_id","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]}]}},"/thinkpad-fan-control":{"put":{"tags":["device"],"summary":"ThinkPad Fan Control","description":"Enables/Disabled Fan Control for ThinkPads, if acpi driver is present.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThinkPadFanControlRequest"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/devices":{"get":{"tags":["device"],"summary":"All Devices","description":"Returns a list of all detected devices and their associated information.","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DevicesResponse"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/devices/{device_uid}/settings":{"get":{"tags":["device"],"summary":"All Device Settings","description":"Returns all the currently applied settings for the given device. It returns the Config Settings model, which includes all possibilities for each channel.","parameters":[{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingsResponse"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/devices/{device_uid}/settings/{channel_name}/manual":{"put":{"tags":["device"],"summary":"Device Channel Manual","description":"Applies a fan duty to a specific device channel.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingManualRequest"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/devices/{device_uid}/settings/{channel_name}/profile":{"put":{"tags":["device"],"summary":"Device Channel Profile","description":"Applies a Profile to a specific device channel.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingProfileUID"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/devices/{device_uid}/settings/{channel_name}/lcd":{"put":{"tags":["device"],"summary":"Device Channel LCD","description":"Applies LCD Settings to a specific device channel.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LcdSettings"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/devices/{device_uid}/settings/{channel_name}/lcd/images":{"get":{"tags":["device"],"summary":"Retrieve Device Channel LCD","description":"Retrieves the currently applied LCD Image file.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"put":{"tags":["device"],"summary":"Update Device Channel LCD Settings","description":"Used to apply LCD settings that contain images.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"query","name":"log","schema":{"type":["boolean","null"],"default":null},"style":"form"}],"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"post":{"tags":["device"],"summary":"Process Device Channel LCD Image","description":"This takes and image file and processes it for optimal use by the specified device channel. This is useful for a UI Preview and is used internally before applying the image to the device.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/devices/{device_uid}/settings/{channel_name}/lcd/shutdown-image":{"put":{"tags":["device"],"summary":"Set LCD Shutdown Image","description":"Upload and save an LCD image that will be applied to the device when the daemon shuts down.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"delete":{"tags":["device"],"summary":"Clear LCD Shutdown Image","description":"Remove the saved LCD shutdown image for the given device channel.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/devices/{device_uid}/settings/{channel_name}/lighting":{"put":{"tags":["device"],"summary":"Device Channel Lighting","description":"Applies Lighting Settings (RGB) to a specific device channel.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LightingSettings"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/devices/{device_uid}/settings/{channel_name}/pwm":{"put":{"tags":["device"],"summary":"DEPRECATED: Device Channel PWM Mode","description":"DEPRECATED: Applies PWM Mode to a specific device channel.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingPWMMode"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/devices/{device_uid}/settings/{channel_name}/reset":{"put":{"tags":["device"],"summary":"Device Channel Reset","description":"Resents the specific device channel settings to not-set/device default.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/devices/{device_uid}/asetek690":{"patch":{"tags":["device"],"summary":"Device AseTek690","description":"Set the driver type for liquidctl AseTek cooler. This is needed to set Legacy690Lc or Modern690Lc device driver type.","parameters":[{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AseTek690Request"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/status":{"get":{"tags":["status"],"summary":"Retrieve Status","description":"Returns the status of all devices and their channels, returning only the most recent status by default.","parameters":[{"in":"query","name":"all","schema":{"type":["boolean","null"],"default":null},"style":"form"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusResponse"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"post":{"tags":["status"],"summary":"Retrieve Status","description":"Returns the status of all devices and their channels,with the selected filters from the request body. This endpoint has the most options available for retrieving all statuses.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusResponse"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/status/{device_uid}":{"get":{"tags":["status"],"summary":"Retrieve Device Status","description":"Returns the status of all channels for a specific device, returning only the most recent status by default.","parameters":[{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"query","name":"all","schema":{"type":["boolean","null"],"default":null},"style":"form"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceStatusDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/status/{device_uid}/channels/{channel_name}":{"get":{"tags":["status"],"summary":"Retrieve Device Channel Status","description":"Returns the status of a specific channel for a specific device, returning only the most recent status by default.","parameters":[{"in":"path","name":"channel_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"query","name":"all","schema":{"type":["boolean","null"],"default":null},"style":"form"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceChannelStatusDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/profiles":{"get":{"tags":["profile"],"summary":"Retrieve Profile List","description":"Returns a list of all the persisted Profiles.","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfilesDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"put":{"tags":["profile"],"summary":"Update Profile","description":"Updates the Profile with the given properties. Dependent on the Profile UID.","requestBody":{"description":"Profile Settings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Profile"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"post":{"tags":["profile"],"summary":"Create Profile","description":"Creates the given Profile","requestBody":{"description":"Profile Settings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Profile"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/profiles/{profile_uid}":{"delete":{"tags":["profile"],"summary":"Delete Profile","description":"Deletes the Profile with the given Profile UID","parameters":[{"in":"path","name":"profile_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/profiles/order":{"post":{"tags":["profile"],"summary":"Save Profile Order","description":"Saves the order of Profiles as given.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfilesDto"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/functions":{"get":{"tags":["function"],"summary":"Retrieve Function List","description":"Returns a list of all the persisted Functions.","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunctionsDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"put":{"tags":["function"],"summary":"Update Function","description":"Updates the Function with the given properties. Dependent on the Function UID.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Function"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"post":{"tags":["function"],"summary":"Create Function","description":"Creates the given Function","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Function"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/functions/{function_uid}":{"delete":{"tags":["function"],"summary":"Delete Function","description":"Deletes the Function with the given Function UID","parameters":[{"in":"path","name":"function_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/functions/order":{"post":{"tags":["function"],"summary":"Save Function Order","description":"Saves the order of the Functions as given.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunctionsDto"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/custom-sensors":{"get":{"tags":["custom-sensor"],"summary":"Retrieve Custom Sensor List","description":"Returns a list of all the persisted Custom Sensors.","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomSensorsDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"put":{"tags":["custom-sensor"],"summary":"Update Custom Sensor","description":"Updates the Custom Sensor with the given properties. Dependent on the Custom Sensor ID.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomSensor"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"post":{"tags":["custom-sensor"],"summary":"Create Custom Sensor","description":"Creates the given Custom Sensor","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomSensor"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/custom-sensors/{custom_sensor_id}":{"get":{"tags":["custom-sensor"],"summary":"Retrieve Custom Sensor","description":"Retrieves the Custom Sensor with the given Custom Sensor ID","parameters":[{"in":"path","name":"custom_sensor_id","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomSensor"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"delete":{"tags":["custom-sensor"],"summary":"Delete Custom Sensor","description":"Deletes the Custom Sensor with the given Custom Sensor UID","parameters":[{"in":"path","name":"custom_sensor_id","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/custom-sensors/order":{"post":{"tags":["custom-sensor"],"summary":"Save Custom Sensor Order","description":"Saves the order of the Custom Sensors as given.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomSensorsDto"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/modes":{"get":{"tags":["mode"],"summary":"Retrieve Mode List","description":"Returns a list of all the persisted Modes.","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModesDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"put":{"tags":["mode"],"summary":"Update Mode","description":"Updates the Mode with the given properties.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateModeDto"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"post":{"tags":["mode"],"summary":"Create Mode","description":"Creates a Mode with the given name, based on the currently applied settings.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModeDto"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModeDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/modes/{mode_uid}":{"get":{"tags":["mode"],"summary":"Retrieve Mode","description":"Retrieves the Mode with the given Mode UID","parameters":[{"in":"path","name":"mode_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModeDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"delete":{"tags":["mode"],"summary":"Delete Mode","description":"Deletes the Mode with the given Mode UID","parameters":[{"in":"path","name":"mode_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/modes/{mode_uid}/duplicate":{"post":{"tags":["mode"],"summary":"Duplicate Mode","description":"Duplicates the Mode and it's settings from the given Mode UID and returns the new Mode.","parameters":[{"in":"path","name":"mode_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModeDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/modes/{mode_uid}/settings":{"put":{"tags":["mode"],"summary":"Update Mode Device Settings","description":"Updates the Mode with the given Mode UID device settings to what is currently applied, and returns the Mode with it's new settings.","parameters":[{"in":"path","name":"mode_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModeDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/modes-active":{"get":{"tags":["mode"],"summary":"Retrieve Active Modes","description":"Returns the active and previously active Mode UIDs.","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveModesDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/modes-active/{mode_uid}":{"post":{"tags":["mode"],"summary":"Activate Mode","description":"Activates the Mode with the given Mode UID. This applies all of this Mode's device settings.","parameters":[{"in":"path","name":"mode_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/modes/order":{"post":{"tags":["mode"],"summary":"Save Mode Order","description":"Saves the order of the Modes as given.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModeOrderDto"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/settings":{"get":{"tags":["setting"],"summary":"CoolerControl Settings","description":"Returns the current CoolerControl settings.","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoolerControlSettingsDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"patch":{"tags":["setting"],"summary":"Update CoolerControl Settings","description":"Applies only the given properties.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoolerControlSettingsDto"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/settings/devices":{"get":{"tags":["setting"],"summary":"CoolerControl All Device Settings","description":"Returns the current CoolerControl device settings for all devices.","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoolerControlAllDeviceSettingsDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/settings/devices/{device_uid}":{"get":{"tags":["setting"],"summary":"CoolerControl Device Settings","description":"Returns the current CoolerControl device settings for the given device UID.","parameters":[{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoolerControlDeviceSettingsDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"put":{"tags":["setting"],"summary":"Update CoolerControl Device Settings","description":"Updates the CoolerControl device settings for the given device UID.","parameters":[{"in":"path","name":"device_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"requestBody":{"description":"Device Specific settings that generally apply to how the application deals with the device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CCDeviceSettings"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/settings/ui":{"get":{"tags":["setting"],"summary":"CoolerControl UI Settings","description":"Returns the current CoolerControl UI Settings.","responses":{"200":{"description":"plain text","content":{"text/plain; charset=utf-8":{}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]}]},"put":{"tags":["setting"],"summary":"Update CoolerControl UI Settings","description":"Updates and persists the CoolerControl UI settings.","requestBody":{"content":{"text/plain; charset=utf-8":{}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]}]}},"/plugins":{"get":{"tags":["plugins"],"summary":"CoolerControl Plugins","description":"Returns the current list of active CoolerControl plugins.","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PluginsDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]}]}},"/plugins/lib/cc-plugin-lib.js":{"get":{"tags":["plugins"],"summary":"CoolerControl Plugin UI Library","description":"Returns the CoolerControl plugin UI library for plugins to use in their UI code.","responses":{"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}}}},"/plugins/{plugin_id}/config":{"get":{"tags":["plugins"],"summary":"CoolerControl Plugin Config","description":"Returns the current CoolerControl plugin config for the given plugin ID.","parameters":[{"in":"path","name":"plugin_id","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"plain text","content":{"text/plain; charset=utf-8":{}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]}]},"put":{"tags":["plugins"],"summary":"Update CoolerControl Plugin Config","description":"Updates the CoolerControl plugin config for the given plugin ID.","parameters":[{"in":"path","name":"plugin_id","required":true,"schema":{"type":"string"},"style":"simple"}],"requestBody":{"content":{"text/plain; charset=utf-8":{}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]}]}},"/plugins/{plugin_id}/ui":{"get":{"tags":["plugins"],"summary":"CoolerControl Plugin UI Check","description":"Returns if the CoolerControl plugin has a UI or not.","parameters":[{"in":"path","name":"plugin_id","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HasUiDto"}}}}},"security":[{"CookieAuth":[]}]}},"/plugins/{plugin_id}/ui/{file_name}":{"get":{"tags":["plugins"],"summary":"CoolerControl Plugin UI","description":"Returns the CoolerControl plugin UI file for the given plugin ID.","parameters":[{"in":"path","name":"file_name","required":true,"schema":{"type":"string"},"style":"simple"},{"in":"path","name":"plugin_id","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]}]}},"/alerts":{"get":{"tags":["alert"],"summary":"Retrieve Alert List","description":"Returns a list of all the persisted Alerts.","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertsDto"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"put":{"tags":["alert"],"summary":"Update Alert","description":"Updates the Alert with the given properties. Dependent on the Alert UID.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertDto"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"post":{"tags":["alert"],"summary":"Create Alert","description":"Creates the given Alert","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertDto"}}},"required":true},"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/alerts/{alert_uid}":{"delete":{"tags":["alert"],"summary":"Delete Alert","description":"Deletes the Alert with the given Alert UID","parameters":[{"in":"path","name":"alert_uid","required":true,"schema":{"type":"string"},"style":"simple"}],"responses":{"200":{"description":"no content"},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/detect":{"get":{"tags":["detect"],"summary":"Detect Hardware","description":"Run Super-I/O hardware detection and return results without loading modules.","responses":{"200":{"description":"Response for GET/POST /detect","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DetectResponse"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]},"post":{"tags":["detect"],"summary":"Detect Hardware and Load Modules","description":"Run Super-I/O hardware detection and optionally load kernel modules.","requestBody":{"description":"Request body for POST /detect","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DetectRequest"}}},"required":true},"responses":{"200":{"description":"Response for GET/POST /detect","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DetectResponse"}}}},"400":{"description":"Bad Request. The request is invalid"},"401":{"description":"Unauthorized. Invalid credentials were provided."},"403":{"description":"Forbidden. Insufficient permissions."},"404":{"description":"Whatever you're looking for, it's not here."},"500":{"description":"An internal error has occurred."},"429":{"description":"Too Many Requests. Login attempts have been rate limited."},"502":{"description":"Bad Gateway. An error has occurred with an external library."}},"security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/sse/logs":{"get":{"tags":["sse"],"summary":"Log Server Sent Events","description":"Subscribes and returns the Server Sent Events for a Log stream","security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/sse/status":{"get":{"tags":["sse"],"summary":"Recent Status Server Sent Events","description":"Subscribes and returns the Server Sent Events for a Status stream","security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/sse/modes":{"get":{"tags":["sse"],"summary":"Activated Mode Events","description":"Subscribes and returns the Server Sent Events for a ModeActivated stream","security":[{"CookieAuth":[]},{"BearerAuth":[]}]}},"/sse/alerts":{"get":{"tags":["sse"],"summary":"Alert Events","description":"Subscribes and returns Events for when an Alert State has changed","security":[{"CookieAuth":[]},{"BearerAuth":[]}]}}},"components":{"securitySchemes":{"CookieAuth":{"type":"apiKey","in":"cookie","name":"cc","description":"The private session cookie used for authentication."},"BasicAuth":{"type":"http","scheme":"basic","bearerFormat":"","description":"HTTP Basic authentication, mostly used to generate a secure authentication cookie."},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"cc_","description":"Bearer token authentication for external services."}},"schemas":{"ActiveModesDto":{"type":"object","properties":{"current_mode_uid":{"type":["string","null"]},"previous_mode_uid":{"type":["string","null"]}}},"AlertDto":{"type":"object","properties":{"channel_source":{"$ref":"#/components/schemas/ChannelSource"},"desktop_notify":{"description":"Toggle a desktop notification when this alert enters an `Active` state. (true by default)","type":"boolean","default":true},"desktop_notify_audio":{"description":"Toggle whether the desktop notification attempts to play an audio sound\n when this alert enters an `Active` state.\n Note: only applies when `desktop_notify` is enabled.","type":"boolean","default":false},"desktop_notify_recovery":{"description":"Toggle a desktop notification when this alert enters an `Inactive` state.","type":"boolean","default":true},"max":{"type":"number","format":"double"},"min":{"type":"number","format":"double"},"name":{"type":"string"},"shutdown_on_activation":{"description":"Toggle whether to issue a system shutdown when this Alert enters an `Active` state.\n Duration of the alert before shutdown is determined by `shutdown_active_duration`.","type":"boolean","default":false},"state":{"anyOf":[{"$ref":"#/components/schemas/AlertState"},{"type":"null"}]},"uid":{"type":"string"},"warmup_duration":{"description":"Time in seconds throughout which the alert condition must hold before the alert is activated","type":"number","format":"double"}},"required":["uid","name","channel_source","min","max","warmup_duration"]},"AlertLog":{"type":"object","properties":{"message":{"type":"string"},"name":{"type":"string"},"state":{"$ref":"#/components/schemas/AlertState"},"timestamp":{"type":"string","format":"date-time"},"uid":{"type":"string"}},"required":["uid","name","state","message","timestamp"]},"AlertPath":{"type":"object","properties":{"alert_uid":{"type":"string"}},"required":["alert_uid"]},"AlertState":{"oneOf":[{"type":"string","enum":["Active","Inactive"]},{"description":"Alert condition was satisfied at the stored time\n but the duration threshold has not been reached.","type":"object","properties":{"WarmUp":{"type":"string","format":"date-time"}},"additionalProperties":false,"required":["WarmUp"]},{"description":"Represents an error state. e.g. when one of the components in the alert isn't found.","type":"string","const":"Error"}]},"AlertsDto":{"type":"object","properties":{"alerts":{"type":"array","items":{"$ref":"#/components/schemas/AlertDto"}},"logs":{"type":"array","items":{"$ref":"#/components/schemas/AlertLog"}}},"required":["alerts","logs"]},"AseTek690Request":{"type":"object","properties":{"is_legacy690":{"type":"boolean"}},"required":["is_legacy690"]},"BaseDriver":{"type":"string","enum":["Aquacomputer","Legacy690Lc","Modern690Lc","Hydro690Lc","HydroPro","AsusRyujin","AuraLed","CommanderCore","CommanderPro","Coolit","CorsairHidPsu","Ddr4Temperature","VengeanceRgb","HydroPlatinum","Kraken2","KrakenX3","KrakenZ3","MockKrakenZ3","MpgCooler","EvgaPascal","RogTuring","NzxtEPsu","RgbFusion2","SmartDevice","SmartDevice2","H1V2","MsiAcpiEc","LianLiUni","NotSupported"]},"CCChannelSettings":{"type":"object","properties":{"disabled":{"type":"boolean"},"extension":{"description":"Specialized settings (extensions) that apply to a specific device channel.","anyOf":[{"$ref":"#/components/schemas/ChannelExtensions"},{"type":"null"}]},"label":{"type":["string","null"]}},"required":["disabled"]},"CCDeviceSettings":{"description":"Device Specific settings that generally apply to how the application deals with the device.","type":"object","properties":{"channel_settings":{"description":"A list of channels specific settings, including disable and extension settings.","type":"object","additionalProperties":{"$ref":"#/components/schemas/CCChannelSettings"}},"disable":{"description":"All communication with this device will be avoided if disabled","type":"boolean"},"extensions":{"description":"Specialized settings (extensions) that apply to a specific device.","allOf":[{"$ref":"#/components/schemas/DeviceExtensions"}]},"name":{"description":"The device name for this setting. Helpful after blacklisting(disabling) devices.","type":"string"}},"required":["name","disable","extensions","channel_settings"]},"CSPath":{"type":"object","properties":{"custom_sensor_id":{"type":"string"}},"required":["custom_sensor_id"]},"ChannelExtensionNames":{"description":"Channel extension names that signal which `ChannelExtensions` are applicable\n for a particular device channel.","type":"string","enum":["AutoHWCurve","AmdRdnaGpu"]},"ChannelExtensions":{"description":"Device Channel specific settings\n This is used to store specialized settings (extensions) that apply to a specific device channel.","anyOf":[{"description":"Whether to use the device channel's internal hardware fan curve functionality.","type":"object","properties":{"auto_hw_curve_enabled":{"type":"boolean"}},"required":["auto_hw_curve_enabled"]},{"description":"Whether to use the AMDGPU RDNA3/4 features.\n It allows the device to run at zero RPM when the temperature is below a certain threshold.","type":"object","properties":{"hw_fan_curve_enabled":{"description":"Whether to use the internal HW Curve feature, instead of setting regular\n flat curves. Using this reduces functionality.","type":"boolean"}},"required":["hw_fan_curve_enabled"]}]},"ChannelInfo":{"type":"object","properties":{"label":{"type":["string","null"]},"lcd_info":{"anyOf":[{"$ref":"#/components/schemas/LcdInfo"},{"type":"null"}]},"lcd_modes":{"type":"array","items":{"$ref":"#/components/schemas/LcdMode"}},"lighting_modes":{"type":"array","items":{"$ref":"#/components/schemas/LightingMode"}},"speed_options":{"anyOf":[{"$ref":"#/components/schemas/SpeedOptions"},{"type":"null"}]}},"required":["lighting_modes","lcd_modes"]},"ChannelMetric":{"type":"string","enum":["Temp","Duty","Load","RPM","Freq"]},"ChannelSource":{"description":"A source for displaying sensor data that is related to a particular channel.\n This is like `TempSource` but not limited to temperature sensors. (Load, Duty, etc.)","type":"object","properties":{"channel_metric":{"$ref":"#/components/schemas/ChannelMetric"},"channel_name":{"description":"The internal name for this channel source. NOT the Label.","type":"string"},"device_uid":{"description":"The associated device uid containing current values","type":"string"}},"required":["device_uid","channel_name","channel_metric"]},"ChannelStatus":{"type":"object","properties":{"duty":{"type":["number","null"],"format":"double"},"freq":{"type":["integer","null"],"format":"uint32","minimum":0},"name":{"type":"string"},"pwm_mode":{"type":["integer","null"],"format":"uint8","maximum":255,"minimum":0},"rpm":{"type":["integer","null"],"format":"uint32","minimum":0},"watts":{"type":["number","null"],"format":"double"}},"required":["name"]},"CoolerControlAllDeviceSettingsDto":{"type":"object","properties":{"devices":{"type":"array","items":{"$ref":"#/components/schemas/CoolerControlDeviceSettingsDto"}}},"required":["devices"]},"CoolerControlDeviceSettingsDto":{"type":"object","properties":{"channel_settings":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/CCChannelSettings"}},"disable":{"type":"boolean"},"extensions":{"$ref":"#/components/schemas/DeviceExtensions"},"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name","disable","extensions","channel_settings"]},"CoolerControlSettingsDto":{"type":"object","properties":{"allow_unencrypted":{"description":"Allow unencrypted HTTP connections from non-localhost addresses","type":["boolean","null"]},"apply_on_boot":{"type":["boolean","null"]},"compress":{"type":["boolean","null"]},"drivetemp_suspend":{"type":["boolean","null"]},"hide_duplicate_devices":{"type":["boolean","null"]},"liquidctl_integration":{"type":["boolean","null"]},"no_init":{"type":["boolean","null"]},"origins":{"description":"Custom origins to allow in CORS (for reverse proxy setups)","type":["array","null"],"items":{"type":"string"}},"poll_rate":{"type":["number","null"],"format":"double"},"protocol_header":{"description":"Header to check for proxy client protocol (e.g., \"X-Forwarded-Proto\")","type":["string","null"]},"startup_delay":{"type":["integer","null"],"format":"uint16","maximum":65535,"minimum":0},"thinkpad_full_speed":{"type":["boolean","null"]}}},"CreateModeDto":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]},"CreateTokenRequest":{"type":"object","properties":{"expires_at":{"type":["string","null"],"format":"date-time"},"label":{"type":"string"}},"required":["label"]},"CreateTokenResponse":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"},"id":{"type":"string"},"label":{"type":"string"},"token":{"type":"string"}},"required":["id","label","token","created_at"]},"CustomSensor":{"type":"object","properties":{"children":{"description":"The Custom Sensor's children, if any.\n\n Each Custom Sensor is either a child, parent, or standalone, not a combination of those.\n Custom Sensors are limited to 1 level of hierarchy. This removes the possibility\n of circular references.\n\n The children and parents vectors are managed and filled internally. For GET endpoints,\n they provide this information for clients. For POST or PUT endpoints,\n any values here are essentially ignored.","type":"array","default":[],"items":{"type":"string"}},"cs_type":{"$ref":"#/components/schemas/CustomSensorType"},"file_path":{"type":["string","null"]},"id":{"description":"ID MUST be unique, as `temp_name` must be unique.","type":"string"},"mix_function":{"$ref":"#/components/schemas/CustomSensorMixFunctionType"},"offset":{"type":["integer","null"],"format":"int8","maximum":127,"minimum":-128},"parents":{"description":"The Custom Sensor's parents, if any. See `children` for more details.","type":"array","default":[],"items":{"type":"string"}},"sources":{"type":"array","items":{"$ref":"#/components/schemas/CustomTempSourceData"}}},"required":["id","cs_type","mix_function","sources"]},"CustomSensorMixFunctionType":{"type":"string","enum":["Min","Max","Delta","Avg","WeightedAvg"]},"CustomSensorType":{"type":"string","enum":["Mix","File","Offset"]},"CustomSensorsDto":{"type":"object","properties":{"custom_sensors":{"type":"array","items":{"$ref":"#/components/schemas/CustomSensor"}}},"required":["custom_sensors"]},"CustomTempSourceData":{"type":"object","properties":{"temp_source":{"$ref":"#/components/schemas/TempSource"},"weight":{"type":"integer","format":"uint8","maximum":255,"minimum":0}},"required":["temp_source","weight"]},"DetectRequest":{"description":"Request body for POST /detect","type":"object","properties":{"load_modules":{"type":"boolean","default":false}}},"DetectResponse":{"description":"Response for GET/POST /detect","type":"object","properties":{"blacklisted":{"type":"array","items":{"type":"string"}},"detected_chips":{"type":"array","items":{"$ref":"#/components/schemas/DetectedChipDto"}},"environment":{"$ref":"#/components/schemas/EnvironmentDto"},"skipped":{"type":"array","items":{"$ref":"#/components/schemas/SkippedDriverDto"}}},"required":["detected_chips","skipped","blacklisted","environment"]},"DetectedChipDto":{"type":"object","properties":{"address":{"type":"string"},"base_address":{"type":"string"},"driver":{"type":"string"},"features":{"type":"array","items":{"type":"string"}},"module_status":{"type":"string"},"name":{"type":"string"}},"required":["name","driver","address","base_address","features","module_status"]},"DeviceChannelPath":{"type":"object","properties":{"channel_name":{"type":"string"},"device_uid":{"type":"string"}},"required":["device_uid","channel_name"]},"DeviceChannelStatusDto":{"type":"object","properties":{"status_history":{"type":"array","items":{"$ref":"#/components/schemas/Status"}}},"required":["status_history"]},"DeviceDto":{"type":"object","properties":{"d_type":{"$ref":"#/components/schemas/DeviceType"},"info":{"$ref":"#/components/schemas/DeviceInfo"},"lc_info":{"anyOf":[{"$ref":"#/components/schemas/LcInfo"},{"type":"null"}]},"name":{"type":"string"},"type_index":{"type":"integer","format":"uint8","maximum":255,"minimum":0},"uid":{"type":"string"}},"required":["name","d_type","type_index","uid","info"]},"DeviceExtensions":{"description":"Device specific extension settings\n This is used to store specialized settings (extensions) that apply to a specific device.\n More than one of these settings can be applied at a time.","type":"object","properties":{"delay_millis":{"description":"The delay in milliseconds to force between applying settings to this device.\n This is to help with communication issues with some devices that may not handle\n multiple settings applied in quick succession. (The driver does not always handle this)","type":"integer","format":"uint16","maximum":65535,"minimum":0},"direct_access":{"description":"Whether to enable Direct Access for the liquidctl driver,\n which will cause liquidctl to ignore the `HWMon` kernel driver","type":"boolean"}},"required":["direct_access","delay_millis"]},"DeviceInfo":{"description":"Needed Device info per device","type":"object","properties":{"channels":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ChannelInfo"}},"driver_info":{"$ref":"#/components/schemas/DriverInfo"},"lighting_speeds":{"type":"array","items":{"type":"string"}},"model":{"type":["string","null"]},"profile_max_length":{"type":"integer","format":"uint8","maximum":255,"minimum":0},"profile_min_length":{"type":"integer","format":"uint8","maximum":255,"minimum":0},"temp_max":{"description":"The absolute maximum temp to use for Profiles for this device","type":"integer","format":"uint8","maximum":255,"minimum":0},"temp_min":{"description":"The absolute minimum temp to use for Profiles for this device","type":"integer","format":"uint8","maximum":255,"minimum":0},"temps":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/TempInfo"}},"thinkpad_fan_control":{"description":"When present, then this is a `ThinkPad` device. True or False indicates whether Fan control\n is enabled for the kernel module and changing values is possible","type":["boolean","null"]}},"required":["channels","temps","lighting_speeds","temp_min","temp_max","profile_max_length","profile_min_length","driver_info"]},"DevicePath":{"type":"object","properties":{"device_uid":{"type":"string"}},"required":["device_uid"]},"DeviceStatusDto":{"type":"object","properties":{"d_type":{"$ref":"#/components/schemas/DeviceType"},"status_history":{"description":"Status history wrapped in Arc for O(1) cloning for serialization","type":"array","items":{"$ref":"#/components/schemas/Status"}},"type_index":{"type":"integer","format":"uint8","maximum":255,"minimum":0},"uid":{"type":"string"}},"required":["d_type","type_index","uid","status_history"]},"DeviceType":{"type":"string","enum":["CPU","GPU","Liquidctl","Hwmon","CustomSensors","ServicePlugin"]},"DevicesResponse":{"type":"object","properties":{"devices":{"type":"array","items":{"$ref":"#/components/schemas/DeviceDto"}}},"required":["devices"]},"DriverInfo":{"description":"Device Driver Information","type":"object","properties":{"drv_type":{"$ref":"#/components/schemas/DriverType"},"locations":{"description":"If available various paths used to access the device.\n This can include paths like the kernel device path, hwmon path, HID path, or PCI Bus ID","type":"array","items":{"type":"string"}},"name":{"description":"If available the kernel driver name or liquidctl driver class.","type":["string","null"]},"version":{"description":"If available the driver's version.\n For kernel-based drivers this is the current kernel version.\n For liquidctl-based drivers this is the liquidctl version.\n For Nvidia-based drivers this is the version of the installed nvidia proprietary drivers.","type":["string","null"]}},"required":["drv_type","locations"]},"DriverType":{"description":"The Driver Type, or source of the driver actively being used for this device.","type":"string","enum":["Kernel","Liquidctl","NVML","NvidiaCLI","CoolerControl","External"]},"EnvironmentDto":{"type":"object","properties":{"has_dev_port":{"type":"boolean"},"is_container":{"type":"boolean"}},"required":["is_container","has_dev_port"]},"Function":{"type":"object","properties":{"deviance":{"description":"The temperature deviance threshold in degrees","type":["number","null"],"format":"double"},"duty_maximum":{"description":"The maximum duty change (step size) to apply\n A duty maximum of `0` indicates a fixed step size. Use `duty_minimum` to set the step size.\n Previously `duty_maximum`.","type":"integer","format":"uint8","maximum":255,"minimum":0},"duty_minimum":{"description":"The minimum duty change (step size) to apply\n Previously `duty_minimum`.","type":"integer","format":"uint8","maximum":255,"minimum":0},"f_type":{"description":"The type of this function","allOf":[{"$ref":"#/components/schemas/FunctionType"}]},"name":{"description":"The user given name for this function","type":"string"},"only_downward":{"description":"Whether to apply settings only on the way down","type":["boolean","null"]},"response_delay":{"description":"The response delay in seconds","type":["integer","null"],"format":"uint8","maximum":255,"minimum":0},"sample_window":{"description":"The sample window this function should use, particularly applicable to moving averages","type":["integer","null"],"format":"uint8","maximum":255,"minimum":0},"step_size_max_decreasing":{"description":"The maximum step size to apply when decreasing.\n A value of `0` indicates a fixed step size. Use `step_size_minimum_decreasing` to set the step size.","type":"integer","format":"uint8","maximum":255,"minimum":0},"step_size_min_decreasing":{"description":"The minimum step size to apply when decreasing.\n A value of `0` indicates a symmetric step size. Use `duty_minimum` to set the step size.","type":"integer","format":"uint8","maximum":255,"minimum":0},"threshold_hopping":{"description":"Whether to temporarily bypass thresholds when fan speed remains unchanged for 30+ seconds to meet curve target.","type":"boolean"},"uid":{"description":"The Unique identifier for this function","type":"string"}},"required":["uid","name","f_type","duty_minimum","duty_maximum","step_size_min_decreasing","step_size_max_decreasing","threshold_hopping"]},"FunctionPath":{"type":"object","properties":{"function_uid":{"type":"string"}},"required":["function_uid"]},"FunctionType":{"type":"string","enum":["Identity","Standard","ExponentialMovingAvg"]},"FunctionsDto":{"type":"object","properties":{"functions":{"type":"array","items":{"$ref":"#/components/schemas/Function"}}},"required":["functions"]},"HasUiDto":{"type":"object","properties":{"has_ui":{"type":"boolean"}},"required":["has_ui"]},"HealthCheck":{"type":"object","properties":{"description":{"type":"string"},"current_timestamp":{"type":"string","format":"date-time"},"details":{"$ref":"#/components/schemas/HealthDetails"},"links":{"type":"object","additionalProperties":{"type":"string"}},"status":{"type":"string"},"system":{"$ref":"#/components/schemas/SystemDetails"}},"required":["status","description","current_timestamp","details","system","links"]},"HealthDetails":{"type":"object","properties":{"errors":{"type":"integer","format":"uint","minimum":0},"liquidctl_connected":{"type":"boolean"},"memory_mb":{"type":"number","format":"double"},"pid":{"type":"integer","format":"uint32","minimum":0},"uptime":{"type":"string"},"version":{"type":"string"},"warnings":{"type":"integer","format":"uint","minimum":0}},"required":["uptime","version","pid","memory_mb","warnings","errors","liquidctl_connected"]},"LcInfo":{"description":"Specific Liquidctl device information","type":"object","properties":{"driver_type":{"description":"An Enum representation of the various Liquidctl driver classes","allOf":[{"$ref":"#/components/schemas/BaseDriver"}]},"firmware_version":{"description":"The detected firmware version at initialization","type":["string","null"]},"unknown_asetek":{"description":"An indicator for needed user input to determine actual asetek690lc device","type":"boolean"}},"required":["driver_type","unknown_asetek"]},"LcdCarouselSettings":{"description":"Settings for the LCD Carousel.\n\n This can be used to have a carousel of images (static or gif), of sensor data,\n or a combination of both.","type":"object","properties":{"images_path":{"description":"The absolute path directory location for images for the carousel. All applicable images\n present are processed when the setting is applied.","type":["string","null"]},"interval":{"description":"The interval in seconds (2-900) in which to change images in the carousel.","type":"integer","format":"uint64","minimum":0}},"required":["interval"]},"LcdImageUpdateQuery":{"type":"object","properties":{"log":{"type":["boolean","null"],"default":null}}},"LcdInfo":{"description":"Specific LCD Screen info","type":"object","properties":{"max_image_size_bytes":{"type":"integer","format":"uint32","minimum":0},"screen_height":{"type":"integer","format":"uint32","minimum":0},"screen_width":{"type":"integer","format":"uint32","minimum":0}},"required":["screen_width","screen_height","max_image_size_bytes"]},"LcdMode":{"type":"object","properties":{"brightness":{"type":"boolean"},"colors_max":{"type":"integer","format":"uint8","maximum":255,"minimum":0},"colors_min":{"type":"integer","format":"uint8","maximum":255,"minimum":0},"frontend_name":{"type":"string"},"image":{"type":"boolean"},"name":{"type":"string"},"orientation":{"type":"boolean"},"type_":{"$ref":"#/components/schemas/LcdModeType"}},"required":["name","frontend_name","brightness","orientation","image","colors_min","colors_max","type_"]},"LcdModeName":{"type":"string","enum":["none","liquid","image","temp","carousel"]},"LcdModeType":{"type":"string","enum":["None","Liquidctl","Custom"]},"LcdSettings":{"type":"object","properties":{"brightness":{"description":"The LCD brightness (0-100%)","type":["integer","null"],"format":"uint8","maximum":255,"minimum":0},"carousel":{"anyOf":[{"$ref":"#/components/schemas/LcdCarouselSettings"},{"type":"null"}]},"colors":{"description":"a list of RGB tuple values, eg [(20,20,120), (0,0,255)]","type":"array","items":{"type":"array","items":[{"type":"integer","format":"uint8","maximum":255,"minimum":0},{"type":"integer","format":"uint8","maximum":255,"minimum":0},{"type":"integer","format":"uint8","maximum":255,"minimum":0}],"maxItems":3,"minItems":3}},"image_file_processed":{"description":"The LCD Image processed file path location, where the preprocessed image is located.","type":["string","null"]},"mode":{"description":"The Lcd mode name","allOf":[{"$ref":"#/components/schemas/LcdModeName"}]},"orientation":{"description":"The LCD Image orientation (0,90,180,270)","type":["integer","null"],"format":"uint16","maximum":65535,"minimum":0},"temp_source":{"description":"A temp source for displaying a temperature.","anyOf":[{"$ref":"#/components/schemas/TempSource"},{"type":"null"}]}},"required":["mode","colors"]},"LightingMode":{"type":"object","properties":{"backward_enabled":{"type":"boolean"},"frontend_name":{"type":"string"},"max_colors":{"type":"integer","format":"uint8","maximum":255,"minimum":0},"min_colors":{"type":"integer","format":"uint8","maximum":255,"minimum":0},"name":{"type":"string"},"speed_enabled":{"type":"boolean"},"type_":{"$ref":"#/components/schemas/LightingModeType"}},"required":["name","frontend_name","min_colors","max_colors","speed_enabled","backward_enabled","type_"]},"LightingModeType":{"type":"string","enum":["None","Liquidctl","Custom"]},"LightingSettings":{"type":"object","properties":{"backward":{"description":"run backwards or not","type":["boolean","null"]},"colors":{"description":"a list of RGB tuple values, eg [(20,20,120), (0,0,255)]","type":"array","items":{"type":"array","items":[{"type":"integer","format":"uint8","maximum":255,"minimum":0},{"type":"integer","format":"uint8","maximum":255,"minimum":0},{"type":"integer","format":"uint8","maximum":255,"minimum":0}],"maxItems":3,"minItems":3}},"mode":{"description":"The lighting mode name","type":"string"},"speed":{"description":"The speed to set","type":["string","null"]}},"required":["mode","colors"]},"ModeDto":{"type":"object","properties":{"device_settings":{"type":"array","items":{"type":"array","items":[{"type":"string"},{"type":"array","items":{"$ref":"#/components/schemas/Setting"}}],"maxItems":2,"minItems":2}},"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name","device_settings"]},"ModeOrderDto":{"type":"object","properties":{"mode_uids":{"type":"array","items":{"type":"string"}}},"required":["mode_uids"]},"ModePath":{"type":"object","properties":{"mode_uid":{"type":"string"}},"required":["mode_uid"]},"ModesDto":{"type":"object","properties":{"modes":{"type":"array","items":{"$ref":"#/components/schemas/ModeDto"}}},"required":["modes"]},"PluginDto":{"type":"object","properties":{"description":{"type":["string","null"]},"address":{"type":"string"},"id":{"type":"string"},"path":{"type":"string"},"privileged":{"type":"boolean"},"service_type":{"type":"string"},"url":{"type":["string","null"]},"version":{"type":["string","null"]}},"required":["id","service_type","address","privileged","path"]},"PluginPath":{"type":"object","properties":{"plugin_id":{"type":"string"}},"required":["plugin_id"]},"PluginUiPath":{"type":"object","properties":{"file_name":{"type":"string"},"plugin_id":{"type":"string"}},"required":["plugin_id","file_name"]},"PluginsDto":{"type":"object","properties":{"plugins":{"type":"array","items":{"$ref":"#/components/schemas/PluginDto"}}},"required":["plugins"]},"Profile":{"description":"Profile Settings","type":"object","properties":{"function_uid":{"description":"The function uid to apply to this profile","type":"string"},"member_profile_uids":{"description":"The profiles that make up the mix profile","type":"array","items":{"type":"string"}},"mix_function_type":{"description":"The function to mix the members with if this is a Mix Profile","anyOf":[{"$ref":"#/components/schemas/ProfileMixFunctionType"},{"type":"null"}]},"name":{"description":"The User given name for this Profile","type":"string"},"offset_profile":{"description":"The graph offset to apply to the associated member profile\n This can also be used as a static offset when there is only one duty/offset pair.","type":["array","null"],"items":{"type":"array","items":[{"type":"integer","format":"uint8","maximum":255,"minimum":0},{"type":"integer","format":"int8","maximum":127,"minimum":-128}],"maxItems":2,"minItems":2}},"p_type":{"description":"The profile type","allOf":[{"$ref":"#/components/schemas/ProfileType"}]},"speed_fixed":{"description":"The fixed duty speed to set. eg: 20 (%)","type":["integer","null"],"format":"uint8","maximum":255,"minimum":0},"speed_profile":{"description":"The profile temp/duty speeds to set. eg: [(20.0, 50), (25.7, 80)]","type":["array","null"],"items":{"type":"array","items":[{"type":"number","format":"double"},{"type":"integer","format":"uint8","maximum":255,"minimum":0}],"maxItems":2,"minItems":2}},"temp_max":{"description":"The maximum temp for this profile","type":["number","null"],"format":"double"},"temp_min":{"description":"The minimum temp for this profile","type":["number","null"],"format":"double"},"temp_source":{"description":"The associated temperature source","anyOf":[{"$ref":"#/components/schemas/TempSource"},{"type":"null"}]},"uid":{"description":"The Unique Identifier for this Profile","type":"string"}},"required":["uid","p_type","name","function_uid","member_profile_uids"]},"ProfileMixFunctionType":{"type":"string","enum":["Min","Max","Avg","Diff","Sum"]},"ProfilePath":{"type":"object","properties":{"profile_uid":{"type":"string"}},"required":["profile_uid"]},"ProfileType":{"type":"string","enum":["Default","Fixed","Graph","Mix","Overlay"]},"ProfilesDto":{"type":"object","properties":{"profiles":{"type":"array","items":{"$ref":"#/components/schemas/Profile"}}},"required":["profiles"]},"SetPasswdRequest":{"type":"object","properties":{"current_password":{"type":"string"}},"required":["current_password"]},"Setting":{"description":"Setting is used to store applied Settings to a device channel.\n These are the general core settings that apply to a wide range of device and channel types.\n Specialized settings are stored in `DeviceExtensions` and `ChannelExtensions`.\n Only one specific lighting or speed setting is applied to a specific channel at a time.","type":"object","properties":{"channel_name":{"type":"string"},"lcd":{"description":"Settings for LCD screens","anyOf":[{"$ref":"#/components/schemas/LcdSettings"},{"type":"null"}]},"lighting":{"description":"Settings for lighting","anyOf":[{"$ref":"#/components/schemas/LightingSettings"},{"type":"null"}]},"profile_uid":{"description":"The Profile UID that applies to this device channel","type":["string","null"]},"reset_to_default":{"description":"Used to set hwmon & nvidia channels back to their default 'automatic' values.","type":["boolean","null"]},"speed_fixed":{"description":"The fixed duty speed to set. eg: 20 (%)","type":["integer","null"],"format":"uint8","maximum":255,"minimum":0}},"required":["channel_name"]},"SettingManualRequest":{"type":"object","properties":{"speed_fixed":{"type":"integer","format":"uint8","maximum":255,"minimum":0}},"required":["speed_fixed"]},"SettingPWMMode":{"type":"object","properties":{"pwm_mode":{"type":"integer","format":"uint8","maximum":255,"minimum":0}},"required":["pwm_mode"]},"SettingProfileUID":{"type":"object","properties":{"profile_uid":{"type":"string"}},"required":["profile_uid"]},"SettingsResponse":{"type":"object","properties":{"settings":{"type":"array","items":{"$ref":"#/components/schemas/Setting"}}},"required":["settings"]},"SkippedDriverDto":{"type":"object","properties":{"driver":{"type":"string"},"preferred":{"type":"string"},"reason":{"type":"string"}},"required":["driver","reason","preferred"]},"SpeedOptions":{"type":"object","properties":{"extension":{"description":"If present, then this channel has special settings that are applicable.","anyOf":[{"$ref":"#/components/schemas/ChannelExtensionNames"},{"type":"null"}]},"fixed_enabled":{"description":"True if manual fan speed control is supported; if false, speeds are read-only (monitoring only).","type":"boolean"},"max_duty":{"description":"The maximum fan duty for this speed channel","type":"integer","format":"uint8","maximum":255,"minimum":0},"min_duty":{"description":"The minimum fan duty for this speed channel","type":"integer","format":"uint8","maximum":255,"minimum":0}},"required":["min_duty","max_duty","fixed_enabled"]},"Status":{"description":"A Model which contains various applicable device statuses","type":"object","properties":{"channels":{"type":"array","items":{"$ref":"#/components/schemas/ChannelStatus"}},"temps":{"type":"array","items":{"$ref":"#/components/schemas/TempStatus"}},"timestamp":{"type":"string","format":"date-time"}},"required":["timestamp","temps","channels"]},"StatusQuery":{"type":"object","properties":{"all":{"type":["boolean","null"],"default":null}}},"StatusRequest":{"type":"object","properties":{"all":{"type":["boolean","null"]},"since":{"type":["string","null"],"format":"date-time"}}},"StatusResponse":{"type":"object","properties":{"devices":{"type":"array","items":{"$ref":"#/components/schemas/DeviceStatusDto"}}},"required":["devices"]},"SystemDetails":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]},"TempInfo":{"type":"object","properties":{"label":{"type":"string"},"number":{"type":"integer","format":"uint8","maximum":255,"minimum":0}},"required":["label","number"]},"TempSource":{"type":"object","properties":{"device_uid":{"description":"The associated device uid containing current temp values","type":"string"},"temp_name":{"description":"The internal name for this Temperature Source. NOT the `TempInfo` Label.","type":"string"}},"required":["temp_name","device_uid"]},"TempStatus":{"type":"object","properties":{"name":{"type":"string"},"temp":{"type":"number","format":"double"}},"required":["name","temp"]},"ThinkPadFanControlRequest":{"type":"object","properties":{"enable":{"type":"boolean"}},"required":["enable"]},"TokenInfo":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"},"id":{"type":"string"},"label":{"type":"string"},"last_used":{"type":["string","null"],"format":"date-time"}},"required":["id","label","created_at"]},"TokenListResponse":{"type":"object","properties":{"tokens":{"type":"array","items":{"$ref":"#/components/schemas/TokenInfo"}}},"required":["tokens"]},"TokenPath":{"type":"object","properties":{"token_id":{"type":"string"}},"required":["token_id"]},"UpdateModeDto":{"type":"object","properties":{"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name"]}}},"tags":[{"name":"base","description":"Foundational endpoints for this API"},{"name":"auth","description":"Authentication"},{"name":"device","description":"Device Interaction"},{"name":"status","description":"Device Status"},{"name":"profile","description":"Profiles"},{"name":"function","description":"Functions"},{"name":"custom-sensor","description":"Custom Sensors"},{"name":"mode","description":"Modes"},{"name":"setting","description":"Settings"},{"name":"alert","description":"Alerts"},{"name":"sse","description":"Server Side Events"},{"name":"plugins","description":"Plugins"}]} \ No newline at end of file From e757a25b8bd4c4061e8560f8b0b620ce2c71df20 Mon Sep 17 00:00:00 2001 From: damachine Date: Mon, 9 Mar 2026 19:06:34 +0100 Subject: [PATCH 3/5] feat: native support for CC4's LCD shutdown image --- .github/workflows/install.yml | 32 ----- Makefile | 22 ++-- PKGBUILD | 6 - aur/PKGBUILD | 3 - aur/coolerdash.install | 18 +-- coolerdash.install | 18 +-- .../startup-delay.conf | 2 - etc/systemd/coolerdash-helperd.service | 15 --- packaging/coolerdash.spec | 14 +-- packaging/debian/install | 2 - src/main.c | 115 +++--------------- src/srv/cc_main.c | 92 ++++++++++---- src/srv/cc_main.h | 16 ++- 13 files changed, 113 insertions(+), 242 deletions(-) delete mode 100644 etc/systemd/cc-plugin-coolerdash.service.d/startup-delay.conf delete mode 100644 etc/systemd/coolerdash-helperd.service diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index 6be6128..ff59bed 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -156,22 +156,6 @@ jobs: echo "⚠️ Manual page not found" fi - # Check systemd helperd service - if [ -f "/tmp/install-test/usr/lib/systemd/system/coolerdash-helperd.service" ]; then - echo "✅ Systemd helperd service installed" - else - echo "❌ Systemd helperd service not found" - exit 1 - fi - - # Check systemd startup-delay drop-in - if [ -f "/tmp/install-test/etc/systemd/system/cc-plugin-coolerdash.service.d/startup-delay.conf" ]; then - echo "✅ Systemd startup-delay drop-in installed" - else - echo "❌ Systemd startup-delay drop-in not found" - exit 1 - fi - # Check udev rules if [ -f "/tmp/install-test/usr/lib/udev/rules.d/99-coolerdash.rules" ]; then echo "✅ Udev rules installed" @@ -233,22 +217,6 @@ jobs: exit 1 fi - # Check systemd helperd service - if [ -f "/tmp/install-test/usr/lib/systemd/system/coolerdash-helperd.service" ]; then - echo "✅ Systemd helperd service installed" - else - echo "❌ Systemd helperd service not found" - exit 1 - fi - - # Check systemd startup-delay drop-in - if [ -f "/tmp/install-test/etc/systemd/system/cc-plugin-coolerdash.service.d/startup-delay.conf" ]; then - echo "✅ Systemd startup-delay drop-in installed" - else - echo "❌ Systemd startup-delay drop-in not found" - exit 1 - fi - # Check udev rules if [ -f "/tmp/install-test/usr/lib/udev/rules.d/99-coolerdash.rules" ]; then echo "✅ Udev rules installed" diff --git a/Makefile b/Makefile index d91ab85..5375657 100644 --- a/Makefile +++ b/Makefile @@ -247,6 +247,18 @@ install: check-deps $(TARGET) $(SUDO) userdel -rf coolerdash; \ LEGACY_FOUND=1; \ fi; \ + if [ -d /etc/systemd/system/cc-plugin-coolerdash.service.d ]; then \ + $(SUDO) rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d; \ + LEGACY_FOUND=1; \ + fi; \ + if [ -f /usr/lib/systemd/system/coolerdash-helperd.service ]; then \ + $(SUDO) rm -f /usr/lib/systemd/system/coolerdash-helperd.service; \ + LEGACY_FOUND=1; \ + fi; \ + if [ -f /etc/systemd/system/coolerdash-helperd.service ]; then \ + $(SUDO) rm -f /etc/systemd/system/coolerdash-helperd.service; \ + LEGACY_FOUND=1; \ + fi; \ if [ "$$LEGACY_FOUND" -eq 1 ]; then \ printf " $(GREEN)OK$(RESET) Legacy cleanup complete\n"; \ else \ @@ -284,10 +296,6 @@ install: check-deps $(TARGET) @install -m644 $(MANIFEST) "$(DESTDIR)/etc/coolercontrol/plugins/coolerdash/manifest.toml" @sed -i 's/{{VERSION}}/$(VERSION)/g' "$(DESTDIR)/etc/coolercontrol/plugins/coolerdash/manifest.toml" @sed -i 's/{{VERSION}}/$(VERSION)/g' "$(DESTDIR)/etc/coolercontrol/plugins/coolerdash/ui/index.html" - @install -Dm644 etc/systemd/cc-plugin-coolerdash.service.d/startup-delay.conf "$(DESTDIR)/etc/systemd/system/cc-plugin-coolerdash.service.d/startup-delay.conf" - @install -Dm644 etc/systemd/coolerdash-helperd.service "$(DESTDIR)/usr/lib/systemd/system/coolerdash-helperd.service" - @printf " $(GREEN)Drop-in:$(RESET) $(DESTDIR)/etc/systemd/system/cc-plugin-coolerdash.service.d/startup-delay.conf\n" - @printf " $(GREEN)Service:$(RESET) $(DESTDIR)/usr/lib/systemd/system/coolerdash-helperd.service\n" @printf " $(GREEN)Binary:$(RESET) $(DESTDIR)/usr/libexec/coolerdash/coolerdash\n" @printf " $(GREEN)Config JSON:$(RESET) $(DESTDIR)/etc/coolercontrol/plugins/coolerdash/config.json\n" @printf " $(GREEN)Web UI:$(RESET) $(DESTDIR)/etc/coolercontrol/plugins/coolerdash/ui/index.html\n" @@ -324,7 +332,6 @@ install: check-deps $(TARGET) @printf "$(YELLOW)Next steps:$(RESET)\n" @if [ "$(REALOS)" = "yes" ]; then \ $(SUDO) systemctl daemon-reload 2>/dev/null || true; \ - $(SUDO) systemctl enable --now coolerdash-helperd.service 2>/dev/null || true; \ $(SUDO) systemctl restart coolercontrold.service 2>/dev/null || true; \ fi @printf " $(PURPLE)Reload systemd:$(RESET) systemctl daemon-reload\n" @@ -398,11 +405,6 @@ uninstall: LEGACY_FOUND=1; \ fi; \ fi - @if [ "$(REALOS)" = "yes" ]; then \ - $(SUDO) rm -f /etc/systemd/system/coolerdash-helperd.service; \ - fi - @$(SUDO) rm -f "$(DESTDIR)/usr/lib/systemd/system/coolerdash-helperd.service" - @$(SUDO) rm -rf "$(DESTDIR)/etc/systemd/system/cc-plugin-coolerdash.service.d" @$(SUDO) rm -rf "$(DESTDIR)/etc/coolercontrol/plugins/coolerdash" @$(SUDO) rm -rf "$(DESTDIR)/usr/libexec/coolerdash" @$(SUDO) rm -rf "$(DESTDIR)/usr/share/licenses/coolerdash" diff --git a/PKGBUILD b/PKGBUILD index 7313cc0..cd9bc4d 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -56,9 +56,6 @@ build() { cp -a etc/applications/coolerdash.desktop "${srcdir}/etc/applications/" cp -a etc/icons/coolerdash.svg "${srcdir}/etc/icons/" cp -a etc/udev/rules.d/99-coolerdash.rules "${srcdir}/etc/udev/rules.d/" - mkdir -p "${srcdir}/etc/systemd/cc-plugin-coolerdash.service.d" - cp -a etc/systemd/coolerdash-helperd.service "${srcdir}/etc/systemd/" - cp -a etc/systemd/cc-plugin-coolerdash.service.d/startup-delay.conf "${srcdir}/etc/systemd/cc-plugin-coolerdash.service.d/" } check() { @@ -96,8 +93,5 @@ package() { install -Dm644 "${srcdir}/etc/applications/coolerdash.desktop" "${pkgdir}/usr/share/applications/coolerdash.desktop" install -Dm644 "${srcdir}/etc/udev/rules.d/99-coolerdash.rules" "${pkgdir}/usr/lib/udev/rules.d/99-coolerdash.rules" install -Dm644 "${srcdir}/etc/icons/coolerdash.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/coolerdash.svg" - install -Dm644 "${srcdir}/etc/systemd/coolerdash-helperd.service" "${pkgdir}/usr/lib/systemd/system/coolerdash-helperd.service" - install -Dm644 "${srcdir}/etc/systemd/cc-plugin-coolerdash.service.d/startup-delay.conf" "${pkgdir}/etc/systemd/system/cc-plugin-coolerdash.service.d/startup-delay.conf" - install -Dm644 "${srcdir}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" } diff --git a/aur/PKGBUILD b/aur/PKGBUILD index 2e0b33c..402247a 100644 --- a/aur/PKGBUILD +++ b/aur/PKGBUILD @@ -77,8 +77,5 @@ package() { install -Dm644 "${srcdir}/${pkgname}/etc/applications/coolerdash.desktop" "${pkgdir}/usr/share/applications/coolerdash.desktop" install -Dm644 "${srcdir}/${pkgname}/etc/udev/rules.d/99-coolerdash.rules" "${pkgdir}/usr/lib/udev/rules.d/99-coolerdash.rules" install -Dm644 "${srcdir}/${pkgname}/etc/icons/coolerdash.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/coolerdash.svg" - install -Dm644 "${srcdir}/${pkgname}/etc/systemd/coolerdash-helperd.service" "${pkgdir}/usr/lib/systemd/system/coolerdash-helperd.service" - install -Dm644 "${srcdir}/${pkgname}/etc/systemd/cc-plugin-coolerdash.service.d/startup-delay.conf" "${pkgdir}/etc/systemd/system/cc-plugin-coolerdash.service.d/startup-delay.conf" - install -Dm644 "${srcdir}/${pkgname}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" } diff --git a/aur/coolerdash.install b/aur/coolerdash.install index 1142640..91683e4 100644 --- a/aur/coolerdash.install +++ b/aur/coolerdash.install @@ -40,17 +40,14 @@ post_install() { udevadm trigger --subsystem-match=usb fi - # Migrate helperd from /etc to /usr/lib + # Remove legacy files rm -f /etc/systemd/system/multi-user.target.wants/coolerdash-helperd.service rm -f /etc/systemd/system/coolerdash-helperd.service + rm -f /usr/lib/systemd/system/coolerdash-helperd.service + rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d systemctl daemon-reload - # Enable helperd - if systemctl list-unit-files coolerdash-helperd.service | grep -q coolerdash-helperd; then - systemctl enable --now coolerdash-helperd.service || echo "Note: coolerdash-helperd.service failed. Enable manually." - fi - # Restart CoolerControl if systemctl list-unit-files coolercontrold.service | grep -q coolercontrold; then systemctl restart coolercontrold.service || echo "Note: CoolerControl restart failed." @@ -62,17 +59,14 @@ post_install() { } post_upgrade() { - # Migrate helperd from /etc to /usr/lib + # Remove legacy files rm -f /etc/systemd/system/multi-user.target.wants/coolerdash-helperd.service rm -f /etc/systemd/system/coolerdash-helperd.service + rm -f /usr/lib/systemd/system/coolerdash-helperd.service + rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d systemctl daemon-reload - # Enable helperd - if systemctl list-unit-files coolerdash-helperd.service | grep -q coolerdash-helperd; then - systemctl enable --now coolerdash-helperd.service || echo "Note: coolerdash-helperd.service failed. Enable manually." - fi - # Restart plugin if systemctl list-unit-files cc-plugin-coolerdash.service | grep -q cc-plugin-coolerdash; then systemctl restart cc-plugin-coolerdash.service || echo "Note: Plugin restart failed." diff --git a/coolerdash.install b/coolerdash.install index 1142640..91683e4 100644 --- a/coolerdash.install +++ b/coolerdash.install @@ -40,17 +40,14 @@ post_install() { udevadm trigger --subsystem-match=usb fi - # Migrate helperd from /etc to /usr/lib + # Remove legacy files rm -f /etc/systemd/system/multi-user.target.wants/coolerdash-helperd.service rm -f /etc/systemd/system/coolerdash-helperd.service + rm -f /usr/lib/systemd/system/coolerdash-helperd.service + rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d systemctl daemon-reload - # Enable helperd - if systemctl list-unit-files coolerdash-helperd.service | grep -q coolerdash-helperd; then - systemctl enable --now coolerdash-helperd.service || echo "Note: coolerdash-helperd.service failed. Enable manually." - fi - # Restart CoolerControl if systemctl list-unit-files coolercontrold.service | grep -q coolercontrold; then systemctl restart coolercontrold.service || echo "Note: CoolerControl restart failed." @@ -62,17 +59,14 @@ post_install() { } post_upgrade() { - # Migrate helperd from /etc to /usr/lib + # Remove legacy files rm -f /etc/systemd/system/multi-user.target.wants/coolerdash-helperd.service rm -f /etc/systemd/system/coolerdash-helperd.service + rm -f /usr/lib/systemd/system/coolerdash-helperd.service + rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d systemctl daemon-reload - # Enable helperd - if systemctl list-unit-files coolerdash-helperd.service | grep -q coolerdash-helperd; then - systemctl enable --now coolerdash-helperd.service || echo "Note: coolerdash-helperd.service failed. Enable manually." - fi - # Restart plugin if systemctl list-unit-files cc-plugin-coolerdash.service | grep -q cc-plugin-coolerdash; then systemctl restart cc-plugin-coolerdash.service || echo "Note: Plugin restart failed." diff --git a/etc/systemd/cc-plugin-coolerdash.service.d/startup-delay.conf b/etc/systemd/cc-plugin-coolerdash.service.d/startup-delay.conf deleted file mode 100644 index 3b9aee6..0000000 --- a/etc/systemd/cc-plugin-coolerdash.service.d/startup-delay.conf +++ /dev/null @@ -1,2 +0,0 @@ -[Service] -ExecStartPre=/bin/sleep 10 diff --git a/etc/systemd/coolerdash-helperd.service b/etc/systemd/coolerdash-helperd.service deleted file mode 100644 index bd8fe81..0000000 --- a/etc/systemd/coolerdash-helperd.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=CoolerDash helper daemon -BindsTo=coolercontrold.service -PartOf=coolercontrold.service -After=coolercontrold.service - -[Service] -Type=simple -ExecStart=/bin/sleep infinity -ExecStop=/usr/libexec/coolerdash/coolerdash --shutdown -TimeoutStopSec=3 -Restart=no - -[Install] -WantedBy=multi-user.target diff --git a/packaging/coolerdash.spec b/packaging/coolerdash.spec index cbf5d86..5c65fe7 100644 --- a/packaging/coolerdash.spec +++ b/packaging/coolerdash.spec @@ -78,14 +78,13 @@ fi if [ -f /etc/coolercontrol/plugins/coolerdash/config.json ]; then chmod 666 /etc/coolercontrol/plugins/coolerdash/config.json fi -# Migrate helperd from /etc to /usr/lib +# Remove legacy files rm -f /etc/systemd/system/multi-user.target.wants/coolerdash-helperd.service rm -f /etc/systemd/system/coolerdash-helperd.service +rm -f /usr/lib/systemd/system/coolerdash-helperd.service +rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d if command -v systemctl >/dev/null 2>&1; then systemctl daemon-reload - if systemctl list-unit-files coolerdash-helperd.service | grep -q coolerdash-helperd; then - systemctl enable --now coolerdash-helperd.service || echo "Note: coolerdash-helperd.service failed. Enable manually." - fi if systemctl is-active --quiet coolercontrold.service; then systemctl restart coolercontrold.service || echo "Note: CoolerControl restart failed." fi @@ -93,10 +92,6 @@ fi %preun if command -v systemctl >/dev/null 2>&1; then - if systemctl list-unit-files coolerdash-helperd.service | grep -q coolerdash-helperd; then - systemctl stop --no-block coolerdash-helperd.service - systemctl disable coolerdash-helperd.service - fi if systemctl list-unit-files cc-plugin-coolerdash.service | grep -q cc-plugin-coolerdash; then systemctl stop cc-plugin-coolerdash.service systemctl disable cc-plugin-coolerdash.service @@ -155,9 +150,6 @@ fi /usr/share/applications/coolerdash.desktop /usr/share/icons/hicolor/scalable/apps/coolerdash.svg /usr/lib/udev/rules.d/99-coolerdash.rules -/usr/lib/systemd/system/coolerdash-helperd.service -%dir /etc/systemd/system/cc-plugin-coolerdash.service.d -/etc/systemd/system/cc-plugin-coolerdash.service.d/startup-delay.conf %changelog * %(date "+%a %b %d %Y") damachine - %{version}-1 diff --git a/packaging/debian/install b/packaging/debian/install index e0b4112..7c7725b 100644 --- a/packaging/debian/install +++ b/packaging/debian/install @@ -12,5 +12,3 @@ usr/share/man/man1/coolerdash.1 usr/share/applications/coolerdash.desktop usr/share/icons/hicolor/scalable/apps/coolerdash.svg usr/lib/udev/rules.d/99-coolerdash.rules -usr/lib/systemd/system/coolerdash-helperd.service -etc/systemd/system/cc-plugin-coolerdash.service.d/startup-delay.conf diff --git a/src/main.c b/src/main.c index 5e5de32..1a1a196 100644 --- a/src/main.c +++ b/src/main.c @@ -39,7 +39,6 @@ // Security and performance constants #define DEFAULT_VERSION "unknown" -#define SHUTDOWN_RETRY_COUNT 2 #define VERSION_BUFFER_SIZE 32 /** @@ -58,9 +57,6 @@ int verbose_logging = 0; // Only ERROR and WARNING by default (exported) // treated as circular int force_display_circular = 0; -// CLI mode: send shutdown image and exit -int send_shutdown_only = 0; - /** * @brief Global pointer to configuration. * @details Points to the current configuration used by the daemon. Initialized @@ -204,8 +200,7 @@ static void show_help(const char *program_name) printf(" --circle Force circle mode (alternating CPU/GPU every 2.5 " "seconds)\n"); printf(" --develop Developer: force display to be treated as " - "circular for testing\n"); - printf(" --shutdown Send shutdown image to device and exit (use with systemd ExecStop)\n\n"); + "circular for testing\n\n"); printf("DISPLAY MODES:\n"); printf( " dual Default mode - shows CPU and GPU simultaneously\n"); @@ -222,8 +217,6 @@ static void show_help(const char *program_name) printf(" %s --circle # Standalone with circle mode " "(alternating display)\n", program_name); - printf(" %s --shutdown # Send shutdown image and exit\n", - program_name); printf(" %s --dual --verbose # Force dual mode with detailed " "logging\n", program_name); @@ -293,88 +286,6 @@ static void show_system_diagnostics(const Config *config, int api_width, config->display_refresh_interval); } -/** - * @brief Validate session and configuration for shutdown image. - * @details Checks if session is initialized and configuration is valid. - */ -static int validate_shutdown_conditions(void) -{ - if (!is_session_initialized() || !g_config_ptr) - { - return 0; - } - return 1; -} - -/** - * @brief Get device UID for shutdown operation. - * @details Retrieves device UID from liquidctl data. - */ -static int get_device_uid_for_shutdown(char *device_uid, size_t uid_size) -{ - if (!get_liquidctl_data(g_config_ptr, device_uid, uid_size, NULL, 0, NULL, - NULL) || - !device_uid[0]) - { - return 0; - } - return 1; -} - -/** - * @brief Send shutdown image if needed or turn off LCD if image is missing. - * @details Checks if shutdown image should be sent to LCD device and performs - * the transmission if conditions are met. If shutdown image is missing, sets - * LCD brightness to 0 to turn off the display. - */ -static void send_shutdown_image_if_needed(void) -{ - if (!validate_shutdown_conditions()) - { - return; - } - - char device_uid[128]; - if (!get_device_uid_for_shutdown(device_uid, sizeof(device_uid))) - { - return; - } - - const char *shutdown_image_path = g_config_ptr->paths_image_shutdown; - if (!shutdown_image_path || !shutdown_image_path[0]) - { - return; - } - - FILE *image_file = fopen(shutdown_image_path, "r"); - if (image_file) - { - fclose(image_file); - // Use blocking upload with timeout and retries to ensure image is sent - if (!send_image_to_lcd_blocking(g_config_ptr, shutdown_image_path, - device_uid, 5, SHUTDOWN_RETRY_COUNT)) - { - log_message(LOG_WARNING, "Shutdown image upload failed after retries"); - } - } - else - { - // Turn off display using blocking upload to ensure it reaches device - Config temp_config = *g_config_ptr; - temp_config.lcd_brightness = 0; - - const char *fallback_image = g_config_ptr->paths_image_coolerdash; - if (fallback_image && fallback_image[0]) - { - if (!send_image_to_lcd_blocking(&temp_config, fallback_image, - device_uid, 5, SHUTDOWN_RETRY_COUNT)) - { - log_message(LOG_WARNING, "Fallback shutdown action failed after retries"); - } - } - } -} - /** * @brief Enhanced signal handler with atomic operations and secure shutdown. * @details Signal-safe implementation using only async-signal-safe functions. @@ -559,10 +470,6 @@ static const char *parse_arguments(int argc, char **argv, force_display_circular = 1; verbose_logging = 1; // Developer mode implies verbose logging } - else if (strcmp(argv[i], "--shutdown") == 0) - { - send_shutdown_only = 1; - } else if (argv[i][0] != '-') { config_path = argv[i]; @@ -730,13 +637,12 @@ static void initialize_device_info(Config *config) /** * @brief Perform cleanup operations. - * @details Removes PID and image files, sends shutdown image, closes - * CoolerControl session. + * @details Removes image files and closes CoolerControl session. + * Shutdown image is handled by CC4 natively via register_shutdown_image_with_cc(). */ static void perform_cleanup(const Config *config) { log_message(LOG_INFO, "Daemon shutdown initiated"); - send_shutdown_image_if_needed(); // Close CoolerControl session and free resources cleanup_coolercontrol_session(); @@ -790,11 +696,18 @@ int main(int argc, char **argv) log_message(LOG_STATUS, "CoolerDash initializing device cache...\n"); initialize_device_info(&config); - if (send_shutdown_only) + /* CC4: Register shutdown.png once at startup so CoolerControl displays + * it natively when the CC daemon stops (MR !417 / CC 4.0). */ + if (config.paths_image_shutdown[0]) { - log_message(LOG_STATUS, "Shutdown mode: performing cleanup (send image) and exiting"); - perform_cleanup(&config); - return EXIT_SUCCESS; + char device_uid[128] = {0}; + if (get_liquidctl_data(&config, device_uid, sizeof(device_uid), + NULL, 0, NULL, NULL) && device_uid[0]) + { + register_shutdown_image_with_cc(&config, + config.paths_image_shutdown, + device_uid); + } } log_message(LOG_STATUS, "Starting daemon"); diff --git a/src/srv/cc_main.c b/src/srv/cc_main.c index 532073f..cbe6ce4 100644 --- a/src/srv/cc_main.c +++ b/src/srv/cc_main.c @@ -667,45 +667,83 @@ int send_image_to_lcd(const Config *config, const char *image_path, } /** - * @brief Blocking wrapper for `send_image_to_lcd` with timeout and retries. - * @details Temporarily sets CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT on the - * global CURL handle, attempts the upload up to `retries` times and restores - * timeouts to defaults after completion. + * @brief Register shutdown image with CC4's persistent LCD shutdown endpoint. + * @details Called once at daemon startup. Uploads shutdown.png to + * PUT /devices/{uid}/settings/lcd/lcd/shutdown-image so CoolerControl + * displays it automatically whenever the CC daemon itself stops. + * Gracefully skips if the endpoint is unavailable (CC3 / pre-CC4). */ -int send_image_to_lcd_blocking(const Config *config, const char *image_path, - const char *device_uid, int timeout_seconds, - int retries) +int register_shutdown_image_with_cc(const Config *config, + const char *image_path, + const char *device_uid) { if (!validate_upload_params(image_path, device_uid)) return 0; - if (timeout_seconds <= 0) - timeout_seconds = 5; // sensible default - if (retries <= 0) - retries = 1; + if (!image_path || !image_path[0]) + return 1; - int attempt; - int success = 0; + /* Verify the file exists before attempting to upload */ + FILE *f = fopen(image_path, "r"); + if (!f) + { + log_message(LOG_WARNING, + "CC4 shutdown registration skipped: image not found: %s", + image_path); + return 0; + } + fclose(f); + + char upload_url[CC_URL_SIZE]; + int written = snprintf(upload_url, sizeof(upload_url), + "%s/devices/%s/settings/lcd/lcd/shutdown-image", + config->daemon_address, device_uid); + if (written < 0 || (size_t)written >= sizeof(upload_url)) + return 0; - for (attempt = 0; attempt < retries; attempt++) + curl_mime *form = build_lcd_upload_form(config, image_path); + if (!form) + return 0; + + http_response response = {0}; + if (!cc_init_response_buffer(&response, 4096)) { - // Set conservative timeouts for shutdown path - curl_easy_setopt(cc_session.curl_handle, CURLOPT_TIMEOUT, - (long)timeout_seconds); - curl_easy_setopt(cc_session.curl_handle, CURLOPT_CONNECTTIMEOUT, - (long)(timeout_seconds / 2 > 0 ? timeout_seconds / 2 : 1)); + curl_mime_free(form); + return 0; + } - success = send_image_to_lcd(config, image_path, device_uid); - if (success) - break; + struct curl_slist *headers = + configure_lcd_upload_curl(config, upload_url, form, &response); - log_message(LOG_WARNING, "Shutdown upload attempt %d/%d failed", - attempt + 1, retries); + CURLcode res = curl_easy_perform(cc_session.curl_handle); + long http_code = -1; + curl_easy_getinfo(cc_session.curl_handle, CURLINFO_RESPONSE_CODE, &http_code); + + int success = 0; + if (res == CURLE_OK && (http_code == 200 || http_code == 204)) + { + log_message(LOG_STATUS, + "Shutdown image registered with CC4 (HTTP %ld)", http_code); + success = 1; + } + else if (res == CURLE_OK && http_code == 404) + { + log_message(LOG_WARNING, + "CC4 shutdown image endpoint not available (HTTP 404) " + "- requires CoolerControl 4.0 or later"); + } + else + { + log_message(LOG_WARNING, + "Shutdown image registration failed: CURL %d, HTTP %ld", + res, http_code); } - // Restore to no timeout (behaviour prior to explicit shutdown upload) - curl_easy_setopt(cc_session.curl_handle, CURLOPT_TIMEOUT, 0L); - curl_easy_setopt(cc_session.curl_handle, CURLOPT_CONNECTTIMEOUT, 0L); + cc_cleanup_response_buffer(&response); + if (headers) + curl_slist_free_all(headers); + curl_mime_free(form); + cleanup_lcd_upload_curl(); return success; } diff --git a/src/srv/cc_main.h b/src/srv/cc_main.h index a7e4ba6..03e2c88 100644 --- a/src/srv/cc_main.h +++ b/src/srv/cc_main.h @@ -120,15 +120,13 @@ int send_image_to_lcd(const struct Config *config, const char *image_path, const char *device_uid); /** - * @brief Blocking variant of `send_image_to_lcd` with timeout and retries. - * @details Used for shutdown path to ensure the image upload completes - * synchronously. Sets temporary CURLOPT_TIMEOUT/CURLOPT_CONNECTTIMEOUT for - * the duration of the operation and restores defaults afterwards. + * @brief Register shutdown image with CC4's persistent LCD shutdown endpoint. + * @details Called once at daemon startup. CC4 will display the image + * automatically whenever the coolercontrold daemon stops. + * Silently skips on 404 (CC3 / pre-CC4 compatibility). */ -int send_image_to_lcd_blocking(const struct Config *config, - const char *image_path, - const char *device_uid, - int timeout_seconds, - int retries); +int register_shutdown_image_with_cc(const struct Config *config, + const char *image_path, + const char *device_uid); #endif // CC_MAIN_H From 9b1ce18075c3971620836a371b1524106aff8572 Mon Sep 17 00:00:00 2001 From: damachine Date: Mon, 9 Mar 2026 19:31:06 +0100 Subject: [PATCH 4/5] update docs and remove legacy service & udev rules --- .github/workflows/install.yml | 16 ------- Makefile | 15 +----- PKGBUILD | 4 -- aur/PKGBUILD | 2 - aur/coolerdash.install | 8 +--- coolerdash.install | 8 +--- docs/config-guide.md | 89 +++++++++++++++++++---------------- docs/coolercontrol-api.md | 34 +++++++++++-- docs/developer-guide.md | 24 ++++++---- docs/display-modes.md | 25 +++++----- docs/plugin-integration.md | 71 +++++++++++++++++----------- docs/plugin-ui-theming.md | 1 + packaging/coolerdash.spec | 2 +- packaging/debian/install | 1 - packaging/debian/postinst | 9 ++-- 15 files changed, 159 insertions(+), 150 deletions(-) diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml index ff59bed..db4b518 100644 --- a/.github/workflows/install.yml +++ b/.github/workflows/install.yml @@ -156,14 +156,6 @@ jobs: echo "⚠️ Manual page not found" fi - # Check udev rules - if [ -f "/tmp/install-test/usr/lib/udev/rules.d/99-coolerdash.rules" ]; then - echo "✅ Udev rules installed" - else - echo "❌ Udev rules not found" - exit 1 - fi - # Check license file (Linux packaging standard) if [ -f "/tmp/install-test/usr/share/licenses/coolerdash/LICENSE" ]; then echo "✅ License file installed (/usr/share/licenses/)" @@ -217,14 +209,6 @@ jobs: exit 1 fi - # Check udev rules - if [ -f "/tmp/install-test/usr/lib/udev/rules.d/99-coolerdash.rules" ]; then - echo "✅ Udev rules installed" - else - echo "❌ Udev rules not found" - exit 1 - fi - # Check license file (Arch packaging guideline) if [ -f "/tmp/install-test/usr/share/licenses/coolerdash/LICENSE" ]; then echo "✅ License file installed (/usr/share/licenses/)" diff --git a/Makefile b/Makefile index 5375657..54f2640 100644 --- a/Makefile +++ b/Makefile @@ -318,14 +318,6 @@ install: check-deps $(TARGET) @printf "$(CYAN)Installing icon...$(RESET)\n" @install -Dm644 etc/icons/coolerdash.svg "$(DESTDIR)/usr/share/icons/hicolor/scalable/apps/coolerdash.svg" @printf " $(GREEN)Icon:$(RESET) $(DESTDIR)/usr/share/icons/hicolor/scalable/apps/coolerdash.svg\n" - @printf "$(CYAN)Installing udev rules for USB power management...$(RESET)\n" - @install -Dm644 etc/udev/rules.d/99-coolerdash.rules "$(DESTDIR)/usr/lib/udev/rules.d/99-coolerdash.rules" - @printf " $(GREEN)udev rule:$(RESET) $(DESTDIR)/usr/lib/udev/rules.d/99-coolerdash.rules\n" - @if [ "$(REALOS)" = "yes" ]; then \ - $(SUDO) udevadm control --reload-rules 2>/dev/null || true; \ - $(SUDO) udevadm trigger --subsystem-match=usb 2>/dev/null || true; \ - printf " $(GREEN)OK$(RESET) USB power management configured\n"; \ - fi @printf "\n" @printf "$(WHITE)INSTALLATION SUCCESSFUL$(RESET)\n" @printf "\n" @@ -410,13 +402,8 @@ uninstall: @$(SUDO) rm -rf "$(DESTDIR)/usr/share/licenses/coolerdash" @$(SUDO) rm -f "$(DESTDIR)/usr/share/man/man1/coolerdash.1" @$(SUDO) rm -f "$(DESTDIR)/usr/share/applications/coolerdash.desktop" - @printf "$(CYAN)Removing udev rule...$(RESET)\n" + # Legacy cleanup: remove udev rule if installed by older version @$(SUDO) rm -f "$(DESTDIR)/usr/lib/udev/rules.d/99-coolerdash.rules" - @if [ "$(REALOS)" = "yes" ]; then \ - $(SUDO) udevadm control --reload-rules 2>/dev/null || true; \ - $(SUDO) udevadm trigger --subsystem-match=usb 2>/dev/null || true; \ - printf " $(GREEN)OK$(RESET) udev rules reloaded\n"; \ - fi @$(SUDO) rm -f "$(DESTDIR)/usr/share/icons/hicolor/scalable/apps/coolerdash.svg" @if [ "$(REALOS)" = "yes" ]; then \ if id -u coolerdash >/dev/null 2>&1; then \ diff --git a/PKGBUILD b/PKGBUILD index cd9bc4d..7deb63b 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,9 +1,7 @@ # Maintainer: damachin3 (damachine3 at proton dot me) # Website: https://github.com/damachine/coolerdash - # This PKGBUILD is for building the coolerdash package from local source. # It assumes the source code is already present in the current directory. - pkgname=coolerdash pkgver=$(cat VERSION) pkgrel=1 @@ -55,7 +53,6 @@ build() { cp -a etc/coolercontrol/plugins/coolerdash/manifest.toml "${srcdir}/etc/coolercontrol/plugins/coolerdash/" cp -a etc/applications/coolerdash.desktop "${srcdir}/etc/applications/" cp -a etc/icons/coolerdash.svg "${srcdir}/etc/icons/" - cp -a etc/udev/rules.d/99-coolerdash.rules "${srcdir}/etc/udev/rules.d/" } check() { @@ -91,7 +88,6 @@ package() { install -Dm644 "${srcdir}/man/coolerdash.1" "${pkgdir}/usr/share/man/man1/coolerdash.1" install -Dm644 "${srcdir}/etc/applications/coolerdash.desktop" "${pkgdir}/usr/share/applications/coolerdash.desktop" - install -Dm644 "${srcdir}/etc/udev/rules.d/99-coolerdash.rules" "${pkgdir}/usr/lib/udev/rules.d/99-coolerdash.rules" install -Dm644 "${srcdir}/etc/icons/coolerdash.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/coolerdash.svg" install -Dm644 "${srcdir}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" } diff --git a/aur/PKGBUILD b/aur/PKGBUILD index 402247a..17f2310 100644 --- a/aur/PKGBUILD +++ b/aur/PKGBUILD @@ -1,6 +1,5 @@ # Maintainer: damachin3 (damachine3 at proton dot me) # Website: https://github.com/damachine/coolerdash - pkgname=coolerdash-git pkgver= pkgrel=1 @@ -75,7 +74,6 @@ package() { install -Dm644 "${srcdir}/${pkgname}/man/coolerdash.1" "${pkgdir}/usr/share/man/man1/coolerdash.1" install -Dm644 "${srcdir}/${pkgname}/etc/applications/coolerdash.desktop" "${pkgdir}/usr/share/applications/coolerdash.desktop" - install -Dm644 "${srcdir}/${pkgname}/etc/udev/rules.d/99-coolerdash.rules" "${pkgdir}/usr/lib/udev/rules.d/99-coolerdash.rules" install -Dm644 "${srcdir}/${pkgname}/etc/icons/coolerdash.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/coolerdash.svg" install -Dm644 "${srcdir}/${pkgname}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" } diff --git a/aur/coolerdash.install b/aur/coolerdash.install index 91683e4..df75292 100644 --- a/aur/coolerdash.install +++ b/aur/coolerdash.install @@ -34,17 +34,12 @@ pre_install() { } post_install() { - # Reload udev rules - if command -v udevadm >/dev/null 2>&1; then - udevadm control --reload-rules - udevadm trigger --subsystem-match=usb - fi - # Remove legacy files rm -f /etc/systemd/system/multi-user.target.wants/coolerdash-helperd.service rm -f /etc/systemd/system/coolerdash-helperd.service rm -f /usr/lib/systemd/system/coolerdash-helperd.service rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d + rm -f /usr/lib/udev/rules.d/99-coolerdash.rules systemctl daemon-reload @@ -64,6 +59,7 @@ post_upgrade() { rm -f /etc/systemd/system/coolerdash-helperd.service rm -f /usr/lib/systemd/system/coolerdash-helperd.service rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d + rm -f /usr/lib/udev/rules.d/99-coolerdash.rules systemctl daemon-reload diff --git a/coolerdash.install b/coolerdash.install index 91683e4..df75292 100644 --- a/coolerdash.install +++ b/coolerdash.install @@ -34,17 +34,12 @@ pre_install() { } post_install() { - # Reload udev rules - if command -v udevadm >/dev/null 2>&1; then - udevadm control --reload-rules - udevadm trigger --subsystem-match=usb - fi - # Remove legacy files rm -f /etc/systemd/system/multi-user.target.wants/coolerdash-helperd.service rm -f /etc/systemd/system/coolerdash-helperd.service rm -f /usr/lib/systemd/system/coolerdash-helperd.service rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d + rm -f /usr/lib/udev/rules.d/99-coolerdash.rules systemctl daemon-reload @@ -64,6 +59,7 @@ post_upgrade() { rm -f /etc/systemd/system/coolerdash-helperd.service rm -f /usr/lib/systemd/system/coolerdash-helperd.service rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d + rm -f /usr/lib/udev/rules.d/99-coolerdash.rules systemctl daemon-reload diff --git a/docs/config-guide.md b/docs/config-guide.md index a3276f8..2a5a032 100644 --- a/docs/config-guide.md +++ b/docs/config-guide.md @@ -19,27 +19,24 @@ sudo systemctl restart coolercontrold ## Daemon Settings -Configure connection to the CoolerControl daemon API. +Connection to the CoolerControl daemon. -### Example ```json "daemon": { "address": "http://localhost:11987", + "access_token": "", "password": "coolAdmin" } ``` -### Settings -- **`address`**: API endpoint URL (default: `http://localhost:11987`) -- **`password`**: API authentication password (default: `coolAdmin`) +| Key | Default | Description | +|-----|---------|-------------| +| `address` | `http://localhost:11987` | API endpoint | +| `access_token` | `""` | **CC4:** Bearer token from CoolerControl UI → Access Protection. Format: `cc_`. Takes precedence over `password`. | +| `password` | `coolAdmin` | **CC3 / fallback.** Ignored when `access_token` is set. | -### HTTPS Example -```json -"daemon": { - "address": "https://192.168.1.100:11987", - "password": "mySecurePassword123" -} -``` +> CC4: generate a token in CoolerControl UI under **Access Protection** and paste it into `access_token`. +> CC3: leave `access_token` empty, set `password`. --- @@ -95,6 +92,11 @@ LCD display configuration tested with NZXT Kraken 2023. - **`circle_switch_interval`**: Slot switch interval for circle mode in seconds (1–60, default: `5`) - **`content_scale_factor`**: Safe area percentage (0.5–1.0, default: `0.98`) - **`inscribe_factor`**: Inscribe factor for circular displays (default: `0.70710678` = 1/√2) +- **`sensor_slot_up`**: Sensor shown in top slot: `cpu`, `gpu`, or `liquid` (default: `cpu`) +- **`sensor_slot_mid`**: Sensor shown in middle slot (default: `liquid`) +- **`sensor_slot_down`**: Sensor shown in bottom slot (default: `gpu`) + +Sensor slots control which sensor appears in each display position for both dual and circle mode. ### Brightness Examples ```json @@ -182,35 +184,36 @@ Controls the safe area percentage used for rendering content (determines margin/ --- -## 🎨 Visual Layout - -Display layout and spacing configuration. All positioning is now calculated dynamically from display dimensions. +## Visual Layout -### Example Configuration -```ini -[layout] -bar_height=24 -bar_width=98 -bar_gap=12 -bar_border=2.0 -``` - -### Settings -- **`bar_height`**: Temperature bar thickness in pixels (default: auto-scaled) -- **`bar_width`**: Bar width in percent of display width (1–100%, default: 98) -- **`bar_gap`**: Gap between bars in pixels (default: auto-scaled) -- **`bar_border`**: Border thickness in pixels (default: 1.0) - -### Layout Examples +All values are in pixels unless noted. Positions are calculated dynamically from display dimensions. ```json -// Compact -"layout": { "bar_height": 18, "bar_width": 90, "bar_gap": 5, "bar_border": 1.0 } - -// Spacious -"layout": { "bar_height": 26, "bar_width": 98, "bar_gap": 15, "bar_border": 2.0 } +"layout": { + "bar_height": 24, + "bar_width": 98, + "bar_gap": 12, + "bar_border": 1.0, + "bar_border_enabled": 1, + "label_margin_left": 1, + "label_margin_bar": 1, + "bar_height_up": 0, + "bar_height_mid": 0, + "bar_height_down": 0 +} ``` +| Key | Default | Description | +|-----|---------|-------------| +| `bar_height` | `24` | Bar height in pixels | +| `bar_width` | `98` | Bar width in % of display width | +| `bar_gap` | `12` | Gap between bars in pixels | +| `bar_border` | `1.0` | Border thickness in pixels | +| `bar_border_enabled` | `1` | Enable bar border (`1`/`0`) | +| `label_margin_left` | `1` | Left label margin multiplier | +| `label_margin_bar` | `1` | Margin between label and bar | +| `bar_height_up/mid/down` | `0` | Per-slot bar height override. `0` = use `bar_height` | + --- ## Colors @@ -255,14 +258,18 @@ Four color zones based on temperature thresholds. Configured per-sensor in the ` "threshold_1": 55.0, "threshold_2": 65.0, "threshold_3": 75.0, - "threshold_1_bar": { "r": 0, "g": 255, "b": 0 }, - "threshold_2_bar": { "r": 255, "g": 140, "b": 0 }, - "threshold_3_bar": { "r": 255, "g": 70, "b": 0 }, - "threshold_4_bar": { "r": 255, "g": 0, "b": 0 } - } + "max_scale": 115.0, + "threshold_1_color": { "r": 0, "g": 255, "b": 0 }, + "threshold_2_color": { "r": 255, "g": 140, "b": 0 }, + "threshold_3_color": { "r": 255, "g": 70, "b": 0 }, + "threshold_4_color": { "r": 255, "g": 0, "b": 0 } + }, + "gpu": { ... } } ``` +`max_scale` sets the temperature at 100% bar fill. Bars transition through 4 color zones as temperature rises through the thresholds. + --- ## Complete Example diff --git a/docs/coolercontrol-api.md b/docs/coolercontrol-api.md index 786cd26..0ad6161 100644 --- a/docs/coolercontrol-api.md +++ b/docs/coolercontrol-api.md @@ -29,10 +29,11 @@ CoolerDash acts as a specialized LCD client for CoolerControl, fetching temperat ### API Endpoints Used ``` -POST /login - Authentication -GET /devices - Device enumeration -POST /status - Temperature sensor data -PUT /devices/{uid}/settings/lcd/... - LCD image upload +POST /login - CC3 authentication (Basic Auth cookie) +GET /devices - Device enumeration +POST /status - Temperature sensor data +PUT /devices/{uid}/settings/lcd/lcd/images - LCD image upload +PUT /devices/{uid}/settings/lcd/lcd/shutdown-image - Shutdown image registration (CC4 only) ``` ### Dependencies @@ -171,7 +172,17 @@ static CoolerControlSession cc_session = { **Function**: `init_coolercontrol_session(const Config *config)` -**Steps**: +**CC4 — Bearer Token (primary):** +- If `access_token` is set in config, all requests use `Authorization: Bearer cc_` +- No cookie jar, no `/login` call needed +- Token is generated in CoolerControl UI under **Access Protection** + +**CC3 — Basic Auth cookie (fallback):** +- Used when `access_token` is empty +- Sends `POST /login` with HTTP Basic Auth (`CCAdmin:{password}`) +- Cookie jar stored at `/tmp/coolerdash_cookie_{PID}.txt` (in-memory, written only on cleanup, deleted immediately after) + +**Steps (CC3 path)**: 1. Initialize libcurl global state 2. Create CURL easy handle 3. Configure cookie jar for session persistence @@ -181,6 +192,19 @@ static CoolerControlSession cc_session = { 7. Validate response (200/204) 8. Store cookies for subsequent requests +--- + +#### 4. Shutdown Image Registration (CC4) + +**Function**: `register_shutdown_image_with_cc(const Config *config, const char *image_path, const char *device_uid)` + +Called **once at startup** after device initialization. Registers `shutdown.png` with CC4: + +- Endpoint: `PUT {address}/devices/{uid}/settings/lcd/lcd/shutdown-image` +- Same multipart form as live image upload +- CC4 stores image server-side; displays it when CoolerControl stops +- Returns gracefully on 404 (CC3 — endpoint does not exist) + **Implementation**: ```c int init_coolercontrol_session(const Config *config) diff --git a/docs/developer-guide.md b/docs/developer-guide.md index 8f6e6f3..6764589 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -29,12 +29,13 @@ CoolerDash extends the LCD functionality of [CoolerControl](https://gitlab.com/c ### Key Features -- **Real-time Temperature Monitoring:** CPU/GPU sensor data via CoolerControl REST API +- **Real-time Temperature Monitoring:** CPU/GPU/liquid sensor data via CoolerControl REST API - **Adaptive Display Rendering:** Automatic circular/rectangular display detection -- **Customizable UI:** Full color/layout/font configuration via INI file -- **Secure Session Management:** HTTP Basic Auth with cookie-based session persistence +- **Customizable UI:** Full color/layout/font/sensor-slot configuration via `config.json` +- **CC4 Authentication:** Bearer Token (primary) with CC3 Basic Auth cookie fallback +- **Shutdown Image:** Registered with CC4 at startup — CC handles display on daemon stop - **Efficient Caching:** One-time device information retrieval at startup -- **Systemd Integration:** Native service management with automatic startup +- **CoolerControl Plugin:** Managed by `cc-plugin-coolerdash.service`, no separate systemd service needed ### System Requirements @@ -45,7 +46,7 @@ CoolerDash extends the LCD functionality of [CoolerControl](https://gitlab.com/c - `jansson` — JSON parsing (config + API) - `libcurl-gnutls` — HTTP client - `ttf-roboto` — Font rendering -- **Required Service:** CoolerControl >=3.x (must be running) +- **Required Service:** CoolerControl >=4.x recommended (CC3 supported as fallback) --- @@ -59,13 +60,16 @@ CoolerDash extends the LCD functionality of [CoolerControl](https://gitlab.com/c │ ┌───────────────────────────────────────────────────────────┐ │ │ │ 1. Configuration Loading (device/config.c) │ │ │ │ 2. Session Initialization (cc_main.c) │ │ +│ │ ├─ CC4: Bearer Token header │ │ +│ │ └─ CC3: Basic Auth cookie via /login │ │ │ │ 3. Device Cache Setup (cc_conf.c) │ │ -│ │ 4. Main Loop (configurable interval) │ │ +│ │ 4. Shutdown Image Registration (cc_main.c, CC4 only) │ │ +│ │ 5. Main Loop (configurable interval) │ │ │ │ ├─ Temperature Reading (cc_sensor.c) │ │ │ │ ├─ Image Rendering (display.c → dual.c|circle.c) │ │ │ │ └─ LCD Upload (cc_main.c) │ │ -│ │ 5. Signal Handling (SIGTERM/SIGINT → shutdown image) │ │ -│ │ 6. Cleanup (session + image files) │ │ +│ │ 6. Signal Handling (SIGTERM/SIGINT → graceful stop) │ │ +│ │ 7. Cleanup (session + image files) │ │ │ └───────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ↓ ↓ ↓ @@ -109,7 +113,7 @@ coolerdash/ │ └── circle.c/h # Circle mode (alternating sensor) ├── etc/ │ ├── coolercontrol/plugins/coolerdash/config.json # User configuration -│ └── systemd/coolerdash.service +│ └── systemd/coolerdash.service ← managed by CoolerControl (cc-plugin-coolerdash.service) ├── docs/ # Documentation │ ├── config.md # Configuration guide │ ├── devices.md # Supported hardware @@ -124,7 +128,7 @@ coolerdash/ |--------|---------|---------------|-------|------------| | **main.c** | Daemon lifecycle | Signal handling, PID management, main loop | — | `main()` | | **device/config** | Config system | JSON loading, hardcoded defaults | — | `load_plugin_config()` | -| **srv/cc_main** | HTTP session | Login, LCD upload, cleanup | — | 4 functions | +| **srv/cc_main** | HTTP session | Login/token auth, LCD upload, shutdown image registration | — | 5 functions | | **srv/cc_conf** | Device cache | UID/name/dimensions, display shape | — | 4 functions | | **srv/cc_sensor** | Temperature | CPU/GPU sensor reading | — | 1 function | | **mods/display** | Mode dispatch | Route to dual/circle, shared Cairo helpers | — | `draw_display_image()` | diff --git a/docs/display-modes.md b/docs/display-modes.md index 3c3eda9..9c624fa 100644 --- a/docs/display-modes.md +++ b/docs/display-modes.md @@ -21,13 +21,10 @@ CoolerDash supports two distinct display modes for rendering temperature informa ### Mode Selection -The mode is selected through a three-tier configuration system: +The mode is selected through a two-tier configuration system: 1. **System Default** (`src/device/config.c`): `display_mode = "dual"` 2. **User Configuration** (`/etc/coolercontrol/plugins/coolerdash/config.json`): `"mode": "dual"|"circle"` -3. **CLI Override** (`src/main.c`): `--dual` or `--circle` flags - -Priority: **CLI > JSON > Default** --- @@ -169,17 +166,23 @@ Alternates between CPU and GPU display every 5 seconds, optimized for high-resol ### Key Components -#### 1. SensorMode Enumeration -```c -typedef enum { - SENSOR_CPU = 0, - SENSOR_GPU = 1 -} SensorMode; +#### 1. SensorMode + +Circle mode cycles through the three sensor slots configured in `config.json`: + +```json +"display": { + "sensor_slot_up": "cpu", + "sensor_slot_mid": "liquid", + "sensor_slot_down": "gpu" +} ``` +The slots are `cpu`, `gpu`, or `liquid`. Each slot is displayed full-screen in turn. + #### 2. Global State Management ```c -static SensorMode current_sensor = SENSOR_CPU; +static int current_slot = 0; // cycles 0→1→2→0 static time_t last_switch_time = 0; ``` diff --git a/docs/plugin-integration.md b/docs/plugin-integration.md index 3478a5b..c5d2f50 100644 --- a/docs/plugin-integration.md +++ b/docs/plugin-integration.md @@ -2,13 +2,37 @@ ## Overview -CoolerDash is now fully integrated as a CoolerControl plugin with enhanced UI support. +CoolerDash runs as a native CoolerControl plugin managed by `cc-plugin-coolerdash.service`. No separate systemd service or startup delay is needed. CoolerControl handles the plugin lifecycle. -## New Features in 2.2.x +## CC4 Integration -### 1. Theme-Adaptive UI +### Authentication -The plugin UI now automatically adapts to the user's CoolerControl theme: +CC4 uses Bearer Token authentication. Generate a token in CoolerControl UI under **Access Protection** and set it in `config.json`: + +```json +"daemon": { + "access_token": "cc_" +} +``` + +CC3 fallback: leave `access_token` empty, set `password` instead. + +### Shutdown Image + +CoolerDash registers `shutdown.png` with CC4 once at startup: + +``` +PUT /devices/{uid}/settings/lcd/lcd/shutdown-image +``` + +CC4 stores the image server-side and displays it when CoolerControl stops. No helper daemon or `ExecStop` workaround needed. + +## Plugin UI + +### Theme-Adaptive UI + +The plugin UI automatically adapts to the user's CoolerControl theme: - **Dark/Light Theme Support** - Seamlessly integrates with user preferences - **Theme Color Variables** - Uses CoolerControl's native color system @@ -24,9 +48,7 @@ The plugin UI now automatically adapts to the user's CoolerControl theme: --colors-accent /* Accent/highlight color */ ``` -### 2. Tailwind CSS Integration - -The UI leverages Tailwind CSS classes for rapid development: +### Tailwind CSS ```html
@@ -34,22 +56,18 @@ The UI leverages Tailwind CSS classes for rapid development:
``` -### 3. PrimeIcons Support - -Consistent iconography using CoolerControl's icon library: +### PrimeIcons ```html ``` -### 4. Manifest Enhancements - -The `manifest.toml` now includes: +### Manifest ```toml -version = "2.2.x" # Displayed in plugin list -url = "https://github.com/damachine/coolerdash" # Link to project homepage +version = "{{VERSION}}" +url = "https://github.com/damachine/coolerdash" ``` These fields are displayed on the CoolerControl plugin page, helping users: @@ -59,18 +77,18 @@ These fields are displayed on the CoolerControl plugin page, helping users: ## Plugin UI Structure -### Before (2.0.4 and earlier) +### Before (2.x and earlier) -- ❌ Hardcoded colors (didn't adapt to themes) -- ❌ Custom CSS only (no Tailwind support) -- ❌ Toast notifications (not standard in CC) +- ❌ Hardcoded colors +- ❌ Separate helperd service for shutdown +- ❌ Custom CSS only -### After (2.2.x) +### Current (3.x) -- ✅ Theme-adaptive colors using CSS variables -- ✅ Tailwind CSS for consistent styling -- ✅ Standard console logging (no custom toasts) -- ✅ PrimeIcons for consistent iconography +- ✅ Theme-adaptive colors via CSS variables +- ✅ Tailwind CSS +- ✅ Shutdown handled natively by CC4 +- ✅ PrimeIcons ## Configuration UI @@ -151,10 +169,7 @@ If you're updating from an older version: ## Acknowledgments -Special thanks to @codifryed (CoolerControl developer) for: -- Providing the custom-device plugin as reference -- Sharing information about theme colors and Tailwind CSS support -- Implementing the version/url manifest fields +Special thanks to @codifryed (CoolerControl developer) for the custom-device plugin reference and CC4 API design. ## Related Documentation diff --git a/docs/plugin-ui-theming.md b/docs/plugin-ui-theming.md index 5e6fca6..e95006f 100644 --- a/docs/plugin-ui-theming.md +++ b/docs/plugin-ui-theming.md @@ -186,5 +186,6 @@ These will be displayed on the plugin page in CoolerControl's UI. ## Changelog +- **3.x** - CC4 Bearer Token auth, native shutdown image via CC4 API - **2.2.x** - Added theme color support and Tailwind CSS integration - **2.0.4** - Initial plugin UI implementation diff --git a/packaging/coolerdash.spec b/packaging/coolerdash.spec index 5c65fe7..4eba070 100644 --- a/packaging/coolerdash.spec +++ b/packaging/coolerdash.spec @@ -83,6 +83,7 @@ rm -f /etc/systemd/system/multi-user.target.wants/coolerdash-helperd.service rm -f /etc/systemd/system/coolerdash-helperd.service rm -f /usr/lib/systemd/system/coolerdash-helperd.service rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d +rm -f /usr/lib/udev/rules.d/99-coolerdash.rules if command -v systemctl >/dev/null 2>&1; then systemctl daemon-reload if systemctl is-active --quiet coolercontrold.service; then @@ -149,7 +150,6 @@ fi /usr/share/man/man1/coolerdash.1.gz /usr/share/applications/coolerdash.desktop /usr/share/icons/hicolor/scalable/apps/coolerdash.svg -/usr/lib/udev/rules.d/99-coolerdash.rules %changelog * %(date "+%a %b %d %Y") damachine - %{version}-1 diff --git a/packaging/debian/install b/packaging/debian/install index 7c7725b..920e12b 100644 --- a/packaging/debian/install +++ b/packaging/debian/install @@ -11,4 +11,3 @@ etc/coolercontrol/plugins/coolerdash/ui/cc-plugin-lib.js usr/share/man/man1/coolerdash.1 usr/share/applications/coolerdash.desktop usr/share/icons/hicolor/scalable/apps/coolerdash.svg -usr/lib/udev/rules.d/99-coolerdash.rules diff --git a/packaging/debian/postinst b/packaging/debian/postinst index f95df70..7bbda52 100755 --- a/packaging/debian/postinst +++ b/packaging/debian/postinst @@ -8,16 +8,15 @@ case "$1" in chmod 666 /etc/coolercontrol/plugins/coolerdash/config.json fi - # Migrate helperd from /etc to /usr/lib + # Remove legacy files rm -f /etc/systemd/system/multi-user.target.wants/coolerdash-helperd.service rm -f /etc/systemd/system/coolerdash-helperd.service + rm -f /usr/lib/systemd/system/coolerdash-helperd.service + rm -rf /etc/systemd/system/cc-plugin-coolerdash.service.d + rm -f /usr/lib/udev/rules.d/99-coolerdash.rules - # Enable services if command -v systemctl >/dev/null 2>&1; then systemctl daemon-reload - if systemctl list-unit-files coolerdash-helperd.service | grep -q coolerdash-helperd; then - systemctl enable --now coolerdash-helperd.service || echo "Note: coolerdash-helperd.service failed. Enable manually." - fi if systemctl is-active --quiet coolercontrold.service; then systemctl restart coolercontrold.service || echo "Note: CoolerControl restart failed." fi From 4c98a4f47cd1371c64d6e1b0a90122ae5f839c63 Mon Sep 17 00:00:00 2001 From: damachine Date: Mon, 9 Mar 2026 23:30:20 +0100 Subject: [PATCH 5/5] chore: sensor fixes and ui improvements --- aur/coolerdash.install | 11 +- coolerdash.install | 11 +- .../plugins/coolerdash/ui/index.html | 255 +++++++++++++----- src/srv/cc_sensor.c | 2 +- 4 files changed, 206 insertions(+), 73 deletions(-) diff --git a/aur/coolerdash.install b/aur/coolerdash.install index df75292..492dd11 100644 --- a/aur/coolerdash.install +++ b/aur/coolerdash.install @@ -43,9 +43,14 @@ post_install() { systemctl daemon-reload - # Restart CoolerControl - if systemctl list-unit-files coolercontrold.service | grep -q coolercontrold; then - systemctl restart coolercontrold.service || echo "Note: CoolerControl restart failed." + # Restart plugin service directly if it already exists (reinstall case) + if systemctl list-unit-files cc-plugin-coolerdash.service | grep -q cc-plugin-coolerdash; then + systemctl restart cc-plugin-coolerdash.service || echo "Note: Plugin restart failed." + else + # Fresh install: restart CoolerControl so it discovers and starts the plugin service + if systemctl list-unit-files coolercontrold.service | grep -q coolercontrold; then + systemctl restart coolercontrold.service || echo "Note: CoolerControl restart failed." + fi fi echo "================================================================" diff --git a/coolerdash.install b/coolerdash.install index df75292..492dd11 100644 --- a/coolerdash.install +++ b/coolerdash.install @@ -43,9 +43,14 @@ post_install() { systemctl daemon-reload - # Restart CoolerControl - if systemctl list-unit-files coolercontrold.service | grep -q coolercontrold; then - systemctl restart coolercontrold.service || echo "Note: CoolerControl restart failed." + # Restart plugin service directly if it already exists (reinstall case) + if systemctl list-unit-files cc-plugin-coolerdash.service | grep -q cc-plugin-coolerdash; then + systemctl restart cc-plugin-coolerdash.service || echo "Note: Plugin restart failed." + else + # Fresh install: restart CoolerControl so it discovers and starts the plugin service + if systemctl list-unit-files coolercontrold.service | grep -q coolercontrold; then + systemctl restart coolercontrold.service || echo "Note: CoolerControl restart failed." + fi fi echo "================================================================" diff --git a/etc/coolercontrol/plugins/coolerdash/ui/index.html b/etc/coolercontrol/plugins/coolerdash/ui/index.html index 6d2c95d..c866bdd 100644 --- a/etc/coolercontrol/plugins/coolerdash/ui/index.html +++ b/etc/coolercontrol/plugins/coolerdash/ui/index.html @@ -588,9 +588,14 @@

CoolerDash Configuration

Authentication — CC4 (Bearer Token)

- CoolerControl 4 recommends Bearer Tokens for plugins. - Create an access token under CoolerControl → Access Protection. - Format: cc_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx — takes priority over the password. + CoolerControl 4.0 requires API authentication. +
    +
  1. Open the CoolerControl UI
  2. +
  3. Bottom-left corner → 🔒 Access Protection → Access Tokens
  4. +
  5. Generate a new Access Token
  6. +
  7. Copy your token key (cc_…)
  8. +
  9. Paste the token in the Access Token field below
  10. +
@@ -606,10 +611,10 @@

Authentication — Legacy / CC3 (Password)

TLS / HTTPS (Optional)

-
+
- Path to a CA certificate for self-signed HTTPS. Example: /etc/coolercontrol/coolercontrol.crt + CA certificate for self-signed HTTPS (e.g. /etc/coolercontrol/coolercontrol.crt)
@@ -621,9 +626,8 @@

TLS / HTTPS (Optional)

-

- Note: TLS options only affect the CoolerDash daemon, not this browser UI. - Localhost connections always use plain HTTP regardless of TLS settings. +

+ TLS options affect the CoolerDash daemon only — not this browser UI. Localhost always uses plain HTTP.

@@ -764,6 +768,13 @@

Display Geometry

+ +

Shutdown Image

+
+ + Displayed on the LCD when the daemon stops. Default: /etc/coolercontrol/plugins/coolerdash/shutdown.png + +
@@ -952,26 +963,19 @@

Text Colors

Sensor Configuration - Configure thresholds, bar colors, and position offsets for each sensor assigned to a display slot. - New sensors are automatically added when assigned in the Display tab. + Configure thresholds, bar colors, and position offsets for every sensor. + Standard sensors (CPU, GPU, Liquid) are always shown at the top. All sensors discovered from the CC4 API are listed automatically below.
+

- Loading sensor configurations... + Discovering sensors from CoolerControl API…

-

Image Paths

- -
- - Image displayed when daemon stops (Default: /etc/coolercontrol/plugins/coolerdash/shutdown.png) - -
-

Backup & Restore

Configuration Backup @@ -1291,6 +1295,27 @@

System Environment

window.runPluginScript = function(mainFunction) { mainFunction(); }; } + // Fallback for getDevices() — used when cc-plugin-lib.js is not loaded (dev/browser mode) + // Inside CC4's sandboxed iframe, cc-plugin-lib.js provides the real getDevices() via postMessage. + if (typeof getDevices === 'undefined') { + window.getDevices = async function() { + var headers = makeAuthHeaders(lastApiToken, lastApiPassword); + var res = await fetch((lastApiAddress || 'http://localhost:11987') + '/devices', { headers: headers }); + if (!res.ok) return { devices: [] }; + return res.json(); + }; + } + + // Fallback for getStatus() — used when cc-plugin-lib.js is not loaded (dev/browser mode) + if (typeof getStatus === 'undefined') { + window.getStatus = async function() { + var headers = makeAuthHeaders(lastApiToken, lastApiPassword); + var res = await fetch((lastApiAddress || 'http://localhost:11987') + '/status', { headers: headers }); + if (!res.ok) throw new Error('HTTP ' + res.status); + return res.json(); + }; + } + // ===== AUTH HEADER HELPER ===== function makeAuthHeaders(token, password) { var t = (token || '').trim(); @@ -1301,23 +1326,42 @@

System Environment

} // ===== SENSOR DISCOVERY ===== + function setSensorDiscoveryStatus(type, html) { + var el = document.getElementById('sensor-discovery-status'); + if (!el) return; + el.style.display = 'block'; + if (type === 'error') { + el.style.background = 'rgba(233, 69, 96, 0.08)'; + el.style.borderLeft = '4px solid var(--accent)'; + } else if (type === 'warn') { + el.style.background = 'rgba(255, 140, 0, 0.10)'; + el.style.borderLeft = '4px solid #ff8c00'; + } else { + el.style.background = 'rgba(34, 197, 94, 0.08)'; + el.style.borderLeft = '4px solid var(--success)'; + } + el.style.padding = '12px 16px'; + el.style.borderRadius = '6px'; + el.style.fontSize = '13px'; + el.innerHTML = html; + } + async function discoverSensors(apiAddress, token, password) { - try { - const authHeaders = makeAuthHeaders(token, password); - const headers = Object.assign({ 'Content-Type': 'application/json' }, authHeaders); + // Keep global auth state in sync so that getDevices/getStatus fallbacks can use it + lastApiAddress = apiAddress; + lastApiToken = token; + lastApiPassword = password; - // GET /devices for device names - const devRes = await fetch(apiAddress + '/devices', { headers: authHeaders }); - const devData = devRes.ok ? await devRes.json() : { devices: [] }; + try { + // Use cc-plugin-lib functions (bypasses CORS sandbox in CC4 iframe). + // Falls back to direct fetch in dev/browser mode via the fallback definitions above. + const devData = await getDevices(); const deviceNames = {}; for (const dev of (devData.devices || [])) { if (dev.uid) deviceNames[dev.uid] = dev.name || dev.uid; } - // POST /status for current sensor data - const statusRes = await fetch(apiAddress + '/status', { method: 'POST', headers, body: '{}' }); - if (!statusRes.ok) return; - const statusData = await statusRes.json(); + const statusData = await getStatus(); const sensors = []; for (const dev of (statusData.devices || [])) { @@ -1385,13 +1429,53 @@

System Environment

discoveredSensors = sensors; updateSensorSlotOptions(); - // Re-render sensor configs to update display names - if (Object.keys(currentSensorConfig).length > 0) { - renderSensorConfigs({ sensors: currentSensorConfig }); + // Re-render sensor configs to show all discovered CC4 sensors + renderSensorConfigs({ sensors: currentSensorConfig }); + + if (sensors.length === 0) { + setSensorDiscoveryStatus('warn', + 'No extra sensors found — CoolerControl API is reachable, but returned no temperature or channel data.
' + + 'Only Standard sensors (CPU, GPU, Liquid) are shown. ' + + '' + ); + } else { + setSensorDiscoveryStatus('ok', + '✔ ' + sensors.length + ' sensor(s) discovered from CoolerControl API — see “CC4 Sensors” section below. ' + + '' + ); } } catch (error) { console.warn('Sensor discovery failed:', error); + setSensorDiscoveryStatus('error', + 'CC4 Sensor Discovery failed — ' + escapeHtml(error.message) + '.
' + + 'Make sure CoolerControl is running and the API address in the Connection tab is correct. ' + + '' + ); + } + } + + function retrySensorDiscovery() { + var apiAddr = lastApiAddress || 'http://localhost:11987'; + var apiToken = lastApiToken || ''; + var apiPass = lastApiPassword || ''; + // Also read current form values in case they were just changed + var addrInput = document.querySelector('[name="daemon.address"]'); + var tokenInput = document.querySelector('[name="daemon.access_token"]'); + var passInput = document.querySelector('[name="daemon.password"]'); + if (addrInput && addrInput.value) apiAddr = addrInput.value; + if (tokenInput) apiToken = tokenInput.value; + if (passInput) apiPass = passInput.value; + var statusEl = document.getElementById('sensor-discovery-status'); + if (statusEl) { + statusEl.style.display = 'block'; + statusEl.style.background = 'rgba(45, 66, 99, 0.3)'; + statusEl.style.borderLeft = '4px solid var(--border)'; + statusEl.style.padding = '12px 16px'; + statusEl.style.borderRadius = '6px'; + statusEl.style.fontSize = '13px'; + statusEl.innerHTML = 'Discovering sensors…'; } + discoverSensors(apiAddr, apiToken, apiPass); } function updateSensorSlotOptions() { @@ -1518,7 +1602,13 @@

System Environment

var sensors = config.sensors || {}; - // Ensure all discovered sensors have a config entry + // Always ensure legacy standard sensors have config entries + var legacyDefaults = ['cpu', 'gpu', 'liquid']; + for (var li = 0; li < legacyDefaults.length; li++) { + if (!sensors[legacyDefaults[li]]) sensors[legacyDefaults[li]] = getDefaultSensorConfig(legacyDefaults[li]); + } + + // Ensure all discovered CC4 sensors have a config entry for (var d = 0; d < discoveredSensors.length; d++) { var dsId = discoveredSensors[d].id; if (!sensors[dsId]) { @@ -1553,14 +1643,37 @@

System Environment

}); if (sensorIds.length === 0) { - container.innerHTML = '

No sensor configurations found. Assign sensors in the Display tab.

'; + container.innerHTML = '

No sensors found. Make sure CoolerControl is running and reachable.

'; return; } container.innerHTML = ''; + // Track group transitions: legacy sensors first, then all CC4-discovered sensors + var addedStdHeader = false; + var addedDynHeader = false; + for (var si = 0; si < sensorIds.length; si++) { var sensorId = sensorIds[si]; + + // Insert group header on first legacy sensor + if (!addedStdHeader && legacyOrder.hasOwnProperty(sensorId)) { + addedStdHeader = true; + var stdHdr = document.createElement('div'); + stdHdr.className = 'section-title'; + stdHdr.style.marginTop = '0'; + stdHdr.textContent = 'Standard Sensors'; + container.appendChild(stdHdr); + } + // Insert group header on first dynamically discovered sensor + else if (!addedDynHeader && !legacyOrder.hasOwnProperty(sensorId)) { + addedDynHeader = true; + var dynHdr = document.createElement('div'); + dynHdr.className = 'section-title'; + dynHdr.textContent = 'CC4 Sensors (' + (sensorIds.length - si) + ' discovered)'; + container.appendChild(dynHdr); + } + var sc = sensors[sensorId]; var safeName = sensorId.replace(/[^a-zA-Z0-9]/g, '_'); var displayName = escapeHtml(getSensorDisplayName(sensorId)); @@ -1777,16 +1890,14 @@

System Environment

errorEl.style.display = 'none'; try { - var headers = makeAuthHeaders(token, password); - var response = await fetch(apiAddress + '/devices', { headers: headers }); - if (!response.ok) throw new Error('HTTP ' + response.status); - var data = await response.json(); + // Use cc-plugin-lib (bypasses CORS sandbox); falls back to direct fetch in dev mode. + var data = await getDevices(); var devices = data.devices || []; var found = null; for (var i = 0; i < devices.length; i++) { var dev = devices[i]; - var dtype = dev.type || ''; + var dtype = dev.d_type || dev.type || ''; if (dtype === 'Liquidctl') { var lcdInfo = dev.info && dev.info.channels && dev.info.channels.lcd && dev.info.channels.lcd.lcd_info; if (lcdInfo && lcdInfo.screen_width > 0 && lcdInfo.screen_height > 0) { @@ -1865,32 +1976,36 @@

System Environment

async function fetchSystemInfo(apiAddress, token, password) { try { - var headers = makeAuthHeaders(token, password); - - var healthRes = await fetch(apiAddress + '/health', { headers: headers }); - if (healthRes.ok) { - var health = await healthRes.json(); - var ccVersion = (health.details && health.details.version) || '\u2014'; - document.getElementById('sys-cc-version').textContent = ccVersion; + lastApiAddress = apiAddress; + lastApiToken = token; + lastApiPassword = password; + + // /health has no cc-plugin-lib equivalent; only fetch it in dev/browser mode + if (!isInCoolerControl) { + var headers = makeAuthHeaders(token, password); + var healthRes = await fetch(apiAddress + '/health', { headers: headers }); + if (healthRes.ok) { + var health = await healthRes.json(); + var ccVersion = (health.details && health.details.version) || '\u2014'; + document.getElementById('sys-cc-version').textContent = ccVersion; + } } - var devRes = await fetch(apiAddress + '/devices', { headers: headers }); - if (devRes.ok) { - var data = await devRes.json(); - var devices = data.devices || []; - var kernel = ''; - for (var i = 0; i < devices.length; i++) { - var drvType = devices[i].info && devices[i].info.driver_info && devices[i].info.driver_info.drv_type; - var ver = devices[i].info && devices[i].info.driver_info && devices[i].info.driver_info.version; - if (drvType === 'Kernel' && ver) { - kernel = ver; - break; - } - } - if (kernel) { - document.getElementById('sys-kernel').textContent = kernel; + // Use cc-plugin-lib (bypasses CORS sandbox); falls back to direct fetch in dev mode. + var data = await getDevices(); + var devices = data.devices || []; + var kernel = ''; + for (var i = 0; i < devices.length; i++) { + var drvType = devices[i].info && devices[i].info.driver_info && devices[i].info.driver_info.drv_type; + var ver = devices[i].info && devices[i].info.driver_info && devices[i].info.driver_info.version; + if (drvType === 'Kernel' && ver) { + kernel = ver; + break; } } + if (kernel) { + document.getElementById('sys-kernel').textContent = kernel; + } } catch (error) { console.warn('System info fetch failed:', error); } @@ -2151,16 +2266,23 @@

System Environment

} async function restartPluginDaemon(config) { + // CC4 restarts cc-plugin-coolerdash.service automatically when the plugin + // config is updated via PUT /plugins/{id}/config (text/plain as per CC4 API spec). + // This function triggers that restart by re-uploading the current config. try { var apiAddress = (config.daemon && config.daemon.address) || 'http://localhost:11987'; var token = (config.daemon && config.daemon.access_token) || ''; var password = (config.daemon && config.daemon.password) || ''; - var headers = Object.assign({ 'Content-Type': 'application/json' }, makeAuthHeaders(token, password)); + var authHdr = makeAuthHeaders(token, password); - var response = await fetch(apiAddress + '/plugins/coolerdash/restart', { method: 'POST', headers: headers }); - if (!response.ok) console.warn("Could not restart plugin automatically"); + var response = await fetch(apiAddress + '/plugins/coolerdash/config', { + method: 'PUT', + headers: Object.assign({ 'Content-Type': 'text/plain; charset=utf-8' }, authHdr), + body: JSON.stringify(config, null, 2) + }); + if (!response.ok) console.warn('Could not trigger plugin restart via config update: HTTP ' + response.status); } catch (error) { - console.error("Plugin restart failed:", error); + console.error('Plugin restart trigger failed:', error); } } @@ -2194,9 +2316,10 @@

System Environment

var authHdr = makeAuthHeaders(token, password); if (token || password) { + // CC4 API spec: PUT /plugins/{id}/config expects text/plain var response = await fetch(apiAddress + '/plugins/coolerdash/config', { method: 'PUT', - headers: Object.assign({ 'Content-Type': 'application/json' }, authHdr), + headers: Object.assign({ 'Content-Type': 'text/plain; charset=utf-8' }, authHdr), body: JSON.stringify(config, null, 2) }); if (response.ok) return true; diff --git a/src/srv/cc_sensor.c b/src/srv/cc_sensor.c index 340dd38..43cacec 100644 --- a/src/srv/cc_sensor.c +++ b/src/srv/cc_sensor.c @@ -220,7 +220,7 @@ static void collect_device_channels(const json_t *device, { float watts = (float)json_number_value(watts_val); int n = snprintf(sensor_name, sizeof(sensor_name), - "%s Power", ch_name); + "%s Watts", ch_name); if (n > 0 && (size_t)n < sizeof(sensor_name)) add_sensor_entry(data, sensor_name, device_uid, device_type, SENSOR_CATEGORY_WATTS,