Bitcoin Forum
November 09, 2024, 04:29:32 AM *
News: Latest Bitcoin Core release: 28.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Bitcoin monitor script for Conky [Updated: v92704]  (Read 3215 times)
Rena (OP)
Newbie
*
Offline Offline

Activity: 19
Merit: 0



View Profile WWW
April 19, 2011, 03:33:32 AM
Last edit: May 02, 2011, 08:06:13 AM by Rena
 #1

Here's a quick Lua script I wrote for the system monitor Conky to display Bitcoin network/mining status, account balance, transaction history and MtGox ticker data. It's a little messy but it works. Tongue Requires Json4Lua and LuaSocket, talks to an instance of bitcoin[d] on the local system.

Code:
--[[ Bitcoin Lua script for Conky, by Rena. Build #92704.
Usage: Add a "lua_load" line to your .conkyrc to load this script,
then add variables to your config:

${lua bitcoin_balance [account name]}
 (You can also use lua_bar, lua_graph, etc. A graph might be interesting for
  those who perform a lot of transactions!)

${lua bitcoin_info format string} (or lua_parse)
 Format string is any string (can contain line breaks), in which any instance of
 %foo% is replaced with the value of the variable foo output by
 "bitcoin getinfo". Cannot contain multiple spaces; use ${tab} for alignment.
 (This is a limitation of how Conky passes strings to Lua.)

${lua bitcoin_transactions account num format} (or lua_parse)
 List up to 'num' recent transactions (most recent first) on an account. Account
 name must be quoted if it contains a space, can be * for all accounts. Format
 works same as bitcoin_info.

${if_empty ${lua bitcoin_running}}...${else}...${endif}
 To display text only if Bitcoin is/isn't running. bitcoin_running returns an
 empty string when Bitcoin running and '0' when it isn't, so that if_empty can
 be used like a normal 'if'.

${lua mtgox_ticker format string} (or lua_parse)
 Works same as bitcoin_info. Field names are those under 'ticker', so e.g.
 write %high%, not %ticker.high%.

Send bug reports, suggestions, flames, cat photos and whatever else
to ('moc.liamg\064rekcahrepyh'):reverse().
Send spare change to 15R7dpxfWuDrQY9CtUzMsg6A7sKMdWbTd3 why not.

Version history:
-92704:
 -Added mtgox_ticker() and ability to fetch JSON URLs. (Adds dependency on
  LuaSocket.)
 -Changed keypoololdest to a formatted date. Raw timestamp is keypoololdest_raw.
-92432: Initial release. ]]

local json = require('json') --http://json.luaforge.net/
local http = require('socket.http') --http://luaforge.net/projects/luasocket/

--options
local UpdateFreq = 5*60 --only update once every this many seconds
local SignPlus = '${color3}+' --prefix for positive numbers
local SignMinus = '${color2}-' --prefix for negative numbers
local DateFmt = '%H:%M:%S %y/%m/%d' --strftime date format
local FeeEmpty = '-.--' --string to display for %fee% when empty

--state
local Account = {} --indexed by account name
local LastQuery = 0 --last time bitcoin was called
local QueryCache = {}
local URLCache = {}

--[[ Send a query to bitcoin.
This function caches queries so that bitcoin is only called once per UpdateFreq
to avoid creating a lot of needless overhead by spawning processes over and
over. ]]
local function bitcoin_query(query)
local now = os.time()
LastQuery = now
local cache = QueryCache[query]
if cache then
if os.difftime(now, cache.Time) < UpdateFreq then
return cache.Result
end
else
QueryCache[query] = {}
cache = QueryCache[query]
end

--hack: redirect stderr to stdout and look for either an error message or
--what looks like JSON data.
local data, found = '', false
local app = io.popen('bitcoin ' .. query .. ' 2>&1')
for line in app:lines() do
if line:sub(1,6) == 'error:' then
if line == "error: no response from server"
or line == "error: couldn't connect to server" then
Running = false
return nil, "Bitcoin not running"
else
Running = false --presumably...
io.stderr:write("Bitcoin: " .. line .. "\n")
return nil, line
end
elseif found then data = data .. line
elseif line:sub(1,1) == '{' or line:sub(1,1) == '[' then
found = true
data = data .. line
end
end
app:close()
Running = true --well it must be, we just talked to it.

cache.Time, cache.Result = now, json.decode(data)
return cache.Result
end

--[[ Fetch JSON data from a HTTP URL.
This function caches queries in URLCache so that the same URL isn't accessed
more than once per UpdateFreq. This is a simple naive cache that only works if
the URL is exactly the same each time.
On success, returns JSON data as table.
On failure, returns nil and error message. ]]
local function get_json_url(url)
if not URLCache[url] then URLCache[url] = {LastQuery=0} end

local now = os.time()
if (now - URLCache[url].LastQuery) < UpdateFreq then
return URLCache[url].LastResult
end
URLCache[url].LastQuery = now

local text, status = http.request(url)
if (not text) or (status < 200) or (status >= 300) then
URLCache[url].LastResult = 'Error: ' .. tostring(status)
return nil, URLCache[url].LastResult
end

local err
URLCache[url].LastResult, err = json.decode(text)
if URLCache[url].LastResult then return URLCache[url].LastResult
else return nil, err
end
end

--[[ Conky splits args at space, regardless of quotes. So if arg begins with a
quote, look for end quote and piece the string together.
note that this fails if there are multiple spaces in a row since Conky
compresses them into one. ]]
local function readquotedarg(args, idx)
idx = idx or 1
local str = ''
if args[idx] and args[idx]:sub(1,1) == '"' then
if args[idx]:sub(-1) == '"' then
return table.remove(args, idx):sub(1, -2)
end
str = table.remove(args, idx):sub(2)
while args[idx] and args[idx]:sub(-1) ~= '"' do
str = str .. ' ' .. table.remove(args, idx)
end
return str .. ' ' .. table.remove(args, idx):sub(1, -2)
elseif args[idx] then return table.remove(args, idx)
else return nil
end
end

--[[ Check if Bitcoin is running. ]]
function conky_bitcoin_running(...)
local now = os.time()
if Running == nil --don't know
or os.difftime(now, LastQuery) >= UpdateFreq then
bitcoin_query('getinfo') --make some random query to find out
end
return Running and '' or '0' --for Conky if_empty
end

--[[ Balance for particular account. ]]
function conky_bitcoin_balance(...)
local args = {...}
local account = readquotedarg(args) or "Your Address" --default account name

--create status table for this account if not done already.
if not Account[account] then Account[account] = {
LastUpdate = 0, LastBalance = 0
} end

--Query bitcoin to find account balance.
local info = bitcoin_query('listaccounts')

--display negative balance to signal error, since some Conky functions
--expect a number.
if not info then Account[account].LastBalance = -1 end

Account[account].LastUpdate = now
return tonumber(Account[account].LastBalance)
end

--[[ Recent transaction list. ]]
function conky_bitcoin_transactions(...)
local args = {...}
local account = readquotedarg(args) or "Your Address"
local numshow = tonumber(args[1]) or 5
local fmt = table.concat(args, ' ', 2) --format string

local info = bitcoin_query(('listtransactions "%s" %d'):
format(account, numshow))

--add some convenient fields to info
for i = 1,#info do
if not info[i].sign then --cache means these may already be set
info[i].sign = (info[i].amount > 0) and
SignPlus or SignMinus
info[i].absamount = math.abs(info[i].amount)
info[i].absfee = info[i].fee and math.abs(info[i].fee) or
FeeEmpty
info[i].rawtime = info[i].time
info[i].time = os.date(DateFmt, tonumber(info[i].rawtime))
info[i].fee = info[i].fee or FeeEmpty
end
end

--transactions seem to be listed oldest first, so reverse iterate.
local res = ''
for i = math.min(numshow,#info), 1, -1 do
res = res .. fmt:gsub('%%([%w_]+)%%', info[i]) .. '\n'
end
return res
end

--[[ General info. ]]
function conky_bitcoin_info(...)
local args, info = {...}, {}
local fmt = table.concat(args, ' ') --format string

--query Bitcoin and parse results.
local info = bitcoin_query('getinfo')

if not info.keypoololdest_raw then
info.keypoololdest_raw = info.keypoololdest
info.keypoololdest = os.date(DateFmt, tonumber(info.keypoololdest))
end

--generate and return output string.
LastInfoStr = fmt:gsub('%%([%w_]+)%%', info)
LastInfoUpdate = now
return LastInfoStr
end

--[[ MtGox info. ]]
function conky_mtgox_ticker(...)
local args, info = {...}, {}
local fmt = table.concat(args, ' ') --format string

local data, err = get_json_url('http://mtgox.com/code/data/ticker.php')
if data then MtGoxLastResult = fmt:gsub('%%([%w_]+)%%', data.ticker)
else MtGoxLastResult = tostring(err)
end
return MtGoxLastResult
end

Example config:
Code:
${color1}BTC${hr}
${if_empty ${lua bitcoin_running}}${lua_parse bitcoin_info Balance:${tab 40}${color0}%balance%${color1}
Tx Fee:${tab 40}${color0}%paytxfee%${color1}
Connections:${tab 10}${color0}%connections%${color1}
Blocks:${tab 40}${color0}%blocks%${color1}
Oldest:${tab 40}${color0}%keypoololdest%${color1}
Difficulty:${tab 20}${color0}%difficulty%${color1}
Version:${tab 40}${color0}%version%${color1}}
${lua_parse bitcoin_transactions * 5 %sign%%absamount% %confirmations%${tab 16}%absfee%${tab 16}%time%}
${else}${color0}NO DATA${endif}
(Ugly ain't it? Tongue But the result looks quite nice! Screenshot here as it's too big to attach to the post. :p)
Rena (OP)
Newbie
*
Offline Offline

Activity: 19
Merit: 0



View Profile WWW
May 02, 2011, 08:07:43 AM
 #2

Updated to add MtGox ticker data. Adds a dependency on LuaSocket. It's structured to be fairly simple to add support for any JSON HTTP URL, so feel free to ask.
mrlithium
Newbie
*
Offline Offline

Activity: 46
Merit: 0


View Profile
March 23, 2013, 03:40:24 PM
Last edit: March 23, 2013, 03:53:09 PM by mrlithium
 #3

You didnt have anything in your conky rc example file on the  mtgox_ticker.  I spent a few hours learning conky.
This is the best I came up with. I don't know what format  "format" is looking for.

Also, the ticker url has changed so that code doesnt work anymore, replace the old ticker URL with http://data.mtgox.com/code/data/ticker.php?Currency=USD . Note that this URL likely to change soon too (says its deprecated).

(Put this in your .conkyrc file once youve lua load'ed the script that does the heavy lifting by grabbing the tickerdata)

${lua_parse mtgox_ticker Buy:%buy% Sell:%sell% Last:%last%}
${lua_parse mtgox_ticker High:%high% Low:%low% Vol:%vol%}
${lua_parse mtgox_ticker Avg:%avg% VWAP:%vwap% }

But it's working, fully, just theres a heck of a lot of decimal places which I don't know how to round off Smiley

https://i.imgur.com/7EN610E.png
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!