As you probably know, we recently moved from NJ to Austin, TX. In our new place, my office space is challenging as it’s located next to a very strategic place… the kitchen!
So, yes, you will find me closer to the refrigerator. (I do maintain my running routine, so I should be fine.) However, the kitchen is a central place which means that I am more accessible for my family.
Usually, the following happens…
Son (opens the office door): “Dad! where is my Power Rangers Dino Charge Beast Saber Sword?”
- Daughter (opens the office door): “Dad, where is the phone charger?“.
- Wife (very softly opens the office door): “Just a quick question… can you pick up the kids later today?“.
- Mother-in-law when visiting (opens the office door): “What is the issue with the water pressure??” (BTW, there is no issue with the water pressure…).
Yes, I did ask them (many times) to be more mindful/considerable and check if I’m on a call before approaching these super-important questions.
Then I asked my son the following:
- Me: “Buddy, you see that I’m working and on a video call. Can you wait a bit with your question?“.
- Son: “But dad! I didn’t know that you were in a meeting with the video on…“.
Well, he is right. He didn’t know that I was on a video call. So… How can I “better educate” my family to know when I’m on a call and prefer not to be disturbed?
My options:
Ask again (and again) – I will do that, but I need something more immediate…
- Use some physical indication every time I’m in a meeting (see 👉) – That wouldn’t scale…
- Relocate my office space – Then I’ll be far away from the refrigerator 😉
- Leverage & integrate different technologies – Yes! 👍
The Plan
Let’s integrate Webex (status indication) with Philips Hue! That way, they will know (see! 👀) my availability status!
- Green == Available.
- Blue == On a call.
- Orange== On a Webex meeting with video OFF.
- Red== On a Webex meeting with video ON.
- Yellow == Asking for quiet time (DoNotDisturb).
Cisco Webex and Philips Hue expose their APIs
As both Webex and Philips Hue expose their APIs, that shouldn’t be complicated. Right?
Well, I found two challenges
- Webex Token:
- Webex API requires a valid and permanent token
- The personal token is limited to 12 hours
- The guest user token cannot access the “status” data as it isn’t part of the organization
- The integration token (OAuth grant using a web browser) isn’t applicable for a simple script
- How can I find out if/when the Webex video is turned on/off during a call? 🤔
After some quick research, I came up with the following solutions
- Create a new Webex Bot and leverage its token (which is permanent and part of the organization).
- Option #1 – Use this Webex API call. The response returns if the participants are using video or not. However, when I tried that, I always got “video: off” in the response. I had a quick discussion with the Webex Dev team, and they confirmed it is a bug (also, the API call works ONLY if you are the meeting host…).
- Option #2 – Use the macOS “log stream” CLI command to track when the video is being used. True, It’s a bit more complicated, but… worth it. (To learn more about the “log stream,” check here).
The Code (Mac OS only) 👨💻
To constantly tail the Mac OS “log stream” log (to track if the video is on/off), and at the same time, run the Webex API code portion, I created a multithreaded python script. One thread is dedicated to tail the “log stream”, while the second thread is dedicated to the Webex API integration code.
Let’s review the code
Step #1 – Import the required python modules.
- requests – Python HTTP library.
- phue – Philips Hue library.
- time – To set up a sleep statement.
- Threading & Subprocess – For multithreading.
importrequests from phue import Bridge from threading import Thread from time import sleep import subprocess
Step #2 – Define Webex and Philips Hue variables.
- myID – My personal Webex ID (check here for more info).
- URL – Webex API call to determine my availability status.
- token – Webex Bot token.
- bridgeIpAddress – Philips Hue bridge IP address.
# Webex variables myID = "xxx" url = "https://webexapis.com/v1/people/" + myID token = "xxx" # pHue variables bridgeIpAddress = 'x.x.x.x'
Step #3 – Define the Philips Hue connectivity function (Note: the first connection will require authentication using: “b.connect()”).
def access_lights(bridgeIpAddress): b = Bridge(bridgeIpAddress) # b.connect() light_names = b.get_light_objects('name') return light_names
Step #4 – Define the five availability options/functions.
- The functions are applicable for the “Office Lightstrip” (you can configure a group of lights as well).
- on/off – On/Off state of the light.
- hue – The hue value to set light to (The hue value is a wrapping value between 0 and 65535).
- saturation – Saturation of the light (254 is the most saturated, colored, and 0 is the least saturated, white).
- brightness – The brightness value to set the light to (Brightness is a scale from 1, the minimum the light is capable of, to 254, the maximum).
def active(): lights = access_lights(bridgeIpAddress) for light in ['Office Lightstrip']: lights[light].on = True lights[light].hue = 25500 # Green lights[light].saturation = 254 lights[light].brightness = 254 def call(): lights = access_lights(bridgeIpAddress) for light in ['Office Lightstrip']: lights[light].on = True lights[light].hue = 46920 # Blue lights[light].saturation = 254 lights[light].brightness = 254 def meeting_without_video(): lights = access_lights(bridgeIpAddress) for light in ['Office Lightstrip']: lights[light].on = True lights[light].hue = 7000 # Orange lights[light].saturation = 254 lights[light].brightness = 254 def meeting_with_video(): lights = access_lights(bridgeIpAddress) for light in ['Office Lightstrip']: lights[light].on = True lights[light].hue = 65535 # Red lights[light].saturation = 254 lights[light].brightness = 254 def DoNotDisturb(): lights = access_lights(bridgeIpAddress) for light in ['Office Lightstrip']: lights[light].on = True lights[light].hue = 12750 # Yellow lights[light].saturation = 254 lights[light].brightness = 254
Step #5 – Define the function to check if the is video is on/off.
- The creation of the “output.txt” file was needed as I was unable to (really) tail the “log stream” process/output (the “output.txt” file stores the video status information, which is used in step 6). If you have a suggestion for a better approach, let me know!
- the full (unparsed) “log stream” command is: “log stream –info –debug –predicate ‘(process == “VDCAssistant”) and (eventMessage contains “Post event kCameraStream”)’” Learn more about the “log stream” command).
def func1(): f = open("output.txt", "w") subprocess.call(['/usr/bin/log', 'stream' ,'--info', '--debug', '--predicate' , '(process == "VDCAssistant") and (eventMessage contains "Post event kCameraStream")'], stdout=f) return f
Step #6 – Check the Webex status indication AND the video status to determine the Philips Hue light color.
- This function runs on a 5 seconds loop to check the 8 conditions.
- print statements were added for debugging/demo purposes.
def func2(): while True: sleep(5) headers = { 'Authorization': 'Bearer ' + token } response = requests.request("GET", url, headers=headers) r_json = response.json() myStatus = r_json["status"] with open("output.txt", "r") as file: result = (list(file)[-1]) if "kCameraStreamStart" in result and myStatus == "meeting": # print("Meeting is ON and Video is ON") meeting_with_video() elif "kCameraStreamStart" not in result and myStatus == "meeting": # print("Meeting is ON and Video is OFF") meeting_without_video() elif "kCameraStreamStart" in result and myStatus == "active": # print("Available") active() elif "kCameraStreamStart" not in result and myStatus == "active": # print("Available") active() elif "kCameraStreamStart" in result and myStatus == "call": # print("On a Call") call() elif "kCameraStreamStart" not in result and myStatus == "call": # print("On a Call") call() elif "kCameraStreamStart" in result and myStatus == "DoNotDisturb": # print("DoNotDisturb") DoNotDisturb() elif "kCameraStreamStart" not in result and myStatus == "DoNotDisturb": # print("DoNotDisturb") DoNotDisturb() if __name__ == '__main__': Thread(target = func1).start() Thread(target = func2).start()
The Demo 🎥
Final thoughts
- The power of automation, innovation, and integration – Cool right! 😎
- What is your challenge? Can you leverage automation/different technologies to mitigate it?
- Another great example of integrating Webex status with Home Assistant
Related resources
We’d love to hear what you think. Ask a question or leave a comment below.
And stay connected with Cisco DevNet on social!
Twitter @CiscoDevNet | Facebook | LinkedIn
Visit the new Developer Video Channel
Great!
Great as always, Yossi !!!
This is working great! I had to change the log stream parameters in func1 to work with Monterey. I followed this guide for the new ones. https://stackoverflow.com/questions/60535678/macos-detect-when-camera-is-turned-on-off. Then changed these items in func2:
with open(“output.txt”, “r”) as file:
result = file.readlines()[-2] #changed
print(result)
if “On;” in result and myStatus == “meeting”: #changed
print(“Meeting is ON and Video is ON”)
meeting_with_video()
elif “Off;” in result and myStatus == “meeting”: #changed
print(“Meeting is ON and Video is OFF”)
meeting_without_video()
Thanks for the feedback and for updating the code for macOS Monterey!
This is amazing – it is JUST the feature I have been looking for a long time!
We are constantly using Webex Meetings for our corporate meetings. Unfortunately, I am working on a Windows 10 platform.
Is IFTTT safe to use in a corporate environment, w/r to data security?
How easy would it be to migrate this to Windows? Pls note I am not familiar with Python or anything, yet would not shy away to dig into some log files and make adaptations, once provided with some instructions.
Or would you know of anyone who implemented this for Windows 10/11?
So much hoping to find help on this subject!