Merge branch 'masto-notifications' into 'master'

Audio and Masto Rewrites

See merge request bhowell/the-screenless-office!1
workspace
Brendan Howell 5 years ago
commit 2756a9a894

@ -1,13 +1,13 @@
import sys
from . import officemgr
from . import mgmt
def main(args=None):
if args is None:
args = sys.argv[1:]
print("starting Screenless Office...")
mgr = officemgr.OfficeManager()
print("initializing Screenless Office...")
mgr = mgmt.Management()
print("starting Office Manager...")
print("starting Management...")
mgr.run()

@ -1,4 +1,4 @@
import subprocess
import vlc
from bureau import Bureau, add_command
@ -17,7 +17,7 @@ class Audio(Bureau):
Bureau.__init__(self)
self.urldb = self.open_db("urldb")
subprocess.call(["mocp", "-S"])
self.player = vlc.MediaPlayer()
@add_command("p", "Play an album, track or a live stream.")
def play(self, data):
@ -31,44 +31,58 @@ class Audio(Bureau):
url = self.urldb.get(shortcode)
self.log.debug(" playing url " + url)
subprocess.call(["mocp", "-c"])
subprocess.call(["mocp", "-a", url])
subprocess.call(["mocp", "-p"])
self.player.set_mrl(url)
self.player.play()
@add_command("stop", "Halt audio playback.")
def stop(self):
"""
Stops all audio currently playing audio output.
"""
subprocess.call(["mocp", "-P"])
self.player.pause()
@add_command("resu", "Resume playback.")
def resume(self):
"""
Resume playback of paused audio.
"""
subprocess.call(["mocp", "-U"])
self.player.play()
@add_command("next", "Play the next song.")
def play_next(self):
"""
Skip to the next song in the playlist or album.
"""
subprocess.call(["mocp", "-f"])
#subprocess.call(["mocp", "-f"])
# TODO
pass
@add_command("prev", "Play the previous song.")
def play_prev(self):
"""
Skip to the previous song in the playlist or album.
"""
subprocess.call(["mocp", "-r"])
#subprocess.call(["mocp", "-r"])
# TODO
pass
@add_command("nowp", "Now Playing")
def now_playing(self):
"""
Prints the currently playing song or stream on the small printer.
"""
out = subprocess.check_output(["mocp", "-i"]).decode("utf-8")
#out = subprocess.check_output(["mocp", "-i"]).decode("utf-8")
# TODO: sort out how to do this with
out = "Now Playing: "
out += self.player.get_media().get_meta(vlc.Meta.Title) + "\n"
nowplaying = self.player.get_media().get_meta(vlc.Meta.NowPlaying)
if nowplaying == "":
out += "by " + self.player.get_media().get_meta(vlc.Meta.Title) + "\n"
out += "from the album '" + self.player.get_media().get_meta(vlc.Meta.Album) \
+ "'\n"
else:
out += nowplaying + "\n"
self.log.debug("info output:" + out)
self.print_small(out)

@ -7,9 +7,9 @@ import os.path
import random
import string
import subprocess
import sys
import tempfile
import textwrap
#import traceback
import threading
import lmdb
@ -60,6 +60,19 @@ def add_api(apistr, name=""):
return decorator
#def log_traceback(func):
# """ this is a decorator that catches tracebacks for logging"""
# def wrapper(*args):
# my_bureau = args[0]
# try:
# func(*args)
# except Exception as e:
# my_bureau.log.error("CRASH TRACE: {0}".format(my_bureau.name),
# exc_info=e)
# raise
# return wrapper
class LogPrinter(logging.Handler):
"""
LogPrinter prints logs on a receipt printer for screenless debugging.
@ -151,11 +164,6 @@ class Bureau(object):
self.api = {}
modpath = os.path.dirname(__file__)
#slimerjs = os.path.join(modpath, "..", "lib", "slimerjs", "slimerjs")
#renderer = os.path.join(modpath, "..", "slimerjs", "rasterize.js")
#self.slimerjs = os.path.abspath(slimerjs)
#self.html2pdf = self.slimerjs + " --headless " + \
# os.path.abspath(renderer) + " "
mypath = inspect.getfile(self.__class__)
self.mdir = os.path.dirname(mypath)
@ -181,7 +189,6 @@ class Bureau(object):
log_format = logging.Formatter('LOG ${levelname} $name: $message', style='$')
log_printer.setFormatter(log_format)
self.log.addHandler(log_printer)
sys.excepthook = self._log_exception
# setup a dir to store files and data
self.datadir = os.path.join(basepath, self.prefix)
@ -200,9 +207,9 @@ class Bureau(object):
self.log.debug("commands: ")
self.log.debug(str(self.commands))
def _log_exception(typ, value, tb):
self.log.error("CRASH TRACE: {0}".format(str(value)), exc_info=(typ, value, tb))
sys.__excepthook__(typ, value, tb)
# def _log_exception(typ, value, tb):
# self.log.error("CRASH TRACE: {0}".format(str(value)), exc_info=(typ, value, tb))
# sys.__excepthook__(typ, value, tb)
def open_db(self, name):
"""
@ -361,6 +368,16 @@ class Bureau(object):
print(("hi! testing. " + self.name + " bureau seems to work!"))
return "seems to work."
def _run_io(self):
"""
wrapper for run_io so that we can catch threaded exceptions and log
"""
try:
self.run_io()
except Exception as err:
self.log.exception("%s CRASHED with %s\n", self.name, err)
raise
def run_io(self):
"""process hardware or timed input
@ -375,6 +392,16 @@ class Bureau(object):
pass
def run(self):
"""
wrapper running the main loop and logging all exceptions
"""
try:
self._run()
except Exception as err:
self.log.exception("%s CRASHED with %s\n", self.name, err)
raise
def _run(self):
"""
main loop for processing messages
@ -382,7 +409,7 @@ class Bureau(object):
"""
# start the hardware input handler
io_handler = threading.Thread(target=self.run_io)
io_handler = threading.Thread(target=self._run_io)
io_handler.start()
# register commands and api methods
@ -398,10 +425,6 @@ class Bureau(object):
msg = self._recv.recv_string(flags=zmq.NOBLOCK)
else:
continue
# msg = self._recv.recv_string(flags=zmq.NOBLOCK)
#except zmq.ZMQError:
# time.sleep(0.05) # don't waste CPU
# continue
try:
self.log.debug("got message:" + msg)
dot = msg.find(".")
@ -411,7 +434,6 @@ class Bureau(object):
self.log.debug("dot at %d", dot)
# TODO: maybe trim off the trailing "." for convenience
data = msg[dot + 1:]
# data = str(data) # force to be a string
else:
data = None
self.log.debug("data: " + str(data))

@ -292,6 +292,8 @@ class PublicRelations(Bureau):
prn.codepage = "cp437"
# TODO: add fancier formatting i.e. inverted text for username/handle
# TODO: clean this up to use the built in formatting from escpos lib
# and make a print_status function to use for notifications too
toots = self.masto.timeline(limit=count)
out = ""
for t in toots:
@ -324,8 +326,20 @@ class PublicRelations(Bureau):
tw_shortcode = self.short_tweet_id(str(t["id"]))
prn.barcode("PRmad." + tw_shortcode, "CODE128", function_type="B")
prn.text("\r\n\r\n")
notifications = self.masto.notifications()
if len(notifications) > 0:
prn.set(text_type="B")
prn.text("NOTIFICATIONS:\r\n")
prn.set(text_type="NORMAL")
for note in notifications:
username = t.account.display_name.encode("cp437", "ignore") + \
b" (" + t.account.acct.encode("cp437", "ignore") + b")"
prn.text(note["type"] + " " + str(note["created_at"]) + " from ")
prn._raw(username)
prn.text(":\r\n" + str(note.keys()) + "\r\n")
prn.text("\r\n\r\n")
prn.cut()

@ -22,7 +22,7 @@ class Sales(Bureau):
pass # we've already got a db folder
@add_command("p", "Play Media")
def print_fortune(self, data):
def play_media(self, data):
"""
Shows media on a connected projector or plays audio.
"""

@ -67,6 +67,7 @@ class TypingPool(Bureau):
def run_io(self):
val = ""
upper = False
ctrl = False
#TODO: this is crap, needs to be multi-threaded and have one
# such loop for each active device
self.active_devices[0].grab()
@ -74,7 +75,7 @@ class TypingPool(Bureau):
for ev in self.active_devices[0].read_loop():
if ev.type == evdev.ecodes.EV_KEY:
data = evdev.categorize(ev)
if data.keystate == 1:
if data.keystate == 1: # key-down
if data.scancode == 28:
print("sending barcode:", val)
self.send(val[0:2], val[2:])
@ -82,15 +83,27 @@ class TypingPool(Bureau):
else:
try:
new_key = KEYS[data.scancode]
if new_key == "LSHFT" or new_key == "RSHFT":
if ctrl and (new_key == "j"):
self.log.debug("ignoring line-feed")
elif new_key == "LSHFT" or new_key == "RSHFT":
upper = True
elif new_key == "LCTRL" or new_key == "RCTRL":
ctrl = True
else:
if upper:
new_key = new_key.upper()
upper = False
val += new_key
except KeyError:
print("Error invalid keycode:", data.scancode)
if data.keystate == 0: # key-up for mod-keys
try:
new_key = KEYS[data.scancode]
if new_key == "LSHFT" or new_key == "RSHFT":
upper = False
if new_key == "LCTRL" or new_key == "RCTRL":
ctrl = False
except KeyError:
print("Error invalid keycode:", data.scancode)
def main():

Loading…
Cancel
Save