annotate web_to_mqtt.py @ 5:d97a5930db7e

closer
author drewp@bigasterisk.com
date Wed, 06 Mar 2024 16:38:58 -0800
parents e273cc60b389
children e36abecb48a1
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
1 import asyncio
5
drewp@bigasterisk.com
parents: 4
diff changeset
2 import itertools
drewp@bigasterisk.com
parents: 4
diff changeset
3 import random
drewp@bigasterisk.com
parents: 4
diff changeset
4 import struct
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
5 import subprocess
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
6 import tempfile
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
7
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
8 import aiomqtt
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
9 from PIL import Image, ImageChops
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
10
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
11
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
12 class WebRenderer:
5
drewp@bigasterisk.com
parents: 4
diff changeset
13
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
14 def __init__(self):
5
drewp@bigasterisk.com
parents: 4
diff changeset
15 self.chrome_proc = subprocess.Popen(["google-chrome", "--headless"])
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
16 print("Chrome subprocess started.")
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
17
5
drewp@bigasterisk.com
parents: 4
diff changeset
18 async def capture_screenshot(self, url) -> Image.Image:
drewp@bigasterisk.com
parents: 4
diff changeset
19 out = tempfile.NamedTemporaryFile(suffix=".png", prefix='webrenderer_')
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
20 screenshot_command = [
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
21 "google-chrome",
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
22 "--headless",
5
drewp@bigasterisk.com
parents: 4
diff changeset
23 "--window-size=320,320",
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
24 f"--screenshot={out.name}",
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
25 url,
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
26 ]
5
drewp@bigasterisk.com
parents: 4
diff changeset
27 subprocess.run(screenshot_command,
drewp@bigasterisk.com
parents: 4
diff changeset
28 stdout=subprocess.DEVNULL,
drewp@bigasterisk.com
parents: 4
diff changeset
29 stderr=subprocess.DEVNULL)
drewp@bigasterisk.com
parents: 4
diff changeset
30 return Image.open(out.name).convert('RGB')
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
31
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
32
5
drewp@bigasterisk.com
parents: 4
diff changeset
33 block = 320 // 10
drewp@bigasterisk.com
parents: 4
diff changeset
34
drewp@bigasterisk.com
parents: 4
diff changeset
35 dirtyQueue = {}
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
36
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
37
5
drewp@bigasterisk.com
parents: 4
diff changeset
38 async def check_for_changes(renderer, client, last_image):
drewp@bigasterisk.com
parents: 4
diff changeset
39 current_image = await renderer.capture_screenshot(
drewp@bigasterisk.com
parents: 4
diff changeset
40 "http://localhost:8002/front-door-display/scheduleLcd.html")
drewp@bigasterisk.com
parents: 4
diff changeset
41 diff_image = ImageChops.difference(last_image, current_image)
drewp@bigasterisk.com
parents: 4
diff changeset
42 for y in range(0, current_image.height, block):
drewp@bigasterisk.com
parents: 4
diff changeset
43 for x in range(0, current_image.width, block):
drewp@bigasterisk.com
parents: 4
diff changeset
44 box = (x, y, x + block, y + block)
drewp@bigasterisk.com
parents: 4
diff changeset
45 region = diff_image.crop(box)
drewp@bigasterisk.com
parents: 4
diff changeset
46 if region.getbbox():
drewp@bigasterisk.com
parents: 4
diff changeset
47 dirtyQueue[(x, y)] = current_image.crop(box)
drewp@bigasterisk.com
parents: 4
diff changeset
48 await asyncio.sleep(0)
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
49
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
50 return current_image
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
51
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
52
5
drewp@bigasterisk.com
parents: 4
diff changeset
53 async def sendDirty(client):
drewp@bigasterisk.com
parents: 4
diff changeset
54 while True:
drewp@bigasterisk.com
parents: 4
diff changeset
55 if dirtyQueue:
drewp@bigasterisk.com
parents: 4
diff changeset
56 # pos = random.choice(list(dirtyQueue.keys()))
drewp@bigasterisk.com
parents: 4
diff changeset
57 pos = min(list(dirtyQueue.keys()))
drewp@bigasterisk.com
parents: 4
diff changeset
58 img = dirtyQueue.pop(pos)
drewp@bigasterisk.com
parents: 4
diff changeset
59 await tell_lcd(client, pos[0], pos[1], img)
drewp@bigasterisk.com
parents: 4
diff changeset
60 await asyncio.sleep(.15)
drewp@bigasterisk.com
parents: 4
diff changeset
61
drewp@bigasterisk.com
parents: 4
diff changeset
62 framesSent = itertools.count()
drewp@bigasterisk.com
parents: 4
diff changeset
63
drewp@bigasterisk.com
parents: 4
diff changeset
64
drewp@bigasterisk.com
parents: 4
diff changeset
65 async def tell_lcd(client: aiomqtt.Client, x: int, y: int,
drewp@bigasterisk.com
parents: 4
diff changeset
66 region: Image.Image):
drewp@bigasterisk.com
parents: 4
diff changeset
67 seq = next(framesSent)
drewp@bigasterisk.com
parents: 4
diff changeset
68 msg = struct.pack('HHHHH', seq, x, y, region.width, region.height) + region.tobytes()
drewp@bigasterisk.com
parents: 4
diff changeset
69 print(f'send {seq=} {x=} {y=} {region.width=} {region.height=} ', end='\r', flush=True)
drewp@bigasterisk.com
parents: 4
diff changeset
70 await client.publish('display/squib/updates', msg, qos=0)
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
71
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
72
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
73 async def main():
5
drewp@bigasterisk.com
parents: 4
diff changeset
74 # also listen for dirty msgs from the web page; see ts.
drewp@bigasterisk.com
parents: 4
diff changeset
75 # also get notified of new mqtt listeners who need a full image refresh.
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
76 renderer = WebRenderer()
5
drewp@bigasterisk.com
parents: 4
diff changeset
77 async with aiomqtt.Client("mqtt2") as client:
drewp@bigasterisk.com
parents: 4
diff changeset
78 asyncio.create_task(sendDirty(client))
drewp@bigasterisk.com
parents: 4
diff changeset
79 last_image = Image.new('RGB', (320, 320))
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
80 while True:
5
drewp@bigasterisk.com
parents: 4
diff changeset
81 last_image = await check_for_changes(renderer, client, last_image)
drewp@bigasterisk.com
parents: 4
diff changeset
82 # we could get the web page to tell us when any dom changes
drewp@bigasterisk.com
parents: 4
diff changeset
83 await asyncio.sleep(5)
4
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
84
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
85
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
86 if __name__ == "__main__":
e273cc60b389 draft of web-to-lcd and simulator
drewp@bigasterisk.com
parents:
diff changeset
87 asyncio.run(main())