Giter Site home page Giter Site logo

speedyg0nz / maginkcal Goto Github PK

View Code? Open in Web Editor NEW
3.1K 46.0 106.0 564 KB

E-Ink Magic Calendar that automatically syncs to Google Calendar and runs off a battery powered Raspberry Pi Zero

License: Apache License 2.0

Python 91.53% HTML 1.25% CSS 7.22%

maginkcal's Introduction

MagInkCal

This repo contains the code needed to drive an E-Ink Magic Calendar that uses a battery powered (PiSugar2) Raspberry Pi Zero WH to retrieve events from a Google Calendar, format it into the desired layout, before pushing it to a Waveshare 12.48" tri-color E-Ink display. Note that the code has only been tested on the specific hardware mentioned, and customization of the code is necessary for it to work with other E-Ink displays or Battery/RTC add-ons. That said, enjoy working on your project and hopefully this helps to jump-start your magic calendar journey.

20210924_175459

Background

Back in 2019, I started a thread in Reddit to bounce an idea I had with the community: to replicate the Android Magic Calendar concept that inspired many DIY projects in the subsequent years. But specifically, I wanted it to run on battery so I could position it anywhere in house, and even hang it on the wall without a wire dangling beneath it. I also wanted the parts to be plug and play since I had neither the desire nor the steady hands needed to solder anything. After sitting on that idea for close to a year, I finally got my act together and ordered the parts I needed for this project. I posted another update to Reddit in 2020, but got overwhelmed with life/work so it took me almost another year before posting the full set of instructions and code here. An update was also posted on Reddit to share this with the community.

Hardware Required

Update (22 Nov 2022): Looks like the 12.48" E-Ink Display is finally back in stock over at Waveshare! And a new version of the PiSugar with USB-C port has been released over the past year as well.

How It Works

Through PiSugar2's web interface, the onboard RTC can be set to wake and trigger the RPi to boot up daily at a time of your preference. Upon boot, a cronjob on the RPi is triggered to run a Python script that fetches calendar events from Google Calendar for the next few weeks, and formats them into the desired layout before displaying it on the E-Ink display. The RPi then shuts down to conserve battery. The calendar remains displayed on the E-Ink screen, because well, E-Ink...

Some features of the calendar:

  • Battery life is the big question so I'll address it first. I'm getting around 3-4 weeks before needing to recharge the PiSugar2. I'm fairly happy with this but I'm sure this can be extended if I optimize the code further.
  • Since I had the luxury of using red for the E-Ink display, I used it to highlight the current date, as well as recently added/updated events.
  • I don't like having long bars that span across multiple days for multi-day events, so I chose to display only the start and end dates for those events, and adding small left/right arrows accordingly,
  • Given limited space (oh why are large E-Ink screens still so expensive!) and resolution on the display, I could only show 3 events per day and an indicator (e.g. 4 more) for those not displayed
  • The calendar always starts from the current week, and displays the next four (total 35 days). If the dates cross over to the new month, it's displayed in grey instead of black.

MagInkCal Basics

Setting Up Raspberry Pi Zero

  1. Start by flashing Raspberrypi OS Lite to a MicroSD Card. (March 2023 Edit: If you're still using the original Raspberry Pi Zero, there are known issues between the latest RPiOS "bullseye" release and chromium-browser, which is required to run this code. As such, I would recommend that you keep to the legacy "buster" OS if you're still running this on older RPiO hardware.)

  2. After setting up the OS, run the following commmand in the RPi Terminal, and use the raspi-config interface to setup Wifi connection, enable SSH, I2C, SPI, and set the timezone to your location.

sudo raspi-config
  1. Run the following commands in the RPi Terminal to setup the environment to run the Python scripts.
sudo apt update
sudo apt-get install python3-pip
sudo apt-get install chromium-chromedriver
sudo apt-get install libopenjp2-7-dev
pip3 install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
pip3 install pytz
pip3 install selenium
pip3 install Pillow
  1. Run the following commands in the RPi Terminal to install the libraries needed to drive the E-Ink display. See this page for more details.
sudo apt-get install python3-pil
sudo pip3 install RPi.GPIO
sudo pip3 install spidev
sudo apt-get install wiringpi
  1. Run the following commands in the RPi Terminal to install the web interface for PiSugar2 display. See this page for more details. After running the command, you would be able to access the web interface at http://your_raspberry_ip:8421 in your browser. From there you should be able to specify when you wish to schedule the PiSugar2 boot up your RPi.
curl http://cdn.pisugar.com/release/Pisugar-power-manager.sh | sudo bash
  1. Download the over the files in this repo to a folder in your PC first.

  2. In order for you to access your Google Calendar events, it's necessary to first grant the access. Follow the instructions here on your PC to get the credentials.json file from your Google API. Don't worry, take your time. I'll be waiting here.

  3. Once done, copy the credentials.json file to the "gcal" folder in this project. Run the following command on your PC. A web browser should appear, asking you to grant access to your calendar. Once done, you should see a "token.pickle" file in your "gcal" folder.

python3 quickstart.py
  1. Copy all the files over to your RPi using your preferred means.

  2. Run the following command in the RPi Terminal to open crontab.

crontab -e
  1. Specifically, add the following command to crontab so that the MagInkCal Python script runs each time the RPi is booted up.
@reboot cd /location/to/your/maginkcal && python3 maginkcal.py
  1. That's all! Your Magic Calendar should now be refreshed at the time interval that you specified in the PiSugar2 web interface!

PS: I'm aware that the instructions above may not be complete, especially when it comes to the Python libraries to be installed, so feel free to ping me if you noticed anything missing and I'll add it to the steps above.

Acknowledgements

  • Quattrocento Font: Font used for the calendar display
  • Bootstrap Calendar CSS: Stylesheet that was adapted heavily for the calendar display
  • emagra: For adding in new features, such as 24hr display and multiple calendar selection.
  • /u/aceisace: For the tips on E-Ink development and the InkyCal repo (worth checking out even though I didn't use it for this project).

Contributing

I won't be updating this code much, since it has been serving me well. Nevertheless, feel free to fork the repo and modify it for your own purpose. At the same time, check out other similar projects, such as InkyCal. It's much more polished and also actively developed.

Buy Me A Coffee

If this project has helped you in any way, do buy me a coffee so I can continue to build more of such projects in the future and share them with the community!

Buy Me A Coffee

What's Next

Honestly, the cost of this project is way too high for a single purpose device. Personally, I've been looking at E-Ink tablets that emulate the experience of writing on paper, and allow the users to take notes on the go. Those familiar with this range of products would be aware of the reMarkable tablet, Ratta Supernote, Kobo Elipsa and many others. My next project is likely to enhance one of these devices such that the calendar will be displayed when it's not in use. While this is usually possible by manually setting the sleep screen image / screensaver, I'm looking to have the screensaver updated automatically on a daily basis, like how it was done in this project.

Edit (Apr 2023): So after finishing up this project, I went on to build an E-Ink Dashboard that offers rich, timely and glanceable information pulled from Google Calendar, OpenWeatherMap and ChatGPT! Find out more over at: https://github.com/speedyg0nz/MagInkDash

maginkcal's People

Contributors

emagra avatar speedyg0nz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

maginkcal's Issues

Quickstart.py may need a current directory declaration

Hey @speedyg0nz. Just wanted to let you know of an error I ran into and the method I used to fix it. For some reason running the quickstart.py as is returned an error.

Adding this to line 22
CURR_DIR = os.path.dirname(os.path.realpath(__file__))

and changing line 39-41 from this:
flow = InstalledAppFlow.from_client_secrets_file( 'credentials.json', SCOPES) creds = flow.run_local_server(port=0)

to this:
credential_file=str(CURR_DIR)+'/credentials.json' flow = InstalledAppFlow.from_client_secrets_file( credential_file, SCOPES) creds = flow.run_local_server(port=0)

That fixed the issue for me. Unfortunately, I'm having trouble replicating it now so I can't specify what the error it threw actually was. Apologies there. Starting from a fresh repo in a new environment doesn't have the issue at all. This is probably an edge case.
I think this is an environment thing but thought it worth sharing regardless in case anyone else encounters it in the future.

ERROR - Message: Unable to obtain driver for chrome using Selenium Manager

Cant get the script to run due to chromedriver error. I see you have fixed that in render.py due to latest selenium updates but for me this doesnt seem to work. Followed your instructions and used legacy version of rpi OS. Checked $PATH and /usr/lib is present and chromedriver is located at /usr/lib/chromium-browser/chromedriver in my installation.

Running RPI3. Also dont have a PiSugar installed at the moment but that shouldnt stop it from running and should not be related to this error at the moment.

Error log:

2023-08-06 00:24:42,384 INFO - Starting daily calendar update
2023-08-06 00:24:42,425 INFO - Invalid time sync command
2023-08-06 00:24:42,462 INFO - Invalid battery output
2023-08-06 00:24:42,464 INFO - Battery level at start: -1.000
2023-08-06 00:24:42,465 INFO - Time synchronised to 2023-08-06 00:24:42.464773+03:00
2023-08-06 00:24:42,757 INFO - Retrieving events between 2023-08-01T00:00:00+03:00 and 2023-09-04T23:59:59.999999+03:00...
2023-08-06 00:24:43,131 INFO - No upcoming events found.
2023-08-06 00:24:43,132 INFO - Calendar events retrieved in 0:00:00.665384
2023-08-06 00:24:43,137 ERROR - Message: Unable to obtain driver for chrome using Selenium Manager.; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors/driver_location

2023-08-06 00:24:43,139 INFO - Completed daily calendar update
2023-08-06 00:24:43,139 INFO - Checking if configured to shutdown safely - Current hour: 0

Long-lived gcal credential?

Hi!

Thanks so much for sharing your project! It has inspired me to adapt it and build my own.

I noticed that the google calendar credential expires after a couple days, do you know how to get a long-lived credential?

Thanks again!

Matt

Multiple accounts?

Any way to use multiple accounts within this project? All of them have CalDAV option (first acc is iCloud, two others are Gmail)
Maybe some kind of CalDAV proxy? Before digging into the source code

Error 'NoneType' object has no attribute 'DEV_ModuleInit'

Hello,
firstly thank you very much about your work on this project!! I think it is a great project ;)

My problem starts when it starts the phase of drawing in the ink waveshare 7.5 inches b, the logs I see are these:

pi@raspberrypi:~/MagInkCal $ python3 maginkcal.py 
/usr/lib/python3/dist-packages/requests/__init__.py:91: RequestsDependencyWarning: urllib3 (1.26.7) or chardet (3.0.4) doesn't match a supported version!
  RequestsDependencyWarning)
Starting daily calendar update
Invalid time sync command
Invalid battery output
Battery level at start: -1.000
Time synchronised to 2021-11-26 08:28:24.184037+01:00
Retrieving events between 2021-11-22T00:00:00+01:00 and 2021-12-26T23:59:59.999999+01:00...
Calendar events retrieved in 0:00:00.630859
Screenshot captured and saved to file.
Image colours processed. Extracted grayscale and red images.
EPD init...
'NoneType' object has no attribute 'DEV_ModuleInit'
Completed daily calendar update
Checking if configured to shutdown safely - Current hour: 8

As you can see, I think the problem is on third to last line:

'NoneType' object has no attribute 'DEV_ModuleInit'

I inspect a little bit your code and the error occurs in the line 115 of epdconfig.py file : spi.DEV_ModuleInit

I have searched on internet and this line should initialize the SPI module, but it does not work.

I have followed your README.md 2 or 3 times to try to see what I could have missed and I am not able to resolve my problem by myself :(

On the other way I have used the example python code from waveshare and it works correctly: epd_7in5b_V2_test.py

@speedyg0nz: Could you help me solving this problem to run your project?

By the way, I am using a Raspberry pi 4b and the SPI is enabled in my Raspberry pi 4b

An other thing very curios that happens is:

  • When I reboot the raspberry the waveshare example works correctly
  • But if I have tried to run the python3 maginkcal.py before of running waveshare example, the waveshare example does not work, the screen does not show the images of example and the running apparently is the same but faster without waiting the ending of painting of the ink screen of each image.
  • Nevertheless, the python3 maginkcal.py always returns the output on this comment

ASCII Codec Issue

When running the application on my Pi Zero WH, I am getting the below error in regards to Codec. I tried this on 3 separate Google Accounts, including one with an empty calendar, to ensure it was not some Event with bad characters (although it should be able to test for that ideally...).

Any idea what could be causing this, or a way to ensure proper encode happens? Obviously it happens after the time sync, but its happening before any rendering is done as my eink is still blank.

Other than using my own timezone in the config file and my own pickle file, I have not altered any code from the repo up till now.

Starting daily calendar update
Battery level at start: 100.000
Time synchronised to 2023-02-07 15:49:45.641810-06:00
'ascii' codec can't decode byte 0xe7 in position 1: ordinal not in range(128)
Completed daily calendar update
Checking if configured to shutdown safely - Current hour: 15

Pytz module not found

Hi, I have an Inkycal but I prefer the formatting of this MagicInkCal, so despite my lack of experience and knowledge I've been trying for the past 6 months to get it to work.

When I attempt to run the program on my rpi zero wh (with the legacy 'buster' OS), I get an error that says
"from pytz import timezone,
ModuleNotFoundError: No module named 'pytz'"

I get a similar error when I try to run this in a virtual environment on VSC.

Does anyone know of a relatively straighforward solution? My apologies if the way I frame this question does not make sense, I am in way over my head with this project.

Chromedriver Issues - Status codes (-4, or -11, or 127)

Running the code as direct clone, i get:
chromedriver unexpectedly exited. Status code was: -4

Which occurs in Render.py at line:
driver = webdriver.Chrome(,options=opts)

I have scoured the internet trying to find a solution. Have changed versions, forced downgrades, written in explicit executable paths, set binary locations, etc etc etc that were found in countless Stackoverflow articles. Nothing seems to work. I even started this project from scratch with hopes to trigger a better result. However, the Chromium driver part simply will not work.

Would LOVE to know what versions of Chromium-browser, Selenium, Chromedriver, etc you are using in your successful build to see if I could replicate. Or if you could try your own project again from scratch to see if I am just crazy... Feels like today's packages just are not lining up with your instructions :(

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.