mirror of
https://github.com/actuallymentor/battery.git
synced 2026-03-20 15:40:24 +00:00
🐛 force the killing of dangling processes on uninstall
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
// Command line interactors
|
||||
const { exec } = require('node:child_process')
|
||||
const sudo = require( 'sudo-prompt' )
|
||||
const { log, alert } = require( './helpers' )
|
||||
const { log, alert, wait } = require( './helpers' )
|
||||
const { USER } = process.env
|
||||
const path_fix = 'PATH=$PATH:/bin:/usr/bin:/usr/local/bin:/usr/sbin:/opt/homebrew'
|
||||
const path_fix = 'PATH=$PATH:/bin:/usr/bin:/usr/local/bin:/usr/sbin:/opt/homebrew:/usr/bin/'
|
||||
const battery = `${ path_fix } battery`
|
||||
const { app } = require( 'electron' )
|
||||
const shell_options = {
|
||||
@@ -12,13 +12,13 @@ const shell_options = {
|
||||
}
|
||||
|
||||
// Execute without sudo
|
||||
const exec_async = command => new Promise( ( resolve, reject ) => {
|
||||
const exec_async_no_timeout = command => new Promise( ( resolve, reject ) => {
|
||||
|
||||
log( `Executing ${ command }` )
|
||||
|
||||
exec( command, shell_options, ( error, stdout, stderr ) => {
|
||||
|
||||
if( error ) return reject( error )
|
||||
if( error ) return reject( error, stderr, stdout )
|
||||
if( stderr ) return reject( stderr )
|
||||
if( stdout ) return resolve( stdout )
|
||||
|
||||
@@ -26,6 +26,13 @@ const exec_async = command => new Promise( ( resolve, reject ) => {
|
||||
|
||||
} )
|
||||
|
||||
const exec_async = ( command, timeout_in_ms=2000, throw_on_timeout=false ) => Promise.race( [
|
||||
exec_async_no_timeout( command ),
|
||||
wait( timeout_in_ms ).then( () => {
|
||||
if( throw_on_timeout ) throw new Error( `${ command } timed out` )
|
||||
} )
|
||||
] )
|
||||
|
||||
// Execute with sudo
|
||||
const exec_sudo_async = async command => new Promise( async ( resolve, reject ) => {
|
||||
|
||||
@@ -47,7 +54,10 @@ const exec_sudo_async = async command => new Promise( async ( resolve, reject )
|
||||
const enable_battery_limiter = async () => {
|
||||
|
||||
try {
|
||||
await exec_async( `${ battery } maintain 80` )
|
||||
// Start battery maintainer
|
||||
const status = await get_battery_status()
|
||||
await exec_async( `${ battery } maintain ${ status?.maintain_percentage || 80 }` )
|
||||
log( `enable_battery_limiter exec complete` )
|
||||
} catch( e ) {
|
||||
log( 'Error enabling battery: ', e )
|
||||
alert( e.message )
|
||||
@@ -70,6 +80,13 @@ const update_or_install_battery = async () => {
|
||||
|
||||
try {
|
||||
|
||||
// Check for network
|
||||
const online = await Promise.race( [
|
||||
exec_async( `${ path_fix } curl icanhasip.com &> /dev/null` ).then( () => true ).catch( () => false ),
|
||||
exec_async( `${ path_fix } curl github.com &> /dev/null` ).then( () => true ).catch( () => false )
|
||||
] )
|
||||
log( `Internet online: ${ online }` )
|
||||
|
||||
// Check if xcode build tools are installed
|
||||
const xcode_installed = await exec_async( `${ path_fix } which git` ).catch( () => false )
|
||||
if( !xcode_installed ) {
|
||||
@@ -96,8 +113,15 @@ const update_or_install_battery = async () => {
|
||||
const is_installed = battery_installed && smc_installed
|
||||
log( 'Is installed? ', is_installed )
|
||||
|
||||
// Kill running instances of battery
|
||||
const processes = await exec_async( `ps aux | grep "/usr/local/bin/battery " | wc -l | grep -Eo "\\d*"` )
|
||||
log( `Found ${ `${ processes }`.replace( /\n/, '' ) } battery related processed to kill` )
|
||||
await exec_async( `${ battery } maintain stop` )
|
||||
await exec_async( `pkill -f "/usr/local/bin/battery.*"` ).catch( e => log( `Error killing existing battery progesses, usually means no running processes` ) )
|
||||
|
||||
// If installed, update
|
||||
if( is_installed && visudo_complete ) {
|
||||
if( !online ) return log( `Skipping battery update because we are offline` )
|
||||
log( `Updating battery...` )
|
||||
const result = await exec_async( `${ battery } update silent` )
|
||||
log( `Update result: `, result )
|
||||
@@ -106,6 +130,7 @@ const update_or_install_battery = async () => {
|
||||
// If not installed, run install script
|
||||
if( !is_installed || !visudo_complete ) {
|
||||
log( `Installing battery for ${ USER }...` )
|
||||
if( !online ) return alert( `Battery needs an internet connection to download the latest version, please connect to the internet and open the app again.` )
|
||||
await alert( `Welcome to the Battery limiting tool. The app needs to install/update some components, so it will ask for your password. This should only be needed once.` )
|
||||
const result = await exec_sudo_async( `curl -s https://raw.githubusercontent.com/actuallymentor/battery/main/setup.sh | bash -s -- $USER` )
|
||||
log( `Install result: `, result )
|
||||
@@ -125,6 +150,7 @@ const is_limiter_enabled = async () => {
|
||||
|
||||
try {
|
||||
const message = await exec_async( `${ battery } status` )
|
||||
log( `Limiter status message: `, message )
|
||||
return message.includes( 'being maintained at' )
|
||||
} catch( e ) {
|
||||
log( `Error getting battery status: `, e )
|
||||
@@ -138,6 +164,8 @@ const get_battery_status = async () => {
|
||||
try {
|
||||
const message = await exec_async( `${ battery } status_csv` )
|
||||
let [ percentage, remaining, charging, discharging, maintain_percentage ] = message.split( ',' )
|
||||
maintain_percentage = maintain_percentage.trim()
|
||||
maintain_percentage = maintain_percentage.length ? maintain_percentage : undefined
|
||||
charging = charging == 'enabled'
|
||||
discharging = discharging == 'discharging'
|
||||
remaining = remaining.match( /\d{1,2}:\d{1,2}/ ) ? remaining : 'unknown'
|
||||
@@ -147,7 +175,7 @@ const get_battery_status = async () => {
|
||||
if( discharging ) daemon_state += `forcing discharge to 80%`
|
||||
else daemon_state += `smc charging ${ charging ? 'enabled' : 'disabled' }`
|
||||
|
||||
return [ battery_state, daemon_state ]
|
||||
return [ battery_state, daemon_state, maintain_percentage ]
|
||||
|
||||
} catch( e ) {
|
||||
log( `Error getting battery status: `, e )
|
||||
|
||||
@@ -24,8 +24,12 @@ const log = async ( ...messages ) => {
|
||||
|
||||
const { dialog } = require('electron')
|
||||
const alert = ( message ) => dialog.showMessageBox( { message } )
|
||||
const wait = time_in_ms => new Promise( resolve => {
|
||||
setTimeout( resolve, time_in_ms )
|
||||
} )
|
||||
|
||||
module.exports = {
|
||||
log,
|
||||
alert
|
||||
alert,
|
||||
wait
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
const { shell, app, Tray, Menu } = require( 'electron' )
|
||||
const { enable_battery_limiter, disable_battery_limiter, update_or_install_battery, is_limiter_enabled, get_battery_status } = require('./battery')
|
||||
const { log } = require("./helpers")
|
||||
const { log, wait } = require("./helpers")
|
||||
const { get_inactive_logo, get_active_logo } = require('./theme')
|
||||
|
||||
/* ///////////////////////////////
|
||||
@@ -11,88 +11,102 @@ let tray = undefined
|
||||
// Set interface to usable
|
||||
const generate_app_menu = async () => {
|
||||
|
||||
// Get battery and daemon status
|
||||
const [ battery_state, daemon_state ] = await get_battery_status()
|
||||
try {
|
||||
// Get battery and daemon status
|
||||
const [ battery_state, daemon_state, maintain_percentage=80 ] = await get_battery_status()
|
||||
|
||||
// Check if limiter is on
|
||||
const limiter_on = await is_limiter_enabled()
|
||||
// Check if limiter is on
|
||||
const limiter_on = await is_limiter_enabled()
|
||||
|
||||
// Set tray icon
|
||||
tray.setImage( limiter_on ? get_active_logo() : get_inactive_logo() )
|
||||
// Set tray icon
|
||||
tray.setImage( limiter_on ? get_active_logo() : get_inactive_logo() )
|
||||
|
||||
// Build menu
|
||||
return Menu.buildFromTemplate( [
|
||||
// Build menu
|
||||
return Menu.buildFromTemplate( [
|
||||
|
||||
{
|
||||
label: 'Enable 80% battery limit',
|
||||
type: 'radio',
|
||||
checked: limiter_on,
|
||||
click: enable_limiter
|
||||
},
|
||||
{
|
||||
sublabel: 'thing',
|
||||
label: 'Disable 80% battery limit',
|
||||
type: 'radio',
|
||||
checked: !limiter_on,
|
||||
click: disable_limiter
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: `Battery: ${ battery_state }`,
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
label: `Power: ${ daemon_state }`,
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: `About v${ app.getVersion() }`,
|
||||
submenu: [
|
||||
{
|
||||
label: `Check for updates`,
|
||||
click: () => shell.openExternal( `https://github.com/actuallymentor/battery/releases` )
|
||||
},
|
||||
{
|
||||
label: `User manual`,
|
||||
click: () => shell.openExternal( `https://github.com/actuallymentor/battery#readme` )
|
||||
},
|
||||
{
|
||||
type: 'normal',
|
||||
label: 'Command-line usage',
|
||||
click: () => shell.openExternal( `https://github.com/actuallymentor/battery#-command-line-version` )
|
||||
},
|
||||
{
|
||||
type: 'normal',
|
||||
label: 'Help and feature requests',
|
||||
click: () => shell.openExternal( `https://github.com/actuallymentor/battery/issues` )
|
||||
{
|
||||
label: `Enable ${ maintain_percentage }% battery limit`,
|
||||
type: 'radio',
|
||||
checked: limiter_on,
|
||||
click: enable_limiter
|
||||
},
|
||||
{
|
||||
sublabel: 'thing',
|
||||
label: `Disable ${ maintain_percentage }% battery limit`,
|
||||
type: 'radio',
|
||||
checked: !limiter_on,
|
||||
click: disable_limiter
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: `Battery: ${ battery_state }`,
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
label: `Power: ${ daemon_state }`,
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: `About v${ app.getVersion() }`,
|
||||
submenu: [
|
||||
{
|
||||
label: `Check for updates`,
|
||||
click: () => shell.openExternal( `https://github.com/actuallymentor/battery/releases` )
|
||||
},
|
||||
{
|
||||
label: `User manual`,
|
||||
click: () => shell.openExternal( `https://github.com/actuallymentor/battery#readme` )
|
||||
},
|
||||
{
|
||||
type: 'normal',
|
||||
label: 'Command-line usage',
|
||||
click: () => shell.openExternal( `https://github.com/actuallymentor/battery#-command-line-version` )
|
||||
},
|
||||
{
|
||||
type: 'normal',
|
||||
label: 'Help and feature requests',
|
||||
click: () => shell.openExternal( `https://github.com/actuallymentor/battery/issues` )
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
click: () => {
|
||||
tray.destroy()
|
||||
app.quit()
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
click: () => {
|
||||
tray.destroy()
|
||||
app.quit()
|
||||
}
|
||||
}
|
||||
|
||||
] )
|
||||
|
||||
] )
|
||||
} catch( e ) {
|
||||
log( `Error generating menu: `, e )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Refresh tray with battery status values
|
||||
const refresh_tray = async () => {
|
||||
const refresh_tray = async ( force_interactive_refresh = false ) => {
|
||||
log( "Refreshing tray icon..." )
|
||||
tray.setContextMenu( await generate_app_menu() )
|
||||
const new_menu = await generate_app_menu()
|
||||
if( force_interactive_refresh ) {
|
||||
log( `Forcing interactive refresh ${ force_interactive_refresh }` )
|
||||
tray.closeContextMenu()
|
||||
tray.popUpContextMenu( new_menu )
|
||||
}
|
||||
tray.setContextMenu( new_menu )
|
||||
}
|
||||
|
||||
// Refresh app logo
|
||||
const refresh_logo = async () => {
|
||||
const refresh_logo = async ( force ) => {
|
||||
|
||||
if( force == 'active' ) return tray.setImage( get_active_logo() )
|
||||
if( force == 'inactive' ) return tray.setImage( get_inactive_logo() )
|
||||
|
||||
const is_enabled = await is_limiter_enabled()
|
||||
if( is_enabled ) return tray.setImage( get_active_logo() )
|
||||
return tray.setImage( get_inactive_logo() )
|
||||
@@ -114,7 +128,10 @@ async function set_initial_interface() {
|
||||
|
||||
log( "Triggering boot-time auto-update" )
|
||||
await update_or_install_battery()
|
||||
log( "Update process complete" )
|
||||
log( "App initialisation process complete" )
|
||||
|
||||
// Start battery handler
|
||||
await enable_battery_limiter()
|
||||
|
||||
|
||||
// Set tray styles
|
||||
@@ -122,7 +139,8 @@ async function set_initial_interface() {
|
||||
await refresh_tray()
|
||||
|
||||
// Set tray open listener
|
||||
tray.on( 'mouse-enter', refresh_tray )
|
||||
tray.on( 'mouse-enter', () => refresh_tray() )
|
||||
tray.on( 'click', () => refresh_tray() )
|
||||
|
||||
|
||||
}
|
||||
@@ -132,19 +150,27 @@ async function set_initial_interface() {
|
||||
// /////////////////////////////*/
|
||||
async function enable_limiter() {
|
||||
|
||||
log( 'Enable limiter' )
|
||||
await enable_battery_limiter()
|
||||
await refresh_tray()
|
||||
await refresh_logo()
|
||||
try {
|
||||
log( 'Enable limiter' )
|
||||
await refresh_logo( 'active' )
|
||||
await enable_battery_limiter()
|
||||
await refresh_tray()
|
||||
} catch( e ) {
|
||||
log( `Error in enable_limiter: `, e )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function disable_limiter() {
|
||||
|
||||
log( 'Disable limiter' )
|
||||
await disable_battery_limiter()
|
||||
await refresh_tray()
|
||||
await refresh_logo()
|
||||
try {
|
||||
log( 'Disable limiter' )
|
||||
await refresh_logo( 'inactive' )
|
||||
await disable_battery_limiter()
|
||||
await refresh_tray()
|
||||
} catch( e ) {
|
||||
log( `Error in disable_limiter: `, e )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "battery",
|
||||
"version": "1.0.5",
|
||||
"description": "A battery charge limiter for M1 Mac devices",
|
||||
"description": "A battery charge limiter for M1/2 Mac devices",
|
||||
"main": "main.js",
|
||||
"build": {
|
||||
"appId": "co.palokaj.battery",
|
||||
|
||||
@@ -226,6 +226,7 @@ if [[ "$action" == "uninstall" ]]; then
|
||||
disable_discharging
|
||||
battery remove_daemon
|
||||
sudo rm -v "$binfolder/smc" "$binfolder/battery"
|
||||
pkill -f "/usr/local/bin/battery.*"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user