├── InkyClean.py ├── LICENSE ├── CHANGELOG.md ├── crontab ├── README.md └── InkyCryptoGraph.py /InkyClean.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from inky import InkyPHAT 5 | from PIL import Image 6 | import time 7 | 8 | cycles = 3 9 | 10 | # Initiate Inky display 11 | inky_display = InkyPHAT("red") 12 | 13 | # Create a new canvas to draw on 14 | img = Image.new("P", (inky_display.WIDTH, inky_display.HEIGHT)) 15 | 16 | # Set colours 17 | colours = (inky_display.RED, inky_display.BLACK, inky_display.WHITE) 18 | colour_names = (inky_display.colour, "black", "white") 19 | 20 | # Loop through the specified number of cycles and completely 21 | # fill the display with each colour in turn. 22 | 23 | for i in range(cycles): 24 | print("Cleaning cycle %i\n" % (i + 1)) 25 | for j, c in enumerate(colours): 26 | print("- updating with %s" % colour_names[j]) 27 | inky_display.set_border(c) 28 | for x in range(inky_display.WIDTH): 29 | for y in range(inky_display.HEIGHT): 30 | img.putpixel((x, y), c) 31 | inky_display.set_image(img) 32 | inky_display.show() 33 | time.sleep(5) 34 | print("\n") 35 | 36 | print("Cleaning complete!") 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yannick Zwijsen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.2.0](https://github.com/yzwijsen/InkyCryptoGraph/compare/v1.1.1...v1.2.0) (2023-04-25) 4 | 5 | 6 | ### Features 7 | 8 | * increase cleaning script sleep time ([#12](https://github.com/yzwijsen/InkyCryptoGraph/issues/12)) ([05effd0](https://github.com/yzwijsen/InkyCryptoGraph/commit/05effd0fd6e249a0053ff8d74e6e6ea6e10728b1)) 9 | 10 | ## [1.1.1](https://github.com/yzwijsen/InkyCryptoGraph/compare/v1.1.0...v1.1.1) (2023-04-21) 11 | 12 | 13 | ### Bug Fixes 14 | 15 | * stop .gitattributes file from being included in release ([#7](https://github.com/yzwijsen/InkyCryptoGraph/issues/7)) ([84472b2](https://github.com/yzwijsen/InkyCryptoGraph/commit/84472b2eb19a7748d30e4b1039c95838f67e7670)) 16 | 17 | ## [1.1.0](https://github.com/yzwijsen/InkyCryptoGraph/compare/v1.0.0...v1.1.0) (2023-04-21) 18 | 19 | 20 | ### Features 21 | 22 | * add crontab file ([#4](https://github.com/yzwijsen/InkyCryptoGraph/issues/4)) ([e1e1f4f](https://github.com/yzwijsen/InkyCryptoGraph/commit/e1e1f4f882146ee1b64b5eff6cbe4f32b2ba4ab6)) 23 | * add display cleaning script ([#2](https://github.com/yzwijsen/InkyCryptoGraph/issues/2)) ([06b37c8](https://github.com/yzwijsen/InkyCryptoGraph/commit/06b37c84fba3477f483026c305592240d450f887)) 24 | -------------------------------------------------------------------------------- /crontab: -------------------------------------------------------------------------------- 1 | # Edit this file to introduce tasks to be run by cron. 2 | # 3 | # Each task to run has to be defined through a single line 4 | # indicating with different fields when the task will be run 5 | # and what command to run for the task 6 | # 7 | # To define the time you can provide concrete values for 8 | # minute (m), hour (h), day of month (dom), month (mon), 9 | # and day of week (dow) or use '*' in these fields (for 'any'). 10 | # 11 | # Notice that tasks will be started based on the cron's system 12 | # daemon's notion of time and timezones. 13 | # 14 | # Output of the crontab jobs (including errors) is sent through 15 | # email to the user the crontab file belongs to (unless redirected). 16 | # 17 | # For example, you can run a backup of all your user accounts 18 | # at 5 a.m every week with: 19 | # 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/ 20 | # 21 | # For more information see the manual pages of crontab(5) and cron(8) 22 | # 23 | # m h dom mon dow command 24 | #*/5 0-2,8-23 * * * python3 /home/pi/InkyCryptoGraph.py -p XXBTZEUR -f -r 1 25 | */5 8-23 * * * python3 /home/pi/InkyCryptoGraph.py -p XXBTZEUR -f -r 1 26 | */5 0-2 * * * python3 /home/pi/InkyCryptoGraph.py -p XXBTZEUR -f -bw -r 1 27 | */30 3-7 * * * python3 /home/pi/InkyCryptoGraph.py -p XXBTZEUR -f -bw -r 1 28 | 45 6 * * * python3 /home/pi/InkyClean.py 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # InkyCryptoGraph :chart_with_upwards_trend::money_with_wings: 2 | 3 | [![GitHub license](https://img.shields.io/github/license/yzwijsen/InkyCryptoGraph)](https://github.com/yzwijsen/InkyCryptoGraph/blob/main/LICENSE) 4 | 5 | Customizable crypto ticker and graph for Pimoroni InkyPHAT e-ink display hat. 6 | 7 | Default Colors | Dark mode 8 | :-------------------------:|:-------------------------: 9 | ![Example picture 2](https://i.imgur.com/gshzbpWl.jpg) | ![Example picture 1](https://i.imgur.com/VVFmSCel.jpg) 10 | 11 | ## :scroll: Description 12 | InkyCryptoGraph is a Python script that fetches cryptocurrency data from the Kraken Exchange API and displays it on an InkyPHAT or InkyWHAT e-ink display hat for Raspberry Pi. The script can display the current price, low, high, and a historical price graph. 13 | 14 | ## :wrench: Pre-requisites 15 | * [Raspberry Pi](https://www.raspberrypi.org/products/) with [InkyPHAT](https://shop.pimoroni.com/products/inky-phat) or [InkyWHAT](https://shop.pimoroni.com/products/inky-what) e-ink display hat 16 | * Python 3.x 17 | * [Inky](https://github.com/pimoroni/inky) library by Pimoroni 18 | * [Pillow](https://pillow.readthedocs.io/en/stable/) library 19 | 20 | ## :inbox_tray: Installation 21 | 1. Clone this repository: `git clone https://github.com/yzwijsen/InkyCryptoGraph.git` 22 | 2. Install required libraries: 23 | - using pip: `pip install inky Pillow` 24 | - manually: `curl https://get.pimoroni.com/inky | bash` (Full instructions [here](https://learn.pimoroni.com/tutorial/sandyj/getting-started-with-inky-phat)) 25 | 3. Run the script: `python3 /home/pi/InkyCryptoGraph.py -p XXBTZEUR -r 1` 26 | 4. Setup Cron to run the script every x minutes 27 | 28 | ## :recycle: Cleaning script 29 | 30 | I have included a modified version of the Pimoroni Inky cleaning script. 31 | This script should be ran once in a while to keep the screen in good condition. It does this by running through all the colors and overwriting the entire screen. 32 | This will keep your screen in good condition and reduce artifacts / ghosting. 33 | 34 | > **Note** 35 | > You can include the cleaning script in your crontab file to have it run automatically, just make sure that it doesn't overlap with the main script. Otherwise the screen will stop updating untill you restart the main script. A window of 15 minutes should be more than enough to run the cleaning script. 36 | > 37 | > The included crontab example already has an entry for this cleaning script. 38 | 39 | ## :clock7: Cron / Scheduling 40 | 41 | To keep the screen updated you can setup a cron job to run the script every x minutes 42 | You can set this up by running `crontab -e` and adding an entry for the main script and, optionally, the cleaning script. 43 | 44 | Below is an example configuration. You can also find this in the **crontab** file included in this repository. 45 | 46 | ```cron 47 | */5 8-23 * * * python3 /home/pi/InkyCryptoGraph.py -p XXBTZEUR -f -r 1 48 | */5 0-2 * * * python3 /home/pi/InkyCryptoGraph.py -p XXBTZEUR -f -bw -r 1 49 | */30 3-7 * * * python3 /home/pi/InkyCryptoGraph.py -p XXBTZEUR -f -bw -r 1 50 | 45 6 * * * python3 /home/pi/InkyClean.py 51 | ``` 52 | 53 | This crontab file will run the script in color mode every 5 minutes from 8AM till 11PM. 54 | The rest of the time it will run in dark mode. We're also lowering the update time to every 30 minutes between 3AM and 7AM. 55 | Finally, we run the cleaning script once every day at 6:45 AM 56 | 57 | ## :gear: Parameters 58 | The script accepts various command line arguments to customize the output: 59 | 60 | | Parameter | Description | 61 | |-----------------------|------------------------------------------------------------------------------------------| 62 | | `--assetpair` `-p` | Asset Pair to track. Ex: XXBTZUSD, XXBTZEUR, XETHZUSD, ... | 63 | | `--currencysymbol` `-c`| Currency symbol should be auto detected, if not you can set it manually. | 64 | | `--range` `-r` | How many days of historical price data to show in the graph. | 65 | | `--holdings` `-ho` | Set your holdings. When set the ticker will show the value of your holdings instead of the current crypto price.| 66 | | `--flipscreen` `-f` | Flips the screen 180°. | 67 | | `--verbose` `-v` | Print verbose output. | 68 | | `--blackandwhite` `-bw`| Only use black and white colors. This reduces the time needed to draw to the display. | 69 | | `--backgroundcolor` `-bgc`| Display background color. 0 = white, 1 = black, 2 = red/yellow. Ignored when BlackAndWhite mode is enabled.| 70 | | `--graphforegroundcolor` `-gfgc`| Graph foreground color. 0 = white, 1 = black, 2 = red/yellow. Ignored when BlackAndWhite mode is enabled.| 71 | | `--graphbackgroundcolor` `-gbgc`| Graph background color. 0 = white, 1 = black, 2 = red/yellow. Ignored when BlackAndWhite mode is enabled.| 72 | | `--pricecolor` `-pc` | Price color. 0 = white, 1 = black, 2 = red/yellow. Ignored when BlackAndWhite mode is enabled.| 73 | | `--textcolor` `-tc` | Price color. 0 = white, 1 = black, 2.| 74 | | `--bordercolor` `-pc` | Border color. 0 = white, 1 = black, 2 = red/yellow. Ignored when BlackAndWhite mode is enabled.| 75 | | `--linethickness` `-tc` | Graph line thickness in pixels.| 76 | 77 | -------------------------------------------------------------------------------- /InkyCryptoGraph.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from inky import InkyPHAT, InkyWHAT 4 | from PIL import Image, ImageFont, ImageDraw 5 | from font_source_sans_pro import SourceSansPro, SourceSansProBold 6 | import time 7 | from datetime import datetime 8 | import requests 9 | import argparse 10 | 11 | # Config 12 | inky_display_type = 0 # 0 for InkyPHAT, 1 for InkyWHAT 13 | inky_display_color = "red" 14 | 15 | kraken_ticker_url = "https://api.kraken.com/0/public/Ticker" 16 | kraken_ohlc_url = "https://api.kraken.com/0/public/OHLC" 17 | max_api_datapoints = 720 # Max number of datapoints returned by the Kraken API OHLC call (https://stackoverflow.com/questions/48508150/kraken-api-ohlc-request-doesnt-honor-the-since-parameter) 18 | 19 | date_time_format = "%d/%m/%Y %H:%M" 20 | currency_thousands_seperator = " " 21 | padding = 5 # padding between edge of container and content. used for graph bounds and graph, screen edge and text,... 22 | 23 | # Arguments 24 | parser = argparse.ArgumentParser(description="Crypto ticker with graph for InkyPHAT e-ink display hat. https://github.com/yzwijsen/InkyCryptoGraph") 25 | parser.add_argument("--assetpair", "-p", type=str, default="XXBTZUSD", help="Asset Pair to track. Ex: XXBTZUSD, XXBTZEUR, XETHZUSD,...") 26 | parser.add_argument("--currencysymbol", "-c", type=str, help="Currency symbol should be auto detected, if not you can set it manually.") 27 | parser.add_argument("--range", "-r", type=int, default=1, help="How many days of historical price data to show in the graph.") 28 | parser.add_argument("--holdings", "-ho", type=float, help="Set your holdings. When set the ticker will show the value of your holdings instead of the current crypto price.") 29 | parser.add_argument("--flipscreen","-f", action="store_true", help="Flips the screen 180°.") 30 | parser.add_argument("--verbose", "-v", action="store_true", help="print verbose output.") 31 | parser.add_argument("--blackandwhite", "-bw", action="store_true", help="Only use black and white colors. This reduces the time needed to draw to the display.") 32 | parser.add_argument("--backgroundcolor", "-bgc", type=int, default=0, help="Display background color. 0 = white, 1 = black, 2 = red/yellow. Ignored when BlackAndWhite mode is enabled.") 33 | parser.add_argument("--graphforegroundcolor", "-gfgc", type=int, default=0, help="Graph foreground color. 0 = white, 1 = black, 2 = red/yellow. Ignored when BlackAndWhite mode is enabled.") 34 | parser.add_argument("--graphbackgroundcolor", "-gbgc", type=int, default=1, help="Graph background color. 0 = white, 1 = black, 2 = red/yellow. Ignored when BlackAndWhite mode is enabled.") 35 | parser.add_argument("--pricecolor", "-pc", type=int, default=1, help="Price color. 0 = white, 1 = black, 2 = red/yellow. Ignored when BlackAndWhite mode is enabled.") 36 | parser.add_argument("--textcolor", "-tc", type=int, default=2, help="Price color. 0 = white, 1 = black, 2 = red/yellow. Ignored when BlackAndWhite mode is enabled.") 37 | parser.add_argument("--bordercolor", "-bc", type=int, default=1, help="Border color. 0 = white, 1 = black, 2 = red/yellow. Ignored when BlackAndWhite mode is enabled.") 38 | parser.add_argument("--linethickness", "-lt", type=int, default=1, help="Graph line thickness in pixels.") 39 | args = parser.parse_args() 40 | 41 | #region Functions 42 | 43 | def raise_system_exit(error_description, exception_details): 44 | msg = "Error: " + error_description + " (" + str(exception_details) + ")\nExiting..." 45 | raise SystemExit(msg) 46 | 47 | def get_current_price(pair): 48 | url = kraken_ticker_url + "?pair=" + pair 49 | 50 | # Get ticker data 51 | try: 52 | response = requests.get(url) 53 | except Exception as e: 54 | raise_system_exit("Kraken API not reachable. Make sure you are connected to the internet and that the Kraken API is up.",e) 55 | 56 | # Read JSON response 57 | try: 58 | result = response.json()["result"][pair]["c"][0] 59 | except Exception as e: 60 | raise_system_exit("Invalid or no JSON response received from Kraken API. Make sure you set a valid asset pair.",e) 61 | 62 | return result 63 | 64 | def get_historical_price_data(pair, range, interval): 65 | # Calculate timestamp for API call 66 | current_timestamp = time.time() 67 | since_timestamp = str(int(current_timestamp) - 86400 * range) # subtract x days from current timestamp 68 | 69 | # Get OHLC data 70 | url = kraken_ohlc_url + "?pair=" + pair + "&interval=" + str(interval) + "&since=" + since_timestamp 71 | try: 72 | response = requests.get(url) 73 | except Exception as e: 74 | raise_system_exit("Kraken API not reachable. Make sure you are connected to the internet and that the Kraken API is up.",e) 75 | 76 | # Read JSON response 77 | try: 78 | prices = response.json()["result"][pair] # 0 = time, 1 = open, 2 = high, 3 = low, 4 = close, 5 = vwap, 6 = volume, 7 = count 79 | except Exception as e: 80 | raise_system_exit("Invalid or no JSON response received from Kraken API. Make sure you set a valid asset pair.",e) 81 | 82 | # Generate a list with timestamp, high price and low price from OHLC data and convert to int/float 83 | price_data = [] 84 | for price in prices: 85 | price_data.append((int(price[0]),float(price[2]),float(price[3]))) # 0 = time, 1 = open, 2 = high, 3 = low, 4 = close, 5 = vwap, 6 = volume, 7 = count 86 | 87 | return price_data 88 | 89 | # gets the lowest possible interval to match the requested price history range without passing the max data points threshold of the Kraken API 90 | def get_interval(range, max_datapoints): 91 | interval_list = [5, 15, 30, 60, 240, 1440, 10080, 21600] # 1 is also a valid option but we're discarding it as it won't even allow us to get 1 day worth of data 92 | for interval in interval_list: 93 | datapoints_count = 1440 / interval * range 94 | if datapoints_count < max_datapoints: 95 | break 96 | return interval 97 | 98 | # takes a timestamp and price value and plots it within the graph bounds 99 | def plot_graph_point(time,value): 100 | plot_point_x = int(round(((time - min_time) * delta_screen_x / delta_time + graph_bounds[0][0] + padding))) 101 | plot_point_y = int(round(((value - min_value) * delta_screen_y / delta_value + graph_bounds[0][1] + padding))) 102 | 103 | # flip the Y value since InkyPHAT screen y-axis is inverted 104 | plot_point_y = graph_bounds[1][1] - plot_point_y + graph_bounds[0][1] 105 | 106 | return (plot_point_x,plot_point_y) 107 | 108 | # Returns rounded and formatted currency as string 109 | def format_price(amount): 110 | amount = float(amount) 111 | # Round price and convert to string 112 | amount = str(round(amount)) 113 | 114 | # Add space 115 | separator_index = len(amount) - 3 116 | amount = amount[:separator_index] + currency_thousands_seperator + amount[separator_index:] 117 | 118 | # Add currency symbol 119 | amount = currency_symbol + " " + amount 120 | 121 | return amount 122 | 123 | def text_color(): 124 | return inky_display.WHITE if args.blackandwhite else args.textcolor 125 | 126 | def price_color(): 127 | return inky_display.WHITE if args.blackandwhite else args.pricecolor 128 | 129 | def graph_foreground_color(): 130 | return inky_display.WHITE if args.blackandwhite else args.graphforegroundcolor 131 | 132 | def graph_background_color(): 133 | return inky_display.BLACK if args.blackandwhite else args.graphbackgroundcolor 134 | 135 | def background_color(): 136 | return inky_display.BLACK if args.blackandwhite else args.backgroundcolor 137 | 138 | def border_color(): 139 | return inky_display.WHITE if args.blackandwhite else args.bordercolor 140 | 141 | def print_verbose(*messages): 142 | if args.verbose: 143 | full_message = "" 144 | for message in messages: 145 | full_message += str(message) 146 | full_message += " " 147 | print(full_message) 148 | 149 | def get_currency_symbol(pair): 150 | currency_index = len(pair) - 3 151 | currency = pair[currency_index:] 152 | 153 | if currency == "EUR": 154 | return "€" 155 | elif currency == "GBP": 156 | return "£" 157 | elif currency == "USD": 158 | return "$" 159 | elif currency == "CAD": 160 | return "$" 161 | elif currency == "AUD": 162 | return "$" 163 | elif currency == "JPY": 164 | return "¥" 165 | else: 166 | return currency 167 | 168 | #endregion 169 | 170 | # Make sure assetpair is in upper case 171 | args.assetpair = str.upper(args.assetpair) 172 | 173 | # Set price history interval so we don't exceed the max amount of data points returned by the Kraken api OHLC call 174 | price_history_interval = get_interval(args.range, max_api_datapoints) 175 | 176 | # Set currency symbol 177 | if args.currencysymbol is None: 178 | currency_symbol = get_currency_symbol(args.assetpair) 179 | else: 180 | currency_symbol = args.currencysymbol 181 | 182 | # Print main parameters 183 | print_verbose("\n#### Parameters ####") 184 | print_verbose("Asset Pair: " + args.assetpair) 185 | print_verbose("Range: " + str(args.range) + " Day(s)") 186 | print_verbose("B&W Mode: " + str(args.blackandwhite)) 187 | print_verbose("Flip Screen: " + str(args.flipscreen)) 188 | print_verbose("####################\n") 189 | 190 | print_verbose("Interval: " + str(price_history_interval) + " (Data Points: " + str(1440 / price_history_interval * args.range) + ")\n") 191 | 192 | # Setup fonts 193 | font_small = ImageFont.truetype(SourceSansPro, 12) 194 | font_medium = ImageFont.truetype(SourceSansPro, 16) 195 | font_medium_bold = ImageFont.truetype(SourceSansProBold, 16) 196 | font_large_bold = ImageFont.truetype(SourceSansProBold, 40) 197 | 198 | # Initiate Inky display 199 | inky_display = InkyPHAT(inky_display_color) if inky_display_type == 0 else InkyWHAT(inky_display_color) 200 | 201 | #inky_display.set_border(inky_display.WHITE) 202 | img = Image.new("P", (inky_display.WIDTH, inky_display.HEIGHT)) 203 | draw = ImageDraw.Draw(img) 204 | 205 | # When drawing a rectangle accross the entire inkyphat screen the rectangle borders are missing on the screen's right and bottom side (at least on the inkyphat I have) 206 | # To fix this we make the screen a little smaller. Removing one pixel would probably be enough but then we'd have an uneven amount of pixels which would complicate the rest of the code 207 | display_width = inky_display.WIDTH - 2 208 | display_height = inky_display.HEIGHT - 2 209 | 210 | # Calculate graph bounds 211 | graph_bounds = [(0,display_height / 2),(display_width / 2,display_height)] 212 | 213 | print("Getting price data from Kraken exchange...") 214 | 215 | # Get Current Price 216 | current_price = get_current_price(args.assetpair) 217 | 218 | # Get Historical price data 219 | historical_price_data = get_historical_price_data(args.assetpair, args.range, price_history_interval) 220 | 221 | # Set minmax variables to some initial value from the dataset so we have something to compare to 222 | min_time = max_time = historical_price_data[0][0] 223 | min_value = max_value = historical_price_data[0][1] 224 | low_price = historical_price_data[0][2] 225 | 226 | # Calculate min and max values 227 | for i in historical_price_data: 228 | if i[0] < min_time: 229 | min_time = i[0] 230 | if i[0] > max_time: 231 | max_time = i[0] 232 | if i[1] < min_value: 233 | min_value = i[1] 234 | if i[1] > max_value: 235 | max_value = i[1] 236 | # We calculate the low price here as well since we need to use market low instead of market high (which is what we're using for the graph). 237 | if i[2] < low_price: 238 | low_price = i[2] 239 | 240 | # For the high price we want to use market high, so we just copy max_value 241 | high_price = max_value 242 | 243 | # Calculate holdings value instead of price 244 | if args.holdings is not None: 245 | current_price = float(current_price) * args.holdings 246 | low_price = float(low_price) * args.holdings 247 | high_price = float(high_price) * args.holdings 248 | 249 | # format prices 250 | current_price = format_price(current_price) 251 | low_price = format_price(low_price) 252 | high_price = format_price(high_price) 253 | 254 | print_verbose("Price: " + current_price + "\nLow: " + low_price + "\nHigh: " + high_price + "\n") 255 | 256 | # Calculate graph dimensions 257 | delta_screen_x = graph_bounds[1][0] - graph_bounds[0][0] - padding * 2 258 | delta_screen_y = graph_bounds[1][1] - graph_bounds[0][1] - padding * 2 259 | 260 | # Calculate time & value range 261 | delta_value = max_value - min_value 262 | delta_time = max_time - min_time 263 | 264 | # Draw ticker rectangle 265 | draw.rectangle((0,0,display_width,display_height / 2), background_color(), border_color()) 266 | 267 | # Draw asset pair name 268 | draw.text((padding, 0), args.assetpair, text_color(), font_small) 269 | 270 | # Draw current price 271 | w, h = font_large_bold.getsize(current_price) 272 | x = (display_width / 2) - (w / 2) 273 | y = (display_height / 4) - (h / 2) 274 | draw.text((x, y), current_price, price_color(), font_large_bold) 275 | 276 | # Draw graph rectangle 277 | draw.rectangle(graph_bounds, graph_background_color(), border_color()) 278 | 279 | print("Plotting Historical Price Data...") 280 | 281 | # Draw graph lines 282 | previous_point = plot_graph_point(historical_price_data[0][0],historical_price_data[0][1]) 283 | for i in historical_price_data: 284 | point = plot_graph_point(i[0],i[1]) 285 | print_verbose(i[0], i[1]," ==> ", point) 286 | draw.line((previous_point,point),graph_foreground_color(), args.linethickness) 287 | previous_point = point 288 | 289 | print_verbose() # Prints an empty line for prettier verbose output formatting 290 | 291 | # Draw details rectangle 292 | draw.rectangle((display_width / 2,display_height / 2,display_width,display_height), background_color(), border_color()) 293 | 294 | # Set label text 295 | label_high_text = "High:" 296 | label_low_text = "Low:" 297 | 298 | # Calculate text size 299 | label_high_width,label_high_height = font_medium.getsize(label_high_text) 300 | label_low_width,label_low_height = font_medium.getsize(label_low_text) 301 | high_price_width,high_price_height = font_medium_bold.getsize(high_price) 302 | low_price_width,low_price_heigth = font_medium_bold.getsize(low_price) 303 | 304 | # Calculate positions for labels, top row and bottom row 305 | x_label = (display_width / 2) + padding 306 | y_top = (display_height / 4 * 3) - (display_height / 8) - (label_high_height / 2) 307 | y_bottom = (display_height / 4 * 4) - (display_height / 8) - (label_low_height / 2) 308 | 309 | # Draw labels 310 | draw.text((x_label, y_top), label_high_text, text_color(), font_medium) 311 | draw.text((x_label, y_bottom), label_low_text, text_color(), font_medium) 312 | 313 | # Draw high price 314 | x = display_width - high_price_width - padding 315 | draw.text((x, y_top), high_price, price_color(), font_medium_bold) 316 | 317 | # Draw low price 318 | x = display_width - low_price_width - padding 319 | draw.text((x, y_bottom), low_price, price_color(), font_medium_bold) 320 | 321 | # Draw graph time range 322 | draw.text((padding, display_height / 2), str(args.range) + "D", graph_foreground_color(), font_small) 323 | 324 | # Draw current datetime 325 | date_time = datetime.now() 326 | date_time_string = date_time.strftime(date_time_format) 327 | w, h = font_small.getsize(date_time_string) 328 | x = display_width - w - padding 329 | draw.text((x, 0), date_time_string, text_color(), font_small) 330 | 331 | # Flip img if needed 332 | if args.flipscreen: 333 | img = img.rotate(180) 334 | 335 | # Push image to screen 336 | print("Updating inky display...") 337 | inky_display.set_image(img) 338 | inky_display.show() 339 | 340 | print("Done!") --------------------------------------------------------------------------------