Remix
After the previous post, I got to thinking, maybe the battery monitor could be written to be more cross-platform and maybe try streamlining things a little bit. Here’s the breakdown of that script.
Before Starting on an M1 Mac
This script will get tested on other platforms, and I’ll update that here. For now, we’ll get around an SSL library glitch on an M1 MacBook running macOS Monterey.
Setup Ntfy user, topic, and token
Next, we’ll want to set up a topic, a write-only user, and a token for that user just for monitoring this device. This may not seem that important but, IMO, it’s best to err on the side of security.
1
2
3
| sudo ntfy user add macbook
sudo ntfy access macbook macbook write-only
sudo ntfy token add macbook
|
The standard libraries to import
1
| pip install psutil requests python-decouple
|
The imports
Now for the imports, psutil is our main utility to check battery status and percentage requests for sending to our ntfy server. The rest is mostly for housekeeping to set up logging, timing, and pulling in the local .env values.
1
2
3
4
5
| import psutil
import requests
from time import sleep
from decouple import config
import logging
|
.env setup
Speaking of the .env file, we’ll want to use it in the same directory as the main script/executable.
1
2
| SERVER_URL=https://my-site-here/topic
NTFY_TOKEN=ntfy-token-here
|
Alternatively, you could export them as follows.
1
2
| export SERVER_URL=https://my-site-here/topic
export NTFY_TOKE=ntfy-token-here
|
Setting Globals
Now we’ll want to set up a few global variables, these will set up our configs for logging and connecting to the ntfy server.
1
2
3
| server_url = config('SERVER_URL')
ntfy_token = config('NTFY_TOKEN')
logging.basicConfig(level=logging.ERROR)
|
Pulling the battery data
This one is pretty straightforward but essential, without this function to pull battery data the rest of the script is moot.
1
2
3
4
| def get_battery_status():
battery = psutil.sensors_battery()
status = "Charging" if battery.power_plugged else "Not charging"
return status, battery.percent
|
Sending the data to your Ntfy server
This function assumes you have a ntfy user setup with a token. When called it will format the message for the alert/notification and send it out to our ntfy server.
1
2
3
4
5
6
7
8
9
| def send_data_ntfy(status, percentage):
requests.post(
server_url,
data=f"Current state is {status}, battery is at {percentage}%",
headers={
"Authorization": "Bearer " + ntfy_token
},
timeout=10
)
|
Main loop
And now for the main course, here we’re calling all the functions and checking to see if either the battery status or percentage has changed, if so it’ll call the send_data_ntfy function with the current values to let us know something changed. It’s a little simpler than the previous bash script, but I feel it’s more to what we’d actually be looking for. We also have some logic to trap and log errors when sending to the ntfy server.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| def main():
previous_status = ""
previous_percentage = 0
while True:
status, percentage = get_battery_status()
if (previous_status != status) or (previous_percentage != percentage):
try:
send_data_ntfy(status, percentage)
except requests.RequestException as e:
logging.error(f"Failed to send data: {e}")
previous_status = status
previous_percentage = percentage
sleep(60)
|
Pulling it all together
This is really here to check if we’re running it as is and not part of a larger program, then kick off the main function. That’s it.
1
2
| if __name__ == "__main__":
main()
|
Wrapping up
This was, for me, an exercise in coding something useful into something that was more cross-platform friendly, documenting it for later, and adding another piece of knowledge to the pool. Below will be the contents of the .env and main.py files. I hope you’ve found this helpful. Till next time, fair winds and following seas.
1
2
| SERVER_URL=https://my-site-here/topic
NTFY_TOKEN=ntfy-token-here
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| import psutil
import requests
from time import sleep
from decouple import config
import logging
server_url = config('SERVER_URL')
ntfy_token = config('NTFY_TOKEN')
logging.basicConfig(level=logging.ERROR)
def get_battery_status():
battery = psutil.sensors_battery()
status = "Charging" if battery.power_plugged else "Not charging"
return status, battery.percent
def send_data_ntfy(status, percentage):
requests.post(
server_url,
data=f"Current state is {status}, battery is at {percentage}%",
headers={
"Authorization": "Bearer " + ntfy_token
},
timeout=10
)
def main():
previous_status = ""
previous_percentage = 0
while True:
status, percentage = get_battery_status()
if (previous_status != status) or (previous_percentage != percentage):
try:
send_data_ntfy(status, percentage)
except requests.RequestException as e:
logging.error(f"Failed to send data: {e}")
previous_status = status
previous_percentage = percentage
sleep(60)
if __name__ == "__main__":
main()
|
1
2
3
| psutil
requests
python-decouple
|