HueDriver
Source:
HueDriver.h
HueDriver¶
src/light/drivers/HueDriver.h:34Inherits:
DriverBase
Output driver: sends the buffer to Philips Hue bulbs as pixels — a driver, not a listed device.
The bulbs are pixels of an effect: make a small grid (e.g. 4×1×1), run any effect, and this driver reads its window of the shared buffer and pushes each light's color to the bridge. Same shape as NetworkSendDriver (read a window, send it out), but over the Hue v1 HTTP API not UDP.
It's HTTP, not a wire protocol, so the rate is bounded by connection churn — each PUT opens a fresh TCP connection (the bridge speaks Connection: close), giving smooth ambient color, not real-time (see kPutIntervalMs). The shared output Correction applies as on the LED/network drivers, so brightness and color-order reach the Hue lights too. Only color-capable, reachable lights are driven (parseLights); the room / light dropdowns narrow that (rebuildDriven).
Wire contract (Hue v1 API, plain HTTP, no TLS — bench-confirmed on a BSB002 bridge, API 1.77):
-
Pair —
POST http://<bridgeIp>/api``{"devicetype":"projectMM#device"}; before the link button, the bridge returnslink button not pressed; after,[{"success":{"username":"<key>"}}]. -
List lights —
GET http://<bridgeIp>/api/<appKey>/lights→{"1":{…},"2":{…}}(window index → light id). -
List rooms —
GET http://<bridgeIp>/api/<appKey>/groups→{"1":{"name":…,"lights":["1","2"],"type":"Room"},…}. -
Set a light —
PUT http://<bridgeIp>/api/<appKey>/lights/<id>/state``{"on":true,"bri":0-254,"hue":0-65535,"sat":0-254,"transitiontime":N}(or{"on":false}for a black pixel).
Prior art: the Hue v1 CLIP API (public docs); the effect-as-output mapping is projectMM's own.

Public Attributes¶
uint8_t bridgeIp = {}
: The bridge's LAN IP, entered in the UI (4 octets).
char appKey = {}
: The Hue username/app key — filled by the Pair button, then persisted.
Public Methods¶
inline HueDriver()
: HueDriver reads apply()'s output back as R,G,B to convert to HSV for the bridge, so it references the "RGB" preset (a GRB reorder would corrupt the hue).
virtual inline bool hasCorrectionControls() const override
: Hue converts to HSV, RGB-fixed, so there is no correction UI to show.
virtual inline void defineDriverControls() override
: Register the controls: bridge IP, the persisted app key, the Pair link-button, the room + light filter dropdowns (both default to index 0 = "All", rebuilt in place from the parsed bridge data on every control change), the shared window, then refresh the status line.
virtual inline void setSourceBuffer(Buffer * buf) override
: Take the shared source buffer this driver reads its window from.
virtual inline void onControlChanged(const char * controlName) override
: A control click.
virtual inline void tick() override
: Runs every render tick, but does at most ONE bounded PUT and only when the rate-limit interval has elapsed (a millis() gate, NOT work-every-tick) — otherwise a synchronous HTTP round-trip would stall the single-thread render loop (the "never block the loop" rule, lessons.md).
virtual inline void tick1s() override
: The 1 Hz tick handles the non-render-critical, slower bridge work: the pairing poll, the one-shot light + group fetch, and the periodic DevicesModule announce.
virtual inline void release() override
: Stop any in-flight pairing and release the dropdown-name heap (a re-add re-fetches and re-allocs), then chain to DriverBase::release to clear status.
inline bool wouldPushForTest(uint8_t idx, uint8_t r, uint8_t g, uint8_t b, char * outBody, size_t cap)
: Test seam: drive the changed-light diff + PUT formatting without a live bridge — feed a light's RGB and get back whether it would PUT + the body it would send.
inline void parseLightsForTest(const char * json)
: Test seam: parse a real /lights JSON body through fetchLights' color-light extractor.
inline uint8_t lightCountForTest() const
: Count of kept color+reachable lights.
inline uint16_t hueIdForTest(uint8_t i) const
: The bridge light id at window index i, or 0 when out of range.
inline int8_t colorCountForTest() const
: The same count as the read-only control / bridge field; 0 before any fetch.
inline void parseGroupsForTest(const char * json)
: Test seam: parse a real /groups JSON body through fetchGroups' Room extractor.
inline uint8_t roomCountForTest() const
: Count of kept Rooms (bridge groups with type=="Room").
inline void setRoomForTest(uint8_t r)
: Test seams for the room→light filtering: mirror what a UI Select change does — write the index, then re-derive the driven subset.
inline void setLightForTest(uint8_t l)
: Select light l within the chosen room and re-derive the driven subset.
inline void refreshStatusForTest()
: Recompute the status line without waiting for a tick.
inline uint8_t drivenCountForTest() const
: How many lights survive the room+light filter — the set pushOneChangedLight walks.
inline uint16_t drivenIdForTest(uint8_t i) const
: The bridge light id at filtered index i, or 0 when out of range.
Public Static Methods¶
static inline void rgbToHsvForTest(uint8_t r, uint8_t g, uint8_t b, uint16_t & h, uint8_t & s, uint8_t & v)
: Test seam for the RGB→HSV mapping (no bridge needed).
static inline bool bodyLooksCompleteForTest(const char * body)
: Test seam: the truncation signal fetchLights grows against (a complete /lights body ends '}').