diff --git a/i3pystatus/bitcoin.py b/i3pystatus/bitcoin.py index a6ca236..ee36473 100644 --- a/i3pystatus/bitcoin.py +++ b/i3pystatus/bitcoin.py @@ -1,10 +1,27 @@ import urllib.request import json -import time +from datetime import datetime from i3pystatus import IntervalModule from i3pystatus.core.util import internet, require, user_open +import locale +import threading +from contextlib import contextmanager + +LOCALE_LOCK = threading.Lock() + + +@contextmanager +def setlocale(name): + # To deal with locales only in this module and keep it thread save + with LOCALE_LOCK: + saved = locale.setlocale(locale.LC_ALL) + try: + yield locale.setlocale(locale.LC_ALL, name) + finally: + locale.setlocale(locale.LC_ALL, saved) + class Bitcoin(IntervalModule): @@ -12,8 +29,10 @@ class Bitcoin(IntervalModule): This module fetches and displays current Bitcoin market prices and optionally monitors transactions to and from a list of user-specified wallet addresses. Market data is pulled from the BitcoinAverage Price - Index API while transaction data is pulled - from blockchain.info . + Index API and it is possible to specify + the exchange to be monitored. + Transaction data is pulled from blockchain.info + . .. rubric:: Available formatters @@ -22,6 +41,9 @@ class Bitcoin(IntervalModule): * {bid_price} * {daily_average} * {volume} + * {volume_thousend} + * {volume_percent} + * {age} * {status} * {last_tx_type} * {last_tx_addr} @@ -37,6 +59,7 @@ class Bitcoin(IntervalModule): ("currency", "Base fiat currency used for pricing."), ("wallet_addresses", "List of wallet address(es) to monitor."), ("color", "Standard color"), + ("exchange", "Get ticker from a custom exchange instead"), ("colorize", "Enable color change on price increase/decrease"), ("color_up", "Color for price increases"), ("color_down", "Color for price decreases"), @@ -46,6 +69,7 @@ class Bitcoin(IntervalModule): ) format = "{symbol} {status}{last_price}" currency = "USD" + exchange = None symbol = "฿" wallet_addresses = "" color = "#FFFFFF" @@ -59,14 +83,39 @@ class Bitcoin(IntervalModule): } on_leftclick = "electrum" - on_rightclick = [user_open, "https://bitcoinaverage.com/"] + on_rightclick = ["open_something", "https://bitcoinaverage.com/"] _price_prev = 0 + def _get_age(self, bitcoinaverage_timestamp): + with setlocale('C'): # Deal with locales (months name differ) + # Assume format is always utc, to avoid import pytz + utc_tstamp = datetime.strptime( + bitcoinaverage_timestamp.split(', ')[1], + u'%d %b %Y %H:%M:%S -0000') + diff = datetime.utcnow() - utc_tstamp + return int(diff.total_seconds()) + + def _query_api(self, api_url): + url = "{}{}".format(api_url, self.currency.upper()) + response = urllib.request.urlopen(url).read().decode("utf-8") + return json.loads(response) + def _fetch_price_data(self): - api = "https://api.bitcoinaverage.com/ticker/global/" - url = "{}{}".format(api, self.currency.upper()) - return json.loads(urllib.request.urlopen(url).read().decode("utf-8")) + if self.exchange is None: + api_url = "https://api.bitcoinaverage.com/ticker/global/" + return self._query_api(api_url) + else: + api_url = "https://api.bitcoinaverage.com/exchanges/" + ret = self._query_api(api_url) + exchange = ret[self.exchange.lower()] + # Adapt values to global ticker format + exchange['ask'] = exchange['rates']['ask'] + exchange['bid'] = exchange['rates']['bid'] + exchange['last'] = exchange['rates']['last'] + exchange['24h_avg'] = None + exchange['timestamp'] = ret['timestamp'] + return exchange def _fetch_blockchain_data(self): api = "https://blockchain.info/multiaddr?active=" @@ -77,6 +126,7 @@ class Bitcoin(IntervalModule): @require(internet) def run(self): price_data = self._fetch_price_data() + fdict = { "symbol": self.symbol, "daily_average": price_data["24h_avg"], @@ -84,6 +134,9 @@ class Bitcoin(IntervalModule): "bid_price": price_data["bid"], "last_price": price_data["last"], "volume": price_data["volume_btc"], + "volume_thousend": price_data["volume_btc"] / 1000, + "volume_percent": price_data["volume_percent"], + "age": self._get_age(price_data['timestamp']) } if self._price_prev and fdict["last_price"] > self._price_prev: @@ -125,3 +178,9 @@ class Bitcoin(IntervalModule): "full_text": self.format.format(**fdict), "color": color, } + + def open_something(self, url_or_command): + """ + Wrapper function, to pass the arguments to user_open + """ + user_open(url_or_command)