<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>sasha</title><author><name>sasha</name></author><id>https://teletype.in/atom/aogz</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/aogz?offset=0"></link><link rel="alternate" type="text/html" href="https://blog.aogz.me/?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=aogz"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/aogz?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-05-05T13:43:18.909Z</updated><entry><id>aogz:ultimate-engineering-meeting-tool</id><link rel="alternate" type="text/html" href="https://blog.aogz.me/ultimate-engineering-meeting-tool?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=aogz"></link><title>Streamlining Team Collaboration: How Webfuse Spaces Transform Meetings</title><published>2024-09-25T10:42:23.652Z</published><updated>2024-09-27T09:02:44.425Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/f3/7d/f37d8a93-34ca-415b-b299-fc8a72150ce7.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/03/e8/03e813db-f22a-4e1c-88f0-8179132a0940.png&quot;&gt;Disclaimer: Meetings are just one of the many use cases for WebFuse. Our web augmentation platform offers much more. However, using WebFuse for standups, interviews, PR reviews, and other routine team activities feels like a perfect match.</summary><content type="html">
  &lt;figure id=&quot;3vCu&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/03/e8/03e813db-f22a-4e1c-88f0-8179132a0940.png&quot; width=&quot;1920&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;m1ux&quot;&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Meetings are just one of the many use cases for WebFuse. Our web augmentation platform offers much more. However, using WebFuse for standups, interviews, PR reviews, and other routine team activities feels like a perfect match.&lt;/p&gt;
  &lt;p id=&quot;40xo&quot;&gt;Now, let’s dive into our team huddle use case and explore it in detail.&lt;/p&gt;
  &lt;h2 id=&quot;M5nk&quot;&gt;Why Other Meeting Tools Fall Short&lt;/h2&gt;
  &lt;h3 id=&quot;XTtl&quot;&gt;1. Information Overload&lt;/h3&gt;
  &lt;p id=&quot;2Gbt&quot;&gt;Team members often struggle to find important links, resources, and documentation scattered across different platforms. This fragmentation leads to wasted time and frustration. Sure, you can pin links in various places, but wouldn’t it be ideal to have them all in one fine-tuned space?&lt;/p&gt;
  &lt;h3 id=&quot;Zaf7&quot;&gt;2. Inefficient Collaboration with Original Sources (e.g., Code Reviews)&lt;/h3&gt;
  &lt;p id=&quot;um55&quot;&gt;Code reviews can be cumbersome, involving multiple screens and tools, which makes providing feedback challenging and leads to lengthy back-and-forth communication.&lt;/p&gt;
  &lt;h3 id=&quot;iQLN&quot;&gt;3. Disjointed Communication During Meetings&lt;/h3&gt;
  &lt;p id=&quot;zhrk&quot;&gt;Traditional video conferencing tools often cause distractions as participants switch between tabs or applications, especially when sharing content. This results in disengagement and miscommunication.&lt;/p&gt;
  &lt;h3 id=&quot;kYBf&quot;&gt;4. Integration Challenges with Extensions&lt;/h3&gt;
  &lt;p id=&quot;iZlm&quot;&gt;Managing browser extensions individually can lead to inconsistencies and extra steps for team members, limiting access to essential tools.&lt;/p&gt;
  &lt;h2 id=&quot;wkpD&quot;&gt;Why We Love Using WebFuse Spaces&lt;/h2&gt;
  &lt;h3 id=&quot;BUpP&quot;&gt;Accessing Web Apps Natively in WebFuse Spaces&lt;/h3&gt;
  &lt;figure id=&quot;7Duc&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/88/f6/88f6e300-1d52-493d-a685-3d5dd9a2415c.png&quot; width=&quot;1920&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;MHma&quot;&gt;One of WebFuse’s standout features is its ability to interact with any website natively in all participants&amp;#x27; browsers. This has been a game-changer for our team, especially during code reviews, new ticket assessments, and technical interviews. Imagine discussing a pull request with all team members seeing the same code in real time—without any quality loss. It’s like upgrading from dial-up to high-speed fiber optics!&lt;/p&gt;
  &lt;h3 id=&quot;EPZp&quot;&gt;Configuring Spaces with Apps&lt;/h3&gt;
  &lt;p id=&quot;6sjZ&quot;&gt;In Webfuse, &lt;strong&gt;apps&lt;/strong&gt; are essentially the features you enable within a space, allowing you to tailor it to your team&amp;#x27;s workflow. For our space, we’ve configured several essential apps to enhance collaboration:&lt;/p&gt;
  &lt;ul id=&quot;lRUJ&quot;&gt;
    &lt;li id=&quot;UpCk&quot;&gt;&lt;strong&gt;Chat:&lt;/strong&gt; Enables real-time text communication, perfect for quick messages or sharing links during meetings.&lt;/li&gt;
    &lt;li id=&quot;bSaH&quot;&gt;&lt;strong&gt;Video Chat:&lt;/strong&gt; Facilitates face-to-face interactions, as mentioned earlier, with the flexibility to adapt the layout.&lt;/li&gt;
    &lt;li id=&quot;wTyK&quot;&gt;&lt;strong&gt;Screen Sharing:&lt;/strong&gt; Allows you to share a native desktop application.&lt;/li&gt;
    &lt;li id=&quot;ER80&quot;&gt;&lt;strong&gt;Session Recording:&lt;/strong&gt; Automatically records all meetings, providing a valuable resource for those who missed the meeting or need to revisit discussions.&lt;/li&gt;
    &lt;li id=&quot;EHMu&quot;&gt;&lt;strong&gt;File Sharing:&lt;/strong&gt; Allows team members to upload and share documents or files within the space.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;gVoX&quot;&gt;By selectively enabling these apps, we’ve created a versatile environment that caters to our daily standups, reviews, and collaborative sessions. &lt;/p&gt;
  &lt;h3 id=&quot;yQGW&quot;&gt;Enhancing Productivity with Custom Extensions&lt;/h3&gt;
  &lt;figure id=&quot;khXy&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/66/94/66949d47-46b1-4969-ab67-a79b382131b1.png&quot; width=&quot;1919&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;9va5&quot;&gt;To streamline our workflow, I developed a couple of browser extensions for GitHub that are now indispensable:&lt;/p&gt;
  &lt;ol id=&quot;L99S&quot;&gt;
    &lt;li id=&quot;Estb&quot;&gt;&lt;strong&gt;Sprint Points Calculator:&lt;/strong&gt; This extension calculates sprint points during planning sessions and retrospectives, eliminating the need for mental math.&lt;/li&gt;
    &lt;li id=&quot;soXK&quot;&gt;&lt;strong&gt;Pull Request Line Counter:&lt;/strong&gt; We limit pull requests to 200 lines (excluding documentation and tests). This extension automatically calculates the changed lines in each PR.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;4t3O&quot;&gt;The best part? I ported both extensions to WebFuse Spaces without modifications. They function perfectly within the WebFuse environment, providing all team members access to these tools without installing anything in their browsers.&lt;/p&gt;
  &lt;h3 id=&quot;8LX8&quot;&gt;Space Start Page and Bookmarks&lt;/h3&gt;
  &lt;figure id=&quot;77r7&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/3c/89/3c89e9db-46db-4f8c-8fe3-23e86270a491.png&quot; width=&quot;1919&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Yv8B&quot;&gt;WebFuse isolates everything within a single space. Whether it&amp;#x27;s space settings, apps, extensions, or a custom start page containing all the links we use for daily standups, everything is centralized. No more frantic searches or asking, &amp;quot;Does anyone have the link to the new issues?&amp;quot; Everyone can access essential resources in one place.&lt;/p&gt;
  &lt;p id=&quot;ZF8U&quot;&gt;We also use a bookmarking feature that allows space members to add their bookmarks directly to the space. This collective curation fosters shared ownership and keeps everyone on the same page—literally. It’s like having a team bookshelf, but more organized!&lt;/p&gt;
  &lt;h3 id=&quot;gqMF&quot;&gt;Video Chat and Recording&lt;/h3&gt;
  &lt;p id=&quot;zYBw&quot;&gt;WebFuse also integrates video chat, leveraging Vonage with a wrapper that provides multiple configuration options. This flexibility allows us to adapt to different scenarios:&lt;/p&gt;
  &lt;ul id=&quot;hc5s&quot;&gt;
    &lt;li id=&quot;cp30&quot;&gt;&lt;strong&gt;Full-screen mode:&lt;/strong&gt; Ideal for in-depth discussions, keeping the focus on the topic at hand.&lt;/li&gt;
    &lt;li id=&quot;ByHG&quot;&gt;&lt;strong&gt;Sidebar layout:&lt;/strong&gt; Perfect for stand-ups, allowing participants to keep an eye on content like tickets, PRs, or code snippets.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;aSGm&quot;&gt;This adaptability is particularly valuable during technical interviews. Candidates often compliment how seamless it is to discuss code in the same tab as the video chat. It transforms what could be a fragmented experience into an engaging conversation.&lt;/p&gt;
  &lt;p id=&quot;NBDK&quot;&gt;Another valuable feature of Webfuse&amp;#x27;s platform is the &lt;strong&gt;recording capability&lt;/strong&gt;. All our team meetings are automatically recorded and stored securely within an s3 bucket. Not only can team members catch up if they miss a meeting, but it also provides a comprehensive record of discussions, decisions, and action items.&lt;/p&gt;
  &lt;h2 id=&quot;R2Q2&quot;&gt;Conclusion&lt;/h2&gt;
  &lt;figure id=&quot;R7oL&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/d5/b7/d5b72983-03bd-4f87-890f-5bf69692bb67.png&quot; width=&quot;640&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;mory&quot;&gt;WebFuse has become an essential part of our team’s workflow, enhancing collaboration, improving efficiency, and making remote meetings more enjoyable. By adjusting the space to our workflow, integrating key tools like GitHub, and offering a flexible video chat experience, we’ve turned what could be a mundane process into a dynamic, productive environment.&lt;/p&gt;
  &lt;p id=&quot;JLt6&quot;&gt;If you’re looking to elevate your team’s meetings from frustrating to innovative, give WebFuse a try.&lt;/p&gt;

</content></entry><entry><id>aogz:TQsy_FJDWi6</id><link rel="alternate" type="text/html" href="https://blog.aogz.me/TQsy_FJDWi6?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=aogz"></link><title>Light git prompt</title><published>2023-12-21T21:05:07.540Z</published><updated>2023-12-21T21:05:07.540Z</updated><summary type="html">export GIT_PS1_SHOWDIRTYSTATE=true
export GIT_PS1_SHOWUNTRACKEDFILES=true
export PS1='\[\033[32m\]\u@\h\[\033[00m\]:\[\033[1;36m\]\w\[\033[0;95m\]$(__git_ps1)\[\033[00m\]\$ '</summary><content type="html">
  &lt;p id=&quot;3Okf&quot;&gt;export GIT_PS1_SHOWDIRTYSTATE=true&lt;br /&gt;export GIT_PS1_SHOWUNTRACKEDFILES=true&lt;br /&gt;export PS1=&amp;#x27;\[\033[32m\]\u@\h\[\033[00m\]:\[\033[1;36m\]\w\[\033[0;95m\]$(__git_ps1)\[\033[00m\]\$ &amp;#x27;&lt;br /&gt;&lt;/p&gt;

</content></entry><entry><id>aogz:HDvW--waROF</id><link rel="alternate" type="text/html" href="https://blog.aogz.me/HDvW--waROF?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=aogz"></link><title>Growing a loop backed ZFS pool </title><published>2023-04-13T15:39:46.894Z</published><updated>2023-04-13T20:33:17.744Z</updated><summary type="html">LXD doesn’t let you directly grow a loop backed ZFS pool, but you can do so with:</summary><content type="html">
  &lt;p id=&quot;Ilyk&quot;&gt;LXD doesn’t let you directly grow a loop backed ZFS pool, but you can do so with:&lt;/p&gt;
  &lt;p id=&quot;7gX6&quot;&gt;&lt;code&gt;sudo truncate -s +5G /var/snap/lxd/common/lxd/disks/default.img &lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;NZs3&quot;&gt;&lt;code&gt;sudo zpool set autoexpand=on default &lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;RGAA&quot;&gt;&lt;code&gt;sudo zpool online -e lxd /var/snap/lxd/common/lxd/disks/default.img &lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;748N&quot;&gt;&lt;code&gt;sudo zpool set autoexpand=off default &lt;/code&gt;&lt;/p&gt;

</content></entry><entry><id>aogz:QtgHqzf0G9O</id><link rel="alternate" type="text/html" href="https://blog.aogz.me/QtgHqzf0G9O?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=aogz"></link><title>Fix Fn key for Keychron K2 in Linux</title><published>2023-01-19T21:27:59.720Z</published><updated>2023-12-25T20:49:41.398Z</updated><summary type="html">To temporary fix it, try</summary><content type="html">
  &lt;p id=&quot;Z2N3&quot;&gt;To temporary fix it, try&lt;/p&gt;
  &lt;blockquote id=&quot;O4rX&quot;&gt;echo 0 | sudo tee /sys/module/hid_apple/parameters/fnmode&lt;/blockquote&gt;
  &lt;p id=&quot;E7eV&quot;&gt;or&lt;/p&gt;
  &lt;blockquote id=&quot;psD7&quot;&gt;echo 1 | sudo tee /sys/module/hid_apple/parameters/fnmode&lt;/blockquote&gt;
  &lt;p id=&quot;Dtcu&quot;&gt;To apply changes permanently, do&lt;/p&gt;
  &lt;blockquote id=&quot;5BP7&quot;&gt;echo &amp;quot;options hid_apple fnmode=0&amp;quot; | sudo tee -a /etc/modprobe.d/hid_apple.conf ; &lt;/blockquote&gt;

</content></entry><entry><id>aogz:zXN1ExTCLO5</id><link rel="alternate" type="text/html" href="https://blog.aogz.me/zXN1ExTCLO5?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=aogz"></link><title>Catching a mouse using Raspberry PI</title><published>2022-11-27T19:38:58.967Z</published><updated>2023-01-01T21:26:42.206Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img3.teletype.in/files/a9/a4/a9a4b10c-1545-4720-b148-350dc262be3a.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/b8/d3/b8d37b1d-fad5-4ee4-a9ad-638fed604077.jpeg&quot;&gt;To begin with, I live in Amsterdam, The Netherlands. This is a beatiful country with lots of canals, picturesque streets and cozy houses. Cozy houses are also old. Like, really old. The oldest house of Amsterdam, which is located around 100m from my office was built in 1425.</summary><content type="html">
  &lt;h2 id=&quot;6lZb&quot;&gt;Chapter 0: Introduction&lt;/h2&gt;
  &lt;p id=&quot;P6gf&quot;&gt;To begin with, I live in Amsterdam, The Netherlands. This is a beatiful country with lots of canals, picturesque streets and cozy houses. Cozy houses are also old. Like, really old. The oldest house of Amsterdam, which is located around 100m from my office was built in 1425.&lt;/p&gt;
  &lt;p id=&quot;lWzb&quot;&gt;There is one downside though. Mice. There are mice almost in every old house. It&amp;#x27;s okay if they just live in walls and much worse if they find a way into your appartment. And please forget about mouse traps, the only efficient way to get rid of them is to find the mouse entrance to your appartment and close it. Sometimes it&amp;#x27;s not easy at all. &lt;/p&gt;
  &lt;p id=&quot;XLtK&quot;&gt;This is the moment, where Raspberry PI, Python and Open CV comes to rescue. My idea was to build a piece of software that will detect mice&amp;#x27; motion in the kitchen and notify me about it. It should also work in the dark room, cause normally the mouse was coming at night, and I didn&amp;#x27;t plan to leave the lights on for the whole night. And last, but not least, the process of building the tool should be fun!&lt;/p&gt;
  &lt;h2 id=&quot;P5QC&quot;&gt;&lt;/h2&gt;
  &lt;h2 id=&quot;XLN5&quot;&gt;Chapter 1: Hardware&lt;/h2&gt;
  &lt;p id=&quot;NXJ1&quot;&gt;I already had a Raspbery PI 3B, which I used for a lot of other fun projects, so I decided to buy a camera and a display (so I can see that little monster). So I went to good old pimoroni.com. Luckily it was Black Friday period, so everything I needed was on nice discounts.&lt;/p&gt;
  &lt;p id=&quot;574z&quot;&gt;So, here is the list of equipment required to find the mouse:&lt;/p&gt;
  &lt;ol id=&quot;1E58&quot;&gt;
    &lt;li id=&quot;1hOM&quot;&gt;&lt;a href=&quot;https://www.geekbuying.com/item/Raspberry-Pi-3-Model-B-Project-Board-Development-Board-Mini-PC-Broadcom-BCM2837-1-2GHz-1GB-RAM-Bluetooth-4-1-365154.html?Currency=USD&amp;idev_id=775&amp;idev_subid=350474&amp;idev_tid1=350474&amp;utm_source=affiliate.geekbuying.com&amp;utm_medium=affiliate_775&amp;utm_campaign=775&quot; target=&quot;_blank&quot;&gt;Raspberry Pi 3B&lt;/a&gt;&lt;/li&gt;
    &lt;li id=&quot;9026&quot;&gt;&lt;a href=&quot;https://shop.pimoroni.com/products/night-vision-camera-module-for-raspberry-pi?variant=12516582817875&quot; target=&quot;_blank&quot;&gt;Night vision camera module&lt;/a&gt;&lt;/li&gt;
    &lt;li id=&quot;nYi4&quot;&gt;&lt;a href=&quot;https://shop.pimoroni.com/products/hyperpixel-4?variant=12569485443155&quot; target=&quot;_blank&quot;&gt;HyperPixel 4.0 - Hi-Res Display&lt;/a&gt;&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;BeVo&quot;&gt;I will not go into details, about how to connect and install everything, there is lots of info you can find about it on the internet, so here is just a picture of how it looks like. Cute, isn&amp;#x27;t it?&lt;/p&gt;
  &lt;figure id=&quot;b8Cc&quot; class=&quot;m_custom&quot; data-caption-align=&quot;center&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/b8/d3/b8d37b1d-fad5-4ee4-a9ad-638fed604077.jpeg&quot; width=&quot;314.75&quot; /&gt;
    &lt;figcaption&gt;Raspberry PI + Night vision camera module&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;h2 id=&quot;RQ7v&quot;&gt;Chapter 2: Software&lt;/h2&gt;
  &lt;h3 id=&quot;hIhg&quot;&gt;2.0 Requirements&lt;/h3&gt;
  &lt;pre id=&quot;0IDr&quot;&gt;numpy==1.23.5
opencv-python==4.6.0.66
python-telegram-bot==13.14
imageio==2.22.4
python-dotenv==0.21.0
imageio-ffmpeg==0.4.7
huey==2.4.4&lt;/pre&gt;
  &lt;h3 id=&quot;iwm1&quot;&gt;2.1: Motion detection&lt;/h3&gt;
  &lt;pre id=&quot;Iq1A&quot; data-lang=&quot;python&quot;&gt;import os
import time
import cv2
import logging
import numpy as np

from dotenv import load_dotenv, dotenv_values
from bg import process_frames

load_dotenv()
CONTOUR_THRESHOLD = int(os.getenv(&amp;quot;CONTOUR_THRESHOLD&amp;quot;, 20))
WITH_SCREEN = int(os.getenv(&amp;quot;WITH_SCREEN&amp;quot;, 1))
MIN_GIF_LENGTH = int(os.getenv(&amp;quot;MIN_GIF_LENGTH&amp;quot;, 30))
GIF_SENDING_THRESHOLD = int(os.getenv(&amp;quot;GIF_SENDING_THRESHOLD&amp;quot;, 3))
MAX_CONTOURS = int(os.getenv(&amp;quot;MAX_CONTOURS&amp;quot;, 0))


def run():
    frames_with_motion = []
    previous_frame = None
    cap = cv2.VideoCapture(0)
    last_video_timestamp = None
    
    while True:
        _, frame = cap.read()
        img_rgb = cv2.cvtColor(src=frame, code=cv2.COLOR_BGR2RGB)

        # 2. Prepare image; grayscale and blur
        prepared_frame = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
        prepared_frame = cv2.GaussianBlur(src=prepared_frame, ksize=(5, 5), sigmaX=0)

        # 2. Calculate the difference
        if (previous_frame is None):
            # First frame; there is no previous one yet
            previous_frame = prepared_frame
            continue

        # 3. Set previous frame and continue if there is None
        if (previous_frame is None):
            # First frame; there is no previous one yet
            previous_frame = prepared_frame
            continue

        # calculate difference and update previous frame
        diff_frame = cv2.absdiff(src1=previous_frame, src2=prepared_frame)
        previous_frame = prepared_frame

        # 4. Dilute the image a bit to make differences more seeable; more suitable for contour detection
        kernel = np.ones((6, 6))
        diff_frame = cv2.dilate(diff_frame, kernel, 1)

        # 5. Only take different areas that are different enough (&amp;gt;CONTOUR_THRESHOLD / 255)
        thresh_frame = cv2.threshold(src=diff_frame, thresh=CONTOUR_THRESHOLD, maxval=255, type=cv2.THRESH_BINARY)[1]

        # 6. Find and optionally draw contours
        contours, _ = cv2.findContours(image=thresh_frame, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)

        # Draw contours
        cv2.drawContours(image=img_rgb, contours=contours, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)

        # If contours detected - save to a video, skip frames with too many contours
        if contours:
            frames_with_motion.append(img_rgb)
        else:
            len_frames = len(frames_with_motion)
            if len_frames &amp;gt; MIN_GIF_LENGTH:
                current_timestamp = int(time.time())

                # We don&amp;#x27;t want to send the gifs too often
                if last_video_timestamp:
                    if current_timestamp - last_video_timestamp &amp;lt; GIF_SENDING_THRESHOLD:
                        logging.info(&amp;quot;Skipping, too soon&amp;quot;)
                        continue  # skip, last video too soon
                else:
                    last_video_timestamp = current_timestamp
                
                # Process in background
                process_frames(frames_with_motion, f&amp;#x27;{current_timestamp}.mp4&amp;#x27;)

            frames_with_motion = []

        if WITH_SCREEN:
            cv2.imshow(&amp;#x27;Motion detector&amp;#x27;, img_rgb)

        if (cv2.waitKey(30) == 27):
            break

    # Cleanup
    cv2.destroyAllWindows()    


if __name__ == &amp;quot;__main__&amp;quot;:
    config = dotenv_values()
    logging.info(f&amp;quot;Starting Jerry with threshold setting {config}&amp;quot;)
    run()&lt;/pre&gt;
  &lt;h3 id=&quot;rQDw&quot;&gt;2.2: Notifications&lt;/h3&gt;
  &lt;pre id=&quot;TSzO&quot; data-lang=&quot;python&quot;&gt;import telegram
import os
import imageio
import logging

from huey import SqliteHuey
from dotenv import load_dotenv, dotenv_values

load_dotenv()
TELEGRAM_BOT_TOKEN = os.getenv(&amp;quot;TELEGRAM_BOT_TOKEN&amp;quot;)
TELEGRAM_CHAT_ID = os.getenv(&amp;quot;TELEGRAM_CHAT_ID&amp;quot;)
config = dotenv_values()
logging.info(f&amp;quot;Starting Jerry with threshold setting {config}&amp;quot;)

huey = SqliteHuey(filename=&amp;#x27;q.db&amp;#x27;)


@huey.task()
def send_gif(file_name, text):
    try:
        bot = telegram.Bot(token=TELEGRAM_BOT_TOKEN)
        bot.send_video(
            chat_id=TELEGRAM_CHAT_ID,
            video=open(file_name, &amp;#x27;rb&amp;#x27;),
            caption=text,
            supports_streaming=True
        )
    except Exception as e:
        logging.error(f&amp;quot;Erorr when sending video: {e}&amp;quot;)
    
    try:
        os.remove(file_name)
    except Exception as e:
        logging.error(f&amp;quot;Erorr when deleting video: {e}&amp;quot;)


@huey.task()
def process_frames(frames, filename):
    try:
        imageio.mimsave(filename, frames, fps=24)
    except Exception as e:
        logging.error(f&amp;quot;Error when saving video: {e}&amp;quot;)
    else:
        send_gif(filename, str(len(frames)))&lt;/pre&gt;
  &lt;h3 id=&quot;scLX&quot;&gt;2.3 Process management&lt;/h3&gt;
  &lt;p id=&quot;Ln0t&quot;&gt;I used systemd to manage processes on my raspberry pi, but there are other options as well too (eg cron, bashrc, etc). Here is the config I have in &lt;/p&gt;
  &lt;pre id=&quot;Whmi&quot;&gt;[Unit]
Description=Jerry the Mouse
After=multi-user.target

[Service]
Type=idle
ExecStart=/home/pi/jerry/venv/bin/python main.py
WorkingDirectory=/home/pi/jerry
Restart=always

[Install]
WantedBy=multi-user.target&lt;/pre&gt;
  &lt;p id=&quot;fszM&quot;&gt;...and for the notifications part:&lt;/p&gt;
  &lt;pre id=&quot;mHc8&quot;&gt;[Unit]
Description=Jerry the Mouse (Background)
After=multi-user.target

[Service]
Type=idle
ExecStart=/home/pi/jerry/venv/bin/huey_consumer bg.huey
WorkingDirectory=/home/pi/jerry
Restart=always

[Install]
WantedBy=multi-user.target&lt;/pre&gt;
  &lt;h2 id=&quot;HmXa&quot;&gt;Chapter 3: Results&lt;/h2&gt;
  &lt;p id=&quot;m7B3&quot;&gt;(Un)fortunately, while I was writing this app, the mouse stopped coming. Probably, I just fixed the holes in the kitchen cabinet, through which it might come. However, to test it I attached a thread to a small bicycle light and pulled the string. The result is exactly what I expected at the beginning!&lt;/p&gt;
  &lt;figure id=&quot;303p&quot; class=&quot;m_original&quot; data-caption-align=&quot;center&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/2a/ab/2aab1e85-6d8f-4cbd-b1bc-141f88f33004.gif&quot; width=&quot;640&quot; /&gt;
    &lt;figcaption&gt;Not a real mouse, just a box with a thread attached pulled by me :)&lt;/figcaption&gt;
  &lt;/figure&gt;

</content></entry><entry><id>aogz:pdPNp87lx0J</id><link rel="alternate" type="text/html" href="https://blog.aogz.me/pdPNp87lx0J?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=aogz"></link><title>Check files used/opened by a process</title><published>2022-11-14T13:40:00.992Z</published><updated>2022-11-14T13:40:00.992Z</updated><summary type="html">Useful for debugging:

$ ps aux | grep $PROCESS_NAME
$ ls -l /proc/$PID/fd
$ sudo lsof -i -a -p $PID</summary><content type="html">
  &lt;p id=&quot;w4iS&quot;&gt;Useful for debugging:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;$ ps aux | grep $PROCESS_NAME&lt;br /&gt;$ ls -l /proc/$PID/fd&lt;br /&gt;$ sudo lsof -i -a -p $PID&lt;/code&gt;&lt;/p&gt;

</content></entry><entry><id>aogz:5Joq905O0Tz</id><link rel="alternate" type="text/html" href="https://blog.aogz.me/5Joq905O0Tz?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=aogz"></link><title>redis-cli monitor specific key change</title><published>2022-11-07T10:46:51.284Z</published><updated>2022-11-07T10:46:51.284Z</updated><summary type="html">Monitoring specific keys in redis:</summary><content type="html">
  &lt;p id=&quot;7FCM&quot;&gt;Monitoring specific keys in redis:&lt;/p&gt;
  &lt;p id=&quot;fYDD&quot;&gt;&lt;br /&gt;&lt;code&gt;redis-cli monitor | grep --line-buffered -iE &amp;quot;YOUR_KEY&amp;quot; &amp;gt;&amp;gt; redis-cli.log&lt;/code&gt;&lt;/p&gt;

</content></entry><entry><id>aogz:Ax99FACetc6</id><link rel="alternate" type="text/html" href="https://blog.aogz.me/Ax99FACetc6?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=aogz"></link><title>Increasing the inotify watch limit</title><published>2022-02-28T13:13:40.061Z</published><updated>2022-02-28T13:13:40.061Z</updated><summary type="html">Linux uses the inotify package to observe filesystem events, individual files or directories. Increasing the inotify watch limit should hide the warning messages.</summary><content type="html">
  &lt;p id=&quot;Ewo7&quot;&gt;Linux uses the &lt;a href=&quot;http://man7.org/linux/man-pages/man7/inotify.7.html&quot; target=&quot;_blank&quot;&gt;inotify&lt;/a&gt; package to observe filesystem events, individual files or directories. Increasing the inotify watch limit should hide the warning messages.&lt;/p&gt;
  &lt;pre id=&quot;nje2&quot;&gt;# insert the new value into the system config
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf &amp;amp;&amp;amp; sudo sysctl -p

# check that the new value was applied
cat /proc/sys/fs/inotify/max_user_watches

# config variable name (not runnable)
fs.inotify.max_user_watches=524288
&lt;/pre&gt;

</content></entry><entry><id>aogz:qt4z9u5VvQW</id><link rel="alternate" type="text/html" href="https://blog.aogz.me/qt4z9u5VvQW?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=aogz"></link><title>Reassign keys in Linux</title><published>2022-02-23T15:15:15.866Z</published><updated>2022-02-23T15:15:15.866Z</updated><summary type="html">Get the key code:</summary><content type="html">
  &lt;p id=&quot;FXT4&quot;&gt;Get the key code:&lt;/p&gt;
  &lt;p id=&quot;L1yF&quot;&gt;&lt;code&gt;xev | grep keycode&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;6jf3&quot;&gt;Press the needed key, then copy needed keycode. &lt;/p&gt;
  &lt;p id=&quot;ShmR&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;UUxh&quot;&gt;In my case I want to reassign key with code 94 to Left shift:&lt;/p&gt;
  &lt;p id=&quot;ptgU&quot;&gt;Test it: &lt;code&gt;xmodmap -e &amp;quot;keycode  94 = Shift_L&amp;quot;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;Usrr&quot;&gt;Save it:&lt;/p&gt;
  &lt;pre id=&quot;gSjp&quot;&gt;xmodmap -pke &amp;gt;~/.Xmodmap
echo &amp;quot;xmodmap .Xmodmap&amp;quot; &amp;gt; ~/.xinitrc&lt;/pre&gt;
  &lt;p id=&quot;03C4&quot;&gt;Voila!&lt;/p&gt;

</content></entry><entry><id>aogz:zu4LrqdktlH</id><link rel="alternate" type="text/html" href="https://blog.aogz.me/zu4LrqdktlH?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=aogz"></link><title>Swap command and alt on Apple magic keyboard</title><published>2022-02-08T20:53:24.022Z</published><updated>2022-02-08T20:53:24.022Z</updated><summary type="html">To swap command and alt buttons on Apple Magic Keyboard (and not globally) add the following line in /etc/modprobe.d/hid_apple.conf:</summary><content type="html">
  &lt;p id=&quot;ddmA&quot;&gt;To swap command and alt buttons on Apple Magic Keyboard (and not globally) add the following line in &lt;code&gt;/etc/modprobe.d/hid_apple.conf:&lt;/code&gt;&lt;/p&gt;
  &lt;pre id=&quot;2UCw&quot;&gt;options hid_apple swap_opt_cmd=1&lt;/pre&gt;
  &lt;p id=&quot;EQcw&quot;&gt;&amp;#x60;&lt;/p&gt;

</content></entry></feed>