#!/usr/bin/python2.5

#
# batterylog.py
#
#     Fetch information about your battery (maximum capacity, current capacity,
#     cycle count, health, and serial number) and log it to ~/.batterylog.sqlite3.
#
# RELEASES
#
#     0.2    Added sppower_battery_current_capacity (25 June 2009)
#     0.1    Initial release (23 June 2009)

#
#
# The MIT License
#
# Copyright (c) 2009 Annika Backstrom <annika@sixohthree.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
#   The above copyright notice and this permission notice shall be included in
#   all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#

from xml.dom import minidom

import os
import subprocess
import sqlite3

def power_xml():
    """Fetch the power data as XML from system_profiler."""
    xml = subprocess.Popen(["/usr/sbin/system_profiler", "-xml", "SPPowerDataType"], stdout=subprocess.PIPE).communicate()[0]
    return xml

def next_named_node( element ):
    """Get the next named (non-text) element."""
    while element.nextSibling:
        element = element.nextSibling
        if element.nodeType == minidom.Node.ELEMENT_NODE and element.tagName in ['string', 'integer']:
            return element
    return None

def text_content( element ):
    """Get the contents of the first text child element of the specified element."""
    textNode = element.firstChild
    if textNode.nodeType == minidom.Node.TEXT_NODE:
        return textNode.data
    else:
        return None

def battery_info( xml ):
    """Parse battery information out of an XML string."""
    xml = minidom.parseString(xml)
    keys = xml.getElementsByTagName('key')

    data = {
        'sppower_battery_max_capacity': None,
        'sppower_battery_cycle_count': None,
        'sppower_battery_health': None,
        'sppower_battery_serial_number': None,
        'sppower_battery_current_capacity': None
    }

    # keys to look for
    targets = data.keys()

    # loops through all the keys, looking for ones we care about. if
    # we find one, populate the value in our data dict.
    for key in keys:
        name = text_content(key)
        if name not in targets:
            continue

        next_node = next_named_node(key)
        if next_node == None:
            continue

        value = text_content( next_node )
        if value != None:
            data[name] = value

    return data

def log(data):
    """Log data returned from battery_info() to a SQLite database."""
    dbpath = os.path.join(os.path.expanduser('~'), '.batterylog.sqlite3')
    conn = sqlite3.connect(dbpath)
    c = conn.cursor()

    try:
        c.execute('ANALYZE log')
    except sqlite3.OperationalError, e:
        if e.message[:13] == 'no such table':
            c.execute("""
                CREATE TABLE log (
                    id INTEGER PRIMARY KEY ASC,
                    activity_date TEXT DEFAULT CURRENT_TIMESTAMP,
                    sppower_battery_max_capacity TEXT,
                    sppower_battery_current_capacity TEXT,
                    sppower_battery_cycle_count TEXT,
                    sppower_battery_health TEXT,
                    sppower_battery_serial_number TEXT
                )
            """)
        else:
            raise e

    columns = ', '.join([key for key in data.keys()])
    subs = ', '.join(["?" for key in data.keys()])
    sql = 'INSERT INTO log (%s) VALUES (%s)' % (columns, subs)

    c.execute(sql, data.values())
    conn.commit()
    conn.close()

if __name__ == '__main__':
    data = battery_info( power_xml() )
    log( data )

# vim:ts=4:sw=4:noet:
