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)