Showing posts with label Python 3. Show all posts
Showing posts with label Python 3. Show all posts

Sunday, January 23, 2022

MacOS: Pulling Battery Data from IOReg to CSV File using Python and LaunchAgents

 I wanted to track battery data over time and have not found a reasonable approach using the current apps to store the information in a database. I decided the first step was to figure out how to pull the information and store in a CSV file. Next steps include either pushing to PostgresSQL and to Google Sheets.

First I found the ioreg command which enabled me to pull the data. I could have used a shell script but preferred to use Python 3 since it would give me more options in the future.

I used the command 

                            ioreg -rd1 -c AppleSmartBattery

Which enabled me to pull just the battery information I was interested in. I was able to use python to pull specific items and push to a csv file. I'm not sure I got everything I wanted but it is a start. Once the program worked I created a plist file to run it every 60 minutes via LaunchAgent. The cool part of LaunchAgents is the ability to run after coming out of sleep mode.

I did install pyinstaller to create an "executable" which I placed in the bin directory and it writes to a data directory. Below find the program and plist I used. 


local.batteryinfo.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>Label</key>

<string>com.username.personal.batteryinfo</string>

<key>Program</key>

<string>/Users/username/bin/battery-info</string>

<key>RunAtLoad</key>

<true/>

<key>StandardErrorPath</key>

<string>/dev/null</string>

<key>StandardOutPath</key>

<string>/Users/username/data/batteryinfo.out</string>

<key>StartInterval</key>

<integer>3600</integer>

<key>WorkingDirectory</key>

<string>/Users/username/data</string>

</dict>

</plist>


battery-info.py

!/opt/homebrew/bin/python3

import subprocess
from csv import DictWriter
from datetime import datetime

# datetime object containing current date and time
now = datetime.now()
nowDate = now.strftime("%Y%m%d")
nowTime = now.strftime("%H%M%S")
fileName = "/Users/username/data/batteryinfo.csv"

batteryRawInfo = {}
batteryInfo = {}
excludeList = ['+-o', '{', '}', "\r"]
headersCSV = [
'Date',
'Time',
'DeviceName',
'AtCriticalLevel',
'ExternalConnected',
'AppleRawCurrentCapacity',
'Serial',
'NominalChargeCapacity',
'FullyCharged',
'DesignCycleCount9C',
'MaxCapacity',
'DesignCapacity',
'IsCharging',
'CycleCount',
'AppleRawMaxCapacity',
'TimeRemaining',
'Temperature',
'VirtualTemperature',
'AtCriticalLevel',
]

def exist_and_not_empty(filepath):
try:
import pathlib as p
path = p.Path(filepath)
if '~' in filepath:
path = path.expanduser()
if not path.exists() and path.stat().st_size > 0:
return False
return True
except FileNotFoundError:
return False


def writeCSVFile(fileName, headers, batDict):
with open(fileName, 'a', newline='') as f_object:
# Pass the CSV file object to the Dictwriter() function
# Result - a DictWriter object
dictwriter_object = DictWriter(f_object, fieldnames=headers)
# Pass the data in the dictionary as an argument into the writerow() function
dictwriter_object.writerow(batDict)
# Close the file object
f_object.close()


def main():

with subprocess.Popen(['ioreg', '-rd1', '-c', 'AppleSmartBattery'], stdout=subprocess.PIPE, text=True) as proc:
# print(proc.__dict__)
# with proc.stdout.read() as stdoutString:,
# print(stdoutString)
batteryLines = proc.stdout.readlines()

for line in batteryLines:
if [ele for ele in excludeList if (ele in line)]:
continue

# print("Line: [{}]".format(line.strip().replace(' ','').replace('"','')))
if "=" in line:
(k, v) = line.strip().replace(' ', '').replace('"', '').split("=")
batteryRawInfo[k] = v

# print("Dictionary ........................... ")
# print(batteryRawInfo)

for key, value in batteryRawInfo.items():
# print(f'Key: {key}, Value: {value}')
if key in headersCSV:
batteryInfo[key] = value

batteryInfo["Date"] = nowDate
batteryInfo["Time"] = nowTime

# print(batteryInfo)
writeCSVFile(fileName, headersCSV, batteryInfo)

if __name__ == '__main__':
main()


I did have some issues loading the launch agent and finally download an application called LaunchControl. Helped with a permissions issue and fixed my issue.

Enjoy.

Saturday, February 6, 2021

Installing Python 3.9.1 and psycopg2 on Big Sur 11.1 - Apple M1

 I recently purchased my first Mac.  It is a MacBook Pro 13" M1, great little machine with lots of power.  The work I do is Python, ansible, and other miscellaneous development projects connecting to databases, lately PostgreSQL. I do use a laptop for photography work as well.  The first steps were getting the development environment up and running.  Microsoft VS Code went in smoothly and synced everything once connected to my Microsoft account.

Next was installing Python.  This was interesting as I wanted at least Python 3.8+ and for the M1 it seemed that 3.9.1 was the first real supported release.  The M1 with Big Sur comes with Python 2.7 and you do not want to replace it.

I found two ways to install Python 3.9, one using pyenv and one using brew. I did find that you do not want to mix the two.  I removed both and tired just pyenv by itself and got lots of errors with matplotlib, pandas, and psycopg2.  Most of the solutions I found did not seem to help and maybe I did not do the right things.  So I removed the pyenv install and went the brew install of Python 3.9.1 and here are the steps I used to get them the modules I wanted installed and working.


First installing Python 3.9.1

First thing I noticed with this installation is that everything ends up in /opt/homebrew and you will need to add directories to your path.

In my case homebrew was installed in /opt/homebrew by default. I followed the instructions on the Brew Homepage. Update your .zshrc to update the PATH on terminal startup.

export PATH="/opt/homebrew/bin:$PATH" 
Next install openssl and Python 3.9

$ brew install openssl
$ brew install python@3.9

I had number of modules that had issues getting installed, particularly dependencies for matplotlib and the next steps got numpy, pandas, and matplotlib installed without issues.

$ brew install openblas

$ OPENBLAS="$(brew --prefix openblas)" MACOSX_DEPLOYMENT_TARGET=11.1 python3 -m pip install cython --no-use-pep517

$ OPENBLAS="$(brew --prefix openblas)" MACOSX_DEPLOYMENT_TARGET=11.1 python3 -m pip install numpy --no-use-pep517

$ OPENBLAS="$(brew --prefix openblas)" MACOSX_DEPLOYMENT_TARGET=11.1 python3 -m pip install pandas --no-use-pep517

$ OPENBLAS="$(brew --prefix openblas)" MACOSX_DEPLOYMENT_TARGET=11.1 python3 -m pip install pybind11 --no-use-pep517

$ OPENBLAS="$(brew --prefix openblas)" MACOSX_DEPLOYMENT_TARGET=11.1 python3 -m pip install scipy --no-use-pep517

$ brew install libjpeg zlib

$ python3 -m pip install pillow

$ python3 -m pip install matplotlib

Next I installed PostgreSQL 12.5
   
$ brew install postgresql@12
export PATH="/opt/homebrew/opt/postgresql@12/bin:$PATH" 


Validate pip3 is being used from /opt/homebrew/bin/pip3
   
$ which pip3
Next set the following flags to install psycopg2-binary
   
export LDFLAGS="-L/opt/homebrew/opt/openssl/lib"
export CPPFLAGS="-L/opt/homebrew/opt/openssl/include"

Install psycopg2-binary using pip3
   
$ pip3 install psycopg2-binary

I found that I needed to ensure the python script has at the top a call to use python3
   
#!/usr/bin/env python3