Skip to content

Commit 2117f8f

Browse files
committed
add screensaver functionality to display server
1 parent a8deb6c commit 2117f8f

2 files changed

Lines changed: 98 additions & 1 deletion

File tree

assets/fonts/dotmat.ttf

29.5 KB
Binary file not shown.

display_server.py

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1+
import time
2+
import math
3+
4+
from collections import namedtuple
5+
6+
from PIL import Image
7+
from PIL import ImageDraw
8+
from PIL import ImageFont
9+
from datetime import datetime, timedelta
10+
111
import socket
212
import struct
13+
import threading
314
import zlib
415

516

17+
Size = namedtuple('Size', ['height', 'width'])
18+
19+
620
class DataListener:
721
def __init__(self, controller, ip='', port=1337):
822
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@@ -36,10 +50,82 @@ def __iter__(self):
3650
# continue
3751
elif len(data) != self.frame_size:
3852
self.log('Error receiving UDP frame: Invalid frame size: {}'.format(len(data)))
39-
continue
53+
continue0
4054
yield 'udp:'+addr, data
4155

4256

57+
class PauseFiller(threading.Thread):
58+
59+
def __init__(self, motd, controller):
60+
super().__init__()
61+
self.motd = motd
62+
self.controller = controller
63+
self.stop = False
64+
self.font = ImageFont.truetype('assets/fonts/dotmat.ttf', 10)
65+
66+
def get_pad_data(self, image, image_data, window_size):
67+
height, width, channels = image_data.shape
68+
69+
height_padding = window_size.height - height
70+
padding_top = height_padding // 2
71+
padding_top_data = np.zeros((padding_top, image.width, channels))
72+
padding_bottom = math.ceil(height_padding / 2)
73+
padding_bottom_data = np.zeros((padding_bottom, image.width, channels))
74+
75+
return padding_top_data, padding_bottom_data
76+
77+
def display_text(self, image, slide_image=False):
78+
image_data = np.array(image)
79+
window_size = Size(height=self.controller.image_height, width=self.controller.image_width)
80+
window_position = 0
81+
82+
padding_top, padding_bottom = self.get_pad_data(image, image_data, window_size)
83+
image_data = np.vstack((padding_top, image_data, padding_bottom))
84+
85+
while True:
86+
if self.stop:
87+
break
88+
89+
data = image_data.copy()[:, window_position:min(window_position + window_size[1], image.width), ...]
90+
height, width, channels = data.shape
91+
92+
width_padding = np.zeros((window_size.height, window_size.width - width, channels))
93+
data = np.hstack((data, width_padding))
94+
95+
self.controller.display(data)
96+
if slide_image:
97+
window_position += 1
98+
if window_position >= image.width:
99+
window_position = 0
100+
time.sleep(1000 / 20 / 1000)
101+
102+
def run(self):
103+
text_image = Image.new('RGB', self.font.getsize(self.motd))
104+
draw = ImageDraw.Draw(text_image)
105+
draw.fontmode = "1"
106+
draw.text((0, 0), self.motd, font=self.font, fill=(255, 255, 255))
107+
108+
self.display_text(text_image, slide_image=True)
109+
110+
111+
class IdleWatcher(threading.Thread):
112+
113+
def __init__(self, idle_time):
114+
super().__init__()
115+
self.idle_time = idle_time
116+
117+
def run(self):
118+
global pause_filler
119+
while True:
120+
time.sleep(timedelta(seconds=self.idle_time).total_seconds())
121+
time_now = datetime.utcnow()
122+
time_delta = (time_now - last_frame).total_seconds()
123+
124+
if time_delta > self.idle_time and not pause_filler.is_alive():
125+
pause_filler = PauseFiller(args.motd, controller)
126+
pause_filler.start()
127+
128+
43129
if __name__ == "__main__":
44130
import argparse
45131
import numpy as np
@@ -48,14 +134,25 @@ def __iter__(self):
48134
parser = argparse.ArgumentParser(description="Remote Display Server that shows already rendered images")
49135
parser.add_argument('config', help='path to config file for matelight')
50136
parser.add_argument('-p', '--port', type=int, default=1337, help='port to listen on')
137+
parser.add_argument('--idle-time', type=int, default=20, help='max. number of seconds matelight shall idle')
138+
parser.add_argument('--motd', default='Contribute on code.ilexlux.xyz!')
51139

52140
args = parser.parse_args()
53141

54142
controller = LEDController(args.config)
55143
server = DataListener(controller, port=args.port)
144+
last_frame = datetime.utcnow()
145+
146+
pause_filler = PauseFiller(args.motd, controller)
147+
pause_filler.start()
148+
149+
idle_watcher = IdleWatcher(args.idle_time)
150+
idle_watcher.start()
56151

57152
try:
58153
for _, frame in server:
154+
last_frame = datetime.utcnow()
155+
pause_filler.stop = True
59156
frame = np.fromstring(frame, dtype=np.uint8).reshape(controller.image_height, controller.image_width, 3)
60157
controller.display(frame)
61158

0 commit comments

Comments
 (0)