Событие, основанное на уровне заряда батареи

Я еще ничего не нашел, поэтому я надеялся, что у кого-то здесь есть идея.

Я хочу запустить даже на основе батареи на ноутбуке Apple, достигшей определенного уровня заряда, или когда батарея полностью заряжена.

Я знаю, что могу периодически что-то запускать, проверять уровень заряда батареи и решать, что делать, но я предпочитаю управление событиями, если это возможно.

Ответы (4)

Что ж, этот вопрос старый, но я столкнулся с похожей ситуацией и хотел запустить ярлык (с «Ярлыками Siri») в macOS 12 при определенных уровнях заряда батареи.

Вместо фоновой службы я решил, что для меня лучше подойдет сценарий, поскольку он должен включать и выключать мою розетку HomeKit, к которой подключено зарядное устройство. (Но только пока MacBook перекодирует фильм)

Из ответа Джозефа я взял биты, которые все еще работают, и создал скрипт Tcl, который каждую минуту проверяет уровень заряда батареи.

Я создал файл batt.tclс именем следующего содержания:

#!/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
  
}

Затем я создал 2 ярлыка для «Siri Shortcuts» и назвал их mac-battery-lowи mac-battery-high. Первый включит розетку, второй выключит.

В терминале я просто запускаю tclsh batt.tcl(или chmod 755 batt.tclдважды щелкаю файл), чтобы начать мониторинг батареи. Ctrl+ Cзавершит мониторинг.

Редактировать

Удаленный.

(Это сбивало с толку, так как скрипт был обновлен и имеет лучшее решение.)

Редактировать2

Что я нашел до сих пор:

  1. Процесс «кофеинирования» поддерживает работоспособность системы.
  2. Мы должны накачивать систему кофеином только во время зарядки, поскольку есть и другие процессы, которые нагнетают кофеин, пока пользователь активен.

-> Таким образом, нет необходимости проверять pmset -g assertionsсуществующие утверждения.

  1. Нам просто нужно создать собственное утверждение, которое поддерживает работу Mac во время зарядки. Когда мы достигли желаемого уровня заряда батареи, мы запускаем наше событие, чтобы удалить утверждение. Любой процесс кофеина может все еще работать и поддерживать систему включенной. Или он уже закончен, что позволяет Mac сразу перейти в спящий режим.

Таким образом, мы позволяем системе переходить в режим глубокого сна в любое время, если зарядное устройство выключено. Однако другие могут препятствовать глубокому сну и, таким образом, поддерживать нас в рабочем состоянии. (периодически проверяю)

Я обновил приведенный выше скрипт . При первом запуске он создает файл настроек, который позволяет вам изменять значения для последующих сеансов без определения параметров.

Ха-ха, это было так давно, вся моя идея заключалась в том, чтобы TeamFortress2 был «ПОЛНОСТЬЮ ЗАРЯЖЕН!» звук байта воспроизводится всякий раз, когда мой ноутбук завершает зарядку - может быть, я смогу добраться до него сейчас
Я, с другой стороны, пропускаю предупреждение системы о батарее и забываю подключить зарядное устройство. Вот почему мой Mac постоянно подключен к зарядному устройству, а HomeKit используется для включения и выключения соответствующей розетки. Он должен включаться при 20% и выключаться при 80%. Это также сохраняет здоровье батареи.

Я хочу, чтобы это было на основе ОС, поэтому мне не нужно подключение к Интернету, открытый браузер и т. Д. Просто что-то, что может происходить в фоновом режиме.

Проще всего это сделать с помощью приложения, но вот как это сделать только с помощью встроенных команд ОС. Я знаю, что вы хотите, чтобы он управлялся событиями, но я не уверен, как это сделать, поэтому вот один из способов проверить уровень заряда батареи, а затем сделать что-то еще, если он выше/ниже порогового значения.

Вы можете рассмотреть launchdпроцесс, это в основном запланированная задача, которая запускается каждую xминуту. Они часто используются продвинутыми пользователями, но их не так уж сложно настроить. Вы настраиваете запланированную задачу launchd с файлом .plist, который вы помещаете в этот каталог: Macintosh HD\Library\LaunchDaemonsи вот как вы структурируете файл .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>

Если бы вы сохранили этот файл, как com.me.BatteryInfoв LaunchDaemonsпапке, упомянутой выше, он создал бы запланированную задачу, которая запускается каждые 30 минут. Число 1800прямо выше </dict>— это время в секундах того, как часто вы хотите запускать задачу. Там, где он говорит <string>/Users/yourusername/batt.sh</string>, вы указываете, какой скрипт запускается по расписанию. Вы должны уйти целым <string>и </sting>невредимым.

Строка <string>com.me.BatteryInfo</string>— это уникальное имя запланированной задачи. Если вы собираетесь создать более одного файла, убедитесь, что каждый файл .plist имеет здесь уникальное имя.

Следующее, что вам нужно сделать, это изменить владельца .plist на root. Это требуется в качестве функции безопасности (я полагаю, чтобы программы/пользователи не создавали запланированные вредоносные задачи). Чтобы изменить владельца файла, выполните sudo chown root \Library\LaunchDaemons\yourtask.plist(замените yourtask.plist фактическим именем файла .plist, который вы создали). Эта задача запросит у вас пароль.

Теперь вам нужно создать скрипт, который будет периодически запускаться. Вам нужно создать файл .sh (сценарий bash), чтобы указать компьютеру, что делать. Чтобы создать файл .sh, откройте текстовый редактор для программистов, например Sublime Text или Komodo Edit . НЕ ИСПОЛЬЗУЙТЕ текстовое редактирование, так как оно часто добавляет в ваши файлы текст, который будет мешать вашему сценарию. Text Edit не следует использовать для кода.

Создайте скрипт (файл .sh) со следующим кодом;

#!/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

Замените echo 'charged.'командой (командами) терминала, которую вы хотите запустить, когда батарея заряжена. open /Applications/Notes.appоткроет приложение Notes; вы можете изменить каталог, чтобы открыть другое приложение.

$percent > 95Это говорит следующей строке запускаться только тогда, когда батарея более чем 95заряжена. Вы можете изменить это на все, что захотите. Уровень заряда батареи здесь часто будет немного отличаться от того, что отображается в строке меню вверху. Если вы хотите «запустить, когда батарея полностью заряжена», я рекомендую оставить значение > 95 . Если вы хотите, чтобы задача выполнялась, например, когда уровень заряда батареи опускается ниже 20%, измените ее на$percent < 20

ПРИМЕЧАНИЕ. Поскольку это запланированная задача, ваш сценарий будет запускаться каждые xнесколько минут. Это означает, что если вы поместите open \Applications\Notes.appвнутрь свой скрипт, приложение Notes будет запускаться каждую xминуту (если ваша батарея заряжена)

Эта задача будет выполняться, даже если никто не вошел в систему.

Я знаю, что вы задавали этот вопрос некоторое время назад, но, надеюсь, это поможет кому-то.

Power Manager платный, но поддерживает инициирование событий в зависимости от уровня заряда батареи (внутренней или ИБП). События могут запускать сценарии, запускать приложения или выполнять такие задачи, как завершение работы.

Power Manager управляется событиями и не опрашивает замену батареи.

Вместо этого Power Manager подключается к уровню IOKit OS X и ожидает обновлений от оборудования. События могут запускаться, когда никто не вошел в систему; он не зависит от активного пользователя.

Диспетчер питания - Запустить скрипт при оставшемся заряде батареи

Встроенные задачи помощника по расписанию сосредоточены на выполнении, когда уровень заряда батареи падает до определенного уровня, но их можно настроить для проверки увеличения процента заряда батареи или оставшегося времени.

В этих двух постах рассказывается об источниках питания ИБП, но их легко адаптировать для внутренней батареи вашего MacBook:

Раскрытие информации: я инженер-программист Power Manager.

Это не специфично для Mac, но Mozilla Aurora имеет встроенный API, который содержит множество функций, связанных с батареей. Можно определить состояние батареи (заряжается она или нет), сколько еще времени потребуется, чтобы батарея разрядилась/зарядилась, и ее уровень. Вот простой пример того, как добавить EventListener для вызова функции, когда уровень заряда батареи достигает определенной точки.

Я хочу, чтобы это было на основе ОС, поэтому мне не нужно подключение к Интернету, открытый браузер и т. Д. Просто что-то, что может происходить в фоновом режиме.