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!

The Magic Mirror

Disregard that this post is around 4 months after the build…

We had just had our bathrooms remodeled and were looking at a medicine cabinet for the upstairs bathroom. Alan and I had both been wanting to build a magic mirror but never had the motivation or a fixture that would work.

We spent weeks trying to find a medicine cabinet we liked and would go with the vanity/countertop and then we saw this one.

The color and style matched the rest of the bathroom and the second shelf was the perfect height for allowing the necessary cables and hardware. Only losing one 5 inch section of the middle shelf seemed a small price to pay to design and build something we’ve been talking about for years.

The Parts

The Plan

When the medicine cabinet arrived, we evaluated our options to determine which side the monitor would go on, would we reuse the wood backing of the mirror section, how will we deliver power, etc.

We decided the right-hand mirror was a good place to mount the monitor. It was closer to the power outlets, wasn’t too in the way and would be visible to anyone using the sinks. As carefully as we could, we removed the mirror and its wood backing from the medicine cabinet. The answer to “would we reuse the wood backing of the mirror section” was answered for us, as the mirror was glued too well to the mirror and attempting to remove the mirror shattered it.

Alan grew up working in his Dad’s framing shop and is quite skilled at it, in both the technical aspect (mat cutter, frame nailer) but also the subjective aspect (mat color schemes, layers, etc). This is why we have a 5-foot mat cutter on hand and some black foam board, which was sturdy enough to use as a backing and dark enough to allow as much light to be reflected off the mirror as possible.

The Build

Once the acrylic arrived, Alan cut the black foam backing to the size of the wood backing originally attached to the mirror and an exactly sized window where the monitor would be able to sit flush against the mirror. Using ATG tape along the edges to hold both the acrylic and the foam board, it seemed like we were good to go. The monitor was such a tight fit in the window, that we didn’t even bother taping it in for extra support.

We needed to install some outlets inside the medicine cabinet. While we were waiting for the parts and motivation, we realized there were a couple large scratches on the acrylic sheet! That’s what we get for trying to save $30 by getting acrylic instead of glass. So, we took the acrylic, the monitor and the backing down and placed an order for Smart Mirror Glass and waited.

Build #2

Once the glass arrived, we realized that the reflection off of the acrylic was a little distorted compared to the glass – I guess the acrylic just had some surface imperfections. Note: there is a slight blue tone to it compared to the other mirrors, but it is hardly noticeable.

Before mounting the new glass and Pi to the medicine cabinet, I thought it would be a good idea to cut and insert a 2-gang outlet box in the back of the medicine cabinet. We had a couple 2 AC/2 USB outlets laying around, which would server perfectly for charging razors, toothbrushes and running the Magic Mirror. Unfortunately, there was no way to get the Romex (standard in-wall 2/2 or 3/2 electrical wire) to the available outlet without going up into the attic where there’s barely room to move around and itchy fiberglass – not to mention Scooby-Doo has taught me there’s probably Old Man Jenkins up there disguised as a ghoul. So, I ran it as high as I could through the vanity to the wall with the outlet and fished it up and out.

Repeating our previous steps, we attached the Smart Mirror glass and foam board to the door frame using ATG tape and forced the monitor into its little window. Since the monitor had been inserted and removed, it didn’t quite have the same snug fit – for added support we used a rubber cement that wouldn’t eat through the foam board to secure the monitor in place. Our monitor had mounts for a Raspberry Pi as well as a USB power source for it – which means if you turn off the monitor, it will turn off the Pi and Visa-Versa.

Software

We imaged the Raspberry Pi with Raspbian Stretch (primarily due to the fact that I had the image already on my machine). Once we set the OS up appropriately on the Pi, connected to WiFi and set up SSH remote access, we mounted it on the monitor in the medicine cabinet and closed the door.

We built a panel specifically for the MagicMirror in Home-Assistant, which removes the tabs/sidebar and other extraneous information with a dark theme set. To access that panel, we needed to install Xorg which gives rise to a graphical user interface, since up until now the system was headless. We used the chromium-browser package because it is simple to use and allows you to open a URL as an app (removing address bar, border, etc.).

We made a special user to run the interface, keeping user roles and purpose separate, aptly named mirror. In the home folder for mirror we created .xsession (this file defines what happens when the X server starts).

#!/bin/sh

#Turn off Power saver and Screen Blanking
xset s off -dpms

#Execute window manager for full screen
exec matchbox-window-manager  -use_titlebar no &

#Execute Browser with options
chromium-browser --disk-cache-dir=/dev/null --disk-cache-size=1 -app=http://$HA_URL:$HA_PORT/lovelace/mirror?kiosk

To make sure this all happens automatically, create a systemd script, we chose to place our’s /etc/systemd/system/information-display.service:

[Unit]
Description=Xserver and Chromium
After=network-online.target nodm.service
Requires=network-online.target nodm.service
Before=multi-user.target
DefaultDependencies=no

[Service]
User=mirror
# Yes, I want to delete the profile so that a new one gets created every time the service starts.
ExecStart=/usr/bin/startx -- -nocursor
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Don’t forget to enable your service with systemctl enable information-display.service and start it systemctl start information-display.service

Coming Soon

Adding voice control to the mirror, i.e. “Where is Alan” or “Activate Night Mode”