Avatar

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…

  • Yossi light saberSon (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:

  1. Yossi meeting in progressAsk again (and again) – I will do that, but I need something more immediate…
  2. Use some physical indication every time I’m in a meeting (see ?) – That wouldn’t scale…
  3. Relocate my office space – Then I’ll be far away from the refrigerator ?
  4. 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).

Yossi Webex and Philips hue

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

  1. Webex Token:
    1. Webex API requires a valid and permanent token
    2. The personal token is limited to 12 hours
    3. The guest user token cannot access the “status” data as it isn’t part of the organization
    4. The integration token (OAuth grant using a web browser) isn’t applicable for a simple script
  2. 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.

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 = 254def 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 = 254def 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 = 254def 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 = 254def 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

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

 



Authors

Yossi Meloch

Senior Software Architect

Customer Experience (CX)