【问题标题】:Script to play a warning when battery level is under a threshold in MacOS在 MacOS 中电池电量低于阈值时播放警告的脚本
【发布时间】:2019-05-28 13:50:08
【问题描述】:

我使用的是 MacBook,但电池不可靠。我正在尝试找到一个脚本命令,该命令将使笔记本电脑在电池电量低于阈值时播放哔声。经过一番搜索和测试,我在下面找到了一个快速的解决方案:

1) 我首先创建了一个名为 check_battery 的文件,其内容如下:

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

2) 然后我创建了以下文件:

\#!/bin/bash
sh check_battery>battery;
let t=60
read a < battery
if(( "$a" < "$t" ));
  then
  say "Battery level is lower than 60%!";
  say "BEAP BEAP"
fi;

3) 最后,我尝试将其添加到我的 crontab 作业中,但 crontab 已被 iOS 停止。然后我发现我必须使用launchd,下面详述: https://alvinalexander.com/mac-os-x/mac-osx-startup-crontab-launchd-jobs

它现在可以在我的 MacBook labtop 上运行,但可能有更好的解决方案。如果您有其他解决方案,请与我分享。非常感谢。

【问题讨论】:

  • 希望您不介意:我更改了您问题的标签以帮助提高其可见性。随意将它们改回或更改为您希望的任何其他内容,但希望我做出了一些不错的选择来获得您想要的关注。
  • 谢谢CJK,我是新手。
  • 您说,“...但是 crontab 已被 iOS 停止。” iOS 和这个问题有什么关系?
  • FWIW 如果您在 系统偏好设置 > 节能器 下勾选了 [√] 在菜单栏中显示电池状态,则以下 示例 AppleScript code 获取分配给batteryPercent 变量 的电池电量百分比,然后您可以在其中再次测试if 语句块 来做你想做的事。 tell application "System Events" to set batteryPercent to second word of ((description of every menu bar item of menu bar 1 of application process "SystemUIServer" whose description starts with "Battery") as text)

标签: bash macos scripting applescript battery


【解决方案1】:

我认为您的脚本本身就是一个很好的脚本,但如果定期运行,最终可能会占用大量资源,因为它会调用一个 shell 进程和两个程序来获取您所需要的信息。

在这种情况下,我认为 AppleScript 具有 shell 脚本的几个优点,这在很大程度上要归功于 osascript,它是运行 AppleScript 时唯一需要执行的程序,并且无需创建外壳进程。

AppleScript 检索电池信息

这是一个这样的脚本,可以检索有关计算机电池的信息,并在适当的时间发出警告:

property threshold : 20 -- The level below which the script issues warnings
property verbose : false -- Report current battery level on every run
property file : "/tmp/battery.log" -- The path to the log file (from plist)


set eof of my file to 0

tell the battery()
    set warning_level to the warningLevel()
    if the warning_level = 1 then return check()
    warn about (warning_level - 1) * 5 with alert
end tell

# warn
#   Sends out a visible and audible warning with information about the
#   remaining charge (+level) on the battery.  Invoke without alert to have
#   the +level reported in the notification centre as a percentage.  Invoke 
#   with alert to have the +level reported in a pop-up alert dialog as an
#   estimation of the remaining battery time (in minutes), and which must be
#   dismissed by the user.
to warn about level without alert
    if not alert then display notification ¬
        ["Current battery level: ", level, "%"] as text ¬
        with title ["⚠️ Battery Warning"] sound name "Basso"

    say "Warning: battery low" using "Moira" pitch 127 ¬
        speaking rate 180 without waiting until completion

    if alert then display alert ["Warning: battery level is very low"] ¬
        message ["Estimated time remaining: " & level & " minutes"] ¬
        as critical giving up after 0
end warn

# battery()
#   Contains a script object that defines a number of convenience handlers that
#   retrieve information about the on-board power source
on battery()
    script
        use framework "IOKit"
        use scripting additions
        property warninglevels : ["None", "Early", "Final"]

        on warningLevel() -- A ten-minute warning indicator
            IOPSGetBatteryWarningLevel() of the current application
        end warningLevel

        on info()
            IOPSCopyPowerSourcesInfo() of the current application ¬
                as record
        end info

        to check()
            copy [it, |current capacity|, |is charging|] of ¬
                info() to [ps_info, percentage, charging]

            if the percentage ≤ threshold ¬
                and it is not charging ¬
                then warn about percentage ¬
                without alert

            if verbose then return display notification [¬
                "Percentage: ", percentage, linefeed, ¬
                "Charging: ", charging] as text ¬
                with title ["? Battery Information"]

            ["Script last run on ", current date, linefeed, ¬
                linefeed, __string(ps_info), linefeed] as text
        end check
    end script
end battery

# __string()
#   A that will return an AppleScript record as a formatted string, modified
#   specifically for use in this script, therefore may not port very well to
#   handle other records from other scripts
to __string(obj)
    if class of obj = text then return obj

    try
        set E to {_:obj} as text
    on error E
        my tid:{null, "Can’t make {_:", ¬
            "} into type text.", ¬
            "{", "}", "|"}
        set E to E's text items as text
        my tid:{linefeed, ", "}
        set E to E's text items as text
    end try
end __string

# tid:
#   Set the text item delimiters
on tid:(d as list)
    local d

    if d = {} or d = {0} then set d to ""
    set my text item delimiters to d
end tid:

它最显着地检索当前剩余电池电量的百分比以及电池当前是否正在充电。

脚本顶部有三个属性,您可以根据自己的喜好安全地进行更改。在verbose 模式下,每次运行脚本都会报告电池百分比和充电状态。如果您按分钟运行脚本,这可能会很烦人,因此默认设置为false

但是,当电池电量低于 threshold 时,您会收到通知中心的通知,以及 Moira 舒缓的苏格兰语调,她会发出电池电量不足的声音警告。

如果电池电量严重不足,您会收到一个弹出式警报,提示您必须亲自解雇自己。

该脚本还返回一堆关于电池的其他信息,一般来说,您不会看到这些信息。但是,如果脚本由launchd plist 执行,则返回的信息会记录在日志文件中,您可以在闲暇时查看。

launchd

launchd 提供了一种很好的、​​相当经济高效的方法来定期运行脚本,并且比创建 Stay Open 应用程序轮询数据要好得多(这非常效率低,但在其他场景中有它的用途)。

您已经找到了一个很好的链接来描述有关编写.plist 文件的基础知识以及如何保存它,然后调用(启动)它。我在这里总结一下重点:

  1. 创建一个看起来像这样的.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>Disabled</key>
            <false/>
            <key>Label</key>
            <string>local.battery</string>
            <key>ProgramArguments</key>
            <array>
                    <string>/usr/bin/osascript</string>
                    <string>/Users/CK/Documents/Scripts/AppleScript/scripts/Battery.applescript</string>
            </array>
            <key>RunAtLoad</key>
            <true/>
            <key>StartInterval</key>
            <integer>60</integer>
            <key>StandardErrorPath</key>
            <string>/tmp/battery.err</string>
            <key>StandardOutPath</key>
            <string>/tmp/battery.log</string>
    </dict>
    </plist>
    

    使用与Label 键下的&lt;string&gt; 条目匹配的文件名保存它。在这种情况下,我将我的保存为local.battery.plist。我还将 AppleScript 保存在 plist 中指定的路径中。在我的实验中,我很惊讶launchd 不喜欢它,如果路径中有任何空格(即使转义),或者如果我使用波浪号 (~) 作为我的主目录的简写。因此,我将 AppleScript 保存为简单的 Battery.applescript,并提供了我放置它的完整路径。

    StartInterval 是您可以指定脚本运行的时间间隔(以秒为单位)的地方。就个人而言,我认为 60 秒有点过大,但我将其设置为测试。在实践中,我可能会选择将其设置为 600(10 分钟),但您可以判断什么最适合您。

    最后的两个/tmp/ 路径分别是错误和返回值的写入位置。如果您更改日志文件的路径,请不要忘记更新 AppleScript 中的 file 属性。

  2. 保存或移动 plist 到目录~/Library/LaunchAgents/

  3. 从终端使用以下 shell 命令加载它:

    launchctl load ~/Library/LaunchAgents/local.battery.plist
    
  4. 如果以后您对.plist.applescript 进行了任何更改,请记住卸载然后使用launchctl 重新加载:

    launchctl unload ~/Library/LaunchAgents/local.battery.plist
    launchctl load ~/Library/LaunchAgents/local.battery.plist
    

在将.plist 成功加载到launchd 后,它应该立即运行AppleScript 并将返回的数据写入日志文件。这是我的/tmp/battery.log 文件的输出:

Script last run on Tuesday, 1 January 2019 at 22:01:52

is present:true
is charged:true
power source state:"AC Power"
Current:0
max capacity:100
DesignCycleCount:1000
current capacity:100
name:"InternalBattery-0"
battery provides time remaining:true
is charging:false
hardware serial number:"D8663230496GLLHBJ"
transport type:"Internal"
time to empty:0
power source id:4391011
Type:"InternalBattery"
BatteryHealth:"Check Battery"
time to full charge:0

结束思想

第一次电池信息很有趣,但对于大多数用例来说可能是多余的。 (也就是说,我刚刚注意到它建议我在BatteryHealth 下使用"Check Battery",所以我现在很高兴我提取了额外的数据。

无论如何,如果您认为自己不需要额外的数据,我已经看到user3439894 留下的评论,它提供了一种非常好的、紧凑且简单的方法来检索有关电池的更重要信息,即它的百分比水平以及它是否在收费。可以说,这可能是比我的 ObjC 方法更合适的 AppleScripting 解决方案,后者会带来 small 开销,而且脚本更短。

目前,我坚持使用我的,因为我显然有电池健康问题,应该可以得到解决。

【讨论】:

  • 试想一下……如果电池易于用户更换且价格低廉,那么这就是要走的路。不错的答案+1。
  • 感谢您提供这些有用的提示。
【解决方案2】:

我看到了你的问题并尝试编写代码。

这是 AppleScript:

    on run
    set theBattString to (do shell script "pmset -g batt")
    -- the above will return something like...
    -- Now drawing from 'Battery Power' -InternalBattery-0  82%; discharging; 4:06 remaining

    set batteryLevel to splitText(theBattString, space)

    --I used this line under for counting the number of items in batteryLevel. 
    --Then I get the right item for the time remaining. 
    --It is a static value for me it was item 9.

    --set totalItembatLvl to length of batteryLevel 

    tell application "System Events"
    set remainingTime to item 9 of batteryLevel

    if remainingTime < "3:10" then                 

        display alert "low battery " & remainingTime & " before shut down."


        --batteryLevel & " " & remainingTime


    end if
    end tell
    end run

    on splitText(theText, theDelimiter)
      set AppleScript's text item delimiters to theDelimiter
      set theTextItems to every text item of theText
      set AppleScript's text item delimiters to ""
      return theTextItems
    end splitText

您可以将其保存为空闲应用程序,它会每分钟检查一次电池电量。

    on idle
    set theBattString to (do shell script "pmset -g batt")
    -- the above will return something like...
    -- Now drawing from 'Battery Power' -InternalBattery-0  82%; discharging; 4:06 remaining

    set batteryLevel to splitText(theBattString, space)

    --set totalItembatLvl to length of batteryLevel

    set remainingTime to item 9 of batteryLevel

    if remainingTime < "2:40" then
       display alert "low battery " & remainingTime & " before shut down."

       --batteryLevel & " " & remainingTime

    end if
    return 60
    end idle

on splitText(theText, theDelimiter)
    set AppleScript's text item delimiters to theDelimiter
    set theTextItems to every text item of theText
    set AppleScript's text item delimiters to ""
    return theTextItems
end splitText

告诉我你的想法,也许其他人可以改进这个答案。

问候。

[编辑]

感谢用户 @user3439894 提供了适合您需求的相关代码。

set batteryPercent to word 6 of paragraph 2 of (do shell script "pmset -g batt")

if batteryPercent < 40 then

    beep

    repeat 3 times

        say "Attention " & batteryPercent & "%" & " before shut down."

    end repeat

end if

就像我的第 2 个代码一样,您可以将它放在空闲的应用程序中。

 on idle

    set batteryPercent to word 6 of paragraph 2 of (do shell script "pmset -g batt")

    if batteryPercent < 40 then



        repeat 3 times

            beep

            say "Attention " & batteryPercent & "%" & " of charge before shut down."

        end repeat

    end if

    return 60

end idle

问候

【讨论】:

  • FWIW 以下example AppleScript code 设置batteryPercent value variable 直接转换为电池当前充电的整数百分比,从而消除了大部分繁琐的编码:set batteryPercent to word 6 of paragraph 2 of (do shell script "pmset -g batt") 然后使用if 语句块按照小于x 的百分比阈值行事。
  • 好的,谢谢@user3439894 我将重新编码我的 AppleScript 并用您的信用编辑我的答案。现在代码将提供两种类型的值:百分比和时间。对于那些想要选择的人。
  • 谢谢。我不熟悉 AppleScript。我会进一步调查。
【解决方案3】:

我的文件 test_batt.sh

function current_batt() {
  pmset -g batt | egrep -o "\\d+%" | tr % ' '
}

while (( $(current_batt) > 10 ))
do
  echo 'still good' current_batt
  sleep 1
done

osascript -e 'display dialog "Lorem ipsum dolor sit amet" with title "Title"'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-08
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多