mirror of
https://github.com/miskcoo/ugreen_dx4600_leds_controller.git
synced 2025-07-22 20:10:36 +02:00
Some checks failed
Build kernel module for TrueNAS / build-and-run (push) Has been cancelled
291 lines
9.8 KiB
C++
291 lines
9.8 KiB
C++
|
|
#include <unistd.h>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <map>
|
|
#include <functional>
|
|
|
|
#include "ugreen_leds.h"
|
|
|
|
#define MAX_RETRY_COUNT 5
|
|
#define USLEEP_READ_STATUS_INTERVAL 8000
|
|
#define USLEEP_READ_STATUS_RETRY_INTERVAL 3000
|
|
#define USLEEP_MODIFICATION_INTERVAL 500
|
|
#define USLEEP_MODIFICATION_RETRY_INTERVAL 3000
|
|
#define USLEEP_MODIFICATION_QUERY_RESULT_INTERVAL 2000
|
|
|
|
static std::map<std::string, ugreen_leds_t::led_type_t> led_name_map = {
|
|
{ "power", UGREEN_LED_POWER },
|
|
{ "netdev", UGREEN_LED_NETDEV },
|
|
{ "disk1", UGREEN_LED_DISK1 },
|
|
{ "disk2", UGREEN_LED_DISK2 },
|
|
{ "disk3", UGREEN_LED_DISK3 },
|
|
{ "disk4", UGREEN_LED_DISK4 },
|
|
{ "disk5", UGREEN_LED_DISK5 },
|
|
{ "disk6", UGREEN_LED_DISK6 },
|
|
{ "disk7", UGREEN_LED_DISK7 },
|
|
{ "disk8", UGREEN_LED_DISK8 },
|
|
};
|
|
|
|
using led_type_pair = std::pair<std::string, ugreen_leds_t::led_type_t>;
|
|
|
|
ugreen_leds_t::led_data_t get_status_robust(ugreen_leds_t &leds_controller, ugreen_leds_t::led_type_t led) {
|
|
usleep(USLEEP_READ_STATUS_INTERVAL);
|
|
auto data = leds_controller.get_status(led);
|
|
|
|
for (int retry_cnt = 1; !data.is_available && retry_cnt < MAX_RETRY_COUNT; ++retry_cnt) {
|
|
usleep(USLEEP_READ_STATUS_RETRY_INTERVAL);
|
|
data = leds_controller.get_status(led);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
void show_leds_info(ugreen_leds_t &leds_controller, const std::vector<led_type_pair>& leds) {
|
|
|
|
for (auto led : leds) {
|
|
|
|
auto data = get_status_robust(leds_controller, led.second);
|
|
|
|
if (!data.is_available) {
|
|
std::printf("%s: unavailable or non-existent\n", led.first.c_str());
|
|
continue;
|
|
}
|
|
|
|
std::string op_mode_txt = "unknown";
|
|
|
|
switch(data.op_mode) {
|
|
case ugreen_leds_t::op_mode_t::off:
|
|
op_mode_txt = "off"; break;
|
|
case ugreen_leds_t::op_mode_t::on:
|
|
op_mode_txt = "on"; break;
|
|
case ugreen_leds_t::op_mode_t::blink:
|
|
op_mode_txt = "blink"; break;
|
|
case ugreen_leds_t::op_mode_t::breath:
|
|
op_mode_txt = "breath"; break;
|
|
};
|
|
|
|
std::printf("%s: status = %s, brightness = %d, color = RGB(%d, %d, %d)",
|
|
led.first.c_str(), op_mode_txt.c_str(), (int)data.brightness,
|
|
(int)data.color_r, (int)data.color_g, (int)data.color_b);
|
|
|
|
if (data.op_mode == ugreen_leds_t::op_mode_t::blink) {
|
|
std::printf(", blink_on = %d ms, blink_off = %d ms",
|
|
(int)data.t_on, (int)data.t_off);
|
|
}
|
|
|
|
std::puts("");
|
|
}
|
|
}
|
|
|
|
void show_help() {
|
|
std::cerr
|
|
<< "Usage: ugreen_leds_cli [LED-NAME...] [-on] [-off] [-(blink|breath) T_ON T_OFF]\n"
|
|
" [-color R G B] [-brightness BRIGHTNESS] [-status]\n\n"
|
|
" LED_NAME: separated by white space, possible values are\n"
|
|
" { power, netdev, disk[1-8], all }.\n"
|
|
" -on / -off: turn on / off corresponding LEDs.\n"
|
|
" -blink / -breath: set LED to the blink / breath mode. This \n"
|
|
" mode keeps the LED on for T_ON millseconds and then\n"
|
|
" keeps it off for T_OFF millseconds. \n"
|
|
" T_ON and T_OFF should belong to [0, 65535].\n"
|
|
" -color: set the color of corresponding LEDs.\n"
|
|
" R, G and B should belong to [0, 255].\n"
|
|
" -brightness: set the brightness of corresponding LEDs.\n"
|
|
" BRIGHTNESS should belong to [0, 255].\n"
|
|
" -status: display the status of corresponding LEDs.\n"
|
|
<< std::endl;
|
|
}
|
|
|
|
void show_help_and_exit() {
|
|
show_help();
|
|
std::exit(-1);
|
|
}
|
|
|
|
ugreen_leds_t::led_type_t get_led_type(const std::string& name) {
|
|
if (led_name_map.find(name) == led_name_map.end()) {
|
|
std::cerr << "Err: unknown LED name " << name << std::endl;
|
|
show_help_and_exit();
|
|
}
|
|
|
|
return led_name_map[name];
|
|
}
|
|
|
|
int parse_integer(const std::string& str, int low = 0, int high = 0xffff) {
|
|
std::size_t size;
|
|
int x = std::stoi(str, &size);
|
|
|
|
if (size != str.size()) {
|
|
std::cerr << "Err: " << str << " is not an integer." << std::endl;
|
|
show_help_and_exit();
|
|
}
|
|
|
|
if (x < low || x > high) {
|
|
std::cerr << "Err: " << str << " is not in [" << low << ", " << high << "]" << std::endl;
|
|
show_help_and_exit();
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
|
|
if (argc < 2) {
|
|
show_help();
|
|
return 0;
|
|
}
|
|
|
|
ugreen_leds_t leds_controller;
|
|
if (leds_controller.start() != 0) {
|
|
std::cerr << "Err: fail to open the I2C device." << std::endl;
|
|
std::cerr << "Please check that (1) you have the root permission; " << std::endl;
|
|
std::cerr << " and (2) the i2c-dev module is loaded. " << std::endl;
|
|
return -1;
|
|
}
|
|
|
|
std::deque<std::string> args;
|
|
for (int i = 1; i < argc; ++i)
|
|
args.emplace_back(argv[i]);
|
|
|
|
// parse LED names
|
|
std::vector<led_type_pair> leds;
|
|
|
|
while (!args.empty() && args.front().front() != '-') {
|
|
if (args.front() == "all") {
|
|
for (const auto &v : led_name_map) {
|
|
if (get_status_robust(leds_controller, v.second).is_available)
|
|
leds.push_back(v);
|
|
}
|
|
} else {
|
|
auto led_type = get_led_type(args.front());
|
|
leds.emplace_back(args.front(), led_type);
|
|
}
|
|
|
|
args.pop_front();
|
|
}
|
|
|
|
// if no additional parameters, display current info
|
|
if (args.empty()) {
|
|
show_leds_info(leds_controller, leds);
|
|
return 0;
|
|
}
|
|
|
|
// (is_modification, callback)
|
|
using ops_pair = std::pair<bool, std::function<int(led_type_pair)>>;
|
|
std::vector<ops_pair> ops_seq;
|
|
|
|
while (!args.empty()) {
|
|
if (args.front() == "-on" || args.front() == "-off") {
|
|
// turn on / off LEDs
|
|
uint8_t status = args.front() == "-on";
|
|
ops_seq.emplace_back(true, [=, &leds_controller](led_type_pair led) {
|
|
return leds_controller.set_onoff(led.second, status);
|
|
} );
|
|
|
|
args.pop_front();
|
|
} else if(args.front() == "-blink" || args.front() == "-breath") {
|
|
// set blink
|
|
bool is_blink = (args.front() == "-blink");
|
|
args.pop_front();
|
|
|
|
if (args.size() < 2) {
|
|
std::cerr << "Err: -blink / -breath requires 2 parameters" << std::endl;
|
|
show_help_and_exit();
|
|
}
|
|
|
|
uint16_t t_on = parse_integer(args.front(), 0x0000, 0xffff);
|
|
args.pop_front();
|
|
uint16_t t_off = parse_integer(args.front(), 0x0000, 0xffff);
|
|
args.pop_front();
|
|
|
|
ops_seq.emplace_back(true, [=, &leds_controller](led_type_pair led) {
|
|
if (is_blink) {
|
|
return leds_controller.set_blink(led.second, t_on, t_off);
|
|
} else {
|
|
return leds_controller.set_breath(led.second, t_on, t_off);
|
|
}
|
|
} );
|
|
} else if(args.front() == "-color") {
|
|
// set color
|
|
args.pop_front();
|
|
|
|
if (args.size() < 3) {
|
|
std::cerr << "Err: -color requires 3 parameters" << std::endl;
|
|
show_help_and_exit();
|
|
}
|
|
|
|
uint8_t R = parse_integer(args.front(), 0x00, 0xff);
|
|
args.pop_front();
|
|
uint8_t G = parse_integer(args.front(), 0x00, 0xff);
|
|
args.pop_front();
|
|
uint8_t B = parse_integer(args.front(), 0x00, 0xff);
|
|
args.pop_front();
|
|
ops_seq.emplace_back(true, [=, &leds_controller](led_type_pair led) {
|
|
return leds_controller.set_rgb(led.second, R, G, B);
|
|
} );
|
|
} else if(args.front() == "-brightness") {
|
|
// set brightness
|
|
args.pop_front();
|
|
|
|
if (args.size() < 1) {
|
|
std::cerr << "Err: -brightness requires 1 parameter" << std::endl;
|
|
show_help_and_exit();
|
|
}
|
|
|
|
uint8_t brightness = parse_integer(args.front(), 0x00, 0xff);
|
|
args.pop_front();
|
|
ops_seq.emplace_back(true, [=, &leds_controller](led_type_pair led) {
|
|
return leds_controller.set_brightness(led.second, brightness);
|
|
} );
|
|
} else if(args.front() == "-status") {
|
|
// display the status
|
|
args.pop_front();
|
|
|
|
ops_seq.emplace_back(false, [=, &leds_controller](led_type_pair led) {
|
|
show_leds_info(leds_controller, { led } );
|
|
return 0;
|
|
} );
|
|
} else {
|
|
std::cerr << "Err: unknown parameter " << args.front() << std::endl;
|
|
show_help_and_exit();
|
|
}
|
|
}
|
|
|
|
for (const auto& led : leds) {
|
|
for (const auto& fn_pair : ops_seq) {
|
|
bool is_modification = fn_pair.first;
|
|
const auto &fn = fn_pair.second;
|
|
int last_status = -1;
|
|
|
|
for (int retry_cnt = 0; retry_cnt < MAX_RETRY_COUNT && last_status != 0; ++retry_cnt) {
|
|
|
|
if (retry_cnt == 0) {
|
|
if (is_modification)
|
|
usleep(USLEEP_MODIFICATION_INTERVAL); // usleep_range(200, 0x5dc)
|
|
} else {
|
|
usleep(USLEEP_MODIFICATION_RETRY_INTERVAL);
|
|
}
|
|
|
|
last_status = fn(led);
|
|
|
|
if (last_status == 0 && is_modification) {
|
|
usleep(USLEEP_MODIFICATION_QUERY_RESULT_INTERVAL);
|
|
last_status = !leds_controller.is_last_modification_successful();
|
|
}
|
|
}
|
|
|
|
if (last_status != 0) {
|
|
std::cerr << "failed to change status!" << std::endl;
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|