diff --git a/dashboard.py b/dashboard.py index 5b601c0..cb518ef 100644 --- a/dashboard.py +++ b/dashboard.py @@ -1,139 +1,50 @@ import datetime +import time import json from dashboard_api import led_out -import websocket -#from websocket import create_connection -import colorsys -import signal -import time from threading import Thread -#for downloading messages: - - - -################################################################################ -#VAR SETUP: -create_connection = websocket.create_connection -access_token = credentials.pushbullet_token -PB_ws_url = "wss://stream.pushbullet.com/websocket/"+access_token -notification_stream = create_connection(PB_ws_url) -notification_list = [] ################################################################################ #start of actual programm. class DashBoard(object): - """docstring for DashBoard.""" - - def __init__(self): + """runs all the dashboard operations autonomusly. Accepts outside pokes to change output.""" + def __init__(self, text_speed=7): + self.IO = led_out.OutputHandler(32,16) + self.tspeed = text_speed + self.start() + # self.text_threads = [] + print("INIT") -def display_time(four_digit_time): - datetoshow = datetime.datetime.today().weekday() - if datetoshow==4: - if int(four_digit_time) >= 620 and int(four_digit_time) < 2130: - brightness = 1 - else: - brightness = 0 - elif datetoshow==5: - if int(four_digit_time) >= 1030 and int(four_digit_time) < 2130: - brightness = 1 - else: - brightness = 0 - elif datetoshow==6: - if int(four_digit_time) >= 1030 and int(four_digit_time) < 2115: - brightness = 1 - else: - brightness = 0 - else: - if int(four_digit_time) >= 620 and int(four_digit_time) < 2115: - brightness = 1 - else: - brightness = 0 - print(four_digit_time, datetoshow, len(notification_list), brightness) - output_led.output_time(four_digit_time, datetoshow, len(notification_list), brightness) + def mainloop(self): + """Runs the clockface automatically""" + prev_time = 0 + while True: + if prev_time == datetime.datetime.now().strftime("%H:%M"): + time.sleep(5) + else: + if True: + print("implement me!") + + prev_time = datetime.datetime.now().strftime("%H:%M") + self.IO.clock_face([]) -def error_file(error_digit): - try: - message_to_append = "Error "+error_digit+": "+errors.errors[error_digit] - message_to_log=str(datetime.datetime.now().strftime("%d/%m/%y - %H:%M")) + " " + message_to_append + "\n" - with open("../global_files/fatal-errors.txt","a") as f: - f.write(message_to_log) - f.close() - #file gets uploaded through cronjob - except: - display_time("9999") + def stop(self): + self.time_thread.join(0) -def get_notifications(): - global notification_stream - try: - output = notification_stream.recv() - output = json.loads(output) - except: - notification_stream = create_connection(PB_ws_url) - output = notification_stream.recv() - output = json.loads(output) - - if output["type"] == "push": - if output["push"]["type"] == "mirror": - notification_title = output["push"]["title"] - notification_content = output["push"]["body"] - notification_id = output["push"]["notification_id"] - notification_type = "notification" - notification_all_content = {"title":notification_title,"content":notification_content,"id":notification_id} - elif output["push"]["type"] == "dismissal": - notification_type = "dismissal" - notification_all_content = output["push"]["notification_id"] - elif output["type"] == "nop": - notification_type = "status_test" - notification_all_content = [] - else: - with open("../global_files/logs.txt","a") as f: - f.write(str(datetime.datetime.now().strftime("%d/%m/%y - %H:%M")) + " unnown notification" + "\n") - f.close() - - return notification_type,notification_all_content + def start(self): + self.time_thread = Thread(target=self.mainloop) + self.time_thread.start() -def run_script(): - previous_time = False - while True: - strtimerightformat = str(datetime.datetime.now().time())[0:5] - four_digit_time = strtimerightformat[0:2]+strtimerightformat[3:] - if running_var.display_notif_running == False: - display_time(four_digit_time) - time.sleep(5) + def text_scroll(self, text, color=""): + self.stop() + self.IO.text_scroll(text, self.tspeed, color) + self.start() - - -################################################################################ -#Programm flow -time_thread = Thread(target=run_script) -time_thread.start() -print("UP") - -while True: - notification_type,notification_content = get_notifications() - if notification_type == "notification": - for _ in range(len(notification_list)): - if str(notification_content["id"]) in notification_list[_].values(): - del notification_list[_] - notification_list.append(notification_content) - - notification_thread = Thread(target=output_led.output_notification, args=(notification_content,)) - if running_var.display_notif_running: - error_file(8) - else: - running_var.display_notif_running = True - notification_thread.start() - - elif notification_type == "dismissal": - try: - for _ in range(len(notification_list)): - if notification_content in notification_list[_].values(): - del notification_list[_] - - except: - notification_list = [] +test = DashBoard() +# time.sleep(5) +# test.text_scroll("Hello my choupinous!") diff --git a/dashboard_api/clouds.pbm b/dashboard_api/clouds.pbm new file mode 100644 index 0000000..31d4292 --- /dev/null +++ b/dashboard_api/clouds.pbm @@ -0,0 +1,11 @@ +P1 +# Cloud - non-standard p1! +16 8 +0000000000000000 +0000001111001000 +0000001001000000 +0000010000100000 +0000010000100000 +0000001001000000 +0000001111001000 +0000000000000000 diff --git a/dashboard_api/converter.py b/dashboard_api/converter.py index 455212c..a58232a 100644 --- a/dashboard_api/converter.py +++ b/dashboard_api/converter.py @@ -1,13 +1,14 @@ from PIL import Image, ImageDraw, ImageFont import numpy as np import datetime + """Two colors: 1 main color and 1 accent color. These are labeled in the matrix as 1 and 2""" def text_converter(text, height): """Converts a text to a pixel-matrix returns: np.array((16, x))""" - font = ImageFont.truetype("verdanab.ttf", 16) + font = ImageFont.truetype("verdanab.ttf", height) size = font.getsize(text) img = Image.new("1",size,"black") draw = ImageDraw.Draw(img) @@ -17,7 +18,7 @@ def text_converter(text, height): digits = { - 1 : [[0,1,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1]], + 1 : [[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1]], 2 : [[1,1,1],[0,0,1],[1,1,1],[1,0,0],[1,1,1]], 3 : [[1,1,1],[0,0,1],[1,1,1],[0,0,1],[1,1,1]], 4 : [[1,0,1],[1,0,1],[1,1,1],[0,0,1],[0,0,1]], @@ -30,7 +31,7 @@ digits = { } ##place of numbers, invariable -digit_position = [[3,1], [3,9], [9,1], [9,9]] +digit_position = [[2,4], [2,10], [9,4], [9,10]] def time_converter(): """Converts 4-digit time to a pixel-matrix @@ -46,20 +47,46 @@ def time_converter(): number = digits[time_split[i]] pixels[x: x + 5, y: y + 3] = np.array(number) - return pixels days = np.append(np.zeros((15,16)), np.array([0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,1])).reshape((16,16)) - def date_converter(): today = datetime.datetime.today() weekday = datetime.datetime.weekday(today) # size of the reduced array according to weekday size = [2,4,6,8,10,13,16] - pixels = days #base color background + pixels = days.copy() #base color background lrow = np.append(pixels[15,:size[weekday]], [0 for i in range(16 - size[weekday])]) lrow = np.append(np.zeros((15,16)), lrow).reshape((16,16)) pixels += lrow return pixels + +def weather_converter(name): + equiv = { + "clouds" : "dashboard_api/clouds.pbm", + "sun" : "sun.pbm", + "mix" : "mix.pbm", + "rain" : "rain.pbm", + "snow" : "snow.pbm", + } + if name in equiv: + fname = equiv[name] + else: + return np.zeros((8,16)) + + f = open(fname,"r") + f.readline() + f.readline() + f.readline() + + result = np.zeros((16,16))#should be 8x16 + for i in range(8): + l = f.readline()[:-1] + for ind,bit in enumerate(l): + result[i][ind] = bit + + return result + +weather_converter("clouds") diff --git a/dashboard_api/hat/sim.py b/dashboard_api/hat/sim.py index 65ff3d2..6ea274a 100644 --- a/dashboard_api/hat/sim.py +++ b/dashboard_api/hat/sim.py @@ -3,20 +3,21 @@ import colorsys import pygame.gfxdraw import time import pygame +import numpy class UnicornHat(object): - def __init__(self, width, height, rotation_offset = 0): + def __init__(self, width, height): # Compat with old library # Set some defaults - self.rotation_offset = rotation_offset self.rotation(0) - self.pixels = [(0, 0, 0)] * width * height - self.pixel_size = 25 + self.pixel_size = 20 self.height = height self.width = width - self.window_width = width * self.pixel_size - self.window_height = height * self.pixel_size + self.pixels = numpy.zeros((self.height,self.width,3), dtype=int) + + self.window_width = self.width * self.pixel_size + self.window_height = self.height * self.pixel_size # Init pygame and off we go pygame.init() @@ -24,9 +25,15 @@ class UnicornHat(object): self.screen = pygame.display.set_mode([self.window_width, self.window_height]) self.clear() + def set_pixel(self, x, y, r, g, b): - i = (x * self.width) + y - self.pixels[i] = [int(r), int(g), int(b)] + self.pixels[x][y] = int(r), int(g), int(b) + + + def set_matrix(self, matrix): + self.pixels = matrix + self.show() + def draw(self): for event in pygame.event.get(): # User did something @@ -34,9 +41,21 @@ class UnicornHat(object): print("Exiting...") sys.exit() - for x in range(self.width): - for y in range(self.height): - self.draw_led(x, y) + for i in range(self.height): + for j in range(self.width): + self.draw_led(i,j) + + + def draw_led(self,i, j): + p = self.pixel_size + w_x = int(j * p + p / 2) + #w_y = int((self.height - 1 - y) * p + p / 2) + w_y = int(i * p + p / 2) + r = int(p / 4) + color = self.pixels[i,j,:] + pygame.gfxdraw.aacircle(self.screen, w_x, w_y, r, color) + pygame.gfxdraw.filled_circle(self.screen, w_x, w_y, r, color) + def show(self): self.clear() @@ -44,58 +63,35 @@ class UnicornHat(object): pygame.display.flip() #time.sleep(5) - def draw_led(self, x, y): - p = self.pixel_size - w_x = int(x * p + p / 2) - w_y = int((self.height - 1 - y) * p + p / 2) - r = int(p / 4) - color = self.pixels[self.index(x, y)] - pygame.gfxdraw.aacircle(self.screen, w_x, w_y, r, color) - pygame.gfxdraw.filled_circle(self.screen, w_x, w_y, r, color) def get_shape(self): return (self.width, self.height) + def brightness(self, *args): pass + def rotation(self, r): self._rotation = int(round(r/90.0)) % 3 + def clear(self): self.screen.fill((0, 0, 0)) + def get_rotation(self): return self._rotation * 90 + def set_layout(self, *args): pass - def set_pixel_hsv(self, x, y, h, s=1.0, v=1.0): - r, g, b = [int(n*255) for n in colorsys.hsv_to_rgb(h, s, v)] - self.set_pixel(x, y, r, g, b) def off(self): print("Closing window") #pygame.quit() - def index(self, x, y): - # Offset to match device rotation - rot = (self.get_rotation() + self.rotation_offset) % 360 - - if rot == 0: - xx = x - yy = y - elif rot == 90: - xx = self.height - 1 - y - yy = x - elif rot == 180: - xx = self.width - 1 - x - yy = self.height - 1 - y - elif rot == 270: - xx = y - yy = self.width - 1 - x - return (xx * self.width) + yy """ @@ -105,11 +101,10 @@ class UnicornHat(object): # Unicornhat HD seems to be the other way around (not that there's anything wrong with that), so we rotate it 180° # unicornhathd = UnicornHatSim(16, 16, 180) -twohats = UnicornHatSim(16, 32, 180) - - -for i in range(16): - twohats.set_pixel(i,i, 200,200,200) - twohats.show() - time.sleep(1) """ +# twohats = UnicornHat(32, 16) +# +# for i in range(16): +# twohats.set_pixel(i,i, 200,200,200) +# twohats.show() +# time.sleep(1) diff --git a/dashboard_api/hat/unicorn.py b/dashboard_api/hat/unicorn.py index 52ee423..149a8e4 100644 --- a/dashboard_api/hat/unicorn.py +++ b/dashboard_api/hat/unicorn.py @@ -26,7 +26,7 @@ class UnicornHat(object): self.rotation = 1 self.brightness = 0.5 - self.buffer = numpy.zeros((self.WIDTH,self.HEIGHT,3), dtype=int) + self.buffer = numpy.zeros((self.HEIGHT,self.WIDTH,3), dtype=int) @@ -85,6 +85,12 @@ class UnicornHat(object): """ self.buffer[x][y] = r, g, b + + def set_matrix(self, matrix): + self.buffer = matrix + self.show() + + def set_pixel_hsv(self, x, y, h, s=1.0, v=1.0): """set a single pixel to a colour using HSV. :param x: Horizontal position from 0 to 15 diff --git a/dashboard_api/led_out.py b/dashboard_api/led_out.py index 2dbff1f..e6e89bb 100644 --- a/dashboard_api/led_out.py +++ b/dashboard_api/led_out.py @@ -1,13 +1,12 @@ import time import numpy as np -from threading import Thread -import converter +from dashboard_api import converter try: - from hat import unicorn as HAT + from dashboard_api.hat import unicorn as HAT except ImportError: print("Using the simulator") - from hat import sim as HAT + from dashboard_api.hat import sim as HAT @@ -17,62 +16,90 @@ class OutputHandler(): self.width = width self.height = height self.output = HAT.UnicornHat(width, height) - self.threads = [] - self.running = False - self.primary = [200, 200, 200] + self.primary = [230, 230, 230] self.secondary = [10, 200, 10] self.red = [200, 10, 10] + self.weather_string = "" + self.weather_matrix = [] + def set_matrix(self, matrix, quadrant, colors = []): + """assumes 1 for primary, 2 for secondary color (everything beyond is treated as an error) + quadrant: 1,2,3,4 : 4|1 + ___ + 3|2 + """ - def stop(self): - for t in threads: - t.stop() - self.output.off() + # reshape to the main size: (eg 32x16) (always aligns the given matrix on top left.) + + c1 = self.primary + c2 = self.secondary + c3 = self.red + if len(colors) == 1: + c1 = colors[0] + if len(colors) == 2: + c2 = colors[1] + if len(colors) == 3: + c3 = colors[2] + if len(colors) > 3: + print("Too many colors") + + result = np.zeros((self.height, self.width)) + if quadrant == 1: + result[:matrix.shape[0], self.width-matrix.shape[1]:] = matrix + elif quadrant == 2: + result[self.height-matrix.shape[0]:, self.width-matrix.shape[1]:] = matrix + elif quadrant == 3: + result[self.height-matrix.shape[0]:, :matrix.shape[1]] = matrix + else: # 4 or more + result[:matrix.shape[0], :matrix.shape[1]] = matrix + + # add depth (rgb values) + r3 = np.zeros((self.height,self.width,3),dtype=int) + for i in range(self.height): + for j in range(self.width): + t = int(result[i, j]) + if t == 0: + r3[i, j, :] = [0,0,0] + elif t == 1: + r3[i, j, :] = c1 + elif t == 2: + r3[i, j, :] = c2 + else: + r3[i, j, :] = c3 + self.output.set_matrix(r3) - def run(self, func, args): - self.running = True - t = Thread(target=func,args=tuple(args)) - t.start() - self.threads.append(t) - - - def set_matrix(self, matrix): - """assumes 1 for primary, 2 for secondary color""" - - for x in range(matrix.shape[0]): - for y in range(matrix.shape[1]): - if matrix[x,y] == 1: - self.output.set_pixel(x,y,self.primary[0],self.primary[1],self.primary[2]) - elif matrix[x,y] == 2: - self.output.set_pixel(x,y,self.secondary[0],self.secondary[1],self.secondary[2]) - self.output.show() - - def clock_face(self): + def clock_face(self,weather): hour = converter.time_converter() day = converter.date_converter() + face1 = hour + day - self.set_matrix(hour + day) - self.running = False + if self.weather_matrix == [] or weather != self.weather_string: + self.weather_matrix = converter.weather_converter("clouds") + self.weather_string = weather + + face2 = self.weather_matrix + + face = np.zeros((max(face1.shape[0],face2.shape[0]),face1.shape[1]+face2.shape[1])) + face[:face1.shape[0],:face1.shape[1]] = face1 + face[:face2.shape[0],face1.shape[1]:] = face2 + self.set_matrix(face, 4) - def text_scroll(self, text, speed): - pixels = converter.text_converter(text,16) + def text_scroll(self, text, speed, color): + pixels = converter.text_converter(text, 12) sleep_time = 1 / speed - frames = pixels.shape[1] - 16 + if color == "": + colors = [] + else: + colors = [color] + + frames = pixels.shape[1] - self.width if frames <= 0: frames = 1 + for i in range(frames): - #self.output.clear() - #self.set_matrix(np.zeros((16,16))) - self.set_matrix(pixels[:,i:16+i]) + visible = pixels[:,i:self.width+i] + self.set_matrix(visible,4,colors) time.sleep(sleep_time) - - - self.clock_face() - - -test = OutputHandler(16,32) -test.clock_face() -test.text_scroll("Hello world. How are you?",4) diff --git a/main.py b/main.py index e9b35da..403e42a 100644 --- a/main.py +++ b/main.py @@ -325,15 +325,14 @@ class ChatBot(): def bot_tell_joke(self, params): """Tells you the top joke on r/jokes""" - if len(params) == 0: - number = 1 - else: - params_sorted = self.match_reddit_params(params) - if params_sorted != None: - if len(params_sorted) >= 1: - number = params_sorted[0] - if len(params_sorted) > 1: - self.telegram.send_message("Please only specify one argument: the number of jokes") + + number = 1 + params_sorted = self.match_reddit_params(params) + if params_sorted != None: + if len(params_sorted) >= 1: + number = params_sorted[0] + if len(params_sorted) > 1: + self.telegram.send_message("Please only specify one argument: the number of jokes") joke = reddit.get_random_rising("jokes", number, "text") @@ -395,4 +394,4 @@ class ChatBot(): ####################################################################### -bot = ChatBot("ChatterBot", version="1.03") +bot = ChatBot("ChatterBot", version="1.1") diff --git a/persistence/rw.py b/persistence/rw.py index 888913c..664549a 100644 --- a/persistence/rw.py +++ b/persistence/rw.py @@ -4,7 +4,7 @@ import os class Variables(): """""" - def __init__(self,savefile_path, init_path): + def __init__(self, savefile_path, init_path): self.path = savefile_path self.init_path = init_path self.last_action = ""