diff --git a/notiframe/.vscode/settings.json b/notiframe/.vscode/settings.json new file mode 100644 index 0000000..6805dff --- /dev/null +++ b/notiframe/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python.linting.pylintEnabled": true, + "python.linting.enabled": true, + "python.pythonPath": "/usr/bin/python2" +} \ No newline at end of file diff --git a/notiframe/NotiFrame.py b/notiframe/NotiFrame.py deleted file mode 100755 index f2246b7..0000000 --- a/notiframe/NotiFrame.py +++ /dev/null @@ -1,113 +0,0 @@ -from PIL import Image, ImageDraw, ImageFont, ImageOps -import re -import time -import widgets.widget as widget -import argparse -import os -import toml -try: - import drivers.epd7in5b - testMode = False -except: - testMode = True - -mydir = os.path.dirname(os.path.abspath(__file__)) - -parser = argparse.ArgumentParser(description="NotiFrame display") -parser.add_argument('-t', '--test', help='enable test mode', action='store_true') -args = parser.parse_args() -if args.test: - testMode = True - -print("RUNNING IN TESTMODE: "+str(testMode)) - -if not testMode: - epd = epd7in5b.EPD() - epd.init() - -def convert(input): - if isinstance(input, dict): - return dict((convert(key), convert(value)) for key, value in input.iteritems()) - elif isinstance(input, list): - return [convert(element) for element in input] - elif isinstance(input, unicode): - return input.encode('utf-8') - else: - return input - -uni_config = toml.load(os.path.join(mydir, 'config.toml')) -config = convert(uni_config) - -config2 = {'resWidth': 640, 'resHeight': 384, 'cellsWidth': 3, 'cellsHeight': 3, 'widgets': [ -{'type': 'image', 'posX': 0, 'posY': 0, 'width': 3, 'height': 3, 'bwStyle': 'mono', 'scaleMode': 'fill', 'filename': 'forest.jpg'}, -{'type': 'trello', 'posX': 0, 'posY': 0, 'width': 1, 'height': 3, 'board': 'Organisation', 'list': 'Plans'}, -{'type': 'trello', 'posX': 1, 'posY': 0, 'width': 2, 'height': 3, 'board': 'E-paper', 'list': 'To Do:'} -]} - -#print(toml.dumps(config)) -#print(config) -#print(config2) - -cwidth = int(round(int(config['resWidth'])/int(config['cellsWidth']))) -cheight = int(round(int(config['resHeight'])/int(config['cellsHeight']))) - -image_yellow = Image.new('1', (config['resWidth'], config['resHeight']), 255) # 255: clear the frame -draw_yellow = ImageDraw.Draw(image_yellow) -image_black = Image.new('1', (config['resWidth'], config['resHeight']), 255) # 255: clear the frame -draw_black = ImageDraw.Draw(image_black) - -#str1 = open("text.txt", "r").read() -#str2 = open("text.txt", "r").read() -#str3 = open("text.txt", "r").read() -#strList = [] -#strList.append(str1) -#strList.append(str2) -#strList.append(str3) - -def initWidgets(): - widgetList = [] - for widg_conf in config['widgets']: - if widg_conf['type'] == 'image': - widgetList.append(widget.ImageWidget(cwidth, cheight, (widg_conf['posX'], widg_conf['posY']), - (widg_conf['width'], widg_conf['height']), widg_conf['bwStyle'], widg_conf['scaleMode'], - os.path.join(mydir, os.path.join('widgets/resources/images/', widg_conf['filename'])))) - if widg_conf['type'] == 'trello': - widgetList.append(widget.TrelloWidget(cwidth, cheight, (widg_conf['posX'], widg_conf['posY']), - (widg_conf['width'], widg_conf['height']), widg_conf['board'], widg_conf['list'])) - - #widgetList.append(widget.ImageWidget(cwidth, cheight, (0, 0), (3, 3), "mono", "fill", os.path.join(mydir, os.path.join('widgets/resources/images/', 'forest.jpg')))) - #widgetList.append(widget.TrelloWidget(cwidth, cheight, (0, 0), (1, 3), "Organisation", "Plans")) - #widgetList.append(widget.TrelloWidget(cwidth, cheight, (1, 0), (2, 3), "E-paper", "Done")) - #widgetList.append(widget.ImageWidget(cwidth, cheight, (1, 2), (2, 1), "mono", "fill", "forest.jpg")) - #widgetList.append(widget.TextWidget(cwidth, cheight, (0, 0), (1, 3), str2)) - #widgetList.append(widget.TextWidget(cwidth, cheight, (0, 0), (2, 3), str1)) - #widgetList.append(widget.ImageWidget(cwidth, cheight, (1, 0), (1, 3), "mono", "fill", "g.jpg")) - return widgetList - -def drawWidget(w): - coordX = w.cellX*cwidth - coordY = w.cellY*cheight - image_black.paste(im=w.image_black, mask=w.mask_black, box=(coordX, coordY)) - image_yellow.paste(w.image_yellow, (coordX, coordY)) - -def render(index): - if index is not 0: - for widg in widgetList: - widg.updateWidget() - draw_black.rectangle(xy=((0,0), image_black.size), fill=255) - draw_yellow.rectangle(xy=((0,0), image_yellow.size), fill=255) - for widg in widgetList: - drawWidget(widg) - if not testMode: - epd.display_frame(epd.get_frame_buffer(image_black),epd.get_frame_buffer(image_yellow)) - else: - image_black.save(os.path.join(mydir, 'test/imgBlack.bmp')) - image_yellow.save(os.path.join(mydir, 'test/imgYellow.bmp')) - -widgetList = initWidgets() -i = 0 -while(True): - print("RENDERING "+str(i)) - render(i) - i = (i + 1) - #time.sleep(10) diff --git a/notiframe/fetcher/Fetcher.py b/notiframe/fetcher/Fetcher.py new file mode 100644 index 0000000..8ed329d --- /dev/null +++ b/notiframe/fetcher/Fetcher.py @@ -0,0 +1,54 @@ +from PIL import Image, ImageDraw, ImageFont, ImageOps +import re +import time +#import widgets.widget as widget +import argparse +import os +import toml + +import requests +from io import BytesIO + +try: + import drivers.epd7in5b + testMode = False +except: + testMode = True + +canvas_url_black = 'http://0.0.0.0:5000/display' + +mydir = os.path.dirname(os.path.abspath(__file__)) + +parser = argparse.ArgumentParser(description="NotiFrame display") +parser.add_argument('-t', '--test', help='enable test mode', action='store_true') +args = parser.parse_args() +if args.test: + testMode = True + +print("RUNNING IN TESTMODE: "+str(testMode)) +if not testMode: + epd = drivers.epd7in5b.EPD() + epd.init() + +def render(index): + while(True): + print("requesting display image") + try: + blk = requests.get(canvas_url_black) + #ylw = requests.get(urlylw) + image_black = Image.open(BytesIO(blk.content)) + image_yellow = None + #image_yellow = Image.open(BytesIO(ylw.content)) + #draw_black.rectangle(xy=((0,0), image_black.size), fill=255) + #draw_yellow.rectangle(xy=((0,0), image_yellow.size), fill=255) + if not testMode: + epd.display_frame(epd.get_frame_buffer(image_black.rotate(180)),epd.get_frame_buffer(image_yellow.rotate(180))) + else: + image_black.save(os.path.join(mydir, 'test/imgBlackDisplayClient.bmp')) + #image_yellow.save(os.path.join(mydir, 'test/imgYellow.bmp')) + except: + print("failed to load display image") + i =+ 1 + time.sleep(20) +i = 0 +render(i) \ No newline at end of file diff --git a/notiframe/drivers/__init__.py b/notiframe/fetcher/drivers/__init__.py similarity index 100% rename from notiframe/drivers/__init__.py rename to notiframe/fetcher/drivers/__init__.py diff --git a/notiframe/drivers/epd7in5b.py b/notiframe/fetcher/drivers/epd7in5b.py similarity index 96% rename from notiframe/drivers/epd7in5b.py rename to notiframe/fetcher/drivers/epd7in5b.py index b275fde..94d249a 100755 --- a/notiframe/drivers/epd7in5b.py +++ b/notiframe/fetcher/drivers/epd7in5b.py @@ -25,7 +25,7 @@ # import epdif -import Image +from PIL import Image import RPi.GPIO as GPIO # Display resolution @@ -142,7 +142,7 @@ class EPD: self.digital_write(self.reset_pin, GPIO.LOW) # module reset self.delay_ms(200) self.digital_write(self.reset_pin, GPIO.HIGH) - self.delay_ms(200) + self.delay_ms(200) def get_frame_buffer(self, image): buf = [0xFF] * (self.width * self.height / 8) @@ -175,7 +175,7 @@ class EPD: temp3 = 0x00 #black else: temp3 = 0x03 #white - + temp3 = (temp3 << 4) & 0xFF temp1 = (temp1 << 1) & 0xFF temp2 = (temp2 << 1) & 0xFF @@ -201,4 +201,3 @@ class EPD: self.send_data(0xa5) ### END OF FILE ### - diff --git a/notiframe/drivers/epdif.py b/notiframe/fetcher/drivers/epdif.py similarity index 100% rename from notiframe/drivers/epdif.py rename to notiframe/fetcher/drivers/epdif.py diff --git a/notiframe/fetcher/test/imgBlackDisplayClient.bmp b/notiframe/fetcher/test/imgBlackDisplayClient.bmp new file mode 100644 index 0000000..4419d99 Binary files /dev/null and b/notiframe/fetcher/test/imgBlackDisplayClient.bmp differ diff --git a/notiframe/test/card_image.bmp b/notiframe/test/card_image.bmp deleted file mode 100755 index 931fa42..0000000 Binary files a/notiframe/test/card_image.bmp and /dev/null differ diff --git a/notiframe/test/imgBlack.bmp b/notiframe/test/imgBlack.bmp deleted file mode 100644 index fe5d3d9..0000000 Binary files a/notiframe/test/imgBlack.bmp and /dev/null differ diff --git a/notiframe/test/mask.bmp b/notiframe/test/mask.bmp deleted file mode 100755 index fbe8a88..0000000 Binary files a/notiframe/test/mask.bmp and /dev/null differ diff --git a/notiframe/webserver/Notiframe.py b/notiframe/webserver/Notiframe.py new file mode 100644 index 0000000..6fe70aa --- /dev/null +++ b/notiframe/webserver/Notiframe.py @@ -0,0 +1,239 @@ +from flask import Flask, render_template, request, redirect, url_for, send_file +import toml +import os + +from PIL import Image, ImageDraw, ImageFont, ImageOps +import re +import time +import widgets.widget as widget +import argparse + +app = Flask(__name__) + +mydir = os.path.dirname(os.path.abspath(__file__)) +# print(mydir) +###mydir = os.path.join(mydir, '../') +# print(mydir) + +def convert(input): + if isinstance(input, dict): + return dict((convert(key), convert(value)) for key, value in input.iteritems()) + elif isinstance(input, list): + return [convert(element) for element in input] + elif isinstance(input, unicode): + return input.encode('utf-8') + else: + return input + +#################################################### +#################################################### +#################################################### + +uni_config = toml.load(os.path.join(mydir, 'config.toml')) +config = convert(uni_config) + +config2 = {'resWidth': 640, 'resHeight': 384, 'cellsWidth': 3, 'cellsHeight': 3, 'widgets': [ +{'type': 'image', 'posX': 0, 'posY': 0, 'width': 3, 'height': 3, 'bwStyle': 'mono', 'scaleMode': 'fill', 'filename': 'forest.jpg'}, +{'type': 'trello', 'posX': 0, 'posY': 0, 'width': 1, 'height': 3, 'board': 'Organisation', 'list': 'Plans'}, +{'type': 'trello', 'posX': 1, 'posY': 0, 'width': 2, 'height': 3, 'board': 'E-paper', 'list': 'To Do:'} +]} + +cwidth = int(round(int(config['resWidth'])/int(config['cellsWidth']))) +cheight = int(round(int(config['resHeight'])/int(config['cellsHeight']))) + +image_yellow = Image.new('1', (config['resWidth'], config['resHeight']), 255) # 255: clear the frame +draw_yellow = ImageDraw.Draw(image_yellow) +image_black = Image.new('1', (config['resWidth'], config['resHeight']), 255) # 255: clear the frame +draw_black = ImageDraw.Draw(image_black) + +#################################################### +#################################################### +#################################################### + +# HOMEPAGE +@app.route('/home', methods=['GET', 'POST']) +def home(): # Toml config passed to html page via two dicts. + # Form data passed back to webserver as JSON. + config = read_config() + widgLists, sysList = prep_dict_for_web(config) + + if request.method == 'POST': + jsonData = request.get_json() + print(jsonData) + jsonData = convert(jsonData) + + # update system variables + for key in sysList: + print("%" + jsonData[key] + "$") + if jsonData[key]: + if isInt(jsonData[key]): + #print(request.form[key] + " is int") + sysList[key] = int(jsonData[key]) + else: + sysList[key] = jsonData[key] + # update widget variables + # for i in range(len(widgLists)): + # for key in widgLists[i]: + # if request.form[key+str(i)]: + # widgLists[i][key] = request.form[key+str(i)] + for i in range(len(widgLists)): + for key in widgLists[i]: + # print(request.form[key+str(i)]) + if jsonData[key + str(i)]: + if isInt(jsonData[key + str(i)]): + #print(request.form[key+str(i)] + " is int") + widgLists[i][key] = int(jsonData[key + str(i)]) + else: + widgLists[i][key] = jsonData[key + str(i)] + + update_config(widgLists, sysList) + + widgTypes = widgetTypes() + + return render_template('home.html', title='Overview', widgLists=widgLists, sysList=sysList, widgTypes=widgTypes) + +@app.route('/newWidget', methods=['GET', 'POST']) +def newWidget(): # + #if request.method == 'GET': + widgType = request.args['type'] + conf_defaults = pull_default_conf(widgType) + #import pdb; pdb.set_trace() + if request.method == 'POST': + #widgType = request.form['ty'] + jsonData = request.get_json() + print(jsonData) + jsonData = convert(jsonData) + + return render_template('newWidget.html', title='Overview', conf_defaults=conf_defaults) + + +@app.route('/display', methods=['GET', 'POST']) +def display(): + location = 'test/imgBlack.bmp' + updateWidgets() + return send_file(location) + + +def pull_default_conf(widgType=None): + uni_config = toml.load(os.path.join(mydir, 'config.toml')) + config = convert(uni_config) + + conf_dict = { + 'trello': { + 'board': {'default': '', 'datatype': 'text'}, + 'list': {'default': '', 'datatype': 'text'}, + 'width': {'default': 1, 'datatype': 'number', 'min': 1, 'max': config['cellsWidth']}, + 'height': {'default': 1, 'datatype': 'number', 'min': 1, 'max': config['cellsHeight']}, + 'posX': {'default': 0, 'datatype': 'number', 'min': 0, 'max': config['cellsWidth']-1}, + 'posY': {'default': 0, 'datatype': 'number', 'min': 0, 'max': config['cellsHeight']-1} + }, + 'text': { + 'text': {'default': 'abcdefghijklmnopqrstuvwxyz', 'datatype': 'text'}, + 'width': {'default': 1, 'datatype': 'number', 'min': 1, 'max': config['cellsWidth']}, + 'height': {'default': 1, 'datatype': 'number', 'min': 1, 'max': config['cellsHeight']}, + 'posX': {'default': 0, 'datatype': 'number', 'min': 0, 'max': config['cellsWidth']-1}, + 'posY': {'default': 0, 'datatype': 'number', 'min': 0, 'max': config['cellsHeight']-1} + }, + 'image': { + 'file': {'default': 'img.jpg', 'datatype': 'text'}, + 'bwMode': {'default': 'mono', 'datatype': 'text'}, + 'scaleMode': {'default': 'fill', 'datatype': 'text'}, + 'width': {'default': 1, 'datatype': 'number', 'min': 1, 'max': config['cellsWidth']}, + 'height': {'default': 1, 'datatype': 'number', 'min': 1, 'max': config['cellsHeight']}, + 'posX': {'default': 0, 'datatype': 'number', 'min': 0, 'max': config['cellsWidth']-1}, + 'posY': {'default': 0, 'datatype': 'number', 'min': 0, 'max': config['cellsHeight']-1} + } + } + + if widgType != None: + conf_dict = conf_dict.get(widgType) + + return conf_dict + +def widgetTypes(): + dflt_conf = pull_default_conf() + widgTypes = [] + for key in dflt_conf.keys(): + widgTypes.append(key) + return widgTypes + +def isInt(s): + try: + int(s) + return True + except ValueError: + return False + + +def prep_dict_for_web(config): + widgLists = config['widgets'] # list of dicts + sysList = {} # dict + for key in config: + if key != 'widgets': + sysList[key] = config[key] + return widgLists, sysList + + +def read_config(): + uni_config = toml.load(os.path.join(mydir, 'config.toml')) + config = convert(uni_config) + # print(config) + # config = {'widgets': [ + #{'width': 3, 'posX': 0, 'posY': 0, 'scaleMode': 'fill', 'bwStyle': 'mono', 'type': 'image', 'filename': 'forest.jpg', 'height': 3}, + #{'list': 'Plans', 'height': 3, 'width': 1, 'board': 'Organisation', 'posX': 0, 'posY': 0, 'type': 'trello'}, + #{'list': 'To Do:', 'height': 3, 'width': 2, 'board': 'E-paper', 'posX': 1, 'posY': 0, 'type': 'trello'} + # ], 'cellsHeight': 3, 'resHeight': 384, 'resWidth': 640, 'cellsWidth': 3} + return config + + +def update_config(widgLists, sysList): + config = {'widgets': widgLists} + for key in sysList: + config[key] = sysList[key] + convert(config) + path = os.path.join(mydir, 'config.toml') + with open(path, "w+") as config_file: + config_file.write(toml.dumps(config)) + +#################################################### +#################################################### +#################################################### + +def initWidgets(): + widgetList = [] + for widg_conf in config['widgets']: + if widg_conf['type'] == 'image': + widgetList.append(widget.ImageWidget(cwidth, cheight, (widg_conf['posX'], widg_conf['posY']), + (widg_conf['width'], widg_conf['height']), widg_conf['bwStyle'], widg_conf['scaleMode'], + os.path.join(mydir, os.path.join('widgets/resources/images/', widg_conf['filename'])))) + if widg_conf['type'] == 'trello': + widgetList.append(widget.TrelloWidget(cwidth, cheight, (widg_conf['posX'], widg_conf['posY']), + (widg_conf['width'], widg_conf['height']), widg_conf['board'], widg_conf['list'])) + return widgetList + +def drawWidget(w): + coordX = w.cellX*cwidth + coordY = w.cellY*cheight + image_black.paste(im=w.image_black, mask=w.mask_black, box=(coordX, coordY)) + image_yellow.paste(w.image_yellow, (coordX, coordY)) + +def updateWidgets(): + for widg in widgetList: + print(" updating trello widget") + widg.updateWidget() + print("") + draw_black.rectangle(xy=((0,0), image_black.size), fill=255) + draw_yellow.rectangle(xy=((0,0), image_yellow.size), fill=255) + for widg in widgetList: + drawWidget(widg) + image_black.save(os.path.join(mydir, 'test/imgBlack.bmp')) + image_yellow.save(os.path.join(mydir, 'test/imgYellow.bmp')) + +widgetList = initWidgets() + +#################################################### +#################################################### +#################################################### + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0') diff --git a/notiframe/config.toml b/notiframe/webserver/config.toml similarity index 73% rename from notiframe/config.toml rename to notiframe/webserver/config.toml index ee9157b..ae65947 100755 --- a/notiframe/config.toml +++ b/notiframe/webserver/config.toml @@ -22,20 +22,11 @@ posY = 0 type = "trello" [[widgets]] -list = "To Do:" -height = 2 +list = "Active:" +height = 3 width = 2 board = "E-paper" posX = 1 posY = 0 type = "trello" -[[widgets]] -list = "Tasks" -height = 1 -width = 2 -board = "Organisation" -posX = 1 -posY = 2 -type = "trello" - diff --git a/notiframe/webserver/templates/home.html b/notiframe/webserver/templates/home.html index ebe47db..d4e672f 100644 --- a/notiframe/webserver/templates/home.html +++ b/notiframe/webserver/templates/home.html @@ -8,6 +8,8 @@