363 lines
9.2 KiB
Python
363 lines
9.2 KiB
Python
#! /usr/bin/env python
|
|
|
|
# python imports
|
|
import argparse
|
|
import logging
|
|
import math
|
|
import datetime
|
|
import os
|
|
import os.path
|
|
import sys
|
|
|
|
# 3rd party imports
|
|
from escpos.printer import Usb, Dummy, File
|
|
import feedparser
|
|
import ignition
|
|
from PIL import Image
|
|
import re
|
|
import requests
|
|
|
|
from config import load_config, CONFIG_PATH
|
|
|
|
|
|
# Enable logging
|
|
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
level=logging.INFO)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
config = {}
|
|
|
|
|
|
def initialize():
|
|
"""
|
|
Initializes the printer and returns a printer object to
|
|
generate the print
|
|
"""
|
|
global config
|
|
config = load_config()
|
|
if not config:
|
|
print(f"Config file created, check {CONFIG_PATH}")
|
|
|
|
if os.path.exists(config.get("PRINTER_FILE")):
|
|
return File(config.get("PRINTER_FILE"))
|
|
|
|
printer_id = config.get("PRINTER_USB_ID")
|
|
|
|
if not printer_id:
|
|
logger.error("Please configure your printer")
|
|
|
|
prid1, prid2 = printer_id.split(":")
|
|
|
|
printer_interface = config.get("PRINTER_INTERFACE") or 0
|
|
printer_endpoint = config.get("PRINTER_ENDPOINT") or 0x01
|
|
|
|
return Usb(prid1, prid2, printer_interface, printer_endpoint)
|
|
|
|
|
|
def reset_defaults(printer):
|
|
"""
|
|
Reset the printer to the defaults
|
|
"""
|
|
printer.set(align='left', font='a', width=1, text_type="normal",
|
|
height=1, density=9, invert=False, smooth=False, flip=False)
|
|
|
|
|
|
def print_weather(printer, city=None):
|
|
reset_defaults(printer)
|
|
appkey = config.get("OWM")
|
|
if not appkey:
|
|
logger.error("Open Weather key not set!")
|
|
return
|
|
|
|
if not city:
|
|
city = config.get("CITY")
|
|
|
|
if not city:
|
|
logger.error("No city set")
|
|
|
|
params = {"q": city, "APPID": appkey, "units": "metric"}
|
|
|
|
weatherdata = requests.get('http://api.openweathermap.org/data/2.5/weather', params=params)
|
|
|
|
if "weather" in weatherdata.json():
|
|
weather = weatherdata.json()
|
|
today = datetime.datetime.now()
|
|
current_day = today.strftime("%a, %d %b, %Y")
|
|
|
|
printer.set(align="center",
|
|
font="a",
|
|
text_type="b")
|
|
printer.text(f"{current_day}\n\n{city}\n")
|
|
|
|
reset_defaults(printer)
|
|
printer.set(align="center", font="b")
|
|
|
|
description = weather['weather'][0]['description']
|
|
printer.text(f"{description}\n\n")
|
|
icon_code = weather['weather'][0]['icon']
|
|
|
|
if not os.path.exists(f"icons/weather/{icon_code}.png"):
|
|
icon_path = "icons/weather/any.png"
|
|
|
|
else:
|
|
icon_path = f"icons/weather/{icon_code}.png"
|
|
|
|
# TODO: The image impl should be an option
|
|
printer.image(icon_path,
|
|
impl="bitImageColumn")
|
|
printer.text("\n")
|
|
# TODO: Print a nice icon based on the codes here: https://openweathermap.org/weather-conditions
|
|
temperature = weather['main']['temp']
|
|
humidity = weather['main']['humidity']
|
|
wind = weather['wind']['speed']
|
|
|
|
printer.text(f"Temperature: {temperature}C\n")
|
|
printer.text(f"Humidity: {humidity}%\n")
|
|
printer.text(f"Wind: {wind}km\\h\n")
|
|
|
|
else:
|
|
logger.error("No weather info available")
|
|
|
|
|
|
def print_gemini(printer, link):
|
|
"""
|
|
Given a Gemini link, it prints the file to the printer
|
|
and tries to do some interpretation if it is gemtext
|
|
"""
|
|
response = ignition.request(link)
|
|
|
|
if not response.success():
|
|
logger.error(f"Received error {response.status}")
|
|
return
|
|
|
|
reset_defaults(printer)
|
|
if response.meta == "text/gemini":
|
|
for raw_line in str(response.data()).split("\n"):
|
|
line = raw_line.rstrip()
|
|
if len(line) == 0:
|
|
printer.text("\n")
|
|
continue
|
|
|
|
if line.startswith("# "):
|
|
printer.set(align="left",
|
|
text_type="BU",
|
|
width=2, height=2,
|
|
smooth=True)
|
|
printer.text(line[2:])
|
|
|
|
elif line.startswith("## "):
|
|
printer.set(align="left",
|
|
text_type="BU",
|
|
width=1, height=1,
|
|
smooth=True)
|
|
printer.text(line[3:])
|
|
|
|
elif line.startswith("### "):
|
|
printer.set(align="left",
|
|
font="a",
|
|
text_type="U",
|
|
width=1, height=1,
|
|
smooth=True)
|
|
printer.text(line[4:])
|
|
|
|
elif line.startswith("#### "):
|
|
printer.set(align="left",
|
|
font="b",
|
|
text_type="U",
|
|
width=1, height=1,
|
|
smooth=True)
|
|
printer.text(line[5:])
|
|
|
|
elif line.startswith("=>"):
|
|
reset_defaults(printer)
|
|
printer.set(align="left",
|
|
font="b")
|
|
printer.text(line[3:])
|
|
reset_defaults(printer)
|
|
lnk = _process_link(link, line[3:])
|
|
printer.text("\n")
|
|
printer.qr(lnk, size=6)
|
|
|
|
else:
|
|
printer.set(align="left",
|
|
font="b")
|
|
printer.text(line)
|
|
|
|
printer.text("\n")
|
|
|
|
else:
|
|
printer.text(response.data())
|
|
|
|
|
|
def _process_link(orig_link, link_text):
|
|
"""
|
|
Extracts the link from the text, and completes it if
|
|
necessary
|
|
"""
|
|
uri = link_text.split()[0]
|
|
|
|
if "://" in link_text:
|
|
return uri
|
|
|
|
if link_text.startswith("/"):
|
|
return orig_link + link_text
|
|
|
|
else:
|
|
return orig_link + "/" + link_text
|
|
|
|
|
|
def print_rss(printer, link, news=3):
|
|
"""
|
|
Given an RSS link and a printer, prints the news from it
|
|
"""
|
|
reset_defaults(printer)
|
|
feed = feedparser.parse(link)
|
|
title = feed["channel"]["title"]
|
|
|
|
printer.set(align="center",
|
|
text_type="BU",
|
|
width=2, height=2,
|
|
smooth=True)
|
|
|
|
printer.text(f"{title}\n\n")
|
|
reset_defaults(printer)
|
|
items = feed["items"][:news]
|
|
for item in items:
|
|
_print_rss_item(item)
|
|
|
|
|
|
def _print_rss_item(item):
|
|
title = item["title"]
|
|
date = item["published"]
|
|
text = clear_html(item["summary"])
|
|
link = item["link"]
|
|
|
|
printer.set(align="left",
|
|
text_type="BU",
|
|
smooth=True)
|
|
printer.text(f"{title}\n")
|
|
reset_defaults(printer)
|
|
printer.set(align="right",
|
|
font="b")
|
|
printer.text(f"{date}\n\n")
|
|
printer.text(text)
|
|
printer.text("\n")
|
|
printer.qr(link, size=6)
|
|
printer.text("\n")
|
|
|
|
|
|
def print_text(printer, text):
|
|
"""
|
|
Prints a text in the smallest form possible
|
|
"""
|
|
reset_defaults(printer)
|
|
# Set the font to a small one and align to
|
|
# the left
|
|
printer.set(align="left",
|
|
text_type="NORMAL",
|
|
font="b",
|
|
smooth=True)
|
|
printer.text("\n")
|
|
if text.strip() == "-":
|
|
# Load stdin data
|
|
for line in sys.stdin:
|
|
printer.text(line)
|
|
|
|
printer.text(text)
|
|
|
|
|
|
def print_file(printer, file):
|
|
"""
|
|
Prints a file
|
|
"""
|
|
reset_defaults(printer)
|
|
# Set the font to a small one and align to
|
|
# the left
|
|
printer.set(align="left",
|
|
text_type="NORMAL",
|
|
font="b",
|
|
smooth=True)
|
|
|
|
printer.text("\n")
|
|
|
|
for line in file:
|
|
printer.text(line)
|
|
|
|
file.close()
|
|
|
|
|
|
def print_image(printer, image):
|
|
"""
|
|
Prints an image
|
|
"""
|
|
# Load the image to adjust it
|
|
im = Image.open(image)
|
|
|
|
ratio = float(im.size[0]) / float(im.size[1])
|
|
|
|
if im.size[0] > im.size[1]:
|
|
# The image needs to be rotated
|
|
width = math.floor(384 * ratio)
|
|
im = im.resize((width, 384))
|
|
im = im.transpose(Image.ROTATE_90)
|
|
|
|
else:
|
|
height = math.floor(384.0 / ratio)
|
|
im = im.resize((384, height))
|
|
|
|
im.save("temp.png")
|
|
printer.hw("INIT")
|
|
printer.image("temp.png")
|
|
os.remove("temp.png")
|
|
|
|
|
|
def clear_html(text):
|
|
cleanr = re.compile('<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});')
|
|
cleantext = re.sub(cleanr, '', text)
|
|
return cleantext
|
|
|
|
|
|
def create_parser():
|
|
"""
|
|
Create an argpaser for command line interaction
|
|
"""
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("--weather", default=False)
|
|
parser.add_argument("--news", default=False)
|
|
parser.add_argument("--gemini", default=False)
|
|
parser.add_argument("--text", nargs=1, default=None)
|
|
parser.add_argument("--file", default=None, type=open)
|
|
parser.add_argument("--image", nargs=1, default=None)
|
|
|
|
return parser
|
|
|
|
|
|
if __name__ == "__main__":
|
|
printer = initialize()
|
|
|
|
parser = create_parser()
|
|
args = parser.parse_args()
|
|
|
|
if args.text:
|
|
print_text(printer, args.text[0])
|
|
printer.text("\n\n")
|
|
|
|
if args.file:
|
|
print_file(printer, args.file)
|
|
printer.text("\n\n")
|
|
|
|
if args.weather:
|
|
print_weather(printer)
|
|
printer.text("\n\n")
|
|
|
|
if args.gemini:
|
|
print_gemini(printer, args.gemini)
|
|
|
|
if args.news:
|
|
print_rss(printer, args.news)
|
|
|
|
if args.image:
|
|
print_image(printer, args.image[0])
|
|
printer.text("\n\n")
|