<img align="center" src="https://github.com/aceinnolab/Inkycal/blob/assets/Repo/logo.png" width="800" alt="inkycal logo">

Inkycal is a python3 software for selected E-Paper displays.
It's open-source (non-commercially), fully modular, user-friendly and even runs
well even on the Raspberry Pi Zero. Inkycal even has a web-UI which takes
care of adding your details! No more editing files, Yay :partying_face:

s open-source (non-commercially), fully modular, user-friendly and even runs

The main idea behind Inkycal was to create a dashboard that blends in well
in it's environment and shows you all information without you having to look
them up. Several built-in modules help you stay organised by:

s environment and shows you all information without you having to look

providing the weather forecast for the full week or next few hours

showing your iCalendar agenda, which may be synchronised with your phone

showing a monthly Calendar which may also show events synced with Google etc.

What's more? It's compatible with third party modules, if something is missing,
you can write your own module and share it with the community.

Inkycal has a friendly and helpful community on Discord in case you're in
a pinch setting up Inkycal. Noobs welcome too of course!

Inkycal has a friendly and helpful community on Discord in case you

Inkycal is developed mainly by aceisace - a
university student and a few other developers in their free time.
Open source means we work for nothing, but it doesn't mean we don't need
anything. We invest a large amount of time, effort and coffee to develop
Inkycal.

Please support Inkycal via a DONATION
to help us keep this project alive. Thank You!

This documentation is mainly for developers who wish to create a third party
module

inkycal documentation master file, created by
sphinx-quickstart on Tue May  5 16:23:29 2020.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.

/Users/ace/Desktop/Projekte/Inkycal/inkycal/main.py:docstring of inkycal.main

Args:
- settings_path = str -> the full path to your settings.json file
  if no path is given, tries looking for settings file in /boot folder.
- render = bool (True/False) -> show the image on the epaper display?

settings_path = str -> the full path to your settings.json file
if no path is given, tries looking for settings file in /boot folder.

render = bool (True/False) -> show the image on the epaper display?

Attributes:
- optimize = True/False. Reduce number of colours on the generated image
  to improve rendering on E-Papers. Set this to False for 9.7" E-Paper.

optimize = True/False. Reduce number of colours on the generated image
to improve rendering on E-Papers. Set this to False for 9.7" E-Paper.

to improve rendering on E-Papers. Set this to False for 9.7” E-Paper.

Uses the full filepath of the third party module to check if it is inside
the correct folder, then checks if it's an inkycal module. Lastly, the
init files in /inkycal and /inkycal/modules are updated to allow using
the new module.

the correct folder, then checks if it’s an inkycal module. Lastly, the

Args:
- filepath: The full filepath of the third party module. Modules should be
  in Inkycal/inkycal/modules.

filepath: The full filepath of the third party module. Modules should be
in Inkycal/inkycal/modules.

Usage:
- download a third-party module. The exact link is provided by the
  developer of that module and starts with
  `https://raw.githubusercontent.com/...`

  enter the following in bash to download a module::

    $ cd Inkycal/inkycal/modules #navigate to modules folder in inkycal
    $ wget https://raw.githubusercontent.com/...     #download the module

  then register it with this function::

    >>> from inkycal import Inkycal
    >>> Inkycal.add_module('/full/path/to/the/module/in/inkycal/modules.py')

download a third-party module. The exact link is provided by the
developer of that module and starts with
`https://raw.githubusercontent.com/...`

enter the following in bash to download a module::

  $ cd Inkycal/inkycal/modules #navigate to modules folder in inkycal
  $ wget https://raw.githubusercontent.com/...     #download the module

then register it with this function::

  >>> from inkycal import Inkycal
  >>> Inkycal.add_module('/full/path/to/the/module/in/inkycal/modules.py')

$ cd Inkycal/inkycal/modules #navigate to modules folder in inkycal
$ wget https://raw.githubusercontent.com/...     #download the module

>>> from inkycal import Inkycal
>>> Inkycal.add_module('/full/path/to/the/module/in/inkycal/modules.py')

Uses the Display class to calibrate the display with the default of 3
cycles. After a refresh cycle, a new image is generated and shown.

Looks for given filename.py in /modules folder, removes entries of that
module in init files inside /inkycal and /inkycal/modules

Args:
- filename: The filename (with .py ending) of the module which should be
  unregistered. e.g. `'mymodule.py'`
- remove_file: ->bool (True/False). If set to True, the module is deleted
  after unregistering it, else it remains in the /modules folder

filename: The filename (with .py ending) of the module which should be
unregistered. e.g. `'mymodule.py'`

unregistered. e.g. 

remove_file: ->bool (True/False). If set to True, the module is deleted
after unregistering it, else it remains in the /modules folder

Usage:
- Look for the module in Inkycal/inkycal/modules which should be removed.
  Only the filename (with .py) is required, not the full path.

  Use this function to unregister the module from inkycal::

    >>> Inkycal.remove_module('mymodule.py')

Look for the module in Inkycal/inkycal/modules which should be removed.
Only the filename (with .py) is required, not the full path.

Use this function to unregister the module from inkycal::

  >>> Inkycal.remove_module('mymodule.py')

>>> Inkycal.remove_module('mymodule.py')

Uses an infinity loop to run Inkycal nonstop. Inkycal generates the image
from all modules, assembles them in one image, refreshed the E-Paper and
then sleeps until the next sheduled update.

Attempts to import module names from settings file. Loads the config
for each module and initializes the module. Tries to run the module and
checks if the images could be generated correctly.

Creates an instance of the driver for the selected E-Paper model and allows
rendering images and calibrating the E-Paper display

Flushes the selected display several times with it's supported colours,
removing any previous effects of ghosting.

Flushes the selected display several times with it’s supported colours,

Args:
- cycles: -> int. The number of times to flush the display with it's
  supported colours.

cycles: -> int. The number of times to flush the display with it's
supported colours.

cycles: -> int. The number of times to flush the display with it’s

It's recommended to calibrate the display after every 6 display updates
for best results. For black-white only displays, calibration is less
critical, but not calibrating regularly results in grey-ish text.

It’s recommended to calibrate the display after every 6 display updates

Please note that calibration takes a while to complete. 3 cycles may
take 10 minutes on black-white E-Papers while it takes 20 minutes on coloured
E-Paper displays.

get_display_names() (inkycal.display.display.Display class method)

Fetches all filenames in driver folder and prints them on the console.

Use one of the models to intilialize the Display class in order to gain
access to the E-Paper.

You can use this function directly without creating the Display class:

Args:
- model_name: str -> The name of the E-Paper display to get it's size.

model_name: str -> The name of the E-Paper display to get it's size.

model_name: str -> The name of the E-Paper display to get it’s size.

Returns:
(width, height) ->tuple, showing the size of the display

Display.render(im_black: ~PIL.Image.Image, im_colour=<class 'PIL.Image.Image'>) -> None

(im_black: ~PIL.Image.Image, im_colour=<class 'PIL.Image.Image'>)

Initlializes the E-Paper display, sends image data and executes command
to update the display.

Args:
- im_black: The image for the black-pixels. Anything in this image that is
  black is rendered as black on the display. This is required and ideally
  should be a black-white image.

- im_colour: For E-Paper displays supporting colour, a separate image,
  ideally black-white is required for the coloured pixels. Anything that is
  black in this image will show up as either red/yellow.

im_black: The image for the black-pixels. Anything in this image that is
black is rendered as black on the display. This is required and ideally
should be a black-white image.

im_colour: For E-Paper displays supporting colour, a separate image,
ideally black-white is required for the coloured pixels. Anything that is
black in this image will show up as either red/yellow.

>>> sample_image = Image.open('path/to/file.png')
>>> display = Display('my_black_white_display')
>>> display.render(sample_image)

>>> display = Display('my_coloured_display')
>>> display.render(sample_image, sample_image)

>>> black_image = Image.open('path/to/file.png') # black pixels
>>> colour_image = Image.open('path/to/file.png') # coloured pixels
>>> display.render(black_image, colour_image)

Gets the height of a font and scales it until 80% of the max_height
is filled.

Args:
- font: A PIL Font object.
- max_height: An integer representing the height to adjust the font to
  which the given font should be scaled to.

max_height: An integer representing the height to adjust the font to
which the given font should be scaled to.

draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1, 0.1))

Args:
- image: The image on which the border should be drawn (usually im_black or
  im_colour.

- xy: Tuple representing the top-left corner of the border e.g. (32, 100)
  where 32 is the x co-ordinate and 100 is the y-coordinate.

- size: Size of the border as a tuple -> (width, height).

- radius: Radius of the corners, where 0 = plain rectangle, 5 = round corners.

- thickness: Thickness of the border in pixels.

- shrinkage: A tuple containing decimals presenting a percentage of shrinking
  -> (width_shrink_percentage, height_shrink_percentage).
  e.g. (0.1, 0.2) ~ shrinks the width of border by 10%, shrinks height of
  border by 20%

image: The image on which the border should be drawn (usually im_black or
im_colour.

xy: Tuple representing the top-left corner of the border e.g. (32, 100)
where 32 is the x co-ordinate and 100 is the y-coordinate.

radius: Radius of the corners, where 0 = plain rectangle, 5 = round corners.

shrinkage: A tuple containing decimals presenting a percentage of shrinking
-> (width_shrink_percentage, height_shrink_percentage).
e.g. (0.1, 0.2) ~ shrinks the width of border by 10%, shrinks height of
border by 20%

Searches the /font folder in Inkycal and displays all fonts found in
there.

Returns:
printed output of all available fonts. To access a fontfile, use the
fonts dictionary to access it.

>>> fonts['fontname']

To use a font, use the following sytax, where fontname is one of the
printed fonts of this function:

Returns:
- A timezone if a system timezone was found.
- None if no timezone was found.

The extracted timezone can be used to show the local time instead of UTC. e.g.

>>> import arrow
>>> print(arrow.now()) # returns non-timezone-aware time
>>> print(arrow.now(tz=get_system_tz()) # prints timezone aware time.

Attempts to connect to google.com with a timeout of 5 seconds to check
if the network can be reached.

Returns:
- True if connection could be established.
- False if the internet could not be reached.

Returned output can be used to add a check for internet availability:

>>> if internet_available():
>>> #...do something that requires internet connectivity

Splits a long text to smaller lines which can fit in a line with max_width.
Uses a Font object for more accurate calculations.

Args:
- text -> Text as a string
- font: A PIL font object which is used to calculate the size.
- max_width: int-> a width in pixels defining the maximum width before
  splitting the text into the next chunk.

max_width: int-> a width in pixels defining the maximum width before
splitting the text into the next chunk.

Args:
- image: The image to draw this text on, usually im_black or im_colour.
- xy: tuple-> (x,y) representing the x and y co-ordinate.
- box_size: tuple -> (width, height) representing the size of the text box.
- text: string, the actual text to add on the image.
- font: A PIL Font object e.g.
  ImageFont.truetype(fonts['fontname'], size = 10).

image: The image to draw this text on, usually im_black or im_colour.

box_size: tuple -> (width, height) representing the size of the text box.

font: A PIL Font object e.g.
ImageFont.truetype(fonts['fontname'], size = 10).

ImageFont.truetype(fonts[‘fontname’], size = 10).

Args: (optional)
- alignment: alignment of the text, use 'center', 'left', 'right'.
- autofit: bool (True/False). Automatically increases fontsize to fill in
  as much of the box-height as possible.
- colour: black by default, do not change as it causes issues with rendering
  on e-Paper.
- rotation: Rotate the text with the text-box by a given angle anti-clockwise.
- fill_width: Decimal representing a percentage e.g. 0.9 # 90%. Fill a
  maximum of 90% of the size of the full width of text-box.
- fill_height: Decimal representing a percentage e.g. 0.9 # 90%. Fill a
  maximum of 90% of the size of the full height of the text-box.

alignment: alignment of the text, use 'center', 'left', 'right'.

alignment: alignment of the text, use ‘center’, ‘left’, ‘right’.

autofit: bool (True/False). Automatically increases fontsize to fill in
as much of the box-height as possible.

colour: black by default, do not change as it causes issues with rendering
on e-Paper.

rotation: Rotate the text with the text-box by a given angle anti-clockwise.

fill_width: Decimal representing a percentage e.g. 0.9 # 90%. Fill a
maximum of 90% of the size of the full width of text-box.

fill_height: Decimal representing a percentage e.g. 0.9 # 90%. Fill a
maximum of 90% of the size of the full height of the text-box.

iCalendar parsing moudule for inkycal.
Parses events from given iCalendar URLs / paths

Check if an event is an all day event.
Returns True if event is all day, else False

iCalendar.get_events(timeline_start, timeline_end, timezone=None)

Input an arrow (time) object for:
* the beginning of timeline (events have to end after this time)
* the end of the timeline (events have to begin before this time)
* timezone if events should be formatted to local time
Returns a list of events sorted by date

get_system_tz() (inkycal.modules.ical_parser.iCalendar static method)

Input a string or list of strings containing valid iCalendar filepaths
example: 'path1' (single file) OR ['path1', 'path2'] (multiple files)
returns a list of iCalendars as string (raw)

example: ‘path1’ (single file) OR [‘path1’, ‘path2’] (multiple files)

Input a string or list of strings containing valid iCalendar URLs
example: 'URL1' (single url) OR ['URL1', 'URL2'] (multiple URLs)
add username and password to access protected files

example: ‘URL1’ (single url) OR [‘URL1’, ‘URL2’] (multiple URLs)

print all parsed events in a more readable way
use the format (fmt) parameter to specify the date format
see https://arrow.readthedocs.io/en/latest/#supported-tokens
for more info tokens

see 

Custom image class for Inkycal Project
Takes care of handling images. Made to be used by other modules to handle
images.

In horizontal mode, the image is flipped if the image height is greater
than the image width.

In vertical mode, the image is flipped if the image width is greater
than the image height.

Args:
- path:The full path or url of the image file
  e.g. `https://sample.com/logo.png` or `/home/pi/Downloads/nice_pic.png`

path:The full path or url of the image file
e.g. `https://sample.com/logo.png` or `/home/pi/Downloads/nice_pic.png`

Raises:
- FileNotFoundError: This Exception is raised when the file could not be
  found.
- OSError: A OSError is raised when the URL doesn't point to the correct
  file-format, i.e. is not an image
- TypeError: if the URLS doesn't start with htpp

FileNotFoundError: This Exception is raised when the file could not be
found.

OSError: A OSError is raised when the URL doesn't point to the correct
file-format, i.e. is not an image

OSError: A OSError is raised when the URL doesn’t point to the correct

Replaces white pixels of the first image with transparent ones. Then pastes
the first image on the second one.

Args:
- image1: A PIL Image object in 'RGBA' mode.
- image2: A PIL Image object in 'RGBA' mode.

"Previews an image on gpicview (only works on Rapsbian with Desktop).

“Previews an image on gpicview (only works on Rapsbian with Desktop).

Checks if an image has an alpha band and replaces the transparency with
white pixels.

Args:
- palette: A supported token. (see below)
- dither:->bool. Use dithering? Set to `False` for solid colour fills.

dither:->bool. Use dithering? Set to `False` for solid colour fills.

Returns:
- two images: one for the coloured band and one for the black band.

two images: one for the coloured band and one for the black band.

>>> 'bwr' # black-white-red
>>> 'bwy' # black-white-yellow
>>> 'bw'  # black-white

Hyperlink target "module-inkycal.modules.inky_image" is not referenced.

<img align="center" src="https://github.com/aceisace/Inkycal/raw/assets/Repo/logo.png" width="800">

# clone the repo
git clone https://github.com/aceinnolab/Inkycal

# go to downloaded repo folder
cd Inkycal

# install Inkycal
pip3 install -e ./

Copy the generated settings file to the Raspberry Pi
more coming soon..