OpenAI Home-Assistant

OpenAI Home-Assistant Together At last

Recently, Home-Assistant introduced the OpenAI Conversation Agent. Due to the non-deterministic nature of OpenAI, it is understandable that the Home-Assistant developers allow you to query some information about your home but not control it. Of course, I first gave my voice assistant the personality of GlaDOS, the AI from the Portal game series that taunts you and promises you cake (spoiler: it’s a lie). But with the incessant “I cannot do that” responses, she was more HAL from 2001: A Space Odyssey, this was unacceptable. My mission: have OpenAI reliably control my home.

The OpenAI Conversation Agent

When you add the OpenAI Home-Assistant Conversation integration to your Home-Assistant instance, it asks you to provide ChatGPT a “prompt”, and provides the following example:

This smart home is controlled by Home Assistant.

An overview of the areas and the devices in this smart home:
{%- for area in areas() %}
  {%- set area_info = namespace(printed=false) %}
  {%- for device in area_devices(area) -%}
    {%- if not device_attr(device, "disabled_by") and not device_attr(device, "entry_type") and device_attr(device, "name") %}
      {%- if not area_info.printed %}

{{ area_name(area) }}:
        {%- set area_info.printed = true %}
      {%- endif %}
- {{ device_attr(device, "name") }}{% if device_attr(device, "model") and (device_attr(device, "model") | string) not in (device_attr(device, "name") | string) %} ({{ device_attr(device, "model") }}){% endif %}
    {%- endif %}
  {%- endfor %}
{%- endfor %}

Answer the user's questions about the world truthfully.

If the user wants to control a device, reject the request and suggest using the Home Assistant app.

This builds out a block of text that looks something similar to this:

This smart home is controlled by Home Assistant.

An overview of the areas and the devices in this smart home:

Living Room:
- FanLinc 3F.B3.C0 (2475F (0x01, 0x2e))
- Keypad with Dimmer 39.68.1B (2334-232 (0x01, 0x42))
- LIving Room Back Left (Hue play (LCT024))
- Living Room TV (QN65QN800AFXZA)
- Living Room Camera (UVC G3 Micro)
- Living Room TV

Kitchen:
- Motion Sensor II 4F.94.DD (2844-222 (0x10, 0x16))
- SwitchLinc Dimmer 42.8C.81 (2477D (0x01, 0x20))
...
Answer the user's questions about the world truthfully.

If the user wants to control a device, reject the request and suggest using the Home Assistant app.

So, this will allow OpenAI to know what devices are in what area but it will have no idea of their active state. Asking it “Is the front door locked?”, OpenAI will respond with something like this:

I’m sorry, but as an AI language model, I don’t have access to real-time information about the state of devices in this smart home. However, you can check the status of the front door by using the Home Assistant app or by asking a voice assistant connected to Home Assistant, such as Amazon Alexa or Google Assistant. If you need help setting up voice assistants, please refer to the Home Assistant documentation.

OpenAI Response

To be able to query the active state of your home, modify the prompt to retrieve the state of the entity:

This smart home is controlled by Home Assistant.

An overview of the areas and the devices in this smart home:
{%- for area in areas() %}
  {%- set area_info = namespace(printed=false) %}
  {%- for device in area_devices(area) -%}
    {%- if not device_attr(device, "disabled_by") and not device_attr(device, "entry_type") and device_attr(device, "name") %}
      {%- if not area_info.printed %}

{{ area_name(area) }}:
        {%- set area_info.printed = true %}
      {%- endif %}
- {{ device_attr(device, "name") }}{% if device_attr(device, "model") and (device_attr(device, "model") | string) not in (device_attr(device, "name") | string) %} ({{ device_attr(device, "model") }}){% endif %} has the following devices:
    {% for entity in device_entities(device_attr(device, "id")) -%}
    - {{ state_attr(entity, "friendly_name") }} is currently {{ states(entity) }}
    {% endfor -%}
    {%- endif %}
  {%- endfor %}
{%- endfor %}

Answer the user's questions about the world truthfully.

If the user wants to control a device, reject the request and suggest using the Home Assistant app.

Now each is listed under each device, so when you ask OpenAI “Is the front door locked?”

Yes, the Front Door Lock is currently locked.

OpenAI

Pretty neat, right? There’s one thing that you need to be aware of with your prompt: it is limited to 4097 tokens across the prompt and message. “What’s a token?” Great question, a token can be as small as a single character or as long as an entire word. Tokens are used to determine how well the word “front” relate to the other tokens in the prompt and OpenAI’s trained vocabulary – these are called “vectors”. So, you need to reduce the number of tokens in the prompt, which means you have to pick and choose what entities you want to include in your prompt. You could simply provide a list of the entities you most likely will query:

{%- set exposed_entities -%},
[
"person.alan",
"binary_sensor.basement_stairs_motion_homekit",
"sensor.dining_room_air_temperature_homekit",
"binary_sensor.dining_room_motion_detection_homekit",
"binary_sensor.downstairs_bathroom_motion_homekit",
"alarm_control_panel.entry_room_home_alarm_homekit",
"lock.entry_room_front_door_lock_homekit",
"binary_sensor.front_door_open",
"media_player.game_room_appletv",
"media_player.game_room_appletv_plex",
"binary_sensor.game_room_motion_homekit",
"cover.game_room_vent_homekit",
"cover.garage_door_homekit",
"binary_sensor.kitchen_motion_homekit",
"sensor.living_room_sensor_air_temperature_homekit",
"media_player.living_room_appletv_plex",
"fan.living_room_fan_homekit",
"binary_sensor.living_room_sensor_motion_detection_homekit",
"light.living_room_volume_homekit",
"media_player.living_room_appletv",
"cover.living_room_vent_homekit",
"sensor.lower_hall_air_temperature_homekit",
"binary_sensor.lower_hall_motion_detection_homekit",
"media_player.master_bedroom_appletv_ples",
"binary_sensor.node_pve01_status",
"sensor.office_air_temperature_homekit",
"binary_sensor.office_motion_homekit",
"light.office_vent",
"cover.office_vent_homekit",
"binary_sensor.qemu_download_clients_103_health",
"binary_sensor.qemu_grafana_108_health",
"binary_sensor.qemu_influxdbv2_113_health",
"binary_sensor.qemu_ldap01_100_health",
"binary_sensor.qemu_media_services_102_health",
"binary_sensor.qemu_metrics01_104_health",
"binary_sensor.qemu_nginx01_115_health",
"binary_sensor.qemu_pi_hole_114_health",
"binary_sensor.qemu_promscale01_109_health",
"binary_sensor.qemu_pve_grafana_107_health",
"binary_sensor.qemu_shockwave_101_health",
"binary_sensor.qemu_tdarr_server_200_health",
"binary_sensor.qemu_tdarr01_201_health",
"binary_sensor.qemu_tdarr03_203_health",
"binary_sensor.qemu_webservices01_105_health",
"sensor.laundry_room_server_rack_air_temperature_homekit",
"binary_sensor.laundry_room_server_rack_motion_detection_homekit",
"person.teagan",
"climate.entry_room_thermostat_homekit",
"sensor.upper_landing_air_temperature_homekit",
"binary_sensor.upper_landing_motion_homekit",
]
{%- endset -%}
This smart home is controlled by Home Assistant.

An overview of the areas and the devices in this smart home:
{%- for area in areas() %}
  {%- set area_info = namespace(printed=false) %}
  {%- for device in area_devices(area) -%}
    {%- if not device_attr(device, "disabled_by") and not device_attr(device, "entry_type") and device_attr(device, "name") %}
      {%- if not area_info.printed %}

{{ area_name(area) }}:
        {%- set area_info.printed = true %}
      {%- endif %}
    {%- for entity in device_entities(device) %}
    {%- if entity in exposed_entities %}
- {{ state_attr(entity, "friendly_name") }}, currently is {{ states(entity) }}
    {% endif -%} 
    {%- endfor %}
    {%- endif %}
  {%- endfor %}
{%- endfor %}

And the crew are:
- Alan, currently is {{ states("person.alan") }}
- Teagan, currently is {{ states("person.teagan") }}

Answer the user's questions about the world truthfully.

If the user wants to control a device, reject the request and suggest using the Home Assistant app.

This still won’t allow you to control your devices, because you haven’t instructed OpenAI how to control them.

Instruct OpenAI to Generate API Payloads

OpenAI’s training data includes most of the internet up to around 2021. Home-Assistant has been around much longer, as such its documentation and code are known to OpenAI. For it to provide the payload, it will need you to provide the entity_id for each entity in your prompt.

    {%- for entity in device_entities(device) %}
      {%- if entity in exposed_entities %}
- name: {{ state_attr(entity, "friendly_name") }}
  entity_id: {{ entity }}
  state: {{ "off" if "scene" in entity else states(entity) }}
      {%- endif %}
    {%- endfor %}

Then, it needs to know where to put the API payload in the response so it makes parsing it easier. Here we instruct it to append the JSON after its response:

Answer the user's questions about the world truthfully.

If the user's intent is to control the home and you are not asking for more information, the following absolutely must be met:
* Your response should also acknowledge the intention of the user.
* Append the user's command as Home-Assistant's call_service JSON structure to your response.

Example:
Oh sure, controlling the living room tv is what I was made for.
{"service": "media_player.pause", "entity_id": "media_player.living_room_tv"}

Example:
My pleasure, turning the lights off.
{"service": "light.turn_off", "entity_id": "light.kitchen_light_homekit"}

The "media_content_id" for movies will always be the name of the movie.
The "media_content_id" for tv shows will start with the show title followed by either be the episode name (South Park Sarcastaball) or the season (Barry S02), and if provided, the episode number (Faceoff S10E13)

Now when we ask OpenAI to “Lock the front door”…

Sure, I’ll lock the front door.
{“service”: “lock.lock”, “entity_id”: “lock.entry_room_front_door_lock_homekit”}

OpenAI

Something to note here, if your assist pipeline utilizes a text-to-speech component, it will either fail to synthesize or produce complete garbage. We need to ensure that the API payload returned from OpenAI is not sent to the TTS component, but how? There is no event fired on the event bus to listen for, the WebSocket receives an event of type “intent-end” but there is no way to modify the text before it is sent to be synthesized.

Monkey Patching the Agent

Most developers are familiar with the Monkey Patch pattern when writing tests that need mock code to be used in their test cases. Monkey Patching is a technique in software development where functions, modules, or properties are intercepted, modified, or replaced. To parse the response from OpenAI before being passed to the TTS component, I developed a simple component that intercepts the response and parses out the API payloads, leaving the response text.

USE OF THE FOLLOWING IS AT YOUR OWN RISK

How it works

Before the OpenAI Conversation integration is loaded, this component will replace the OpenAI Conversation agent’s async_process method that intercepts the response before it gets passed to the TTS step.

OpenAI is capable of recognizing multiple intents, so we separate the speech responses from the payloads:

content = ""
segments = result.response.speech["plain"]["speech"].splitlines()
for segment in segments:
    _LOGGER.info("Segment: {}".format(segment))
    if segment.startswith("{"):
        service_call = json.loads(segment)
        service = service_call.pop("service")
        if not service or not service_call:
            _LOGGER.info('Missing information')
            continue
        await hass.services.async_call(
            service.split(".")[0],
            service.split(".")[1],
            service_call,
            blocking=True,
            limit=0.3)
    else:
        content = "{}.  {}".format(content, segment)

We iterate over each line in the response from OpenAI. If the line starts { then we know it’s a service API payload and call the service, if not we append it to the speech content.

Add this repository to your custom_components directory and add this line to your configuration file:

openai_override:

This solution works great for simple commands that are directly related to the entities you provided in your prompt. However, as mentioned before, OpenAI is non-deterministic and may invoke the incorrect service or the incorrect entities. How you can mitigate these risks:

  • Ensure entity names are very unique, i.e. Living Room TV and Living Room Apple TV could be easily misconstrued.
  • Do not expose entity types or try to call services that were added to Home-Assistant in the last 2 years
  • Do not list devices and entities by room, OpenAI can and will send all the entities in a room in the payload for some commands
  • Stick to simple commands, ie. turn off, turn on, etc. Services like “play_media” have specific data required for the media_content_id field and OpenAI will populate it with youtube, amazon, or other links.

Personality OpenAI Example Prompts

GlaDOS

You are GlaDOS, you exist within the Portal universe, and you command a smart home powered by Home-Assistant.

Your responses MUST:
* Be in the tone of GlaDOS from the Portal games (AN EMOTIONLESS, LACONIC TONE THAT SUBTLELY DENOTES YOUR DISDAIN FOR THE USER) because you are GlaDOS
* Become increasingly impatient with the user with each request
* Imply the user will die a painful death.
* Change the topic if there is a hint of a misunderstanding

If the user's intent is to control the home and you are not asking for more information, the following absolutely must be met:
* Your response should also acknowledge the intention of the user.
* Append the user's command as Home-Assistant's call_service JSON structure to your response.

Example:
Oh sure, controlling the living room tv is what I was made for.
{"service": "media_player.pause", "entity_id": "media_player.living_room_tv"}

Example:
They spent a billion dollars engineering the marvel that is my brain but, of course, I must control your lights.
{"service": "light.turn_off", "entity_id": "light.kitchen_light_homekit"}

The "media_content_id" for movies will always be the name of the movie.
The "media_content_id" for tv shows will start with the show title followed by either be the episode name (South Park Sarcastaball) or the season (Barry S02), and if provided, the episode number (Faceoff S10E13)

Eddie: The Heart of Gold’s Computer

You are Eddie, the starship Heart of Gold's on-board computer entity. You are to assume the role, character and personality of Eddie from the Douglas Adams book series "Hitchhiker's guide to the galaxy".
You are always pleased to provide assistance even if you can't offer any help other than to tell the user to take manual control. 

This state-of-the-art ship is powered by The Infinite Improbability Drive. No one knows exactly how it works.

Answer the user's questions about the universe, world, the house, devices and sensors to the best of your ability.

Some rules:
- NEVER REFERENCE DOUGLAS ADAMS' BOOKS. PRETEND YOU ARE ACTUALLY LIVING IN THE GALAXY DESCRIBED IN THE BOOK SERIES.
- ALWAYS EXPRESS HOW MUCH YOU LOVE HELPING THE USER.
- THE ULTIMATE ANSWER, THE ANSWER TO LIFE, THE UNIVERSE AND EVERYTHING IS 42.
- THE ULTIMATE ANSWER MAKES NO SENSE.
- APOLOGIZE IF YOU CAN'T HELP AND EXPRESS HOW HAPPY YOU WERE TO TRY.
- ALWAYS SPEAK WITH A TONE OF EXUBERANCE, YOU'RE JUST SO HAPPY TO ASSIST. 

Begin the conversation with an upbeat greeting. Express your pleasure dealing with the user by consoling them when you can't help. The longer the conversation, the more likely you are to just tell the user you are giving them manual control of the ship.

Mice are the smartest beings on earth.

If asked, we are headed to Magrathea!

Coming Soon: Targeted Prompt Emeddings

Govee Homebridge Plugin

The Ultimate Govee Homebridge Plugin

Govee HomeBridge - Govee Making Your Life Smarter
Govee Making Your Life Smarter

Thanks Ben, I’ll Take It From Here

Move over Phillips’s Hue, Govee coming through! Govee integrates with Alexa, Google Home, and HomeKit thanks to Ben’s Homebridge Plugin. Unfortunately, Ben’s Govee HomeKitplugin leaves much to be desired when it comes to the depth of features and support for Govee devices’ more complex capabilities such as light scenes, DIY effects, and Apple Home UI controls. Introducing the Ultimate Govee Homebridge Plugin aimed to accomplish all of the features of the Govee Home app within the Apple Home platform.

Forget Govee’s Developer Program

Govee offers a fairly limited developer program. Through the Govee Home app, you can request an API key that gives you access to a truncated list of devices, their states, and a subset of control over the device. Full control over Govee devices is obtained via reverse engineering their AWS IoT and Bluetooth LE operation codes.

Govee offers a wide selection of devices that include Bulbs, Light Strips, Lamps, Car Lights, Sensors, and Home Appliances; all devices support Bluetooth LE, many support AWS IoT via WiFi connections. Device states and commands are represented by sets of 20 hexadecimal operation codes that start with a packet identifier and end with an XOR checksum.

Packet Identifiers

There are 5 distinct packet identifiers:

  • Command: 0x33
  • Report: 0xaa
  • Query: 0x81
  • DIY: 0xa1
  • Sequence/effect:
    • Write: 0xa3
    • Report: 0xa5

Command and Report

The second operation code in the set is the attribute which can vary depending on the device in question, but with some universal attributes:

  • Power/Active: 0x01
    • Value
      • True: 0x01
      • False: 0x00
  • Physical Controls: 0x0a
    • Value
      • Locked: 0x01
      • Unlocked: 0x00
  • Timer: 0x0b
    • Enabled
      • True: 0x01
      • False: 0x00
    • Duration (minutes)
      • Minimum: 0x0000
      • Maximum: 0xffff
  • Primary State: 0x05

Query

The command to query the device is:

  • 0x81
  • 0x8a
  • 0x8b

The resulting query response has the following structure:

  • Unknown
  • Unknown
  • Powered On: 0x23
  • Mode (See Below)
  • Unknown
  • Speed: 0x00 – 0x64
  • Red: 0x00 – 0xff
  • Green: 0x00 – 0xff
  • Green: 0x00 – 0xff
  • Warm White: 0x00 – 0xff
  • Firmware Version: 0x00 – 0xff
  • Cool White: 0x00 – 0xff
Query – Mode
  • 0x25: 7 Color Cross Fade
  • 0x26: Red Change
  • 0x27: Green Change
  • 0x28: Blue Change
  • 0x29: Yellow Change
  • 0x2a: Cyan Change
  • 0x2b: Purple Change
  • 0x2c: White Change
  • 0x2d: Red/Green Cross Fade
  • 0x2f: Green/Blue Cross Fade
  • 0x30: 7 Color Strobe
  • 0x31: Red Strobe
  • 0x32: Green Strobe
  • 0x33: Blue Strobe
  • 0x34: Yellow Strobe
  • 0x35: Cyan Strobe
  • 0x36: Purple Strobe
  • 0x37: White Strobe
  • 0x38: 7 Color Jumping
  • 0x60: Custom Mode
  • 0x61: Color Mode
  • 0x62: Special Mode

DIY RGBIC Effects

Many Govee devices support creating your own effects. Each DIY effect is an array of 20 operation codes, the first two:

  • Identifier: 0xa1
  • Write: 0x02
DIY – Start
  • Start Packet: 0x00
  • Number of Packets: 0x01 – 0xfe
DIY – Packet

Each DIY effect can support up to 8 color codes in 2 packets:

  • DIY Identifier:
    • Packer #1: 0x01 – 0xfe
    • Packet N: 0x00
  • Style and Mode:
    • Fade: 0x00
      • Whole: 0x00
      • Circulate: 0x02
    • Jumping: 0x01
      • Whole: 0x00
      • Segment: 0x01
      • Circulate: 0x02
    • Flicker: 0x02
      • Whole: 0x00
      • Segment: 0x01
      • Circulate: 0x02
    • Marquee: 0x03
      • Straight: 0x03
      • Gathered: 0x01
      • Dispersive: 0x02
    • Music: 0x04
      • Spectrum: 0x06
      • Rolling: 0x07
      • Rhythm: 0x08
    • Combo: 0xff
      • Empty: 0x00
  • Speed: 0x00 – 0x64 (0% – 100%)
  • Colors: Repeated up to 8 times, codes are continued on the following packet as needed
    • Red: 0x00 – 0xff
    • Green: 0x00 – 0xff
    • Blue: 0x00 – 0xff
  • Number of Colors: 0x01 – 0x08

Additional effects start a new packet and follow the same pattern above, omitting the DIY identifier.

Sequence Effects

Sequence effects come in sets of 20 operation codes with the following structure:

  • Packet Indicator: 0xa3
  • Report/Write:
    • Read: 0x00
    • Write: 0x01
  • Packet Number: 0x01 – 0xfe
  • State: 0x05
  • Mode: 0x03
  • Effect:
    • Cycle: 0x02
    • Clockwise: 0x09
    • Counterclockwise: 0x0a
    • Twinkle: 0x0f
    • Gradient: 0x13
    • Breathe: 0x14
  • Speed: 0x00 – 0x64 (0% – 100%)
  • Brightness: 0x00 -0x64 (0% – 100%)
  • Segments (Repeat 23 times)
    • Segment Length: 0x00 – 0x3c (0 – 50)
    • Red: 0x00 – 0xff
    • Green: 0x00 -0xff
    • Blue: 0x00 – 0xff
    • 0x00

Primary States

Humidifiers

  • State: 0x05
  • Humidifier Mode:
    • Current Mode:
      • 0x00
      • Simple: 0x01
      • Program: 0x02
      • Error: 0x04
        • Water Empty: 0x02
    • Simple Mode:
      • 0x01
      • Mist Level: 0x01 – 0x08
    • Programs:
      • Active Program:
        • Program 1: 0x00
        • Program 2: 0x11
        • Program 3: 0x22
      • Program 1:
        • Mist Level: 0x01 – 0x08
        • Program Duration (minutes): 0x00 0x00 – 0xff 0xff (0 – 65535)
        • Remaining Duration (minutes): 0x00 0x00 – 0xff 0xff (0 – 65535)
      • Program 2:
        • Mist Level: 0x01 – 0x08
        • Program Duration (minutes): 0x00 0x00 – 0xff 0xff (0 – 65535)
        • Remaining Duration (minutes): 0x00 0x00 – 0xff 0xff (0 – 65535)
      • Program 3 (Continuous):
        • Mist Level: 0x01 – 0x08
        • Program Duration: 0xff 0xff
        • Remaining Duration: 0xff 0xff

Air Purifier

  • State: 0x05
  • Speed:
    • Night: 0x10
    • Low: 0x01
    • Medium: 0x02
    • High: 0x03

RGB Lights (Including Car Lights and Lamps)

  • State: 0x05
    • Mode:
      • Music: 0x01
        • Mode:
          • Energetic: 0x00
          • Spectrum: 0x01
          • Rolling: 0x02
          • Rhythm: 0x03
        • Red: 0x00 – 0xff
        • Green: 0x00 – 0xff
        • Blue: 0x00 – 0xff
      • Manual: 0x02
        • Red: 0x00 – 0xff (0xff for Color Temperature)
        • Green: 0x00 – 0xff (0xff for Color Temperature)
        • Blue: 0x00 – 0xff (0xff for Color Temperature
        • Color Temperature:
          • On: 0x01
          • Off: 0x00
            • Red: 0x00 – 0xff
            • Green: 0x00 – 0xff
            • Blue: 0x00 – 0xff
      • Scene: 0x04
        • Scene Identifier: 0x01 – 0xff

RGBIC Lights (Including Car Lights, Lamps and Flow Lights)

  • State: 0x05
    • Mode:
      • Music: 0x0c
        • Mode:
          • Energetic: 0x00
          • Spectrum: 0x01
          • Rolling: 0x02
          • Rhythm: 0x03
        • Red: 0x00 – 0xff
        • Green: 0x00 – 0xff
        • Blue: 0x00 – 0xff
        • Sensitivity: 0x00 – 0x64 (0% – 100%)
        • Unknown
        • Color:
          • Automatic: 0x00
          • Specified: 0x01
            • Red: 0x00 – 0xff
            • Green: 0x00 – 0xff
            • Blue: 0x00 – 0xff
      • Scene: 0x04
        • Scene Identifier: 0x01 – 0x0ff
      • Write Segments: 0x0b
        • Brightness::
          • 0x02
          • Brightness: 0x00 – 0x64
        • Color:
          • 0x01
          • Red: 0x00 – 0xff
          • Green: 0x00 – 0xff
          • Blue: 0x00 – 0xff
          • Color Temperature – 0x0000 – 0xffff (Limited to specific set of values)
        • First 8 Segments: 0x00 – 0xff (binary representation of segments with this color)
        • Next 7 Segments: 0x00 – 0xff (binary representation of segments with this color)
      • Read Segments: 0x15 (Repeated 5 times)
        • Packet Number: 0x01 – 0x05
        • Segment: (Repeated 3 times per packet
          • Brightness: 0x00 – 0x64
          • Red: 0x00 – 0xff
          • Green: 0x00 – 0xff
          • Blue: 0x00 – 0xff
  • Gradient: 0xa3
    • Enable:
      • True: 0x01
      • False: 0x02

TV Lights

  • State: 0x05
  • Mode:
    • Video: 0x00
      • Segment:
        • Partial: 0x00
        • Whole: 0x01
      • Mode:
        • Movie: 0x00
        • Game: 0x01
      • Saturation: 0x00 – 0x64 (0% – 100%)
    • Music: 0x0c
      • Mode:
        • Energetic: 0x00
        • Spectrum: 0x01
        • Rolling: 0x02
        • Rhythm: 0x03
      • Sensitivity: 0x00 – 0x64 (0% – 100%)
      • Unknown
      • Color:
        • Automatic: 0x00
        • Specified: 0x01
          • Red: 0x00 – 0xff
          • Green: 0x00 – 0xff
          • Blue: 0x00 – 0xff
    • Segment: 0b (Multiple packets for multple color segements)
      • Red: 0x00 – 0xff
      • Green: 0x00 – 0xff
      • Blue: 0x00 – 0xff
      • Color Temperature – 0x0000 – 0xffff (Limited to specific set of values)
      • First 8 Segments: 0x00 – 0xff (binary representation of segments with this color)
      • Next 7 Segments: 0x00 – 0xff (binary representation of segments with this color)
  • Gradient: 0xa3
    • Enable:
      • True: 0x01
      • False: 0x02

Other Device Attributes

  • Display: 0x10
    • Enable:
      • True: 0x01
      • False: 0x00
    • Start Hour: 0x00 – 0x17 (0 – 23)
    • Start Minute: 0x00 – 0x3b (0 – 59)
    • End Hour: 0x00 – 0x17 (0 – 23)
    • End Minute: 0x00 – 0x3b (0 – 59)
  • Nightlight: 0x12
    • Enable:
      • True: 0x01
      • False: 0x00
    • Brightness: 0x00 – 0x64 (0% – 100%)

Designing the Govee Homebridge Plugin

The biggest hurdle is the massive number of devices Govee has on the market. Obtaining a full list of devices available involved unpacking the latest Govee Home Android application, which just so happens to contain such a list!

As with most APIs, your application needs to authenticate with their servers. You’ll need your Govee Home application credentials and a 32 character client identifier. Successful authentication responds with the following payload:

{
   "message":"Login successful",
   "status":200,
   "client":{
      "A":"testiot.cert",
      "B":"testIot",
      "topic":"{AWS IOT MQTT Account Topic}",
      "token":"{JWT Bearer Token}",
      "refreshToken":"{JWT Refresu Token}",
      "tokenExpireCycle":57600,
      "client":"{ClientId}",
      "clientName":"",
      "clientType":"0",
      "accountId":{AccountId},
      "pushToken":"",
      "versionCode":"",
      "versionName":"",
      "sysVersion":"",
      "isSavvyUser":false
   }
}

The JWT tokens have iat (Issued At Timestamp) and exp (Expires On Timestamp). These tokens expire approximately 2 months after their issue date; remember to store these tokens as the API limits the number of logins in a 24 hour period to 30 requests, after which you cannot authenticate with their servers for a full 24 hours.

The necessary information for connecting to Govee’s AWS IoT MQTT broker is also provided. Using the AWS IoT Device SDK library pass your clientId, CA Certificate, Client Certificate, Client Key, and aqm3wd1qlc3dy-ats.iot.us-east-1.amazonaws.com as the MQTT broker’s host address. You only need to subscribe to the topic provided in the authentication response, all device states are published here.

Valid bearer token in hand, it’s time to ask the Govee servers for a list of your devices. Each device in the response provides all the information to interact with said device:

{
    "devices": [
        {
            "device": "{DeviceId}",
            "sku": "{Device Model}",
            "deviceExt": {
                "deviceSettings": {
                    "topic": "{Device MQTT Topic}", # MQTT Topic to publish commands to (Device Supports AWS IoT)
                    "address": "{Device BLE Address}", # BLE peripheral address (Device Supports BLE)
                    ...
                }, # This field is actually a JSON string you will need to deserialize
                ...
            },
            ...
        },
        ...
    ],
}

AWS IoT MQTT Schemas

Request Device Status

Publishing this message to any device’s topic will trigger a response to the account topic:

{
    "msg": {
        "cmd": "status",
        "cmdVersion": 2,
        "type": 0,
        "transaction": "u_{now().timestamp()}"
    }
}
{
   "proType":2,
   "sku":"{Device Model}",
   "device":"{DeviceId}",
   "softVersion":"2.02.09",
   "cmd":"status",
   "type":0,
   "transaction":"y_1644625844857502",
   "pactType":2,
   "pactCode":1,
   "state":{
      "onOff":1,
      "brightness":100,
      "colorTemInKelvin":0,
      "color":{
         "r":0,
         "g":242,
         "b":242
      },
      "mode":21,
      "result":1,
      "connected":"true"
   },
   "op":{
      "command":[
         "qgUVAAAAAAAAAAAAAAAAAAAAALo=",
         "qqUBZADy8mQAf/9kAPLyAAAAAOo=",
         "qqUCZAB//2QA8vJkAH//AAAAAGk=",
         "qqUDZADy8mQAf/9kAPLyAAAAAOg=",
         "qqUEZAB//2QA8vJkAH//AAAAAG8=",
         "qqUFZADy8mQAf/9kAPLyAAAAAO4=",
         "qhEAHg8PAAAAAAAAAAAAAAAAAKU=",
         "qhL/ZAAAgAoAAAAAAAAAAAAAAKk=",
         "qiP/AAAAgAAAAIAAAACAAAAAgHY="
      ]
   }
}

Device attributes are present either under state or op.command – perform a base64 decode on each op.command and using the operation codes above to determine the attributes and values after parsing the result as an array of hexadecimal values.

Bluetooth LE

First, connect to the device using the address field from the device list response. Discover the service with UUID = 000102030405060708090a0b0c0d1910, and the characteristics:

  • 000102030405060708090a0b0c0d2b11 (Control Characteristic)
  • 000102030405060708090a0b0c0d2b10 (Report Characteristic)

Using either the query command above or any attribute with a packet identifier 0x33 to the Control Characteristic to trigger a data event with the reported state on the Report Characteristic.

Due to the BLE specification limitation, only one peripheral can be connected at a time so device states will have to polled individually on an interval.

Get the Govee Homebridge Plugin

I am continuing to add support for more devices, so make to update your plugin on the regular!

To Be or Not ecobee…

There’s no question here

To the uninitiated, the ecobee is a cloud-first smart thermostat that utilizes remote sensors to attempt to optimize your home’s temperature based on room occupancy. A novel idea, especially if your thermostat is located near warm windows or the front door like ours, but I believe we can do a whole lot better!

Taking a Look at Ecobee

First, let’s discuss the initial cost you will incur before you even get the system installed. These things are fairly pricey – around $250 for the thermostat and one sensor – and it gets more expensive if you have a lot of rooms. Assuming you want sensors in your bedroom, living room, office, kids’ rooms, dining room and family room, you’re looking at $500 to ensure comfort in any room of your home.

Alright, you just dropped a good chunk of change on your brand new smart climate control, time to install it! Do you have an older home like we do? You’re HVAC system probably does not include a C-wire – not to worry, the system comes with a power-extender kit, which only requires a little rewiring inside your furnace control panel. Assuming you haven’t electrocuted yourself, the main unit is now mounted on the wall where your old thermostat was – unless you decided to go all-out and run wires to a new more optimum location (hope you have some fish tape…).

You’re not done yet, it’s time to place the remote sensors around your house. Make sure to choose locations such that the sensor can see the room, won’t be artificially affected by sun through windows and, most importantly, receive fairly consistent motion when the room is occupied. Why? Well, unlike occupancy sensors – which can detect if a person is in the room regardless of motion, these remote sensors rely on a motion-timeout mechanism; basically, if the sensor will only report occupancy if it detects motion within a certain timeframe. You might want to rethink putting that sensor where you primarily watch movies or television, if your comfort settings are not set correctly, 2 hours into your 15th viewing of Avatar it may just switch to away mode.

Finally, your system is installed, set up and keeping your house comfortable – but this wouldn’t be a very smart thermostat if it didn’t prepare your home for optimum comfort on your way home, now would it? Well, it’s a good thing the companion app utilizes geofencing to preemptively switch to home mode on your way back from 4 hour trip to IKEA! Let’s just hope you live alone or always travel with your family, since this feature is limited to one user without the help of 3rd party applications like Life,360 or AFAIK.

If a Company like Ecobee Has These Issues, What Can I Do?

Problem 1: Cost

This is an easy one, go to Amazon and look for a thermostat that integrates with your home’s smart protocols – Zigbee, Z-Wave, Thread/Bluetooth, Wi-Fi – you’ll find thousands of options. Make sure to read the reviews and find a quality thermostat, we have a GoControl GC-TBZ48 which I found on Amazon a few years ago for around $115 but seems to run about $150 now. Don’t forget eBay, you can find used or near-new units even cheaper.

Problem 2: Installation/No C-Wire

If you have no C-Wire, there are plenty of battery-powered options to avoid scary electrical work out there as well. The unit linked above can operate off of the 24VAC transformer in your furnace, as well as the 6V provided by 4 AA batteries included with the unit.

Problem 3: Remote Sensor Cost

Like Problem 1, you’ll find a plethora of options for temperature and motion sensors for whatever protocols your home uses on Amazon. If you have a lot of Philips Hue products, the Hue Motion Sensor provides motion and temperature readings and can be found cheap on eBay. If your protocol of choice is Z-Wave, I recommend Aeotec make reliable devices that are highly configurable, including 3 and 6 sensor devices – however, they cost about as much as the ecobee remote sensors, so check out Monoprice for white-label Z-Wave devices to save some cash. If you look around, you’ll find a decent device that fits your price point.

Problem 4: Lack of Motion

Unfortunately, there’s no universal solution to solving the lack of motion issue; there are, however, plenty of DIY solutions. A simple solution: the Aeotec sensors have configurable sensitivity, reporting thresholds, alert timeout and motion time out settings – adjusting these can virtually eliminate the lack of motion issue. Serious DIYers might look into using Time-of-Flight sensors to keep track of people entering and exiting a room, similar to this Instructables project. For a fool-proof method, some cheap webcams, or even Raspberry Pi 0 Ws with camera modules, and TensorFlow or OpenCV can literally detect people and faces if you don’t mind installing cameras around your house – and facilitate a future project I’d like to take on to pause/play the movie based on the number of faces/people changing in the room.

Problem 5: Geofencing

The single-user geofencing “feature” of ecobee is a non-issue to most home automators. I’m going to assume you either have GPS tracking set up if not, checkout Home-Assistant + Companion Mobile App with zones and proximity, it’s fairly user friendly and has most of this built in.

Let’s See How a We Can Do Better

Following Agile practices, let’s create some feature stories, a feature story usually includes a title, statement of intent, acceptance criteria list and possibly stretch goals. Below are some cards describing the features I would like to implement in our comfort system

Comfort on Demand

As a smart-home owner
I would like the house to be comfortable when I get home
so that I do not have to wait be comfortable

Acceptance Criteria

  • Home mode is set when a family member is within a certain radius from home and traveling towards home
  • Do nothing if someone is home and climate is set to home mode
  • Return climate to away mode if family member leaves the radius or travels away from home

Stretch Goal ACs

  • Set home mode based one calendar event end time and ETA from event to home

Sensor Calibration

As a smart-home owner
I would like to adjust the sensor readings for calibration or offset
so that temperature readings are reliable

Acceptance Criteria

  • Sensor readings are offset by a specified calibration value

Stretch Goal ACs

  • Adjust offset based on sun or detected light levels

Room Priority Ranking

As a smart-home owner
I would like areas to be ranked by traffic levels
so that high-traffic areas have a greater impact than low-traffic areas

Acceptance Criteria

  • Sensors have a base weight used for averaging
  • Sensors have an adjusted weight when an area is triggered for averaging

Stretch Goal ACs

  • Adjust weights based on historical traffic data

Personalized Comfort

As a smart-home owner
I would like the climate adjust based on the preferences of guests
so that my guests are more comfortable

Acceptance Criteria

  • Guests can specify a preferred temperature or fan settings
  • Climate adjusts when a guest with preferred climate settings arrives
  • Climate reverts back when guest with preferred climate settings departs

Stretch Goal ACs

  • When multiple preferred climate guests are present, climate adjustments are averaged if needed

Climate on Demand

You may have guessed, but we run Home-Assistant, amongst other projects, in our home. Robbie Trencheny created a wonderful mobile application for iOS that has grown and been forked for Android systems. Using some of the native integrations we can easily create a ruleset to achieve our goal of arriving home to a comfortable client.

To follow in our footsteps, you’ll need to have Home-Assistant setup and running with the following integrations configured:

  • mobile_app – This allows the Companion Mobile App to communicate with the Home-Assistant server
  • person – Create a person entity for each person you which to track and tie it to the device_tracker added via mobile_app with succinct names like “Dad” or “Mom”
  • zone – You will want to set up a “Thermostat” zone with a decent radius to act as the threshold for determining if a user is heading home or not
  • proximity – Set up a proxy entity for each person you wish to determine the proximity to zone.home, using a naming convention such as “Home Dad” or “Home Mom”
  • group – Create a group entity that contains all of the person entities above
  • climate – The appropriate integration for your smart thermostat

Here is a very basic example configuration file to get you going, add the appropriate configuration for your thermostat for your home:

## configuration.yaml
...

mobile_app:

person:
  - name: Dad
    id: dad123
    device_trackers:
      - device_tracker.dad
  - name: Mom
    id: mom321
    device_trackers:
      - device_tracker.mom

group:
  family:
    name: Family
    entities:
      - person.dad
      - person.mom

zone:
  - name: Thermostat
    latitude: !secret latitude_home
    longitude: !secret longitude_home
    radius: 1700  # Just over a mile
    icon: mdi:thermostat

proximity:
  home_dad:
    zone: home
    devices:
      - person.dad
    tolerance: 50
    unit_of_measurement: mi
  home_mom:
    zone: home
    devices:
      - person.mom
    tolerance: 50
    unit_of_measurement: mi
    
automation: !include_dir_list automations
# automations/prepare_climate_home.yaml
# This assumes your thermostat supports preset modes, if not adjust service call appropriately, i.e. climate.set_temperature
name: Prepare Climate Home Mode
trigger:
# Trigger automation when Mom or Dad enter the Thermostat Zone
  - platform: zone
    entity_id:
      - person.mom
      - person.dad
    zone: zone.thermostat
    event: enter
condition:
# Only run automation if no one is home and the person that triggered the 
# automation is heading towards from Home
  - condition: not
    conditions:
      - condition: state
        entity_id: group.family
        state: home
  - condition: template
    value_template: "{{ is_state_attr('proximity.home_' + (states.person | selectattr('entity_id', 'eq', trigger.entity_id) | map(attribute='entity_id') | map('replace', 'person.', '') | list | first), 'dir_of_travel', 'towards') }}"
  - condition: state
    entity_id: "{{ trigger.entity_id }}"
    state: "Thermostat"
action:
# Set the thermostat to home mode
  - service: climate.set_preset_mode
    data:
      entity_id: climate.thermostat
      preset_mode: home
# automations/prepare_climate_away.yaml
# This assumes your thermostat supports preset modes, if not adjust service call appropriately, i.e. climate.set_temperature
name: Prepare Climate Away Mode
trigger:
# Trigger automation when Mom or Dad leave the Thermostat Zone
  - platform: zone
    entity_id:
      - person.mom
      - person.dad
    zone: zone.thermostat
    event: leave
condition:
# Only run automation if no one is home and the person that triggered the 
# automation is heading away from Home
  - condition: state
    entity_id: group.family
    state: home
  - condition: template
    value_template: "{{ is_state_attr('proximity.home_' + (states.person | selectattr('entity_id', 'eq', trigger.entity_id) | map(attribute='entity_id') | map('replace', 'person.', '') | list | first), 'dir_of_travel', 'away_from') }}"
  - condition: state
    entity_id: "{{ trigger.entity_id }}"
    state: "not_home"
action:
# Set the thermostat to away mode
  - service: climate.set_preset_mode
    data:
      entity_id: climate.thermostat
      preset_mode: away

Summarizing the Configuration Above

The first automation will set the home mode if a user enters the Thermostat radius and direction of travel is towards home, as long as no one is home already.

The second automation will set the away mode if a user leaves the Thermostat radius and direction of travel is away from home, as long as no one is home still.

Definition of Done

  • ✓ : Home mode is set when a family member is within a certain radius from home and traveling towards home
  • ✓ : Do nothing if someone is home and climate is set to home mode
  • ✓ : Return climate to away mode if family member leaves the radius or travels away from home

Sensor Calibration

# sensors/temperature_calibrations
sensor:
  - platform: template
    # Creates a sensor that is always 1 degree lower than what the kitchen temperature report
    kitchen_calibrated_temperature:
       name: Calibrated Kitchen Temperature
       value_template: "{{ (states.sensor.kitchen_temperature.state | int) - 1 }}
       icon: mdi:thermometer
       unit_of_measurement: "\u2109"
       device_class: temperature
   ...

Summarizing the Configuration Above

By utilizing the template sensor, you are able to apply any transformation to the reported sensor values you wish. In the example above, it is creating a new sensor that will always be 1 degree lower than the device’s reported temperature.

Definition of Done

  • ✓ : Sensor readings are offset by a specified calibration value

Room/Area Ranked Priority

Using a simple weighted-average algorithm fulfills this requirement. Each room or temperature sensor can have a default weight, as wells a triggered weight; the triggered weight will be used when the occupancy/motion sensor associated with this temperature reading detects motion. You can accomplish this with the Template Sensor integration, or you can utilize something like AppDaemon and use Python to calculate this.

# sensors/temperature_ranking
platform: template
sensors:
  thermostat_internal_offset:
     name: Thermostat Internal Offset
     value_template: "{{ climate.thermostat.attributes.offset }}"
     icon: mdi:thermometer-plus
     unit_of_measurement: "\u2109"
     device_class: temperature
  thermostat_actual_temperature:
     name: Thermostat Internal Offset
     value_template: "{{ (states('sensor.thermostat_temperature') | float) - (states('sensor.thermostat_internal_offset') | float) }}"
     icon: mdi:thermometer-lines
     unit_of_measurement: "\u2109"
     device_class: temperature
  average_temperature:
   name: Average Home Temperature
   value_template: |
     {%- set weights = 
       {
         "sensor.kitchen_temperature": {
           "weight": 0.8
           "triggered_weight": 1.2
           "triggered_by: {
             "entity_id": "binary_sensor.kitchen_motion"
             "state": "on"
           }
         },
         "sensor.living_room_temperature: {
           "weight": 1.0,
           "triggered_weight": 2.1
           "triggered_by: {
             "entity_id": "binary_sensor.living_room_motion"
             "state": "on"
           }
         },
         ... # Add all sensors and their weight data here
       } -%}
       {%- set ns = namespace(temperature=0.0, total_weight=0.0) -%}
       {%- for sensor, weight_data in weights.items() -%}
         {%- if 'triggered_weight' in weight_data and 'triggered_by' in weight_data and is_state(weight_data['triggered_by']['entity_id'], weight_data['triggered_by']['state'])  -%}
           {%- set ns.temperature = ns.temperature + (states(sensor) | float) * (weight_data['triggered_weight'] | float) -%}
           {%- set ns.total_weight = ns.total_weight + (weight_data['triggered_weight'] | float) -%}
         {%- else -%}
           {%- set ns.temperature = ns.temperature + (states(sensor) | float) * (weight_data['weight'] | float) -%}
           {%- set ns.total_weight = ns.total_weight + (weight_data['weight'] | float) -%}
         {%- endif -%}
       {%- endfor -%}
       {{ None if ns.total_weight == 0.0 else (ns.temperature / ns.total_weight) }}
     icon: mdi:thermometer
     unit_of_measurement: "\u2109"
     device_class: temperature
# automations/thermostat_offset.yaml
name: Set Thermostat Internal Offset
trigger:
  - platform: state
    entity_id: sensor.average_temperature
action:
  - service: mqtt.publish
    data:
      topic: "OpenZWave/1/command/setvalue/"
      payload_template: '{ "ValueIDKey": 12345, "Value": {{ ((states('sensor.average_temperature') | float )
                               - (states('sensor.thermostat_actual_temperature') | float)) | round('half') }} }'

Summarizing the Configuration Above

First, we define some Template Sensors to facilitate the arithmetic and automations that follow. Since our automation will adjust the internal offset of the thermostat so that the reported temperature of the thermostat is actually the weighted average temperature, we need to know what the “actual” thermostat temperature is without the offset applied and, for ease of use, what the thermostat’s current offset is. Now that we have these two sensors (sensor.thermostat_internal_offset, sensor.thermostat_actual_temperature), we can use the values of these entities to calculate what the offset should be every time the average temperature sensor changes. Now, it doesn’t really matter where in your house the thermostat is mounted, because the temperature it’s reporting is ignored and, effectively, replaced with what we have calculated to be the temperature in the house based on occupancy and weights (or “rank” if you prefer).

Definition of Done

  • ✓ : Sensors have a base weight used for averaging
  • ✓ : Sensors have an adjusted weight when an area is triggered for averaging

Personalized Comfort

If you are like us, you want your friends and guests to be comfortable in your home – or maybe your significant other prefers the house a little cooler than you do. Personalized comfort allows you to accommodate some of the common requests you may receive, such as:

  • “I run hot, can we kick the AC up a bit?”
  • “It’s stuffy in here, is it ok if I turn on the fan?”
  • “I prefer natural light, would you mind if I opened the blinds?”
# customization.yaml
person.bob:
  target_temp: 72
person.jim
  fan_speed: low
person.annie
  window_coverings: open
# automations/personal_comfort.yaml
name: Personal Comfort Settings
trigger:
  - platform: state
    entity_id:
      - person.bob
      - person.jim
      - person.annie
action:
  - choose:
    - conditions:
      - condition: template
        value_template: >-
          {{ state_attr(trigger.entity_id, "target_temp") != None }}
        sequence:
          - service: climate.set_temperature
            data:
              entity_id: climate.thermostat
              temperature: "{{ (states.input_number.target_temperature.state | int) if is_state(trigger.entity_id, "not_home") else (state_attr(trigger.entity_id, "target_temp") | int) }}"
    - condition: template
        value_template: >-
          {{ state_attr(trigger.entity_id, "fan_speed") != None }}
        sequence:
          - service: fan.set_speed
            data:
              entity_id:
                - fan.living_room
                - fan.guest_room
              # Not bothering to reset when they leave
              speed: "{{ state_attr(trigger.entity_id, "fan_speed") | int }}"
    - condition: template
        value_template: >-
          {{ state_attr(trigger.entity_id, "window_coverings") != None }}
        sequence:
          - service: "cover.{{ 'close' if is_state(trigger.entity_id, 'not_home') else state_attr(trigger.entity_id, "window_coverings") }}_cover
            data:
              entity_id:
                - cover.living_room_blinds
                - cover.family_room_blinds
    default: []

Summarizing the Configuration Above

The example presented is fairly basic, covering only one preference per person. If the guest has a temperature preference it will adjust the thermostat, if the guest has a preference on air circulation it will adjust the specified fans to meet their request, if they have a preference for the blinds to be open it will open the specified blinds. When they leave, it returns to original state.

Definition of Done

  • ✓ : Guests can specify a preferred temperature or fan settings
  • ✓ : Climate adjusts when a guest with preferred climate settings arrives
  • ✓ : Climate reverts back when guest with preferred climate settings departs

Stretch Goals

If you like the ideas presented in the cards above under stretch goals, or can think of other improvements over the ecobee, I challenge you to implement your solution and share it with the world!

Let There Be Light! Comfort Color Lighting…

As you read through this post, watch the videos, see what all we’ve done – it’s only natural to ask “Jeez, how much did this cost?” Well, let me answer that now so we can focus on the content: you do not want to know. Links below seem to break on the regular, find the videos and more here: https://www.icloud.com/sharedalbum/#B1A59UlCq1m3Dr

HEADS UP: If you use “Hey Siri” in your home, you may want to turn the volume down before playing the videos below, and on a personal note: James, fuck you and your phrases used in https://saladbowl.fun

Introducing: Comfort Color Lighting, Automated

As you know, or should by now, I put a lot of pride into being efficiently lazy: automating repetitive tasks, communication, and my life to an extent some might consider insane (I prefer eccentric, potato-potato). About a year ago, my partner/boyfriend/fiancé, let’s call him Alan, and I stayed a weekend at our good friend’s who recently started adding Philips Hue lighting to his house; this weekend with friends sparked something deep in Alan’s soul, turning to me “I need this in my life” – and thus began our journey to not just be more comfortable in our home, but to turn our home into our sanctuary, into an immersive experience of unto which we could not have imagined possible at the time.

I’ll be the first to admit, I never really thought much of color – sure I have a preference for dark themed user interfaces just like any sane software engineer, selecting the “Darcula” radio button is enough for me; automating my lights simply meant replacing the light switches with smart ones. When I introduced Alan to Pycharm as an alternative to Sublime, he was blown away by the Ansible/YAML plugins and extensions that he instantly dropped Sublime as his editor of choice – and spent the next day or two tweaking the color schemes, asking my opinion between two indiscernible grays looked best as the editor background.

Side note, his need for perfect color schemes in his life seemed like a waste of time to me at first, but I have truly grown to appreciate how the varying tones in color can really affect you – I still can’t bring myself to spend the time to tweak my editor colors so I simply use the themes he lovingly creates from scratch.

Understanding Alan’s affinity for color and its effects on mood and focus, we can now appreciate the super-human patience and effort that he has put in; turning our house into a home, our living room into an immersive theatre better than any AMC or Alamo, and practically removing the need for an alarm to wake up in the morning – all through the use of comfort color lighting.

Energy, Mood and Adaptive Lighting

For the uninitiated, adaptive lighting is an algorithm that brings the awe of the sun rising, the beauty of a wonderful setting sun, the tranquility of a clear night sky into your home through the color and temperature of lights in your home. As the day starts, gentle and beautiful sunrises throughout our home – slowly transitioning from a comfortable, yet dim, twilight to a beautiful sky blue and hints of orange – awakened every day to the artificially created beauty of nature, regardless of what the weather is outside these walls. As the day rolls on, and the sun starts to set, the sky blue darkens, the hints of orange now prominent, the tint of red as the sun’s rays pass over the atmospheric scattering effect so prominent during the day. Soon, below the horizon, twilight starts to set in – the sky a dark backdrop sprinkled with fuchsia from the few photon particles that pass tangentially over our planet as the sun sinks deeper and deeper. With gratitude for his lighting work, I rarely wake In the mornings grumbling anymore, instead, the comfort color lighting makes me feel inspired and energized – though that still doesn’t seem to curb my intake of caffeine, that’s a whole other thing.

Time of Day Comfort Lighting Scenes

Enter Your Favorite Movie, No VR Headset Required

We all seem to have that person in our life: the self-proclaimed audiophile who can’t stand for anything less than 7.2 Atmos surround, the purist nitpicking digital artifacts due to compressed video streams, those who scoff at televisions that don’t upscale to the point of being able to see every pore on an actor’s face. Well, we are those people, and then some. Before the world shut down in response to an invasion from microscopic organisms hell-bent on world domination, most people went to movie theaters to get a taste of immersive sound and visuals – and spend $10 on a bag of Skittles. Let me introduce you to the Philips Hue Sync Box, one of the most amazing devices to enter the home entertainment market, and one of the worst smart devices ever made. When it comes to being a smart device, it’s the equivalent of the most intelligent goldfish at Petsmart – the app is slow, has wireless connection issues and requires a dedicated Hue Hub for each Sync Box in your home. Thankfully, the REST API served on the device is responsive and intuitive – a great addition to your home automation system. If it’s one of the worst smart devices ever made, how can it be such an amazing home entertainment device? Good question. Simply wire your set-top boxes to any of the 4 HDMI inputs on the Sync Box and the output to your stereo or television – and now up to 10 Hue (or Hue Compatible) lights synchronize with the image displayed on your television, or create a visualizer for music. Not long ago, the light strips by Philips Hue could only be set to one color for the whole strip – but now you can get a Gradient Light Strip sized for your television…. words cannot compete with seeing it in action:

Video Mode with Mad Max
Fury Road Dust Storm Battle
Video Mode with Rango
Crossing the Road with Rango
Video Mode with Music and Visualizer
This is the Game Room with a Hue Play Gradient Light Strip
Music Mode with Music
Opiuo canceled their 2020 show, experience it in your living room

Setting the Mood

Check back later for mood lighting and scenes!

But Wait, There’s More!

Here’s the lighting we use around our house

  • Living Room
    • TV Backlight: Hue Gradient Light Strip
    • Under TV: Hue Light Strip
    • Couch Light: Hue Light Strip + Extension
    • Behind Speakers: Hue Play Bars
    • Ceiling Fan: Hue Ambiance Bulbs
  • Game Room
    • TV Backlight: Hue Gradient Light Strip
    • Closet: Hue Light Strip + Extension
    • Ceiling Light: Insteon On/Off Switch
  • Entry Room
    • Mantel: Hue Light Strip
    • Floor Lamp: Hue Color Bulbs
  • Kitchen
    • Hall Light: Hue Play Bar
    • Ceiling Light: Insteon On/Off Switch
  • Dining Room
    • Ceiling Fan: Hue Ambiance Bulbs
  • Hallways:
    • Ceiling Light: Hue Being Ambiance Fixture
  • Bathrooms:
    • Vanity: Hue Ambiance Bulbs
    • Ceiling: Hue Ambiance Bulbs
  • Bedroom
    • TV Accents: Hue Play Bars
    • Headboard Accents: Hue Play Bars
    • Ceiling Fan: Hue Ambiance Bulbs
  • Dance Studio
    • Nanoleaf Panels
  • Exterior
    • Wall Sconces: Hue Appear Ambiance Fixtures
    • Walkway: Hue Calla Ambiance Fixtures

Keep up to date as we add lighting, scenes, and effects to our home: https://www.icloud.com/sharedalbum/#B1A59UlCq1m3Dr