Skip to content

Instantly share code, notes, and snippets.

@d3d9
Last active August 26, 2018 19:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save d3d9/694370048c8255b289bbfcdcf0855506 to your computer and use it in GitHub Desktop.
Save d3d9/694370048c8255b289bbfcdcf0855506 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3.7
# -*- coding: utf-8 -*-
import traceback
import argparse
from radbox import *
from datetime import datetime
from decimal import Decimal, ROUND_HALF_UP
from subprocess import check_output
from rgbmatrix import RGBMatrix, RGBMatrixOptions, graphics
from time import localtime, sleep
from PIL import Image
### Arguments
parser = argparse.ArgumentParser()
parser.add_argument("-u", "--base-url", action="store", help="Box service URL (not the booking page)", default=URL("https://www.dein-radschloss.de/"), type=URL)
parser.add_argument("-i", "--station-id", action="store", help="Station ID to be selected. Default: 185", default='185', type=str)
parser.add_argument("-r", "--enable-right", action="store_true", help="Enable sidebar on the right side with additional info.")
parser.add_argument("-sx", "--box-spacing-x", action="store", help="Horizontal space between boxes. Default: 2", default=2, type=int)
parser.add_argument("-sy", "--box-spacing-y", action="store", help="Vertical space between boxes. Default: 2", default=2, type=int)
parser.add_argument("--disable-progress", action="store_false", help="Disable progress bar at the bottom")
parser.add_argument("-d", "--daemon", action="store_true", help="Run as daemon")
parser.add_argument("--update-steps", action="store", help="Loop steps until reload of data. Default: 1500", default=1500, type=int)
parser.add_argument("--sleep-interval", action="store", help="Sleep interval (inside the main loop). Default: 0.0375", default=0.0375, type=float)
# matrix settings
parser.add_argument("-c", "--led-chain", action="store", help="Daisy-chained boards. Default: 2.", default=2, type=int)
parser.add_argument("-b", "--led-brightness", action="store", help="Sets brightness level. Default: 30. Range: 1..100", default=30, type=int)
parser.add_argument("--led-rows", action="store", help="Display rows. 16 for 16x32, 32 for 32x32. Default: 32", default=32, type=int)
parser.add_argument("--led-cols", action="store", help="Panel columns. Typically 32 or 64. (Default: 64)", default=64, type=int)
parser.add_argument("--led-parallel", action="store", help="For Plus-models or RPi2: parallel chains. 1..3. Default: 1", default=1, type=int)
parser.add_argument("--led-pwm-bits", action="store", help="Bits used for PWM. Something between 1..11. Default: 11", default=11, type=int)
parser.add_argument("--led-gpio-mapping", help="Hardware Mapping: regular, adafruit-hat, adafruit-hat-pwm" , choices=['regular', 'adafruit-hat', 'adafruit-hat-pwm'], type=str)
parser.add_argument("--led-scan-mode", action="store", help="Progressive or interlaced scan. 0 Progressive, 1 Interlaced (default)", default=1, choices=range(2), type=int)
parser.add_argument("--led-pwm-lsb-nanoseconds", action="store", help="Base time-unit for the on-time in the lowest significant bit in nanoseconds. Default: 130", default=130, type=int)
parser.add_argument("--led-show-refresh", action="store_true", help="Shows the current refresh rate of the LED panel")
parser.add_argument("--led-slowdown-gpio", action="store", help="Slow down writing to GPIO. Range: 1..100. Default: 1", choices=range(3), type=int)
parser.add_argument("--led-no-hardware-pulse", action="store", help="Don't use hardware pin-pulse generation")
parser.add_argument("--led-rgb-sequence", action="store", help="Switch if your matrix has led colors swapped. Default: RGB", default="RGB", type=str)
parser.add_argument("--led-pixel-mapper", action="store", help="Apply pixel mappers. e.g \"Rotate:90\"", default="", type=str)
parser.add_argument("--led-row-addr-type", action="store", help="0 = default; 1=AB-addressed panels;2=row direct", default=0, type=int, choices=[0,1,2])
parser.add_argument("--led-multiplexing", action="store", help="Multiplexing type: 0=direct; 1=strip; 2=checker; 3=spiral; 4=ZStripe; 5=ZnMirrorZStripe; 6=coreman; 7=Kaler2Scan; 8=ZStripeUneven (Default: 0)", default=0, type=int)
args = parser.parse_args()
options = RGBMatrixOptions()
if args.led_gpio_mapping != None:
options.hardware_mapping = args.led_gpio_mapping
options.rows = args.led_rows
options.cols = args.led_cols
options.chain_length = args.led_chain
options.parallel = args.led_parallel
options.row_address_type = args.led_row_addr_type
options.multiplexing = args.led_multiplexing
options.pwm_bits = args.led_pwm_bits
options.brightness = args.led_brightness
options.pwm_lsb_nanoseconds = args.led_pwm_lsb_nanoseconds
options.led_rgb_sequence = args.led_rgb_sequence
options.pixel_mapper_config = args.led_pixel_mapper
if args.led_show_refresh:
options.show_refresh_rate = 1
if args.led_slowdown_gpio != None:
options.gpio_slowdown = args.led_slowdown_gpio
if args.led_no_hardware_pulse:
options.disable_hardware_pulsing = True
options.daemon = args.daemon
options.drop_privileges = 1
matrix = RGBMatrix(options=options)
canvas = matrix.CreateFrameCanvas()
### Fonts
fontdir = "../rpi-rgb-led-matrix/fonts/"
fontmin = graphics.Font()
fontmin.LoadFont(fontdir+"tom-thumb.bdf")
fontnum = graphics.Font()
fontnum.LoadFont(fontdir+"4x6.bdf")
fontlargernum = graphics.Font()
fontlargernum.LoadFont(fontdir+"5x7.bdf")
### Colors
bookableColor = graphics.Color(0, 255, 0)
occupiedColor = graphics.Color(255, 0, 0)
lighttextColor = graphics.Color(100, 100, 100)
textColor = graphics.Color(190, 190, 190)
barColor = graphics.Color(8, 8, 8)
if options.brightness < 15:
barColor = graphics.Color(12, 12, 12)
### Display configuration
stationid = args.station_id
step = args.update_steps
interval = args.sleep_interval
rightbar = args.enable_right
progress = args.disable_progress
spacebr = 1
text_startr = 8
lineheight = 6
rightbarwidth = 21
### End of configuration
def drawtime(canvas, x_max, r, color, hour, minute, large=False):
width = x_max+1
if large:
graphics.DrawText(canvas, fontlargernum, width - 21, r, color, str(hour).zfill(2))
canvas.SetPixel(width - 11, r-4, color.red, color.green, color.blue)
canvas.SetPixel(width - 11, r-2, color.red, color.green, color.blue)
graphics.DrawText(canvas, fontlargernum, width - 9, r, color, str(minute).zfill(2))
else:
graphics.DrawText(canvas, fontnum, width - 17, r, color, str(hour).zfill(2))
canvas.SetPixel(width - 9, r-4, color.red, color.green, color.blue)
canvas.SetPixel(width - 9, r-2, color.red, color.green, color.blue)
graphics.DrawText(canvas, fontnum, width - 7, r, color, str(minute).zfill(2))
def drawppm(canvas, ppm, cx, cy, unsafe=True):
canvas.SetImage(ppm, cx-ppm.size[0]//2, cy-ppm.size[1]//2, unsafe)
return canvas
def drawsquare(canvas, x1, y1, x2, y2, color):
for y in range(y1, y2):
graphics.DrawLine(canvas, x1, y, x2, y, color)
cx = (x1+x2)//2
cy = (y1+y2)//2
return canvas, (cx, cy)
def loop(canvas, baseurl, station):
i = 0
x_max = canvas.width - 1 - (rightbarwidth + spacebr)*int(rightbar)
y_max = canvas.height - 1
sqw = 0
sqh = 0
spx = args.box_spacing_x
spy = args.box_spacing_y
time_measure = datetime.now()
prev_time_measure = datetime.now()
while True:
canvas.Clear()
r = text_startr
if not i % step:
try:
if not station.getinfo(baseurl=baseurl, loadprices=False):
print("loading current data for selected station was unsuccessful")
maxboxperrow = max(list(len(list(filter(lambda box: box.row == row, station.boxes))) for row in range(station.boxrows or 1)))
sqw = int(Decimal((x_max-spx*maxboxperrow)/maxboxperrow).quantize(0, ROUND_HALF_UP))
startx = (x_max+1-(maxboxperrow*sqw+(maxboxperrow-1)*spx))//2
boxrows = (station.boxrows or 1)
sqh = int(Decimal((y_max-spy*boxrows)/boxrows).quantize(0, ROUND_HALF_UP))
starty = (y_max+1-(boxrows*sqh+(boxrows-1)*spy))//2
except Exception:
print(traceback.print_exc())
rowpos = [startx]*(station.boxrows or 1)
for box in (station.boxes or []):
row = box.row or 0
if type(box) == Box:
if box.state == "bookable":
color = bookableColor
elif box.state == "occupied":
color = occupiedColor
else:
color = lighttextColor
boxnum = (box.displayas or box.number.lstrip('0')).zfill(2)
canvas, (cx, cy) = drawsquare(canvas, rowpos[row], starty+row*(sqh+spy), rowpos[row]+sqw-1, starty+row*(sqh+spy)+sqh, color)
# canvas.SetPixel(cx, cy, 255, 255, 255)
rowpos[row] += sqw + spx
if rightbar:
currenttime = localtime()
drawtime(canvas, canvas.width-1, 8, textColor, currenttime.tm_hour, currenttime.tm_min, True)
if progress:
x_progress = int(x_max - ((i % step)*(x_max/step)))
graphics.DrawLine(canvas, 0, y_max, x_progress, y_max, barColor)
canvas = matrix.SwapOnVSync(canvas)
prev_time_measure = time_measure
time_measure = datetime.now()
sleep(max(interval-(time_measure-prev_time_measure).total_seconds(), 0))
i += 1
time_measure = datetime.now()
try:
baseurl = args.base_url
buchenurl = URL(baseurl+"boxbuchen/")
stations, references = getstations(buchenurl)
station = stations[stationid]
loop(canvas, baseurl, station)
except KeyboardInterrupt:
print("exiting")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment