cannonb4ll/laravel-philips-hue
Laravel wrapper for the Philips Hue API that lets you discover bridges, control lights, groups and scenes, and manage colors, brightness and schedules directly from your Laravel app. Simple configuration and an expressive interface make smart-home automation easy to add.
composer require cannonb4ll/laravel-philips-hue
The package was last released in November 2020, so treat the version constraint in its composer.json as a starting point, not a guarantee. On Laravel 9+ you may need --with-all-dependencies and, if resolution fails, a fork with widened illuminate/* constraints. Package discovery registers the service provider and PhilipsHue facade automatically on supported versions; on older skeletons add them manually.
php artisan vendor:publish --provider="Cannonb4ll\PhilipsHue\PhilipsHueServiceProvider"
That drops config/philips-hue.php — this is the file to read before anything else. It is where the two operating modes are configured:
client_id, client_secret, app_id, and the remote username/token obtained through the Hue developer OAuth flow.Read the published config top to bottom, then the README's remote section. Those two files explain 90% of the behaviour; everything else is a thin client over the Hue REST API.
Press the link button on the bridge, then run the setup command the package ships (list them with php artisan list | grep -i hue) within the 30-second window. If you don't have a command available, POST https://<bridge>/api with {"devicetype":"laravel#dev"} against the same window works identically and is worth knowing — the bridge is the source of truth, the package is just a wrapper.
Put the result in .env:
HUE_BRIDGE_IP=192.168.1.42
HUE_BRIDGE_USERNAME=abc123...
The natural first task is "list lights, toggle one":
use Cannonb4ll\PhilipsHue\Facades\PhilipsHue;
$lights = PhilipsHue::lights(); // collection of light resources
PhilipsHue::light(1)->turnOn();
PhilipsHue::light(1)->setBrightness(180);
PhilipsHue::light(1)->setColor('#ff8800');
Exact method names should be checked against the facade/README — the package's API surface is a direct mapping of Hue's /lights/{id}/state fields, so the mental model transfers even if a method differs.
The package is archived. Do not let PhilipsHue:: calls sprawl across controllers. Define a small contract and one adapter:
interface HomeLighting
{
public function signal(string $color, int $brightness = 200): void;
public function allOff(): void;
}
class HueLighting implements HomeLighting
{
public function signal(string $color, int $brightness = 200): void
{
PhilipsHue::light(1)->turnOn();
PhilipsHue::light(1)->setColor($color);
PhilipsHue::light(1)->setBrightness($brightness);
}
}
Bind it in a service provider. Swapping to a fork, another package, or raw Guzzle later becomes a one-line change, and tests get a trivial fake.
A common production shape: remote mode in staging/CI, local mode in dev, and a config switch so the same code path runs in both. Keep the mode selection in .env, never hardcoded.
The Hue bridge is a slow, rate-limited Zigbee gateway. Never issue light commands inline in a web request that a user is waiting on. Use jobs:
dispatch(new FlashLightJob('red', times: 3))->onQueue('hue');
Consider a dedicated queue with a low concurrency (1–2 workers). The bridge tolerates roughly ~10 commands/second per light and far less in aggregate; parallel workers will produce 429-style errors or silent drops. If you need bursts, batch with Bus::batch() and stagger with delay().
Deployed event listener.// app/Console/Kernel.php
$schedule->job(new SyncPresenceLightsJob)->everyFifteenMinutes()->between('19:00', '23:00');
There is no event stream from the bridge. If you need state, poll on a schedule and cache the result briefly; do not poll inside request handlers.
Fake the facade — this is why the seam above matters:
PhilipsHue::shouldReceive('light->turnOn')->once();
If you bind HomeLighting to a fake instead, most tests never touch the package at all, which is the right outcome for an unmaintained dependency.
Archived status is the real gotcha. Last release 2020, no security or compatibility patches. Pin the version, audit it, and plan a fork. The Hue REST API is stable and simple enough that a hand-rolled Guzzle client is a realistic exit strategy — often under 200 lines for light control.
Config caching freezes bridge IP and credentials. php artisan config:cache in deploy pipelines will bake in whatever .env was present at build time. Ensure the cache is built on the target environment, or read runtime values outside the config file.
The link button window is genuinely 30 seconds. Most "cannot authenticate" reports are just a missed window. Retry rather than debugging code.
Remote mode token lifecycle. Hue Remote API access tokens expire (~15 minutes in practice, depending on your app's grant). You need refresh logic and persistent token storage; the package's helpers are from 2020 and may not handle every refresh path. Log the token exchange, and test refresh explicitly — expiry bugs surface hours later in production, not in dev.
OAuth redirect URI must match exactly. Scheme, host, trailing slash, port — the Hue developer portal does a strict string comparison. http vs https mismatch is the classic failure.
Brightness is 1–254, not 0–100. Values below 1 error out or are rejected. Clamp in your adapter.
Color gamut differs per bulb. RGB → HSB/XY conversion is lossy and gamut A/B/C bulbs render the same hex differently. If color fidelity matters (status indicators), pick from a fixed palette calibrated per physical bulb rather than sending arbitrary hex.
Group vs light commands. Acting on a group is one bridge command and usually what you want for "all lights red"; looping over lights multiplies rate-limit exposure for identical visual effect.
Timeouts should be short. Set a 2–5 second connect timeout. A powered-off bridge will otherwise hang a worker for the default HTTP timeout. Never retry aggressively — you'll amplify a bridge problem into a queue backlog.
Debugging workflow: tail -f storage/logs/laravel.log while running a single command in php artisan tinker. The Hue bridge returns a JSON array of per-command {"error": {...}} objects with HTTP 200 — a "successful" request can still contain errors. Always inspect the body, not just the status code.
Extension points: the service provider and facade root are the places to bind your own decorated client. If a method is missing, the underlying Bridge/transport class is thin enough to extend — but do it in your app, not by editing vendor files.
Don't put it on a critical path. Lights are a nice-to-have signal. Wrap all calls in try/catch, fail open, and let the application continue if the bridge is unreachable.
How can I help you explore Laravel packages today?