├── .gitignore ├── requirements.txt ├── tests.py ├── README.md ├── hue.py ├── main.py └── chat_bot.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .envr 3 | .idea/ 4 | TODO 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | discoverhue==1.0.2 2 | openai==0.26.5 3 | phue==1.1 -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | from chat_bot import trim 2 | 3 | 4 | def test_remove_characters(): 5 | string = """Here is the list of JSONs: 6 | 7 | ``` 8 | [ 9 | { 10 | "light_id": 0, 11 | "color": { 12 | "hue": 7281, 13 | "saturation": 254, 14 | "brightness": 254 15 | } 16 | }, 17 | { 18 | "light_id": 1, 19 | "color": { 20 | "hue": 7281, 21 | "saturation": 254, 22 | "brightness": 254 23 | } 24 | } 25 | ] 26 | ```""" 27 | assert trim(string) == """[ 28 | { 29 | "light_id": 0, 30 | "color": { 31 | "hue": 7281, 32 | "saturation": 254, 33 | "brightness": 254 34 | } 35 | }, 36 | { 37 | "light_id": 1, 38 | "color": { 39 | "hue": 7281, 40 | "saturation": 254, 41 | "brightness": 254 42 | } 43 | } 44 | ]""" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Control Hue with ChatGPT 2 | 3 | Small project illustrating how ChatGPT can be used to control Hue lights. 4 | 5 | ## Setup 6 | 7 | ### Install dependencies 8 | 9 | ```bash 10 | pip install -r requirements.txt 11 | ``` 12 | 13 | ### Set up OpenAI API key 14 | 15 | Get an API Key from [OpenAI](https://openai.com/api/). 16 | 17 | Export it as an environment variable: 18 | 19 | ```bash 20 | export OPENAI_API_KEY={your_api_key} 21 | ``` 22 | 23 | ### Hue 24 | 25 | You should be connected to the same WiFi network as your Hue bridge. 26 | When you first run the script you will need to press the button on 27 | the bridge to authorize the script. 28 | 29 | ## Run 30 | 31 | Simply run the script: 32 | 33 | ```bash 34 | ./main.py 35 | ``` 36 | 37 | Enter commands like "turn on the lights", "turn off the lights" or "make one light blue and the other red" -------------------------------------------------------------------------------- /hue.py: -------------------------------------------------------------------------------- 1 | import time 2 | from pathlib import Path 3 | 4 | import discoverhue 5 | import phue 6 | 7 | 8 | def find_ip() -> str: 9 | """ 10 | Find the IP address of the hue bridge 11 | """ 12 | bridges = discoverhue.find_bridges() 13 | url = list(bridges.values())[0] 14 | return url.split(":")[1].strip("/") 15 | 16 | 17 | def get_ip() -> str: 18 | """ 19 | Get the IP address of the hue bridge from a file or find it if the file does not exist 20 | """ 21 | ip_file_path = Path.home() / ".ip.txt" 22 | if ip_file_path.exists(): 23 | return ip_file_path.read_text() 24 | else: 25 | ip = find_ip() 26 | ip_file_path.write_text(ip) 27 | return ip 28 | 29 | 30 | def get_bridge(): 31 | while True: 32 | try: 33 | return phue.Bridge(get_ip()) 34 | except phue.PhueRegistrationException: 35 | print("Press the button on the bridge") 36 | time.sleep(2) 37 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from json import JSONDecodeError 3 | 4 | from chat_bot import ChatBot 5 | from hue import get_bridge 6 | 7 | HEADER = """ 8 | I have a hue scale from 0 to 65535. 9 | red is 0.0 10 | orange is 7281 11 | yellow is 14563 12 | purple is 50971 13 | pink is 54612 14 | green is 23665 15 | blue is 43690 16 | 17 | Saturation is from 0 to 254 18 | Brightness is from 0 to 254 19 | 20 | Two JSONs should be returned in a list. Each JSON should contain a color and a light_id. 21 | The light ids are 0 and 1. 22 | The color relates a key "color" to a dictionary with the keys "hue", "saturation" and "brightness". 23 | 24 | Give me a list of JSONs to configure the lights in response to the instructions below. 25 | Give only the JSON and no additional characters. 26 | Do not attempt to complete the instruction that I give. 27 | Only give one JSON for each light. 28 | Do not provide an explanation. 29 | Give the two JSONs in a list. 30 | 31 | Here is an example output: 32 | [ 33 | { 34 | "light_id": 0, 35 | "color": { 36 | "hue": 43690, 37 | "saturation": 254, 38 | "brightness": 254 39 | } 40 | }, 41 | { 42 | "light_id": 1, 43 | "color": { 44 | "hue": 43690, 45 | "saturation": 254, 46 | "brightness": 254 47 | } 48 | } 49 | ] 50 | """ 51 | 52 | bot = ChatBot(header=HEADER) 53 | bridge = get_bridge() 54 | 55 | while True: 56 | try: 57 | response = bot(input("What should I do with the lights? ")) 58 | except JSONDecodeError: 59 | print("Oops something went wrong. Try again.") 60 | continue 61 | 62 | for command in response: 63 | light_id = command["light_id"] 64 | color = command["color"] 65 | light = bridge.lights[light_id] 66 | for key, value in color.items(): 67 | setattr(light, key, value) 68 | -------------------------------------------------------------------------------- /chat_bot.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import re 4 | from json import JSONDecodeError 5 | from typing import List 6 | 7 | import openai 8 | 9 | openai.api_key = os.environ["OPENAI_API_KEY"] 10 | 11 | 12 | class ChatBot: 13 | def __init__( 14 | self, 15 | model="gpt-3.5-turbo", 16 | header="", 17 | ): 18 | """ 19 | Keep track of messages submitted by the user and generate responses 20 | by calling the OpenAI API. 21 | 22 | Parameters 23 | ---------- 24 | model 25 | header 26 | The header to prepend to the user's messages 27 | """ 28 | self.model = model 29 | self.header = header 30 | 31 | self.messages = [{ 32 | "role": "system", 33 | "content": header, 34 | }] 35 | 36 | def __call__(self, message: str) -> List[dict]: 37 | """ 38 | Submit the header and all previous messages to the chat bot and return the response. 39 | 40 | Parameters 41 | ---------- 42 | message 43 | A new message added by the user 44 | 45 | Returns 46 | ------- 47 | The response from OpenAI as a list of dictionaries containing the color and light_id 48 | for each light. 49 | """ 50 | self.messages.append({ 51 | "role": "user", 52 | "content": message, 53 | }) 54 | 55 | result = openai.ChatCompletion.create( 56 | model=self.model, 57 | messages=self.messages, 58 | ) 59 | content = result.choices[0].message.content 60 | self.messages.append({ 61 | "role": "assistant", 62 | "content": content, 63 | }) 64 | try: 65 | return json.loads(trim(content)) 66 | except JSONDecodeError: 67 | print(content) 68 | raise 69 | 70 | 71 | def trim(s): 72 | s = re.sub(r"^[a-zA-Z \n:`.,]+", "", s) 73 | s = re.sub(r"[a-zA-Z \n:`.,]+$", "", s) 74 | return s --------------------------------------------------------------------------------