Evento basado en el nivel de carga de la batería

Todavía no he encontrado nada, así que esperaba que tal vez alguien aquí tuviera una idea.

Quiero iniciar un evento basado en la batería de una computadora portátil Apple que alcanza cierto nivel de carga o cuando la batería está completamente cargada.

Sé que puedo hacer que algo se ejecute periódicamente, verificar el nivel de la batería y decidir qué hacer, pero prefiero el evento si es posible.

Respuestas (4)

Bueno, esta pregunta es antigua, pero me encontré con una situación similar y quería ejecutar un atajo (con "Atajos de Siri") en macOS 12 con ciertos niveles de batería.

En lugar de un servicio en segundo plano, decidí que un script funcionaría mejor para mí, ya que debería encender y apagar mi toma de HomeKit a la que está conectado el cargador. (Pero solo mientras la MacBook está transcodificando una película)

De la respuesta de Joseph, tomé los bits que aún funcionan y creé un script Tcl que verifica el nivel de la batería cada minuto.

He creado un archivo llamado batt.tclcon el siguiente contenido:

#!/usr/bin/env tclsh

set run 1
set last 0
set logs 1
set logfile "batt.log.txt"
set settingsfile batt.cfg
set min 15
set max 80
set delay 10000

# leave "nil"; we need a wake lock when we're charging. This is done using a call "caffeinate",     waiting for another PID to be removed.
set wakeLock "nil"

proc log {message} {
  if {$::logs != 1} {
    return
  }
  set msg ""
  append msg [clock format [clock seconds] -format "%d.%m.%Y %H:%M:%S"]
  append msg ": "
  append msg $message
  set of1 [open $::logfile a]
  puts $of1 $msg
  close $of1
}

proc enableWakeLock {} {
  if {$::wakeLock == "nil"} {
    log "Enabling wake lock"
        set ::wakeLock [open "|tclsh" r+]
    set myProcess [pid $::wakeLock]
    exec caffeinate -i -w $myProcess &
  }
}

proc disableWakeLock {} {
  if {$::wakeLock != "nil"} {
    log "Disabling wake lock"
    puts $::wakeLock "exit\n"
    flush $::wakeLock
    close $::wakeLock
    set ::wakeLock "nil"
  }
}

proc replaceAll {input lookup replacement} {
  set offs 0
  set mapping [list $lookup $replacement]
  while {$offs != -1} {
    set input [string map $mapping $input]
    set offs [string first $lookup $input]
  }
  return $input
}

proc loadSettings {} {
  if {[file exists $::settingsfile]} {
    set if1 [open $::settingsfile]
    set data [read $if1]
    close $if1

    set data [string map [list \t " " \n " " \r " "] $data]
    set data [replaceAll $data "  " " "]
    set data [string trim $data " "]

    set lines [split $data " "]
    set id ""
    foreach line $lines {
      if {[string index $line 0] != "#"} {
        if {[string length $id] == 0} {
          set id $line
        } else {
          switch -exact -- $id {
            "low:" {
              set ::min $line
              set id ""
            }
            "high:" {
              set ::max $line
              set id ""
            }
            "logs:" {
              set ::logs $line
              set id ""
            }
            "logfile:" {
              set ::logfile $line
              set id ""
            }
            default {
              puts "Unknown config option <$id>. Aborting."
              exit
            }
          }
        }
      }
    }
  } else {
    puts -nonewline "Generating settings file"
    flush stdout
    set of1 [open $::settingsfile w]
    puts $of1 [list "low:" $::min]
    puts $of1 [list "high:" $::max]
    puts $of1 [list "logfile:" $::logfile]
    puts -nonewline $of1 [list "logs:" $::logs]
    flush $of1
    close $of1
    puts ", done."
  }

  if {$::logs != 1 && $::logs != 0} {
    set msg "Weird \"logs:\" setting: must be 1 or 0. Aborting."
    puts $msg
    exit
  }

  if {$::min >= $::max} {
    set msg "Weird configuration: low charge ($::min) is equal or higher that high charge ($::max). Aborting."
    puts $msg
    exit
  }

}

proc waypointSearch {input waypoints endwaypoint} {
  set offs 0
  set len 0
  foreach wp $waypoints {
    set len [string length $wp]
    set offs [string first $wp $input $offs]
    if {$offs < 0} {
      puts "Waypoint \"$wp\" not found."
    }
#    puts "WP: <$wp> $offs $len"
  }
  set eoffs [string first $endwaypoint $input $offs]
  if {$eoffs < 0} {
    puts "Waypoint \"$endwaypoint\" not found."
  }
  return [string range $input [expr $offs + $len] [expr $eoffs - 1]]
}


loadSettings

set msg "Starting with settngs for low: $min and high: $max."
puts -nonewline "\n\nlow: $min, high: $max"
log $msg

while {$run == 1} {

  set input [exec ioreg -l]
  set lvl [waypointSearch $input [list "AppleSmartBattery " "\"CurrentCapacity\"" "="] "\n"]
  set level [string trim $lvl " \t\n"]

  set changes 0

  if {$last != $level} {
    set v [expr abs(abs($last - $level)-1) * -10000]
    set wouldBe [expr $delay + $v]
    if {$v < 0 && $wouldBe > 0} {
      set delay $wouldBe
      set changes 1
    }

    if {$level <= $::min} {
      set last $level
      set msg "Batt low: $level"
      puts -nonewline "\n" 
      puts -nonewline $msg
      log $msg
      enableWakeLock
      exec shortcuts run mac-battery-low
      continue
    } elseif {$level >= $::max} {
      set last $level
      set msg "Batt high: $level"
      puts -nonewline "\n" 
      puts -nonewline $msg
      log $msg
      exec shortcuts run mac-battery-high
      disableWakeLock
      continue
    }
  } else {
    set canMaxBe [expr ($level*3000)]
    if {$delay + 10000 <= $canMaxBe} {
      incr delay 10000
      set changes 1
    }
  }

  if {$last != $level} {
    set changes 1
  }

  set last $level
  if {$changes == 1} {
    puts -nonewline "\n" 
    puts -nonewline "Batt: $level"
    puts -nonewline " ([expr $delay/1000] s)"
    flush stdout
  }
  after $delay
  
}

Luego creé 2 atajos para "Atajos de Siri" y los nombré mac-battery-lowy mac-battery-high. El primero encenderá la salida, el segundo lo apagará.

En la Terminal, simplemente ejecuto tclsh batt.tcl(o hago chmod 755 batt.tcldoble clic en el archivo) para comenzar a monitorear la batería. Ctrl+ Cfinalizará el seguimiento.

Editar

Remoto.

(Esto fue confuso ya que el script se actualizó y tiene una mejor solución).

editar2

Lo que he encontrado hasta ahora:

  1. El proceso de 'cafeinización' mantiene el sistema en funcionamiento.
  2. Solo debemos cafeinar el sistema mientras estemos cargando ya que también hay otros procesos que cafeinan mientras el usuario está activo.

-> Por lo tanto, no es necesario verificar pmset -g assertionslas afirmaciones existentes.

  1. Simplemente necesitamos crear una afirmación propia que mantenga la Mac encendida mientras la cargamos. Cuando alcanzamos el nivel de batería deseado, activamos nuestro evento para eliminar la afirmación. Es posible que aún se esté ejecutando cualquier proceso de cafeína y se mantenga el sistema encendido. O ya terminó, lo que permite que la Mac entre en suspensión de inmediato.

Por lo tanto, permitimos que el sistema comience a dormir profundamente en cualquier momento si el cargador está apagado. Sin embargo, otros pueden evitar el sueño profundo y, por lo tanto, mantenernos en funcionamiento también. (comprobando periódicamente)

He actualizado el script anterior . En la primera ejecución, crea un archivo de configuración que le permite cambiar los valores para sesiones posteriores sin definir parámetros.

Jaja, esto fue hace mucho tiempo, mi idea era tener TeamFortress2 "¡TOTALMENTE CARGADO!" reproducción de bytes de sonido cada vez que mi computadora portátil haya terminado de cargarse; tal vez pueda hacerlo ahora
Yo, en cambio, pierdo el aviso de batería del sistema y me olvido de conectar el cargador. Es por eso que mi Mac está permanentemente conectada al cargador y HomeKit se usa para encender y apagar la toma de corriente correspondiente. Debe encenderse al 20% y apagarse al 80%. Esto también ahorra la salud de la batería.

Quiero que esto esté basado en el sistema operativo, por lo que no necesito una conexión a Internet o un navegador abierto, etc. Solo algo que puede suceder en segundo plano.

La forma más fácil de hacerlo sería con una aplicación, pero aquí se explica cómo hacerlo solo con los comandos integrados del sistema operativo. Sé que desea que se base en eventos, pero no estoy seguro de cómo lograrlo, así que aquí hay una forma de verificar el nivel de la batería y luego hacer otra cosa si está por encima o por debajo de un umbral.

Puede considerar un launchdproceso, esto es básicamente una tarea programada que se ejecuta cada xminutos. A menudo son utilizados por usuarios avanzados, pero no son demasiado difíciles de configurar. Configura una tarea programada de lanzamiento con un archivo .plist que coloca en este directorio: Macintosh HD\Library\LaunchDaemonsy así es como estructura el archivo .plist;

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Debug</key>
<true/>
<key>ExitTimeOut</key>
<integer>120</integer>
<key>Label</key>
<string>com.me.BatteryInfo</string>
<key>ProgramArguments</key>
<array>
    <string>/Users/yourusername/batt.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StartInterval</key>
<integer>1800</integer>
</dict>
</plist>

Si tuviera que guardar ese archivo como com.me.BatteryInfoen la LaunchDaemonscarpeta mencionada anteriormente, crearía una tarea programada que se ejecuta cada 30 minutos. El número, 1800directamente arriba, </dict>es el tiempo en segundos de la frecuencia con la que desea que se ejecute la tarea. Donde dice <string>/Users/yourusername/batt.sh</string>es donde especifica qué script se ejecuta según lo programado. Debes salir <string>e </sting>intacto.

La línea <string>com.me.BatteryInfo</string>es el nombre único de la tarea programada. Si va a hacer más de uno, asegúrese de que cada .plist tenga un nombre único aquí.

Lo siguiente que debe hacer es cambiar la propiedad de .plist a root. Esto es necesario como función de seguridad (supongo que para evitar que el software o los usuarios creen tareas maliciosas programadas). Para cambiar la propiedad del archivo, haga sudo chown root \Library\LaunchDaemons\yourtask.plist(reemplace yourtask.plist con el nombre de archivo real del .plist que creó). Esta tarea le pedirá una contraseña.

Ahora necesita crear el script que se ejecutará periódicamente. Debe crear un archivo .sh (un script bash) para decirle a la computadora qué hacer. Para crear un archivo .sh, abra un editor de texto para programadores, como Sublime Text o Komodo Edit . NO use Text Edit, ya que a menudo agrega texto a sus archivos que interferiría con su script. Text Edit realmente no debería usarse para el código.

Cree un script (archivo .sh) con el siguiente código;

#!/bin/sh

percent=$(ioreg -l | awk '$3~/Capacity/{c[$3]=$5}END{OFMT="%.3f";max=c["\"MaxCapacity\""];print(max>0?100*c["\"CurrentCapacity\""]/max:"?")}')

if [ $percent > 95 ]
    then
        echo 'charged.'
fi
exit 0

Reemplace echo 'charged.'con los comandos de terminal que le gustaría ejecutar cuando la batería esté cargada. open /Applications/Notes.appabrirá la aplicación Notas; puede cambiar el directorio para abrir una aplicación diferente.

$percent > 95Esto le dice a la siguiente línea que solo se ejecute cuando la batería esté más que 95cargada. Puedes cambiar esto a lo que quieras. El nivel de la batería aquí a menudo será ligeramente diferente al que se muestra en la barra de menú en la parte superior. Si desea 'ejecutar cuando la batería esté completamente cargada', le recomiendo dejar esto como > 95 . Si desea que la tarea se ejecute cuando la batería esté por debajo del 20 %, por ejemplo, cámbiela a$percent < 20

NOTA: Debido a que esta es una tarea programada, su secuencia de comandos se ejecutará cada xcierto número de minutos. Esto significa que si coloca open \Applications\Notes.appdentro de su secuencia de comandos, la aplicación Notas se iniciará cada xminutos (si su batería está cargada)

Esta tarea se ejecutará incluso si nadie ha iniciado sesión.

Sé que hiciste esta pregunta hace un tiempo, pero espero que esto ayude a alguien.

Power Manager no es gratuito, pero admite la activación de eventos en función de los niveles de la batería (interna o UPS). Los eventos pueden ejecutar scripts, iniciar aplicaciones o realizar tareas como apagar.

Power Manager se basa en eventos y no sondea los cambios de batería.

En cambio, Power Manager se conecta a la capa IOKit de OS X y espera actualizaciones del hardware. Los eventos se pueden activar cuando nadie ha iniciado sesión; no depende de un usuario activo.

Administrador de energía: ejecute un script con la batería restante

Las tareas integradas del Asistente de programación se enfocan en el desempeño cuando la batería baja a un nivel particular, pero se pueden modificar para verificar si aumenta el porcentaje de batería o el tiempo restante.

Estas dos publicaciones hablan sobre las fuentes de energía UPS, pero se adaptan fácilmente a la batería interna de su MacBook:

Divulgación: Soy un ingeniero de software detrás de Power Manager.

Esto no es específico de Mac, pero Mozilla Aurora tiene una API integrada que contiene una variedad de funciones relacionadas con la batería. Uno puede detectar el estado de la batería (si se está cargando o no), cuánto tiempo más tardará la batería en descargarse/cargarse y su nivel. Hay un ejemplo simple de cómo agregar un EventListener para llamar a una función cuando el nivel de la batería está en un punto determinado.

Quiero que esto esté basado en el sistema operativo, por lo que no necesito una conexión a Internet o un navegador abierto, etc. Solo algo que puede suceder en segundo plano.