{"slug": "the-battle-of-the-green-dot", "title": "The Battle of the Green Dot", "summary": "A BeZoned engineer built a presence detector that uses an old Philips monitor's built-in webcam and machine learning to automatically switch Microsoft Teams status between Available and Away, addressing complaints that Teams marks users Away whenever they stop typing or moving the mouse. The engineer first tested the webcam with ffmpeg, which reported the device supports 30fps modes from 176x144 up to 1920x1080, after an initial capture attempt failed because the default 29.970030 framerate was unsupported. The project targets a real workplace friction point: Teams' Away status is often misread as not working, even when someone is reading, sketching, or waiting on an AI agent.", "body_md": "# \n\nEven if you've never used Microsoft Teams yourself, you've probably come across people complaining about it. There are plenty of complaints, but one seems to come up over and over again:\n\n1. You're working as usual. Your Teams status is Available. All good.\n2. You look away for ten seconds.\n3. The status is Away. Your manager is writing you to ask why you are not working.\n\nOkay, I'm exaggerating. But the underlying problem is real. The fact you are not moving mouse or typing doesn't mean you are away. You could be:\n\n1. reading an article, documentation or something else work-related\n2. drawing, sketching or taking notes on paper\n3. waiting for an AI agent to finish\n4. dictating feedback or instructions to an AI agent\n\nBut Microsoft Teams does not know that. Neither do your colleagues. Some teams even interpret Away status as \"this person is not working\". Not only is that assumption wrong, but it also puts unnecessary pressure on people to appear Available all the time.\n\nYou'd be \"away\" in the office as well. You'd grab a coffee, walk over to a colleague to ask a question, or go to the bathroom. Nobody questions that in the office...\n\nLuckily, we see things differently at [BeZoned](https://bezoned.com?utm_source=malja&utm_source=blog&utm_campaign=presence_detector). We don't mind if you go Away during the day. We all work from home, and we trust each other. But trust doesn't make presence information useless. We still need to know when someone's around, especially when we're trying to have a quick conversation. When Teams switches to the Away status, but I'm still sitting at my desk, I'm sending the wrong signal. Others can still reach me.\n\nAnd it works the other way around, too. When I have to step away for a moment - to answer the door for a mailman, grab a food delivery because I'm too lazy to cook, or rescue my dog's toy from wherever he's managed to get it stuck - I tend to forget to set my status accordingly.\n\nThat's when I thought: what if Teams could detect whether I'm sitting at my computer and automatically update my status?\n\n# \n\nSometimes you have the solution in front of you for years. In my case, it was an old Philips monitor with a built-in webcam. The webcam was terrible even when I bought the monitor, so I never used it for video calls. Maybe it was finally time to put it to use.\n\nCould a little bit of AI (or rather ML) help me turn this unused webcam into a **presence detector**? When I'm sitting at the desk it would keep me Available on Teams. The second I move away, it would switch my status to Away.\n\n## \n\nI could probably jump directly to the code, but I decided to start small. Let's take a picture with the webcam to see if it even works.\n\nFirst, I needed to list available video devices:\n\n```\nffmpeg -f avfoundation -list_devices true -i \"\"\n```\n\nWhich outputs:\n\n```\n[AVFoundation indev @ 0x7513014140] AVFoundation video devices:\n[AVFoundation indev @ 0x7513014140] [0] USB2.0 FHD UVC WebCam\n```\n\n(The output is simplified, so it only contains the WebCam I'm interested in.)\n\nNow let's take a picture:\n\n```\nffmpeg -f avfoundation -i \"0:none\" -frames:v 1 webcam.jpg\n```\n\nAnd I'm greeted with an error:\n\n```\n[avfoundation @ 0x7676c20000] Selected framerate (29.970030) is not supported by the device.\n[avfoundation @ 0x7676c20000] Supported modes:\n[avfoundation @ 0x7676c20000]   176x144@[30.000030 30.000030]fps\n    Last message repeated 1 times\n[avfoundation @ 0x7676c20000]   320x240@[30.000030 30.000030]fps\n    Last message repeated 1 times\n[avfoundation @ 0x7676c20000]   352x288@[30.000030 30.000030]fps\n    Last message repeated 1 times\n[avfoundation @ 0x7676c20000]   640x360@[30.000030 30.000030]fps\n    Last message repeated 1 times\n[avfoundation @ 0x7676c20000]   640x480@[30.000030 30.000030]fps\n    Last message repeated 1 times\n[avfoundation @ 0x7676c20000]   848x480@[30.000030 30.000030]fps\n[avfoundation @ 0x7676c20000]   1280x720@[30.000030 30.000030]fps\n[avfoundation @ 0x7676c20000]   1920x1080@[30.000030 30.000030]fps\n[in#0 @ 0x7676c1c000] Error opening input: Input/output error\nError opening input file 0:none.\nError opening input files: Input/output error\n```\n\nAfter some fiddling, I got it working by explicitly setting the frame rate, resolution, and pixel format:\n\n```\nffmpeg -f avfoundation \\\n  -framerate 30 \\\n  -video_size 1280x720 \\\n  -pixel_format nv12 \\\n  -i \"0:none\" \\\n  -vf \"select=eq(n\\,60)\" \\\n  -frames:v 1 \\\n  -y webcam.jpg\n```\n\nAnd voila! That's me.\n\nAs I've said - it's a terrible webcam. On the other hand, you can clearly tell there is someone in the picture 😀 It should be more than enough for this use case.\n\n## \n\nI started by passing the image to an LLM and asking it to detect whether there's a person in it.\n\nI used Apple' Foundation Models, available on macOS 27:\n\n```\n# fm respond --model system \\\n  --image webcam.jpg \\\n  \"Is there a real person sitting at the desk? Answer only YES or NO.\"\n\nYES\n```\n\nTo make sure the model wasn't just answering \"YES\" to everything, I tried another picture. This time of an empty chair.\n\nAnd the response?\n\n```\nNO\n```\n\nTa-da! I could have stopped here. The model runs locally, it does not have any dependencies and running a bash script to do this would be easy.\n\n## \n\nBut Apple also provides Vision ML models, so naturally I had to test them as well. Vision models also support human pose detection, which could be used for gesture-based microphone controls, FaceTime-style reactions and other \"very useful and impactful\" features nobody asked for.\n\nI started with a simple Swift script, so I don't have to install anything.\n\n``` python\nimport Foundation\nimport Vision\n\nguard CommandLine.arguments.count > 1 else {\n    fputs(\"Usage: swift detect.swift image.jpg\\n\", stderr)\n    exit(1)\n}\n\nlet url = URL(fileURLWithPath: CommandLine.arguments[1])\n\n// Simple person detection without posture\nlet request = VNDetectHumanRectanglesRequest()\n// Checking just the upper body, because I'm sitting most of the time\nrequest.upperBodyOnly = true\n\nlet handler = VNImageRequestHandler(url: url, options: [:])\n\ndo {\n    try handler.perform([request])\n\n    let people = request.results ?? []\n\n    print(\"\"\"\n    {\n      \"personDetected\": \\(people.count > 0),\n      \"count\": \\(people.count)\n    }\n    \"\"\")\n} catch {\n    fputs(\"Vision error: \\(error)\\n\", stderr)\n    exit(1)\n}\n```\n\nAnd when executed:\n\n```\n# swift detect.swift webcam.jpg\n\n{\n  \"personDetected\": true,\n  \"count\": 1\n}\n```\n\nThe first run was extremely slow, probably because Swift had to compile it. But subsequent detections were... well, *swift*.\n\nI also tried getting the detected person's bounding box and drawing a rectangle around them. The code got a bit longer, so I'll spare you the details. The result:\n\n## \n\nBefore getting too excited about detecting people with my webcam, I first checked whether I could actually update Teams status programmatically. Turns out, there are several ways to do it.\n\n### \n\nIf you've ever tried to address this issue yourself, you've probably come across *mouse jigglers*. They try to mimic user activity with more or less realistic mouse movement tricking MS Teams into thinking someone is actively using the computer.\n\nI wasn't particularly interested in moving my mouse around or pressing random keys just to keep Teams happy.\n\n### \n\nConveniently, we already have a desktop app that can update a user's Teams presence.\n\nAs of now, [BeZoned Sidebar](https://bezoned.com/bezoned-sidebar-app?utm_source=malja&utm_source=blog&utm_campaign=presence_detector) requires the user to click the Presence menu and set it manually.\n\nI could extend the Sidebar app to have this \"presence detection\" functionality baked in, or publish an API to other desktop apps. So anyone could extend the Sidebar functionality.\n\nIf I were building this as a real product feature, integrating it would probably be the best option. But this was just a weekend experiment, and I was the only user. Modifying an app everyone else relies on is a no-go.\n\n### \n\nWhat's left? Talking to the API directly. I could use the very same Graph API that BeZoned uses and set presence there.\n\nI'd need an Entra app registration, a few API permissions and some auth logic. But that would let me update my own Teams presence without touching the BeZoned app or affecting anyone else.\n\n# \n\nI decided to stick with Swift. It's already available on my Mac and I wanted to play with it for a long time.\n\nAdditionally, it's easy to share the codebase with my colleagues if they ever want to run it as well.\n\nWhen the script starts, it authenticates through Entra using the [Device code](https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-authentication-flows#device-code-flow) flow. I just open the auth page, enter the displayed code and sign in.\n\nA deeper integration with Keychain to preserve credentials is a problem for future me.\n\nTo avoid constantly switching between Available and Away, I settled on a simple *state machine*:\n\nIn short, the script requires two consecutive detections of the same result to set the status to Away or Available in Teams.\n\nThe final piece was connecting everything to Teams. I used three endpoints:\n\n- [setPresence](https://learn.microsoft.com/en-us/graph/api/presence-setpresence?view=graph-rest-1.0&tabs=http) updates the presence using a session ID, which is later used to clear the presence override\n- [getPresence](https://learn.microsoft.com/en-us/graph/api/presence-get?view=graph-rest-1.0&tabs=http) returns the current presence - I run this before every \"switch\" to ensure I'm not just hammering API with unnecessary \"Change from Away to Away\" requests.\n- and finally [clearPresence](https://learn.microsoft.com/en-us/graph/api/presence-clearpresence?view=graph-rest-1.0&tabs=http) clears the presence configured from this script if I terminate it or when I lock the screen. Teams is then free to set it according to normal rules.\n\nJust one delegated permission is needed for all three of them: `Presence.ReadWrite`\n\nAnd that's it. When I start my computer, the Swift script launches, wakes up the old webcam and starts checking whether I'm at my desk. My colleagues are updated in real time. Away finally means I'm actually away.\n\n## About the author\n\n- I've worked remotely for most of my software engineering career. It opened up a whole new world to me. I was able to build my own office, cut out the commute, avoid distractions, and... wear the same shirt for a week. :) Of course, there are downsides, but to me, remote work makes sense for both companies and employees. I've always aimed to build remote-first teams because I believe it leads to better documentation, smoother processes, and fewer bottlenecks. But there's always been one thing missing: that sense of belonging, of truly feeling like part of a team. So when I got the chance to help BeZoned build the best online office for remote teams, it was a dream come true. Not only did the idea immediately click with me, but I was also getting to scratch my own itch.\n\n## Comments\n\n[Join the discussion](https://hashnode.com/posts/the-battle-of-the-green-dot/6ae3670695963ad3dbfaa7c9)\n\nNo comments yet. [Be the first to comment.](https://hashnode.com/posts/the-battle-of-the-green-dot/6ae3670695963ad3dbfaa7c9)\n\n## BeZoned\n\nBehind the scenes of building BeZoned: architecture, Microsoft Teams and Graph, product trade-offs, technical constraints, experiments, failures, and lessons from shipping. Along the way, I explore remote work, distributed teams, communication, digital presence, and the systems that make online collaboration actually work.\n\n[Up nextWhy we chose Teams for BeZonedAbout BeZoned Before we start, allow me a short detour. I think I owe you a short description of what BeZoned actually is. Without it, the reasoning would likely not make sense to you. If you already](https://malja.dev/why-we-chose-teams-for-bezoned)", "url": "https://wpnews.pro/news/the-battle-of-the-green-dot", "canonical_source": "https://malja.dev/the-battle-of-the-green-dot", "published_at": "2026-10-29 22:45:18+00:00", "updated_at": "2026-10-29 23:16:56.008683+00:00", "lang": "en", "topics": ["machine-learning", "computer-vision", "ai-tools"], "entities": ["Microsoft Teams", "BeZoned", "Philips", "ffmpeg"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/the-battle-of-the-green-dot", "markdown": "https://wpnews.pro/news/the-battle-of-the-green-dot.md", "text": "https://wpnews.pro/news/the-battle-of-the-green-dot.txt", "jsonld": "https://wpnews.pro/news/the-battle-of-the-green-dot.jsonld"}}