Merge pull request #1737
69ab14d9
More robust battery status handling. (Dion Ahmetaj)
This commit is contained in:
commit
d2059dfab2
@ -41,6 +41,7 @@
|
|||||||
#include "common/command_line.h"
|
#include "common/command_line.h"
|
||||||
#include "string_coding.h"
|
#include "string_coding.h"
|
||||||
#include "storages/portable_storage_template_helper.h"
|
#include "storages/portable_storage_template_helper.h"
|
||||||
|
#include "boost/logic/tribool.hpp"
|
||||||
|
|
||||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
#define MONERO_DEFAULT_LOG_CATEGORY "miner"
|
#define MONERO_DEFAULT_LOG_CATEGORY "miner"
|
||||||
@ -61,6 +62,7 @@ namespace cryptonote
|
|||||||
const command_line::arg_descriptor<std::string> arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true};
|
const command_line::arg_descriptor<std::string> arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true};
|
||||||
const command_line::arg_descriptor<uint32_t> arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true};
|
const command_line::arg_descriptor<uint32_t> arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true};
|
||||||
const command_line::arg_descriptor<bool> arg_bg_mining_enable = {"bg-mining-enable", "enable/disable background mining", true, true};
|
const command_line::arg_descriptor<bool> arg_bg_mining_enable = {"bg-mining-enable", "enable/disable background mining", true, true};
|
||||||
|
const command_line::arg_descriptor<bool> arg_bg_mining_ignore_battery = {"bg-mining-ignore-battery", "if true, assumes plugged in when unable to query system power status", false, true};
|
||||||
const command_line::arg_descriptor<uint64_t> arg_bg_mining_min_idle_interval_seconds = {"bg-mining-min-idle-interval", "Specify min lookback interval in seconds for determining idle state", miner::BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS, true};
|
const command_line::arg_descriptor<uint64_t> arg_bg_mining_min_idle_interval_seconds = {"bg-mining-min-idle-interval", "Specify min lookback interval in seconds for determining idle state", miner::BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS, true};
|
||||||
const command_line::arg_descriptor<uint8_t> arg_bg_mining_idle_threshold_percentage = {"bg-mining-idle-threshold", "Specify minimum avg idle percentage over lookback interval", miner::BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE, true};
|
const command_line::arg_descriptor<uint8_t> arg_bg_mining_idle_threshold_percentage = {"bg-mining-idle-threshold", "Specify minimum avg idle percentage over lookback interval", miner::BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE, true};
|
||||||
const command_line::arg_descriptor<uint8_t> arg_bg_mining_miner_target_percentage = {"bg-mining-miner-target", "Specificy maximum percentage cpu use by miner(s)", miner::BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE, true};
|
const command_line::arg_descriptor<uint8_t> arg_bg_mining_miner_target_percentage = {"bg-mining-miner-target", "Specificy maximum percentage cpu use by miner(s)", miner::BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE, true};
|
||||||
@ -181,6 +183,7 @@ namespace cryptonote
|
|||||||
command_line::add_arg(desc, arg_start_mining);
|
command_line::add_arg(desc, arg_start_mining);
|
||||||
command_line::add_arg(desc, arg_mining_threads);
|
command_line::add_arg(desc, arg_mining_threads);
|
||||||
command_line::add_arg(desc, arg_bg_mining_enable);
|
command_line::add_arg(desc, arg_bg_mining_enable);
|
||||||
|
command_line::add_arg(desc, arg_bg_mining_ignore_battery);
|
||||||
command_line::add_arg(desc, arg_bg_mining_min_idle_interval_seconds);
|
command_line::add_arg(desc, arg_bg_mining_min_idle_interval_seconds);
|
||||||
command_line::add_arg(desc, arg_bg_mining_idle_threshold_percentage);
|
command_line::add_arg(desc, arg_bg_mining_idle_threshold_percentage);
|
||||||
command_line::add_arg(desc, arg_bg_mining_miner_target_percentage);
|
command_line::add_arg(desc, arg_bg_mining_miner_target_percentage);
|
||||||
@ -230,6 +233,8 @@ namespace cryptonote
|
|||||||
// Let init set all parameters even if background mining is not enabled, they can start later with params set
|
// Let init set all parameters even if background mining is not enabled, they can start later with params set
|
||||||
if(command_line::has_arg(vm, arg_bg_mining_enable))
|
if(command_line::has_arg(vm, arg_bg_mining_enable))
|
||||||
set_is_background_mining_enabled( command_line::get_arg(vm, arg_bg_mining_enable) );
|
set_is_background_mining_enabled( command_line::get_arg(vm, arg_bg_mining_enable) );
|
||||||
|
if(command_line::has_arg(vm, arg_bg_mining_ignore_battery))
|
||||||
|
set_ignore_battery( command_line::get_arg(vm, arg_bg_mining_ignore_battery) );
|
||||||
if(command_line::has_arg(vm, arg_bg_mining_min_idle_interval_seconds))
|
if(command_line::has_arg(vm, arg_bg_mining_min_idle_interval_seconds))
|
||||||
set_min_idle_seconds( command_line::get_arg(vm, arg_bg_mining_min_idle_interval_seconds) );
|
set_min_idle_seconds( command_line::get_arg(vm, arg_bg_mining_min_idle_interval_seconds) );
|
||||||
if(command_line::has_arg(vm, arg_bg_mining_idle_threshold_percentage))
|
if(command_line::has_arg(vm, arg_bg_mining_idle_threshold_percentage))
|
||||||
@ -254,7 +259,7 @@ namespace cryptonote
|
|||||||
return m_threads_total;
|
return m_threads_total;
|
||||||
}
|
}
|
||||||
//-----------------------------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------------------------
|
||||||
bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background)
|
bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background, bool ignore_battery)
|
||||||
{
|
{
|
||||||
m_mine_address = adr;
|
m_mine_address = adr;
|
||||||
m_threads_total = static_cast<uint32_t>(threads_count);
|
m_threads_total = static_cast<uint32_t>(threads_count);
|
||||||
@ -278,6 +283,7 @@ namespace cryptonote
|
|||||||
boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0);
|
boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0);
|
||||||
boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0);
|
boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0);
|
||||||
set_is_background_mining_enabled(do_background);
|
set_is_background_mining_enabled(do_background);
|
||||||
|
set_ignore_battery(ignore_battery);
|
||||||
|
|
||||||
for(size_t i = 0; i != threads_count; i++)
|
for(size_t i = 0; i != threads_count; i++)
|
||||||
{
|
{
|
||||||
@ -469,6 +475,11 @@ namespace cryptonote
|
|||||||
return m_is_background_mining_enabled;
|
return m_is_background_mining_enabled;
|
||||||
}
|
}
|
||||||
//-----------------------------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------------------------
|
||||||
|
bool miner::get_ignore_battery() const
|
||||||
|
{
|
||||||
|
return m_ignore_battery;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------
|
||||||
/**
|
/**
|
||||||
* This has differing behaviour depending on if mining has been started/etc.
|
* This has differing behaviour depending on if mining has been started/etc.
|
||||||
* Note: add documentation
|
* Note: add documentation
|
||||||
@ -482,6 +493,11 @@ namespace cryptonote
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//-----------------------------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------------------------
|
||||||
|
void miner::set_ignore_battery(bool ignore_battery)
|
||||||
|
{
|
||||||
|
m_ignore_battery = ignore_battery;
|
||||||
|
}
|
||||||
|
//-----------------------------------------------------------------------------------------------------
|
||||||
uint64_t miner::get_min_idle_seconds() const
|
uint64_t miner::get_min_idle_seconds() const
|
||||||
{
|
{
|
||||||
return m_min_idle_seconds;
|
return m_min_idle_seconds;
|
||||||
@ -575,7 +591,21 @@ namespace cryptonote
|
|||||||
continue; // if interrupted because stop called, loop should end ..
|
continue; // if interrupted because stop called, loop should end ..
|
||||||
}
|
}
|
||||||
|
|
||||||
bool on_ac_power = !on_battery_power();
|
boost::tribool battery_powered(on_battery_power());
|
||||||
|
bool on_ac_power = false;
|
||||||
|
if(indeterminate( battery_powered ))
|
||||||
|
{
|
||||||
|
// name could be better, only ignores battery requirement if we failed
|
||||||
|
// to get the status of the system
|
||||||
|
if( m_ignore_battery )
|
||||||
|
{
|
||||||
|
on_ac_power = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
on_ac_power = !battery_powered;
|
||||||
|
}
|
||||||
|
|
||||||
if( m_is_background_mining_started )
|
if( m_is_background_mining_started )
|
||||||
{
|
{
|
||||||
@ -763,45 +793,54 @@ namespace cryptonote
|
|||||||
return (uint8_t)( ceil( (other * 1.f / total * 1.f) * 100) );
|
return (uint8_t)( ceil( (other * 1.f / total * 1.f) * 100) );
|
||||||
}
|
}
|
||||||
//-----------------------------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------------------------
|
||||||
bool miner::on_battery_power()
|
boost::logic::tribool miner::on_battery_power()
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
SYSTEM_POWER_STATUS power_status;
|
SYSTEM_POWER_STATUS power_status;
|
||||||
if ( GetSystemPowerStatus( &power_status ) != 0 )
|
if ( GetSystemPowerStatus( &power_status ) != 0 )
|
||||||
{
|
{
|
||||||
return power_status.ACLineStatus != 1;
|
return boost::logic::tribool(power_status.ACLineStatus != 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
|
|
||||||
// i've only tested on UBUNTU, these paths might be different on other systems
|
// i've only tested on UBUNTU, these paths might be different on other systems
|
||||||
// need to figure out a way to make this more flexible
|
// need to figure out a way to make this more flexible
|
||||||
const std::string POWER_SUPPLY_STATUS_PATH = "/sys/class/power_supply/ACAD/online";
|
std::string power_supply_path = "";
|
||||||
|
const std::string POWER_SUPPLY_STATUS_PATHS[] =
|
||||||
if( !epee::file_io_utils::is_file_exist(POWER_SUPPLY_STATUS_PATH) )
|
|
||||||
{
|
{
|
||||||
LOG_ERROR("'" << POWER_SUPPLY_STATUS_PATH << "' file does not exist, can't determine if on AC power");
|
"/sys/class/power_supply/ACAD/online",
|
||||||
return false;
|
"/sys/class/power_supply/AC/online"
|
||||||
|
};
|
||||||
|
|
||||||
|
for(const std::string& path : POWER_SUPPLY_STATUS_PATHS)
|
||||||
|
{
|
||||||
|
if( epee::file_io_utils::is_file_exist(path) )
|
||||||
|
{
|
||||||
|
power_supply_path = path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( power_supply_path.empty() )
|
||||||
|
{
|
||||||
|
LOG_ERROR("Couldn't find battery/power status file, can't determine if plugged in!");
|
||||||
|
return boost::logic::tribool(boost::logic::indeterminate);;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ifstream power_stream(POWER_SUPPLY_STATUS_PATH);
|
std::ifstream power_stream(power_supply_path);
|
||||||
if( power_stream.fail() )
|
if( power_stream.fail() )
|
||||||
{
|
{
|
||||||
LOG_ERROR("failed to open '" << POWER_SUPPLY_STATUS_PATH << "'");
|
LOG_ERROR("failed to open '" << power_supply_path << "'");
|
||||||
return false;
|
return boost::logic::tribool(boost::logic::indeterminate);;
|
||||||
}
|
}
|
||||||
|
|
||||||
return power_stream.get() != '1';
|
return boost::logic::tribool( (power_stream.get() != '1') );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LOG_ERROR("couldn't query power status");
|
LOG_ERROR("couldn't query power status");
|
||||||
return false; // shouldn't get here unless no support for querying battery status
|
return boost::logic::tribool(boost::logic::indeterminate);
|
||||||
// TODO: return enum with ability to signify failure in querying for power status
|
|
||||||
// and change bg-mining logic so that it stops. As @vtnerd states, with the current
|
|
||||||
// setup "If someone enabled background mining on a system that fails to grab ac
|
|
||||||
// status, it will just continually check with little hope of ever being resolved
|
|
||||||
// automagically". This is also the case for time/idle stats functions.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
|
#include <boost/logic/tribool_fwd.hpp>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include "cryptonote_basic.h"
|
#include "cryptonote_basic.h"
|
||||||
#include "difficulty.h"
|
#include "difficulty.h"
|
||||||
@ -67,7 +68,7 @@ namespace cryptonote
|
|||||||
static void init_options(boost::program_options::options_description& desc);
|
static void init_options(boost::program_options::options_description& desc);
|
||||||
bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height);
|
bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height);
|
||||||
bool on_block_chain_update();
|
bool on_block_chain_update();
|
||||||
bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background = false);
|
bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background = false, bool ignore_battery = false);
|
||||||
uint64_t get_speed() const;
|
uint64_t get_speed() const;
|
||||||
uint32_t get_threads_count() const;
|
uint32_t get_threads_count() const;
|
||||||
void send_stop_signal();
|
void send_stop_signal();
|
||||||
@ -82,6 +83,7 @@ namespace cryptonote
|
|||||||
void resume();
|
void resume();
|
||||||
void do_print_hashrate(bool do_hr);
|
void do_print_hashrate(bool do_hr);
|
||||||
bool get_is_background_mining_enabled() const;
|
bool get_is_background_mining_enabled() const;
|
||||||
|
bool get_ignore_battery() const;
|
||||||
uint64_t get_min_idle_seconds() const;
|
uint64_t get_min_idle_seconds() const;
|
||||||
bool set_min_idle_seconds(uint64_t min_idle_seconds);
|
bool set_min_idle_seconds(uint64_t min_idle_seconds);
|
||||||
uint8_t get_idle_threshold() const;
|
uint8_t get_idle_threshold() const;
|
||||||
@ -149,8 +151,10 @@ namespace cryptonote
|
|||||||
// background mining stuffs ..
|
// background mining stuffs ..
|
||||||
|
|
||||||
bool set_is_background_mining_enabled(bool is_background_mining_enabled);
|
bool set_is_background_mining_enabled(bool is_background_mining_enabled);
|
||||||
|
void set_ignore_battery(bool ignore_battery);
|
||||||
bool background_worker_thread();
|
bool background_worker_thread();
|
||||||
std::atomic<bool> m_is_background_mining_enabled;
|
std::atomic<bool> m_is_background_mining_enabled;
|
||||||
|
bool m_ignore_battery;
|
||||||
boost::mutex m_is_background_mining_enabled_mutex;
|
boost::mutex m_is_background_mining_enabled_mutex;
|
||||||
boost::condition_variable m_is_background_mining_enabled_cond;
|
boost::condition_variable m_is_background_mining_enabled_cond;
|
||||||
std::atomic<bool> m_is_background_mining_started;
|
std::atomic<bool> m_is_background_mining_started;
|
||||||
@ -164,6 +168,6 @@ namespace cryptonote
|
|||||||
static bool get_system_times(uint64_t& total_time, uint64_t& idle_time);
|
static bool get_system_times(uint64_t& total_time, uint64_t& idle_time);
|
||||||
static bool get_process_time(uint64_t& total_time);
|
static bool get_process_time(uint64_t& total_time);
|
||||||
static uint8_t get_percent_of_total(uint64_t some_time, uint64_t total_time);
|
static uint8_t get_percent_of_total(uint64_t some_time, uint64_t total_time);
|
||||||
static bool on_battery_power();
|
static boost::logic::tribool on_battery_power();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -272,12 +272,18 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
|
|||||||
std::cout << "Mining to a testnet address, make sure this is intentional!" << std::endl;
|
std::cout << "Mining to a testnet address, make sure this is intentional!" << std::endl;
|
||||||
uint64_t threads_count = 1;
|
uint64_t threads_count = 1;
|
||||||
bool do_background_mining = false;
|
bool do_background_mining = false;
|
||||||
if(args.size() > 3)
|
bool ignore_battery = false;
|
||||||
|
if(args.size() > 4)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(args.size() == 3)
|
if(args.size() == 4)
|
||||||
|
{
|
||||||
|
ignore_battery = args[3] == "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(args.size() >= 3)
|
||||||
{
|
{
|
||||||
do_background_mining = args[2] == "true";
|
do_background_mining = args[2] == "true";
|
||||||
}
|
}
|
||||||
@ -288,7 +294,7 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
|
|||||||
threads_count = (ok && 0 < threads_count) ? threads_count : 1;
|
threads_count = (ok && 0 < threads_count) ? threads_count : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_executor.start_mining(adr, threads_count, testnet, do_background_mining);
|
m_executor.start_mining(adr, threads_count, testnet, do_background_mining, ignore_battery);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ t_command_server::t_command_server(
|
|||||||
m_command_lookup.set_handler(
|
m_command_lookup.set_handler(
|
||||||
"start_mining"
|
"start_mining"
|
||||||
, std::bind(&t_command_parser_executor::start_mining, &m_parser, p::_1)
|
, std::bind(&t_command_parser_executor::start_mining, &m_parser, p::_1)
|
||||||
, "Start mining for specified address, start_mining <addr> [<threads>] [do_background_mining], default 1 thread, no background mining"
|
, "Start mining for specified address, start_mining <addr> [<threads>] [do_background_mining] [ignore_battery], default 1 thread, no background mining"
|
||||||
);
|
);
|
||||||
m_command_lookup.set_handler(
|
m_command_lookup.set_handler(
|
||||||
"stop_mining"
|
"stop_mining"
|
||||||
|
@ -929,13 +929,14 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining = false) {
|
bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining, bool ignore_battery) {
|
||||||
cryptonote::COMMAND_RPC_START_MINING::request req;
|
cryptonote::COMMAND_RPC_START_MINING::request req;
|
||||||
cryptonote::COMMAND_RPC_START_MINING::response res;
|
cryptonote::COMMAND_RPC_START_MINING::response res;
|
||||||
req.miner_address = cryptonote::get_account_address_as_str(testnet, address);
|
req.miner_address = cryptonote::get_account_address_as_str(testnet, address);
|
||||||
req.threads_count = num_threads;
|
req.threads_count = num_threads;
|
||||||
req.do_background_mining = do_background_mining;
|
req.do_background_mining = do_background_mining;
|
||||||
|
req.ignore_battery = ignore_battery;
|
||||||
|
|
||||||
std::string fail_message = "Mining did not start";
|
std::string fail_message = "Mining did not start";
|
||||||
|
|
||||||
if (m_is_rpc)
|
if (m_is_rpc)
|
||||||
|
@ -106,7 +106,7 @@ public:
|
|||||||
|
|
||||||
bool print_transaction_pool_stats();
|
bool print_transaction_pool_stats();
|
||||||
|
|
||||||
bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining);
|
bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining = false, bool ignore_battery = false);
|
||||||
|
|
||||||
bool stop_mining();
|
bool stop_mining();
|
||||||
|
|
||||||
|
@ -635,7 +635,7 @@ namespace cryptonote
|
|||||||
boost::thread::attributes attrs;
|
boost::thread::attributes attrs;
|
||||||
attrs.set_stack_size(THREAD_STACK_SIZE);
|
attrs.set_stack_size(THREAD_STACK_SIZE);
|
||||||
|
|
||||||
if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining))
|
if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
|
||||||
{
|
{
|
||||||
res.status = "Failed, mining not started";
|
res.status = "Failed, mining not started";
|
||||||
LOG_PRINT_L0(res.status);
|
LOG_PRINT_L0(res.status);
|
||||||
|
@ -501,11 +501,13 @@ namespace cryptonote
|
|||||||
std::string miner_address;
|
std::string miner_address;
|
||||||
uint64_t threads_count;
|
uint64_t threads_count;
|
||||||
bool do_background_mining;
|
bool do_background_mining;
|
||||||
|
bool ignore_battery;
|
||||||
|
|
||||||
BEGIN_KV_SERIALIZE_MAP()
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
KV_SERIALIZE(miner_address)
|
KV_SERIALIZE(miner_address)
|
||||||
KV_SERIALIZE(threads_count)
|
KV_SERIALIZE(threads_count)
|
||||||
KV_SERIALIZE(do_background_mining)
|
KV_SERIALIZE(do_background_mining)
|
||||||
|
KV_SERIALIZE(ignore_battery)
|
||||||
END_KV_SERIALIZE_MAP()
|
END_KV_SERIALIZE_MAP()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user