This is a replacement for a Milight/LimitlessLED remote/gateway hosted on an ESP8266. Leverages Henryk Plötz's awesome reverse-engineering work.
Milight bulbs are cheap smart bulbs that are controllable with an undocumented 2.4 GHz protocol. In order to control them, you either need a remote ($13), which allows you to control them directly, or a WiFi gateway ($30), which allows you to control them with a mobile app or a UDP protocol.
This project is a replacement for the wifi gateway.
This guide on my blog details setting one of these up.
This project technically supports both NRF24L01 and LT8900 radios, but I recommend using an NRF24. Both modules are SPI devices and should be connected to the standard SPI pins on the ESP8266. See the below diagram for pin connections.
This guide details how to connect an NRF24 to an ESP8266. By default GPIO 4 for CE and GPIO 15 for CSN are used, but these can be configured later in the Web UI under Settings -> Hardware.
Image source: MySensors.org
NodeMCU (Esp8266) | Esp32 | Radio | Color |
---|---|---|---|
GND | GND | GND | Black |
3V3 | 3V3 | VCC | Red |
D2 (GPIO4) | D4 (GPIO4) | CE | Orange |
D8 (GPIO15) | D5 (GPIO5) | CSN/CS | Yellow |
D5 (GPIO14) | D18 (GPIO18) | SCK | Green |
D7 (GPIO13) | D23 (GPIO23) | MOSI | Blue |
D6 (GPIO12) | D19 (GPIO19) | MISO | Violet |
Connect SPI pins (CE, SCK, MOSI, MISO) to appropriate SPI pins on the ESP8266. With default settings, connect RST to GPIO 0, PKT to GPIO 16, CE to GPIO 4, and CSN to GPIO 15. Make sure to properly configure these if using non-default pinouts.
If you have PlatformIO set up, you can compile from source and upload with:
platformio run -e d1_mini --target upload
(make sure to substitute d1_mini
with the board that you're using.)
Alternatively, you can download a pre-compiled firmware image from the releases. This can be used with esptool.py
:
esptool.py write_flash 0x0 <firmware_file.bin>
Make sure you read instructions
This project uses WiFiManager to avoid the need to hardcode AP credentials in the firmware.
When the ESP powers on, you should be able to see a network named "ESPXXXXX", with XXXXX being an identifier for your ESP. Connect to this AP and a window should pop up prompting you to enter WiFi credentials. If your board has a built-in LED (or you wire up an LED), it will flash to indicate the status.
The network password is "milightHub".
Both mDNS and SSDP are supported.
sudo apt-get install avahi-daemon
on Ubuntu), and should then be able to navigate to http://milight-hub.local.The default hostname is milight-hub
. If your network supports local DNS, you can navigate to http://milight-hub
(you may also want to try http://milight-hub.local
if your client supports mDNS).
The UI should look like this:
Add devices using the "+" button. Use the "Sniffer" tab to intercept packets from existing remotes or milight devices if you wish to spoof their device IDs.
More details on this are in the wiki.
Set up HomeAssistant discovery by configuring an MQTT connection with the same broker your HomeAssistant instance is using. If all goes well, lights you create should automatically be discovered by HomeAssistant.
The following remotes can be emulated:
Support has been added for the following bulb types:
Model # | Name | Compatible Bulbs |
---|---|---|
FUT096 | RGB/W |
|
FUT005 FUT006 FUT007 |
CCT |
|
FUT098 | RGB | Most RGB LED Strip Controlers |
FUT020 | RGB | Some other RGB LED strip controllers |
FUT092 | RGB/CCT |
|
FUT091 | CCT v2 | Most newer dual white bulbs and controllers |
FUT089 | 8-zone RGB/CCT | Most newer rgb + dual white bulbs and controllers |
Other remotes or bulbs, but have not been tested.
If it does not work as expected see Troubleshooting.
You can configure aliases or labels for a given (Device Type, Device ID, Group ID) tuple. For example, you might want to call the RGB+CCT remote with the ID 0x1111
and the Group ID 1
to be called living_room
. Aliases are useful in a couple of different ways:
GET
, PUT
, and DELETE
) allow you to interact with aliases via the /gateways/:device_alias
route.Generated API documentation is available here:
API documentation is generated from the OpenAPI spec using redoc.
To configure your ESP to integrate with MQTT, fill out the following settings:
mqtt_server
- IP or hostname should work. Specify a port with standard syntax (e.g., "mymqttbroker.com:1884").mqtt_username
and mqtt_password
There are a few different types of topics used by the light hub. In most cases, the topics are similar to API routes -- they contain the device identifiers in the topic.
A pattern of the form:
milight/commands/:device_id/:device_type/:group_id
Will cause the ESP to subscribe to the topic milight/commands/+/+/+
and interpret the second, third, and fourth tokens as :device_id
, :device_type
, and :group_id
, respectively.
Likewise, a pattern of the form:
milight/states/:device_id/:device_type/:group_id
will cause the ESP to publish state updates to the topic (for example)
milight/states/0x1234/rgb_cct/1
Here's a brief description of supported topics:
mqtt_topic_pattern
- controls the topic that the ESP subscribes to for commands. See the above example.mqtt_update_topic_pattern
- controls the topic that the ESP publishes delta updates to. These are a fairly direct translation of the raw RF packets submitted by milight control devices. They might look something like {"state":"ON"}
.mqtt_state_topic_pattern
- controls the topic that the ESP publishes full state updates to. These are JSON objects that contain the entirety of the current state for a given device. The hub tracks state internally and applies updates as they come in.mqtt_client_status_topic
- nothing fancy for this one! It controls the topic that the ESP publishes client status updates to (in MQTT lingo, this is where birth and LWT messages are sent). If you're integrating with a platform that expects specific fields in the state update topic, you can customize which fields are included in the state updates published by the hub. The default is designed to be compatible with HomeAssistant!
There's a fair amount of duplication in the available fields (for example, computed_color
, oh_color
, hex_color
all control the color
field in state updates). Sorry this is confusing!
If for whatever reason you wish to integrate with this hub using the UDP protocol used by the official Milight gateways, you can do that!
The UDP protocol is documented in this handy github archive. Version 6 has support for the newer RGB+CCT bulbs and also includes response packets, which can theoretically improve reliability. Version 5 has much smaller packets and is probably lower latency.
Transitions between two given states are supported. Depending on how transition commands are being issued, the duration and smoothness of the transition are both configurable. There are a few ways to use transitions:
/transitions
routesThese routes are fully documented in the REST API documentation.
transition
field when issuing commandsWhen you issue a command to a bulb either via REST or MQTT, you can include a transition
field. The value of this field specifies the duration of the transition, in seconds (non-integer values are supported).
For example, the command:
{"brightness":255,"transition":60}
will transition from whatever the current brightness is to brightness=255
over 60 seconds.
{"brightness":255,"kelvin":0,"transition":10.5}
will transition from current values for brightness and kelvin to the specified values -- 255 and 0 respectively -- over 10.5 seconds.
status
or state
. For example,
{"status":"ON","transition":10}
will turn the bulb on, immediately set the brightness to 0, and then transition to brightness=255 over 10 seconds. If you specify a brightness value, the transition will stop there instead of 255.
Some ESP boards have a built-in LED, on pin #2. This LED will flash to indicate the current status of the hub:
In the setup UI, you can turn on "enable_solid_led" to change the LED behavior to:
Note that you must restart the hub to affect the change in "enable_solid_led".
You can configure the LED pin from the web console. Note that pin means the GPIO number, not the D number ... for example, D1 is actually GPIO5 and therefore its pin 5. If you specify the pin as a negative number, it will invert the LED signal (the built-in LED on pin 2 (D4) is inverted, so the default is -2).
If you want to wire up your own LED you can connect it to D1/GPIO5. Put a wire from D1 to one side of a 220 ohm resistor. On the other side, connect it to the positive side (the longer wire) of a 3.3V LED. Then connect the negative side of the LED (the shorter wire) to ground. If you use a different voltage LED, or a high current LED, you will need to add a driver circuit.
Another option is to use an external LED parallel to the (inverted) internal one, this way it will mirror the internal LED without configuring a new LED pin in the UI. To do this connect the (short) GND pin of your LED to D4. The longer one to a 220 ohm resistor and finally the other side of the resistor to a 3V3 pin.
This project is developed and built using PlatformIO.
The Web UI is documented here.
On-board unit tests are available using PlatformIO. Run unit tests with this command:
pio test -e d1_mini
substituting d1_mini
for the environment of your choice.
A remote integration test suite built using rspec is available under ./test/remote
.
h4nc (h4nc.zigbee(a)gmail.com) created a PCB and 3D-printable case for espMH. He's offering ready-made versions. Please get in touch with him at the aforementioned email address for further information.
Find more information from the espmh_pcb repository.
If the project brings you happiness or utility, it's more than enough for me to hear those words.
If you're feeling especially generous, and are open to a charitable donation, that'd make me very happy. Here are some whose mission I support (in no particular order):