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