Control Lights From The Command Line

A simple python script to control Philips Hue lights from the command line.


All the code can be found on my GitHub.

https://github.com/zjrubin/philips_hue


Writing the Python Script

First I designate the python interpreter and all the packages I use. I interact with my Philips Hue lights using the phue python package.

1#!/usr/bin/env python3
2from phue import Bridge
3import os
4import argparse

I then designate some constants. SCRIPT_DIR is used to allow the script to be run from any folder. CREDENTIALS_PATH lets the phue package know where to find the credentials to communicate with the Philips Hue bridge.

1SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
2CREDENTIALS_PATH = os.path.join(SCRIPT_DIR, "credentials.json")

The main function is straightforward. It parses the command line arguments and connects to the Philips Hue bridge. Then, depending on the specific command line flags passed in, it either lists available scenes (specific arrangements of colors and brightness for a group of lights) or activates a particular scene.

 1def main():
 2
 3    args = parse_arguments()
 4
 5    bridge = Bridge(ip="192.168.1.2", config_file_path=CREDENTIALS_PATH)
 6
 7    api = bridge.get_api()
 8
 9    if args.list_scenes:
10        list_scenes(bridge, api)
11    elif args.scene:
12        activate_scene(bridge, api, scene=args.scene)

The script expects one of potentially two flags: -s <SCENE> to activate a scene, or -l to list all the available scenes.

 1def parse_arguments() -> argparse.Namespace:
 2    parser = argparse.ArgumentParser()
 3    parser.add_argument("--scene", "-s", type=str, help="the scene to activate")
 4    parser.add_argument(
 5        "--list-scenes",
 6        "-l",
 7        dest="list_scenes",
 8        action="store_true",
 9        help="list all available scenes",
10    )
11    return parser.parse_args()

If -l or --list-scenes is passed in as an argument, the script outputs all the available scenes in alphabetical order.

 1def list_scenes(bridge: Bridge, api: dict) -> None:
 2    scenes: set = set()
 3    for scene_dict in api["scenes"].values():
 4        scenes.add(scene_dict["name"])
 5
 6    sorted_scenes: list = sorted(scenes)
 7
 8    print("Scenes:")
 9    for scene in sorted_scenes:
10        print(f"\t{scene}")
11    print()

if -s <SCENE> is passed in as an argument, the script activates the inputted scene for all the lights in my basement. (My office is in my basement, so I hard-coded the basement as the one room where the lights are affected. Modifying the script for to work on any room can easily be done).

1def activate_scene(bridge: Bridge, api: dict, scene: str) -> None:
2    basement_id = get_group_id(api=api, room_name="Basement")
3
4    scene_id = get_scene_id(api=api, scene_name=scene)
5
6    bridge.activate_scene(group_id=basement_id, scene_id=scene_id, transition_time=1)

Finally, I have some helper functions. get_group_id returns the numeric id for a named collection of lights. get_scene_id returns the numeric id for a named scene.

 1def get_group_id(api: dict, room_name: str) -> int:
 2    for group_id, group_dict in api["groups"].items():
 3        if room_name == group_dict["name"]:
 4            return int(group_id)
 5
 6
 7def get_scene_id(api: dict, scene_name: str) -> int:
 8    for scene_id, scene_dict in api["scenes"].items():
 9        if scene_name == scene_dict["name"]:
10            return scene_id

And of course, I have the pythonic boiler plate code to run the main function when the script is executed.

1if __name__ == "__main__":
2    main()

Here is the entirety of the python script:

 1#!/usr/bin/env python3
 2from phue import Bridge
 3import os
 4import argparse
 5
 6
 7SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
 8CREDENTIALS_PATH = os.path.join(SCRIPT_DIR, "credentials.json")
 9
10
11def main():
12
13    args = parse_arguments()
14
15    bridge = Bridge(ip="192.168.1.2", config_file_path=CREDENTIALS_PATH)
16
17    api = bridge.get_api()
18
19    if args.list_scenes:
20        list_scenes(bridge, api)
21    elif args.scene:
22        activate_scene(bridge, api, scene=args.scene)
23
24
25def parse_arguments() -> argparse.Namespace:
26    parser = argparse.ArgumentParser()
27    parser.add_argument("--scene", "-s", type=str, help="the scene to activate")
28    parser.add_argument(
29        "--list-scenes",
30        "-l",
31        dest="list_scenes",
32        action="store_true",
33        help="list all available scenes",
34    )
35    return parser.parse_args()
36
37
38def list_scenes(bridge: Bridge, api: dict) -> None:
39    scenes: set = set()
40    for scene_dict in api["scenes"].values():
41        scenes.add(scene_dict["name"])
42
43    sorted_scenes: list = sorted(scenes)
44
45    print("Scenes:")
46    for scene in sorted_scenes:
47        print(f"\t{scene}")
48    print()
49
50
51def activate_scene(bridge: Bridge, api: dict, scene: str) -> None:
52    basement_id = get_group_id(api=api, room_name="Basement")
53
54    scene_id = get_scene_id(api=api, scene_name=scene)
55
56    bridge.activate_scene(group_id=basement_id, scene_id=scene_id, transition_time=1)
57
58
59def get_group_id(api: dict, room_name: str) -> int:
60    for group_id, group_dict in api["groups"].items():
61        if room_name == group_dict["name"]:
62            return int(group_id)
63
64
65def get_scene_id(api: dict, scene_name: str) -> int:
66    for scene_id, scene_dict in api["scenes"].items():
67        if scene_name == scene_dict["name"]:
68            return scene_id
69
70
71def output_api() -> None:
72    with open("hue_api.json", "w") as outfile:
73        import json
74
75        json.dump(bridge.get_api(), outfile, indent=4)
76
77
78if __name__ == "__main__":
79    main()

Creating the symbolic link on my path

I navigated to a directory that is in my PATH and created a symbolic link to the Philips Hue script.

1ln -s philips_hue/hue.py hue

Adding bash aliases

I then created some aliases in my .bashrc file:

1# Philips Hue light controls
2alias bright="hue -s Bright"
3alias dim="hue -s Dimmed"
4alias nightlight="hue -s Nightlight"
5alias relax="hue -s Relax"

Using the script

Finally, I opened a new terminal (to source the changes to my .bashrc) and tested my aliases.

  • (Same video as above)