Mercurial > code > home > repos > front-door-display
diff web_to_mqtt.py @ 5:d97a5930db7e
closer
author | drewp@bigasterisk.com |
---|---|
date | Wed, 06 Mar 2024 16:38:58 -0800 |
parents | e273cc60b389 |
children | e36abecb48a1 |
line wrap: on
line diff
--- a/web_to_mqtt.py Tue Mar 05 18:12:15 2024 -0800 +++ b/web_to_mqtt.py Wed Mar 06 16:38:58 2024 -0800 @@ -1,6 +1,7 @@ import asyncio -import base64 -import io +import itertools +import random +import struct import subprocess import tempfile @@ -9,69 +10,77 @@ class WebRenderer: + def __init__(self): - self.chrome_proc = subprocess.Popen(["google-chrome"]) + self.chrome_proc = subprocess.Popen(["google-chrome", "--headless"]) print("Chrome subprocess started.") - def capture_screenshot(self, url, output_path): - out = tempfile.NamedTemporaryFile(suffix=".png") + async def capture_screenshot(self, url) -> Image.Image: + out = tempfile.NamedTemporaryFile(suffix=".png", prefix='webrenderer_') screenshot_command = [ "google-chrome", "--headless", + "--window-size=320,320", f"--screenshot={out.name}", url, ] - subprocess.run(screenshot_command) - return Image.open(out.name) - + subprocess.run(screenshot_command, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + return Image.open(out.name).convert('RGB') -async def render_webpage_to_png(): - # Code to render the webpage to a PNG - # Replace this with your actual code to render the webpage to a PNG - # For example, you can use libraries like Selenium or requests-html to render the webpage and capture a screenshot - pass +block = 320 // 10 + +dirtyQueue = {} -async def check_for_changes(renderer, last_image): - - renderer.capture_screenshot("https://en.wikipedia.org", "/tmp/output.png") +async def check_for_changes(renderer, client, last_image): + current_image = await renderer.capture_screenshot( + "http://localhost:8002/front-door-display/scheduleLcd.html") + diff_image = ImageChops.difference(last_image, current_image) + for y in range(0, current_image.height, block): + for x in range(0, current_image.width, block): + box = (x, y, x + block, y + block) + region = diff_image.crop(box) + if region.getbbox(): + dirtyQueue[(x, y)] = current_image.crop(box) + await asyncio.sleep(0) - current_image = await render_webpage_to_png() - if last_image is not None: - diff_image = ImageChops.difference(last_image, current_image) - # Iterate over 64x64 pixel squares and check for changes - for y in range(0, diff_image.height, 64): - for x in range(0, diff_image.width, 64): - box = (x, y, x + 64, y + 64) - region = diff_image.crop(box) - if (region.getbbox() - ): # Check if region is not empty (i.e., contains changes) - # Send changed square as MQTT message -queue these - await send_mqtt_message(region) return current_image -async def send_mqtt_message(region): - # Convert changed region to base64 encoded string - buffer = io.BytesIO() - region.save(buffer, format="PNG") - base64_image = base64.b64encode(buffer.getvalue()).decode("utf-8") - mqtt_client = aiomqtt.Client("mqtt_client") - await mqtt_client.connect("mqtt://broker.example.com") - await mqtt_client.publish("changed_squares", base64_image, qos=1) - await mqtt_client.disconnect() +async def sendDirty(client): + while True: + if dirtyQueue: + # pos = random.choice(list(dirtyQueue.keys())) + pos = min(list(dirtyQueue.keys())) + img = dirtyQueue.pop(pos) + await tell_lcd(client, pos[0], pos[1], img) + await asyncio.sleep(.15) + +framesSent = itertools.count() + + +async def tell_lcd(client: aiomqtt.Client, x: int, y: int, + region: Image.Image): + seq = next(framesSent) + msg = struct.pack('HHHHH', seq, x, y, region.width, region.height) + region.tobytes() + print(f'send {seq=} {x=} {y=} {region.width=} {region.height=} ', end='\r', flush=True) + await client.publish('display/squib/updates', msg, qos=0) async def main(): - # also listen for dirty msgs from the web page; see ts + # also listen for dirty msgs from the web page; see ts. + # also get notified of new mqtt listeners who need a full image refresh. renderer = WebRenderer() - async with aiomqtt.Client("mqtt_client") as client: - last_image = None + async with aiomqtt.Client("mqtt2") as client: + asyncio.create_task(sendDirty(client)) + last_image = Image.new('RGB', (320, 320)) while True: - last_image = await check_for_changes(renderer,last_image) - await asyncio.sleep(2) # Adjust the interval as needed + last_image = await check_for_changes(renderer, client, last_image) + # we could get the web page to tell us when any dom changes + await asyncio.sleep(5) if __name__ == "__main__":