1
0
mirror of https://github.com/javierpena/eink-calendar.git synced 2025-08-10 21:52:01 +02:00

First upload

This commit is contained in:
Javier Peña
2021-01-16 15:29:10 +01:00
parent c14a21137c
commit 4f82ac0cbd
23 changed files with 1373 additions and 15 deletions

38
drivers/caldavprovider.py Normal file
View File

@@ -0,0 +1,38 @@
from datetime import datetime
import caldav
import icalendar
import pytz
import urllib3
urllib3.disable_warnings()
class CalDavProvider():
def __init__(self, username, password):
self.tz = pytz.timezone('Europe/Madrid')
self.username = username
self.password = password
def get_calendar(self, url, date_start, date_end):
# print("%s %s" % (date_start, date_end))
client = caldav.DAVClient(url=url, username=self.username, password=self.password, ssl_verify_cert=False)
calendar = caldav.Calendar(client=client, url=url)
returned_events = []
events_found = calendar.date_search(
start=date_start, end=date_end,
compfilter='VEVENT', expand=True)
if events_found:
for event in events_found:
cal = icalendar.Calendar.from_ical(event.data)
single_event = {}
for event in cal.walk('vevent'):
date_start = event.get('dtstart')
duration = event.get('duration')
summary = event.get('summary')
single_event['event_start'] = date_start.dt.astimezone(self.tz)
single_event['event_end'] = (date_start.dt + duration.dt).astimezone(self.tz)
single_event['event_title'] = summary
returned_events.append(single_event)
return returned_events

25
drivers/einkdriver.py Normal file
View File

@@ -0,0 +1,25 @@
from . import epd7in5bc
class EinkDriver():
def __init__(self, xres, yres):
self.epd = epd7in5bc.EPD()
self.epd.init()
self.epd.Clear()
self.xres = xres
self.yres = yres
# image1: black/white image
# image2: black/yellow image
def display(self, image1=None, image2=None):
self.epd.init()
self.epd.display(self.epd.getbuffer(image1), self.epd.getbuffer(image2))
self.epd.sleep()
def end(self):
print("Ending e-ink driver")
self.epd.init()
self.epd.Clear()
self.epd.sleep()
self.epd.Dev_exit()
print("e-ink driver finished")

197
drivers/epd7in5bc.py Normal file
View File

@@ -0,0 +1,197 @@
# *****************************************************************************
# * | File : epd7in5bc.py
# * | Author : Waveshare team
# * | Function : Electronic paper driver
# * | Info :
# *----------------
# * | This version: V4.0
# * | Date : 2019-06-20
# # | Info : python demo
# -----------------------------------------------------------------------------
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import logging
from . import epdconfig
# Display resolution
EPD_WIDTH = 640
EPD_HEIGHT = 384
class EPD:
def __init__(self):
self.reset_pin = epdconfig.RST_PIN
self.dc_pin = epdconfig.DC_PIN
self.busy_pin = epdconfig.BUSY_PIN
self.cs_pin = epdconfig.CS_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
# Hardware reset
def reset(self):
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
epdconfig.digital_write(self.reset_pin, 0)
epdconfig.delay_ms(10)
epdconfig.digital_write(self.reset_pin, 1)
epdconfig.delay_ms(200)
def send_command(self, command):
epdconfig.digital_write(self.dc_pin, 0)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([command])
epdconfig.digital_write(self.cs_pin, 1)
def send_data(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.spi_writebyte([data])
epdconfig.digital_write(self.cs_pin, 1)
def send_data_array(self, data):
epdconfig.digital_write(self.dc_pin, 1)
epdconfig.digital_write(self.cs_pin, 0)
epdconfig.SPI.writebytes2(data)
epdconfig.digital_write(self.cs_pin, 1)
def ReadBusy(self):
logging.debug("e-Paper busy")
while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy
epdconfig.delay_ms(100)
logging.debug("e-Paper busy release")
def init(self):
if (epdconfig.module_init() != 0):
return -1
self.reset()
self.send_command(0x01) # POWER_SETTING
self.send_data(0x37)
self.send_data(0x00)
self.send_command(0x00) # PANEL_SETTING
self.send_data(0xCF)
self.send_data(0x08)
self.send_command(0x30) # PLL_CONTROL
self.send_data(0x3A) # PLL: 0-15:0x3C, 15+:0x3A
self.send_command(0x82) # VCM_DC_SETTING
self.send_data(0x28) #all temperature range
self.send_command(0x06) # BOOSTER_SOFT_START
self.send_data(0xc7)
self.send_data(0xcc)
self.send_data(0x15)
self.send_command(0x50) # VCOM AND DATA INTERVAL SETTING
self.send_data(0x77)
self.send_command(0x60) # TCON_SETTING
self.send_data(0x22)
self.send_command(0x65) # FLASH CONTROL
self.send_data(0x00)
self.send_command(0x61) # TCON_RESOLUTION
self.send_data(self.width >> 8) # source 640
self.send_data(self.width & 0xff)
self.send_data(self.height >> 8) # gate 384
self.send_data(self.height & 0xff)
self.send_command(0xe5) # FLASH MODE
self.send_data(0x03)
return 0
def getbuffer(self, image):
img = image
imwidth, imheight = img.size
if(imwidth == self.width and imheight == self.height):
img = img.convert('1')
elif(imwidth == self.height and imheight == self.width):
img = img.rotate(90, expand=True).convert('1')
else:
logging.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height))
# return a blank buffer
return [0x00] * (int(self.width/8) * self.height)
buf = bytearray(img.tobytes('raw'))
return buf
def display(self, imageblack, imagered):
buf = [0x00] * ((self.width // 2) * self.height)
for i in range(0, int(self.width / 8 * self.height)):
temp1 = imageblack[i]
temp2 = imagered[i]
j = 0
while (j < 8):
if ((temp2 & 0x80) == 0x00):
temp3 = 0x04 #red
elif ((temp1 & 0x80) == 0x00):
temp3 = 0x00 #black
else:
temp3 = 0x03 #white
temp3 = (temp3 << 4) & 0xFF
temp1 = (temp1 << 1) & 0xFF
temp2 = (temp2 << 1) & 0xFF
j += 1
if((temp2 & 0x80) == 0x00):
temp3 |= 0x04 #red
elif ((temp1 & 0x80) == 0x00):
temp3 |= 0x00 #black
else:
temp3 |= 0x03 #white
temp1 = (temp1 << 1) & 0xFF
temp2 = (temp2 << 1) & 0xFF
buf[i * 4 + (j//2)] = temp3
j += 1
self.send_command(0x10)
self.send_data_array(buf)
self.send_command(0x04) # POWER ON
self.ReadBusy()
self.send_command(0x12) # display refresh
epdconfig.delay_ms(100)
self.ReadBusy()
def Clear(self):
buf = [0x33] * ((self.width // 2) * self.height)
self.send_command(0x10)
self.send_data_array(buf)
self.send_command(0x04) # POWER ON
self.ReadBusy()
self.send_command(0x12) # display refresh
epdconfig.delay_ms(100)
self.ReadBusy()
def sleep(self):
self.send_command(0x02) # POWER_OFF
self.ReadBusy()
self.send_command(0x07) # DEEP_SLEEP
self.send_data(0XA5)
def Dev_exit(self):
epdconfig.module_exit()
### END OF FILE ###

156
drivers/epdconfig.py Normal file
View File

@@ -0,0 +1,156 @@
# /*****************************************************************************
# * | File : epdconfig.py
# * | Author : Waveshare team
# * | Function : Hardware underlying interface
# * | Info :
# *----------------
# * | This version: V1.0
# * | Date : 2019-06-21
# * | Info :
# ******************************************************************************
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import os
import logging
import sys
import time
class RaspberryPi:
# Pin definition
RST_PIN = 17
DC_PIN = 25
CS_PIN = 8
BUSY_PIN = 24
def __init__(self):
import spidev
import RPi.GPIO
self.GPIO = RPi.GPIO
# SPI device, bus = 0, device = 0
self.SPI = spidev.SpiDev(0, 0)
def digital_write(self, pin, value):
self.GPIO.output(pin, value)
def digital_read(self, pin):
return self.GPIO.input(pin)
def delay_ms(self, delaytime):
time.sleep(delaytime / 1000.0)
def spi_writebyte(self, data):
self.SPI.writebytes(data)
def module_init(self):
self.GPIO.setmode(self.GPIO.BCM)
self.GPIO.setwarnings(False)
self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)
self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)
self.SPI.max_speed_hz = 4000000
self.SPI.mode = 0b00
return 0
def module_exit(self):
logging.debug("spi end")
self.SPI.close()
logging.debug("close 5V, Module enters 0 power consumption ...")
self.GPIO.setmode(self.GPIO.BCM)
self.GPIO.output(self.RST_PIN, 0)
self.GPIO.output(self.DC_PIN, 0)
self.GPIO.cleanup()
class JetsonNano:
# Pin definition
RST_PIN = 17
DC_PIN = 25
CS_PIN = 8
BUSY_PIN = 24
def __init__(self):
import ctypes
find_dirs = [
os.path.dirname(os.path.realpath(__file__)),
'/usr/local/lib',
'/usr/lib',
]
self.SPI = None
for find_dir in find_dirs:
so_filename = os.path.join(find_dir, 'sysfs_software_spi.so')
if os.path.exists(so_filename):
self.SPI = ctypes.cdll.LoadLibrary(so_filename)
break
if self.SPI is None:
raise RuntimeError('Cannot find sysfs_software_spi.so')
import Jetson.GPIO
self.GPIO = Jetson.GPIO
def digital_write(self, pin, value):
self.GPIO.output(pin, value)
def digital_read(self, pin):
return self.GPIO.input(self.BUSY_PIN)
def delay_ms(self, delaytime):
time.sleep(delaytime / 1000.0)
def spi_writebyte(self, data):
self.SPI.SYSFS_software_spi_transfer(data[0])
def module_init(self):
self.GPIO.setmode(self.GPIO.BCM)
self.GPIO.setwarnings(False)
self.GPIO.setup(self.RST_PIN, self.GPIO.OUT)
self.GPIO.setup(self.DC_PIN, self.GPIO.OUT)
self.GPIO.setup(self.CS_PIN, self.GPIO.OUT)
self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN)
self.SPI.SYSFS_software_spi_begin()
return 0
def module_exit(self):
logging.debug("spi end")
self.SPI.SYSFS_software_spi_end()
logging.debug("close 5V, Module enters 0 power consumption ...")
self.GPIO.output(self.RST_PIN, 0)
self.GPIO.output(self.DC_PIN, 0)
self.GPIO.cleanup()
if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'):
implementation = RaspberryPi()
else:
implementation = JetsonNano()
for func in [x for x in dir(implementation) if not x.startswith('_')]:
setattr(sys.modules[__name__], func, getattr(implementation, func))
### END OF FILE ###

52
drivers/gpiodriver.py Normal file
View File

@@ -0,0 +1,52 @@
from datetime import datetime
import RPi.GPIO as GPIO
import time
row_channels = [6, 5, 19, 13]
column_channel = 26
class GPIODriver():
def __init__(self):
GPIO.setmode(GPIO.BCM)
GPIO.setup(row_channels, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(column_channel, GPIO.OUT)
def wait_for_keypress(self, timeout=60):
self.pressed = None
def callback_1(channel):
# print("Pressed key 1")
self.pressed = 1
def callback_2(channel):
# print("Pressed key 2")
self.pressed = 2
def callback_3(channel):
# print("Pressed key 3")
self.pressed = 3
def callback_4(channel):
# print("Pressed key 4")
self.pressed = 4
GPIO.add_event_detect(row_channels[0], GPIO.RISING, callback=callback_1, bouncetime=500)
GPIO.add_event_detect(row_channels[1], GPIO.RISING, callback=callback_2, bouncetime=500)
GPIO.add_event_detect(row_channels[2], GPIO.RISING, callback=callback_3, bouncetime=500)
GPIO.add_event_detect(row_channels[3], GPIO.RISING, callback=callback_4, bouncetime=500)
GPIO.output(column_channel, GPIO.HIGH)
start_time = datetime.now()
while not self.pressed:
time.sleep(1)
current_time = datetime.now()
delta = current_time - start_time
if delta.seconds > timeout:
for chan in row_channels:
GPIO.remove_event_detect(chan)
GPIO.output(column_channel, GPIO.LOW)
return None
for chan in row_channels:
GPIO.remove_event_detect(chan)
GPIO.output(column_channel, GPIO.LOW)
return self.pressed

61
drivers/pygamedriver.py Normal file
View File

@@ -0,0 +1,61 @@
from datetime import datetime
import pygame
import sys
class PygameDriver():
def __init__(self, xres, yres):
pygame.init()
self.screen = pygame.display.set_mode((xres, yres))
self.xres = xres
self.yres = yres
# image1: black/white image
# image2: black/yellow image
def display(self, image1=None, image2=None):
if image1:
raw_str1 = image1.convert('RGBA').tobytes("raw", 'RGBA')
pygame_surface1 = pygame.image.fromstring(raw_str1, (self.xres, self.yres), 'RGBA')
self.screen.blit(pygame_surface1, (0, 0))
if image2:
raw_str2 = image2.convert('RGB').tobytes("raw", 'RGB')
pygame_surface2 = pygame.image.fromstring(raw_str2, (self.xres, self.yres), 'RGB')
pygame_surface2.set_colorkey((255, 255, 255))
image_pixel_array = pygame.PixelArray(pygame_surface2)
image_pixel_array.replace((0, 0, 0), (127, 100, 0))
del image_pixel_array
self.screen.blit(pygame_surface2, (0, 0))
pygame.display.flip()
def end(self):
pygame.quit()
class PygameKBDriver():
def __init__(self):
pass
def wait_for_keypress(self, timeout=60):
start_time = datetime.now()
while True:
current_time = datetime.now()
delta = current_time - start_time
if delta.seconds > timeout:
return None
events = pygame.event.get()
for event in events:
if event.type == pygame.QUIT:
pygame.quit()
sys.exit(0)
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_1:
return 1
elif event.key == pygame.K_2:
return 2
elif event.key == pygame.K_3:
return 3
elif event.key == pygame.K_4:
return 4
pygame.time.wait(1000)