-
Notifications
You must be signed in to change notification settings - Fork 31
Expand file tree
/
Copy pathmain.py
More file actions
218 lines (163 loc) · 5.64 KB
/
Copy pathmain.py
File metadata and controls
218 lines (163 loc) · 5.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import asyncio
import json
# import logging
import os
import signal
import sys
from pprint import pprint
import aiomqtt # type: ignore
from PIL import Image, ImageDraw, ImageFont # type: ignore
import helpers
# Hardware
# We use Waveshare 2.9inch E-Ink display module (black and white) SPI
# https://www.waveshare.com/2.9inch-e-paper-module.htm
# https://www.waveshare.com/wiki/2.9inch_e-Paper_Module
# Software
# We use the Python implementation provided by Waveshare
# https://github.com/waveshareteam/e-Paper/tree/master/RaspberryPi_JetsonNano/python/lib/waveshare_epd
# As well as Pillow to draw the image rendered to the screen
# https://pillow.readthedocs.io/en/stable/index.html
# https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html
# https://pillow.readthedocs.io/en/stable/handbook/text-anchors.html
# Enable waveshare_epd logs
# logging.basicConfig(level=logging.DEBUG)
client = None
loop = asyncio.new_event_loop()
dirname = os.path.dirname(__file__)
picdir = os.path.join(dirname, "e-paper/pic")
libdir = os.path.join(dirname, "e-paper/lib")
if os.path.exists(libdir):
sys.path.append(libdir)
machine_name = None
epd = None
fontsmall = ImageFont.truetype(os.path.join(picdir, "Font.ttc"), 18)
fontnormal = ImageFont.truetype(os.path.join(picdir, "Font.ttc"), 19)
fontbig = ImageFont.truetype(os.path.join(picdir, "Font.ttc"), 22)
image = None
draw = None
epd2in9_V2 = None
logo = Image.open(os.path.join(dirname, "fairscope.bmp"))
width = None
height = None
BAR_HEIGHT = 26
def drawStatus(status=""):
assert draw is not None
assert width is not None
assert height is not None
# Black bar across the bottom
draw.rectangle((0, height - BAR_HEIGHT, width, height), fill=0)
# White text centered in the bar
draw.text(
(width // 2, height - BAR_HEIGHT // 2), text=status, anchor="mm", font=fontnormal, fill=255
)
def drawMachineName(machine_name):
assert width is not None
assert height is not None
assert draw is not None
# Black bar across the top
draw.rectangle((0, 0, width, BAR_HEIGHT), fill=0)
# White text centered in the bar
draw.text(
(width // 2, BAR_HEIGHT // 2 + 2), text=machine_name, anchor="mm", font=fontbig, fill=255
)
def drawBrand():
assert width is not None
assert height is not None
assert image is not None
# Paste logo centered in the middle white area (between the two bars)
middle_top = BAR_HEIGHT
middle_bottom = height - BAR_HEIGHT
middle_h = middle_bottom - middle_top
x = (width - logo.width) // 2
y = middle_top + (middle_h - logo.height) // 2
image.paste(logo, (x, y))
def render(status=""):
assert epd is not None
assert width is not None
assert height is not None
global image, draw
if image is None or draw is None:
image = Image.new("1", (width, height), 255)
draw = ImageDraw.Draw(image)
# clear screen
# # TODO: only clear relevant area ?
draw.rectangle((0, 0, height, width), fill=255)
# top black bar with machine_name
drawMachineName(machine_name)
# center logo
drawBrand()
# bottom black bar with status
drawStatus(status)
epd.init()
epd.Clear(0xFF)
epd.display_Base(epd.getbuffer(image))
# epd.sleep()
async def configure(config):
assert draw is not None
assert width is not None
assert height is not None
assert epd is not None
assert image is not None
status = config.get("status", "")
drawStatus(status=status)
newimage = image.crop((0, height - BAR_HEIGHT, width, height))
image.paste(newimage, (0, height - BAR_HEIGHT))
epd.display_Partial(epd.getbuffer(image))
async def clear():
assert epd is not None
assert width is not None
assert height is not None
# not functional
epd.init()
epd.Clear(0xFF)
epd.sleep()
# image = Image.new("1", (width, height), 255)
# draw = ImageDraw.Draw(image)
# draw.rectangle((0, 0, height, width), fill=255)
# epd.display_Partial(epd.getbuffer(image))
async def start() -> None:
# There is no display on PlanktoScope HAT < 3.3
if (await helpers.get_hat_version()) != 3.3:
sys.exit()
global epd, epd2in9_V2, width, height, machine_name
from waveshare_epd import epd2in9_V2 # type: ignore
epd = epd2in9_V2.EPD()
# horizontal
width = epd.height
height = epd.width
machine_name = helpers.get_machine_name()
render(status="http://192.168.4.1")
global client
client = aiomqtt.Client(hostname="localhost", port=1883, protocol=aiomqtt.ProtocolVersion.V5)
task_group = asyncio.TaskGroup()
async with client, task_group:
_ = await asyncio.gather(
client.subscribe("display"),
)
async for message in client.messages:
task_group.create_task(handle_message(message))
async def handle_message(message) -> None:
if not message.topic.matches("display"):
return
payload = json.loads(message.payload.decode("utf-8"))
pprint(payload)
action = payload.get("action")
if action is not None:
await handle_action(action, payload)
if client is not None:
await helpers.mqtt_reply(client, message)
async def handle_action(action: str, payload) -> None:
if action == "clear":
await clear()
if action == "configure" and "config" in payload:
await configure(payload["config"])
async def stop() -> None:
if epd is not None:
render(status="OFF")
loop.stop()
for s in (signal.SIGINT, signal.SIGTERM):
loop.add_signal_handler(s, lambda: asyncio.ensure_future(stop()))
def main():
loop.run_until_complete(start())
if __name__ == "__main__":
main()