123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898 |
- /**
- * Driver for handling thresholds in Lenovo ThinkPad series for models since 2011
- *
- * Based on information from https://linrunner.de/tlp/faq/battery.html
- *
- * Original sources: https://github.com/linrunner/TLP/blob/main/bat.d/05-thinkpad
- */
- 'use strict';
- import GLib from 'gi://GLib';
- import GObject from 'gi://GObject';
- import Gio from 'gi://Gio';
- /*
- * On some models the start threshold 0 is not allowed, instead 95 is used (Ex: E14 Gen 3)
- * See issues:
- * #8: https://gitlab.com/marcosdalvarez/thinkpad-battery-threshold-extension/-/issues/8
- * #20: https://gitlab.com/marcosdalvarez/thinkpad-battery-threshold-extension/-/issues/20
- */
- const ALTERNATIVE_DISABLED_START = /(Think[pP]ad) (E14 Gen 3|P14s Gen 2a)/;
- // Driver constants
- const BASE_PATH = '/sys/class/power_supply';
- const START_FILE_OLD = 'charge_start_threshold'; // kernel 4.17 and newer
- const END_FILE_OLD = 'charge_stop_threshold'; // kernel 4.17 and newer
- const START_FILE_NEW = 'charge_control_start_threshold'; // kernel 5.9 and newer
- const END_FILE_NEW = 'charge_control_end_threshold'; // kernel 5.9 and newer
- /**
- * Read file contents
- *
- * @param {string} path Path of file
- * @returns {string} File contents
- */
- const readFile = function (path) {
- try {
- const f = Gio.File.new_for_path(path);
- const [, contents,] = f.load_contents(null);
- const decoder = new TextDecoder('utf-8');
- return decoder.decode(contents);
- } catch (e) {
- return null;
- }
- }
- /**
- * Read integer value from file
- *
- * @param {string} path Path of file
- * @returns {number|null} Return a integer or null
- */
- const readFileInt = function (path) {
- try {
- const v = readFile(path);
- if (v) {
- return parseInt(v);
- } else {
- return null;
- }
- } catch (e) {
- return null;
- }
- }
- /**
- * Test file/directory exists
- *
- * @param {string} path File/directory path
- * @returns {boolean}
- */
- const fileExists = function (path) {
- try {
- const f = Gio.File.new_for_path(path);
- return f.query_exists(null);
- } catch (e) {
- return false;
- }
- }
- /**
- * Environment object
- */
- const Environment = GObject.registerClass({
- GTypeName: 'Environment',
- }, class Environment extends GObject.Object {
- get productVersion() {
- if (this._productVersion === undefined) {
- const tmp = readFile('/sys/class/dmi/id/product_version');
- if (tmp) {
- // Remove non-alphanumeric characters
- const sanitize = /([^ A-Za-z0-9])+/g;
- this._productVersion = tmp.replace(sanitize, '');
- } else {
- this._productVersion = null;
- }
- }
- return this._productVersion;
- }
- get kernelRelease() {
- if (this._kernelRelease === undefined) {
- try {
- let proc = Gio.Subprocess.new(
- ['uname', '-r'],
- Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE
- );
- const [ok, stdout, stderr] = proc.communicate_utf8(null, null);
- proc = null;
- this._kernelRelease = stdout.trim();
- } catch (e) {
- logError(e);
- }
- }
- return this._kernelRelease;
- }
- get kernelMajorVersion() {
- if (this._kernelMajorVersion === undefined) {
- [this._kernelMajorVersion, this._kernelMinorVersion] = this.kernelRelease.split('.', 2);
- }
- return this._kernelMajorVersion;
- }
- get kernelMinorVersion() {
- if (this._kernelMinorVersion === undefined) {
- [this._kernelMajorVersion, this._kernelMinorVersion] = this.kernelRelease.split('.', 2);
- }
- return this._kernelMinorVersion;
- }
- checkMinKernelVersion(major, minor) {
- return (
- this.kernelMajorVersion > major ||
- (this.kernelMajorVersion === major && this.kernelMinorVersion >= minor)
- );
- }
- });
- /**
- * Execute a command and generate events based on its result
- *
- * @param {string} command Command to execute
- * @param {boolean} asRoot True to run with elevated permissions
- */
- const Runnable = GObject.registerClass({
- GTypeName: 'Runnable',
- Signals: {
- 'command-completed': {
- param_types: [GObject.TYPE_JSOBJECT /* error */]
- }
- }
- }, class Runnable extends GObject.Object {
- constructor(command, asRoot = false) {
- super();
- if (!command) {
- throw Error('The command cannot be an empty string');
- }
- this._command = command;
- this._asRoot = asRoot;
- }
- run() {
- const argv = ['sh', '-c', this._command];
- if (this._asRoot) {
- argv.unshift('pkexec');
- }
- try {
- const [, pid] = GLib.spawn_async(null, argv, null, GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, null);
- GLib.child_watch_add(GLib.PRIORITY_DEFAULT_IDLE, pid, (pid, status) => {
- try {
- GLib.spawn_check_exit_status(status);
- this.emit('command-completed', null);
- } catch (e) {
- if (e.code == 126) {
- // Cancelled
- } else {
- logError(e);
- this.emit('command-completed', e);
- }
- }
- GLib.spawn_close_pid(pid);
- });
- } catch (e) {
- logError(e);
- this.emit('command-completed', e);
- }
- }
- });
- /**
- * ThinkPad battery object
- */
- export const ThinkPadBattery = GObject.registerClass({
- GTypeName: 'ThinkPadBattery',
- Properties: {
- 'environment': GObject.ParamSpec.object(
- 'environment',
- 'Environment',
- 'Environment',
- GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
- Environment.$gtype
- ),
- 'name': GObject.ParamSpec.string(
- 'name',
- 'Name',
- 'Battery name',
- GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
- null
- ),
- 'auth-required': GObject.ParamSpec.boolean(
- 'auth-required',
- 'Auth required',
- 'Authorization is required to write the values',
- GObject.ParamFlags.READABLE,
- false
- ),
- 'is-active': GObject.ParamSpec.boolean(
- 'is-active',
- 'Is active',
- 'Indicates if the thresholds are active or not',
- GObject.ParamFlags.READABLE,
- false
- ),
- 'is-available': GObject.ParamSpec.boolean(
- 'is-available',
- 'Is available',
- 'Indicates if the battery are available or not',
- GObject.ParamFlags.READABLE,
- false
- ),
- 'pending-changes': GObject.ParamSpec.boolean(
- 'pending-changes',
- 'Pending changes',
- 'Indicates if the current values do not match the configured values',
- GObject.ParamFlags.READABLE,
- false
- ),
- 'start-value': GObject.ParamSpec.int(
- 'start-value',
- 'Start value',
- 'Current start value',
- GObject.ParamFlags.READABLE,
- 0, 100,
- 0
- ),
- 'end-value': GObject.ParamSpec.int(
- 'end-value',
- 'End value',
- 'Current end value',
- GObject.ParamFlags.READABLE,
- 0, 100,
- 100
- ),
- 'settings': GObject.ParamSpec.object(
- 'settings',
- 'Settings',
- 'Settings',
- GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
- Gio.Settings.$gtype
- ),
- },
- Signals: {
- 'enable-completed': {
- param_types: [GObject.TYPE_JSOBJECT /* error */]
- },
- 'disable-completed': {
- param_types: [GObject.TYPE_JSOBJECT /* error */]
- },
- },
- }, class ThinkPadBattery extends GObject.Object {
- constructor(constructProperties = {}) {
- super(constructProperties);
- if (!this.name) {
- throw Error('Battery name not defined');
- }
- // Battery directory
- this._baseDirectoryPath = `${BASE_PATH}/${this.name}`;
- this._baseDirectory = Gio.File.new_for_path(this._baseDirectoryPath);
- // Set paths
- if (this.environment.checkMinKernelVersion(5, 9)) { // kernel 5.9 and newer
- this._startFilePath = `${BASE_PATH}/${this.name}/${START_FILE_NEW}`;
- this._endFilePath = `${BASE_PATH}/${this.name}/${END_FILE_NEW}`;
- } else if (this.environment.checkMinKernelVersion(4, 17)) { // kernel 4.17 and newer
- this._startFilePath = `${BASE_PATH}/${this.name}/${START_FILE_OLD}`;
- this._endFilePath = `${BASE_PATH}/${this.name}/${END_FILE_OLD}`;
- } else { // Unsupported kernel
- throw Error(`Unsupported kernel version (${this.environment.kernelRelease}). A kernel version greater than or equal to 4.17 is required.`);
- }
- // Activate the directory monitor
- try {
- this._baseMonitor = this._baseDirectory.monitor_directory(Gio.FileMonitorFlags.NONE, null);
- this._monitorId = this._baseMonitor.connect(
- 'changed', (obj, file, otherFile, eventType) => {
- const filePath = file.get_path();
- switch (eventType) {
- case Gio.FileMonitorEvent.CHANGES_DONE_HINT:
- case Gio.FileMonitorEvent.CREATED:
- case Gio.FileMonitorEvent.DELETED:
- switch (filePath) {
- case this._startFilePath:
- this.startValue = readFileInt(this._startFilePath);
- break;
- case this._endFilePath:
- this.endValue = readFileInt(this._endFilePath);
- break;
- case this._baseDirectoryPath:
- this.startValue = readFileInt(this._startFilePath);
- this.endValue = readFileInt(this._endFilePath);
- break;
- default:
- break;
- }
- break;
- case Gio.FileMonitorEvent.ATTRIBUTE_CHANGED:
- this.authRequired = this._checkAuthRequired();
- break;
- default:
- break;
- }
- }
- );
- } catch (e) {
- logError(e, this.name);
- }
- // Update flags on changes in threshold values
- this._startId = this.connect(
- 'notify::start-value', () => {
- this._updateStatuses();
- }
- );
- this._endId = this.connect(
- 'notify::end-value', () => {
- this._updateStatuses();
- }
- );
- this._settingStartId = this.settings.connect(
- `changed::start-${this.name.toLowerCase()}`, () => {
- this._updateStatuses();
- }
- );
- this._settingEndId = this.settings.connect(
- `changed::end-${this.name.toLowerCase()}`, () => {
- this._updateStatuses();
- }
- );
- this._settingDisabledValueId = this.settings.connect(
- `changed::disabled-start-${this.name.toLowerCase()}-value`, () => {
- this._updateStatuses();
- }
- );
- // Load initial values
- this.startValue = readFileInt(this._startFilePath);
- this.endValue = readFileInt(this._endFilePath);
- this.authRequired = this._checkAuthRequired();
- }
- /**
- * Update the statuses
- */
- _updateStatuses() {
- // Check if is available
- this.isAvailable = this.startValue !== null || this.endValue !== null;
- // Check if thresholds is active
- const disabledStartValue = this.settings.get_int(`disabled-start-${this.name.toLowerCase()}-value`);
- if (disabledStartValue === -1) {
- // Use model detection
- if (this.environment.productVersion.search(ALTERNATIVE_DISABLED_START) >= 0) {
- this.isActive = (this.startValue !== null && this.startValue !== 95) || (this.endValue !== null && this.endValue !== 100);
- } else {
- this.isActive = (this.startValue !== null && this.startValue !== 0) || (this.endValue !== null && this.endValue !== 100);
- }
- } else {
- // Use user settings
- this.isActive = (this.startValue !== null && this.startValue !== disabledStartValue) || (this.endValue !== null && this.endValue !== 100);
- }
- const startSetting = this.settings.get_int(`start-${this.name.toLowerCase()}`);
- const endSetting = this.settings.get_int(`end-${this.name.toLowerCase()}`);
- if (this.isActive) {
- this.pendingChanges = (this.startValue !== null && this.startValue !== startSetting) || (this.endValue !== null && this.endValue !== endSetting);
- } else {
- this.pendingChanges = false;
- }
- }
- /**
- * Check if authentication is required to apply the changes
- *
- * @returns {boolean} Returns true if authentication is required to apply the changes
- */
- _checkAuthRequired() {
- try {
- const f = Gio.File.new_for_path(this._startFilePath);
- const info = f.query_info('access::*', Gio.FileQueryInfoFlags.NONE, null);
- if (!info.get_attribute_boolean('access::can-write')) {
- return true;
- }
- } catch (e) {
- // Ignored
- }
- try {
- const f = Gio.File.new_for_path(this._endFilePath);
- const info = f.query_info('access::*', Gio.FileQueryInfoFlags.NONE, null);
- if (!info.get_attribute_boolean('access::can-write')) {
- return true;
- }
- } catch (e) {
- // Ignored
- }
- return false;
- }
- get authRequired() {
- return this._authRequired;
- }
- set authRequired(value) {
- if (this.authRequired !== value) {
- this._authRequired = value;
- this.notify('auth-required');
- }
- }
- get isActive() {
- return this._isActive;
- }
- set isActive(value) {
- if (this.isActive !== value) {
- this._isActive = value;
- this.notify('is-active');
- }
- }
- get isAvailable() {
- return this._isAvailable;
- }
- set isAvailable(value) {
- if (this.isAvailable !== value) {
- this._isAvailable = value;
- this.notify('is-available');
- }
- }
- get startValue() {
- return this._startValue;
- }
- set startValue(value) {
- if (this.startValue !== value) {
- this._startValue = value;
- this.notify('start-value');
- }
- }
- get endValue() {
- return this._endValue;
- }
- set endValue(value) {
- if (this.endValue !== value) {
- this._endValue = value;
- this.notify('end-value');
- }
- }
- get pendingChanges() {
- return this._pendingChanges;
- }
- set pendingChanges(value) {
- if (this.pendingChanges !== value) {
- this._pendingChanges = value;
- this.notify('pending-changes');
- }
- }
- /**
- * Returns the command to set the thresholds.
- * This function does not run the command, it just returns the string corresponding to the command.
- * Passed values are not validated by the function, it is your responsibility to validate them first.
- *
- * @param {number} start Start value
- * @param {number} end End value
- * @returns {string|null} Command to modify the thresholds
- */
- _getSetterCommand(start, end) {
- if (!this.isAvailable) {
- return null;
- }
- if (!Number.isInteger(start) || !Number.isInteger(end)) {
- throw TypeError(`Thresholds (${start}/${end}) on battery (${this.name}) are not integer`);
- }
- if (start < 0 || end > 100 || start >= end) {
- throw RangeError(`Thresholds (${start}/${end}) on battery (${this.name}) out of range`);
- }
- // Commands
- const setStart = `echo ${start.toString()} > ${this._startFilePath}`;
- const setEnd = `echo ${end.toString()} > ${this._endFilePath}`;
- let oldStart = this.startValue;
- let oldEnd = this.endValue;
- if ((oldStart === start || oldStart === null) && (oldEnd === end || oldEnd === null)) {
- // Same thresholds
- return null;
- }
- if (oldStart >= oldEnd) {
- // Invalid threshold reading, happens on ThinkPad E/L series
- oldStart = null;
- oldEnd = null;
- }
- let command = null;
- if (fileExists(this._startFilePath) && fileExists(this._endFilePath)) {
- if (oldStart === start) { // Same start, apply only stop
- command = setEnd;
- } else if (oldEnd === end) { // Same stop, apply only start
- command = setStart;
- } else {
- // Determine sequence
- let startStopSequence = true;
- if (oldEnd != null && start > oldEnd) {
- startStopSequence = false;
- }
- if (startStopSequence) {
- command = setStart + ' && ' + setEnd;
- } else {
- command = setEnd + ' && ' + setStart;
- }
- }
- } else if (fileExists(this._startFilePath)) { // Only start available
- command = setStart;
- } else if (fileExists(this._endFilePath)) { // Only stop available
- command = setEnd;
- }
- return command;
- }
- /**
- * Get the command string to enable the configured thresholds
- *
- * @returns {string|null} Enable thresholds command string
- */
- get enableCommand() {
- const startSetting = this.settings.get_int(`start-${this.name.toLowerCase()}`);
- const endSetting = this.settings.get_int(`end-${this.name.toLowerCase()}`);
- return this._getSetterCommand(startSetting, endSetting);
- }
- /**
- * Get the command string to disable the thresholds
- *
- * @returns {string|null} Enable thresholds command string
- */
- get disableCommand() {
- return this._getSetterCommand(0, 100);
- }
- /**
- * Enable the configured thresholds
- */
- enable() {
- const command = this.enableCommand;
- if (!command) {
- return;
- }
- const runnable = new Runnable(command, this.authRequired);
- runnable.connect('command-completed', (obj, error) => {
- this.emit('enable-completed', error);
- })
- runnable.run();
- }
- /**
- * Disable thresholds
- */
- disable() {
- const command = this.disableCommand;
- if (!command) {
- return;
- }
- const runnable = new Runnable(command, this.authRequired);
- runnable.connect('command-completed', (obj, error) => {
- this.emit('disable-completed', error);
- })
- runnable.run();
- }
- /**
- * Toggle thresholds status
- */
- toggle() {
- this.isActive ? this.disable() : this.enable();
- }
- destroy() {
- if (this._startId) {
- this.disconnect(this._startId);
- }
- if (this._endId) {
- this.disconnect(this._endId);
- }
- if (this._settingStartId) {
- this.settings.disconnect(this._settingStartId);
- }
- if (this._settingEndId) {
- this.settings.disconnect(this._settingEndId);
- }
- if (this._settingDisabledValueId) {
- this.settings.disconnect(this._settingDisabledValueId);
- }
- if (this._monitorId) {
- this._baseMonitor.disconnect(this._monitorId);
- }
- this._baseMonitor.cancel();
- this._baseMonitor = null;
- this._baseDirectory = null;
- }
- });
- export const ThinkPad = GObject.registerClass({
- GTypeName: 'ThinkPad',
- Properties: {
- 'environment': GObject.ParamSpec.object(
- 'environment',
- 'Environment',
- 'Environment',
- GObject.ParamFlags.READABLE,
- Environment.$gtype
- ),
- 'is-active': GObject.ParamSpec.boolean(
- 'is-active',
- 'Is active',
- 'Indicates if the thresholds are active or not',
- GObject.ParamFlags.READABLE,
- false
- ),
- 'is-available': GObject.ParamSpec.boolean(
- 'is-available',
- 'Is available',
- 'Indicates if the battery are available or not',
- GObject.ParamFlags.READABLE,
- false
- ),
- 'pending-changes': GObject.ParamSpec.boolean(
- 'pending-changes',
- 'Pending changes',
- 'Indicates if the current values do not match the configured values',
- GObject.ParamFlags.READABLE,
- false
- ),
- 'batteries': GObject.ParamSpec.jsobject(
- 'batteries',
- 'Batteries',
- 'Batteries object',
- GObject.ParamFlags.READWRITE,
- []
- ),
- 'settings': GObject.ParamSpec.object(
- 'settings',
- 'Settings',
- 'Settings',
- GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
- Gio.Settings.$gtype
- ),
- },
- Signals: {
- 'enable-battery-completed': {
- param_types: [GObject.TYPE_OBJECT /* battery */, GObject.TYPE_JSOBJECT /* error */]
- },
- 'disable-battery-completed': {
- param_types: [GObject.TYPE_OBJECT /* battery */, GObject.TYPE_JSOBJECT /* error */]
- },
- 'enable-all-completed': {
- param_types: [GObject.TYPE_JSOBJECT /* error */]
- },
- 'disable-all-completed': {
- param_types: [GObject.TYPE_JSOBJECT /* error */]
- },
- },
- }, class ThinkPad extends GObject.Object {
- constructor(constructProperties = {}) {
- super(constructProperties);
- if (!this.environment.checkMinKernelVersion(4, 17)) {
- logError(new Error(`Kernel ${this.environment.kernelRelease} not supported`));
- this.batteries = [];
- return;
- }
- // Signals handlers IDs
- this._batteriesId = {};
- // Define batteries
- this.batteries = [
- new ThinkPadBattery({
- 'environment': this.environment,
- 'name': 'BAT0',
- 'settings': this.settings
- }),
- new ThinkPadBattery({
- 'environment': this.environment,
- 'name': 'BAT1',
- 'settings': this.settings
- }),
- ];
- // Connect the signals from the batteries to update the flags and notify commands
- this.batteries.forEach(battery => {
- const isAvailableId = battery.connect(
- 'notify::is-available', (bat) => {
- this.isAvailable = this._checkAvailable();
- }
- );
- const isActiveId = battery.connect(
- 'notify::is-active', (bat) => {
- this.isActive = this._checkActive();
- }
- );
- const pendingChangesId = battery.connect(
- 'notify::pending-changes', (bat) => {
- this.pendingChanges = this._checkPendingChanges();
- }
- );
- const enableCompletedId = battery.connect(
- 'enable-completed', (bat, error) => {
- this.emit('enable-battery-completed', bat, error);
- }
- );
- const disableCompletedId = battery.connect(
- 'disable-completed', (bat, error) => {
- this.emit('disable-battery-completed', bat, error);
- }
- );
- this._batteriesId[battery.name] = [isAvailableId, isActiveId, enableCompletedId, disableCompletedId];
- });
- // Load initial values
- this.isAvailable = this._checkAvailable();
- this.isActive = this._checkActive();
- this.pendingChanges = this._checkPendingChanges();
- }
- get environment() {
- if (this._environment === undefined) {
- this._environment = new Environment();
- }
- return this._environment;
- }
- get isAvailable() {
- return this._isAvailable;
- }
- set isAvailable(value) {
- if (this.isAvailable !== value) {
- this._isAvailable = value;
- this.notify('is-available');
- }
- }
- get isActive() {
- return this._isActive;
- }
- set isActive(value) {
- if (this.isActive !== value) {
- this._isActive = value;
- this.notify('is-active');
- }
- }
- get pendingChanges() {
- return this._pendingChanges;
- }
- set pendingChanges(value) {
- if (this.pendingChanges !== value) {
- this._pendingChanges = value;
- this.notify('pending-changes');
- }
- }
- /**
- * Check if there are changes to the settings that need to be applied
- *
- * @returns {boolean} Returns true if there are changes to the settings that need to be applied
- */
- _checkPendingChanges() {
- return this.batteries.some(bat => {
- return bat.pendingChanges;
- });
- }
- /**
- * Check if at least one battery has the thresholds active
- *
- * @returns {boolean} Returns true if at least one battery has the thresholds active
- */
- _checkActive() {
- return this.batteries.some(bat => {
- return bat.isActive && bat.isAvailable;
- });
- }
- /**
- * Check if at least one battery is available to apply the thresholds
- *
- * @returns {boolean} Returns true if at least one battery is available to apply the thresholds
- */
- _checkAvailable() {
- return this.batteries.some(bat => {
- return bat.isAvailable;
- });
- }
- /**
- * Enable all configured thresholds
- */
- enableAll() {
- let command = '';
- let authRequired = false;
- this.batteries.forEach(battery => {
- const batteryCommand = battery.enableCommand;
- if (batteryCommand) {
- command = `${command}${command ? ' && ' : ''}${batteryCommand}`;
- authRequired = authRequired || battery.authRequired;
- }
- });
- if (!command) {
- return;
- }
- const runnable = new Runnable(command, authRequired);
- runnable.connect('command-completed', (obj, error) => {
- this.emit('enable-all-completed', error);
- })
- runnable.run();
- }
- /**
- * Disable all thresholds
- */
- disableAll() {
- let command = '';
- let authRequired = false;
- this.batteries.forEach(battery => {
- const batteryCommand = battery.disableCommand;
- if (batteryCommand) {
- command = `${command}${command ? ' && ' : ''}${batteryCommand}`;
- authRequired = authRequired || battery.authRequired;
- }
- });
- if (!command) {
- return;
- }
- const runnable = new Runnable(command, authRequired);
- runnable.connect('command-completed', (obj, error) => {
- this.emit('disable-all-completed', error);
- })
- runnable.run();
- }
- destroy() {
- if (this._batteriesId) {
- this.batteries.forEach(battery => {
- const ids = this._batteriesId[battery.name];
- ids.forEach(id => {
- battery.disconnect(id);
- });
- battery.destroy()
- });
- }
- }
- });
|