View Single Post
Old 06-11-2015, 01:17 AM   #75
eureka
but forgot what it's like
eureka ought to be getting tired of karma fortunes by now.eureka ought to be getting tired of karma fortunes by now.eureka ought to be getting tired of karma fortunes by now.eureka ought to be getting tired of karma fortunes by now.eureka ought to be getting tired of karma fortunes by now.eureka ought to be getting tired of karma fortunes by now.eureka ought to be getting tired of karma fortunes by now.eureka ought to be getting tired of karma fortunes by now.eureka ought to be getting tired of karma fortunes by now.eureka ought to be getting tired of karma fortunes by now.eureka ought to be getting tired of karma fortunes by now.
 
Posts: 741
Karma: 2345678
Join Date: Dec 2011
Location: north (by northwest)
Device: Kindle Touch
Quote:
Originally Posted by nick-tech View Post
Thank you NiJuLe for your quick response. It's almost what I want, but i'd like to see the charge/discharge rate permanently in the status bar, just like the charge percentage. Is there a way to get that working?
Here are three files.

/etc/init/battery_current_indicator.conf
Spoiler:
Code:
# Copyright © 2015  eureka
#
# 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.

start on started pillow
stop on stopping pillow

pre-start script
  # Wait for pillow on 'started pillow' event.
  if [ "x${JOB}" == "xpillow" -a "x${UPSTART_EVENTS}" == "xstarted" ]; then
    lipc-wait-event com.lab126.pillow stateChange
    sleep 2
  fi
end script

post-start exec /bin/sh /var/local/battery_current/battery_current_indicator.sh enable

pre-stop exec /bin/sh /var/local/battery_current/battery_current_indicator.sh disable

exec /usr/bin/lua /var/local/battery_current/battery_current_lipc_publisher.lua

/var/local/battery_current/battery_current_indicator.sh
Spoiler:
Code:
#!/bin/sh

# Copyright © 2015  eureka
#
# 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.

# Format given standard input (remove newlines and escape double quotes)
# for using it in JSON message passed to lipc-set-prop.
format_js_script () {
  local line
  local js_script

  while IFS="" read -r line; do js_script="${js_script}${line}"; done

  echo "${js_script}" | sed -e 's,",\\",g'
}

[ "x${1}" = "xdisable" ] && ENABLE_INDICATOR=false || ENABLE_INDICATOR=true

# Construct JavaScript code executed in context of Pillow container.
# It's glued into one big line (newlines are removed), so syntax semicolons
# are required and any comments must be block comments (not line comments).
SCRIPT=`format_js_script << 'JAVASCRIPT' \
        | sed -e "s/__ENABLE_INDICATOR__/${ENABLE_INDICATOR}/"
  /* JavaScript code begins here. */
  (function () {
    var _enableIndicator = __ENABLE_INDICATOR__,
        _originalEventsCallback = StatusBar && StatusBar.eventsCallback,
        _batteryIconId = BatteryState.batteryIconDiv,
        _batteryIconEl = _batteryIconId
                         && document.getElementById(_batteryIconId),
        _batteryCurrentId = "batteryCurrent",
        _batteryCurrentEl = document.getElementById(_batteryCurrentId);

    /* Stock battery icon element not found or LIPC events handler function is
     * not defined under expected name, return immediately. */
    if (!_batteryIconEl || !_originalEventsCallback) {
      return;
    }

    /* Custom element displaying battery current already present in DOM.
     * Either return early if disabling was not requested or execute
     * disabling procedures and then return.
     */
    if (_batteryCurrentEl !== null) {
      if (_enableIndicator) {
        return;
      }
      _batteryCurrentEl.parentNode.removeChild(_batteryCurrentEl);
      return;
    }

    /* Setup element for displaying battery current. */
    _batteryCurrentEl = document.createElement("div");
    _batteryCurrentEl.id = _batteryCurrentId;
    _batteryCurrentEl.style.paddingTop = "2pt";
    _batteryCurrentEl.style.paddingRight = "3pt";
    _batteryCurrentEl.textContent = "_ mA";

    /* It is not possible to unsubscribe from LIPC event in Pillow container
     * (or, at least, I did not find a way to do it).
     *
     * Here part of event handler is attached to 'window' object. It is
     * persisted between disabling and enabling indicator and shows that
     * subscribing was already done.
     */
    if (typeof window.__updateBatteryCurrent__ !== "function") {
      window.__updateBatteryCurrent__ = function (batteryCurrentValue) {
        var batteryCurrentEl = document.getElementById(_batteryCurrentId);

        if (batteryCurrentEl === null) {
          /* Indicator was disabled. */
          return;
        }

        batteryCurrentEl.textContent =
          batteryCurrentValue
          ? (batteryCurrentValue + " mA")
          : "? ma";
      };

      StatusBar.eventsCallback = function (eventDataJson) {
        var eventData = JSON.parse(eventDataJson),
            eventSrcOk =
              eventData
              && (eventData.eventSrc === "com.example.batterycurrent"),
            eventNameOk =
              eventData
              && (eventData.eventName === "currentChanged"),
            eventValues = eventData && eventData.eventValues;

        if (eventSrcOk && eventNameOk && eventValues) {
          window.__updateBatteryCurrent__(eventValues[0]);
        }

        _originalEventsCallback(eventDataJson);
      };

      nativeBridge.registerEventsWatchCallback(StatusBar.eventsCallback);
      nativeBridge.subscribeToEvent(
        "com.example.batterycurrent",
        "currentChanged"
      );
    }

    /* Insert battery current element left of stock battery icon. */
    _batteryIconEl.parentNode.insertBefore(_batteryCurrentEl, _batteryIconEl);

    /* Get initial battery current value and display it. */
    window.__updateBatteryCurrent__(nativeBridge.getIntLipcProperty(
      "com.example.batterycurrent",
      "ma"
    ));
  })();
  /* JavaScript code ends here. */
`

MESSAGE='{
  "pillowId": "default_status_bar",
  "function": "'${SCRIPT}'"
}'

lipc-set-prop -s com.lab126.pillow interrogatePillow "${MESSAGE}"

/var/local/battery_current/battery_current_lipc_publisher.lua
Spoiler:
Code:
#!/usr/bin/lua

-- Copyright © 2015  eureka
--
-- 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.

require("liblipclua")

------------------------------------------------------------------------------
-- Configuration
------------------------------------------------------------------------------

-- Script registers itself as a LIPC publisher under this name.
local LIPC_PUBLISHER_NAME = "com.example.batterycurrent"

-- Battery current will be exported through this LIPC int property.
local LIPC_PROPERTY_NAME = "ma"

-- Changed battery current value is broadcasted by script as a LIPC event with
-- this name.
-- (Set it to nil to disable event sending.)
local LIPC_EVENT_NAME = "currentChanged"

-- Glob pattern of path to file with data of battery current.
--
-- Part of data file path is changed between device models, so real path
-- is found in runtime by glob pattern.
local DATA_FILE_GLOB = "/sys/devices/system/*_battery/*_battery0/battery_current"

-- Time period between re-reading data from data file.
local DATA_REFRESH_PERIOD = 1 -- second(s)

------------------------------------------------------------------------------
-- Supporting functions
------------------------------------------------------------------------------

local function exit_with_message(exit_code, message)
  io.stderr:write(message .. "\n")
  io.stderr:flush()
  os.exit(exit_code)
end

-- Call 'ls' command with passed glob pattern and return found path to file.
local function find_file_path (file_glob)
  local cmd = "ls " .. file_glob
  local output, err_message = io.popen(cmd, "r")
  if output == nil then
    error(err_message)
  end
  local file_path = output:read()
  output:close()
  if file_path == nil then
    error("Can't read output of '" .. cmd .. "'")
  elseif file_path:sub(1, 4) == "ls: " then
    error("Can't find file by glob pattern '" .. file_glob .. "'")
  end
  return file_path
end

-- Return battery current value read from first file line.
local function read_current_from_file (file_path)
  local file, err_message = io.open(file_path)
  if file == nil then
    error(err_message)
  end
  local result = file:read("*n")
  file:close()
  if result == nil then
    error("Can't read number from file '" .. file_path .. "'")
  end
  return result
end

------------------------------------------------------------------------------
-- Program setup
------------------------------------------------------------------------------

local data_file_found, data_file_path = pcall(find_file_path, DATA_FILE_GLOB)
if not data_file_found then
  exit_with_message(1, data_file_path)
end

local lipc_handle, lipc_err_msg, lipc_err_num = lipc.init(LIPC_PUBLISHER_NAME)
if not lipc_handle then
  exit_wit_message(
    lipc_err_num,
    "Failed to initialize LIPC: ("
    .. tostring(lipc_err_num) .. ") "
    .. lipc_err_msg)
else
  lipc.set_error_handler(function(msg) io.stderr:write(msg .. "\n") end)
end

local ma_property = lipc_handle:register_int_property(LIPC_PROPERTY_NAME, "r")

------------------------------------------------------------------------------
-- Main loop
------------------------------------------------------------------------------

local ma_value_read_ok, ma_value, prev_ma_value
while true do
  -- Read battery current value.
  ma_value_read_ok, ma_value = pcall(read_current_from_file, data_file_path)
  if not ma_value_read_ok then
    lipc_handle:close()
    exit_with_message(1, ma_value)
  end
  -- Set LIPC property value.
  ma_property.value = ma_value
  -- Send LIPC event when value is changed.
  if LIPC_EVENT_NAME ~= nil and ma_value ~= prev_ma_value then
    lipc_handle:send_event(LIPC_EVENT_NAME, {ma_value})
    prev_ma_value = ma_value
  end
  -- Use event loop timeout as a pause duration.
  lipc.run_event_loop(DATA_REFRESH_PERIOD)
end
lipc_handle:close()

Put them on their paths. Then indicator could be enabled with start battery_current_indicator or disabled with stop battery_current_indicator. It will also be enabled automatically on pillow (re)start (i.e. on reboot too).

Battery current will be displayed as 78 mA or -98 mA left of stock battery icon in status bar. Sysfs value will be continuously polled in intervals of 1 second and status bar indicator will be refreshed on value change.

It was tested on Kindle Touch 5.3.7.2.

Last edited by eureka; 06-11-2015 at 01:33 AM. Reason: sysfs value isn't watched in realtime, but polled in small intervals
eureka is offline   Reply With Quote