fruitcake/honeywell-client
A PHP client for the Honeywell API, offering a simple, framework-friendly way to authenticate and interact with Honeywell devices and data. Wraps Honeywell endpoints in clean, testable classes so Laravel apps can query and manage connected systems without handling raw HTTP requests.
The package is a thin PHP wrapper around Honeywell's Total Connect Comfort (TCC) web portal — the same account you use at mytotalconnectcomfort.com (or the .eu equivalent) to control T5/T6-class thermostats. It is not the OAuth2 "Honeywell Home / Resideo" developer API, so don't go create a Resideo developer app: you need the username/password of the end-user's TCC portal account.
Minimal path:
composer require fruitcake/honeywell-client
Before writing any code, read in this order:
README.md — the constructor signature and credential names.src/ — there is very little of it; the client class and any request/response models are the whole API surface. Read them end-to-end, it takes five minutes.tests/ (if present) — the most reliable documentation of real method names and return shapes.Because there's no documented Laravel service provider, wire it yourself. Keep credentials out of code:
// config/services.php
'honeywell' => [
'username' => env('HONEYWELL_USERNAME'),
'password' => env('HONEYWELL_PASSWORD'),
],
// app/Providers/AppServiceProvider.php
$this->app->singleton(\Fruitcake\Honeywell\Client::class, fn () => new \Fruitcake\Honeywell\Client(
config('services.honeywell.username'),
config('services.honeywell.password'),
));
First use case: list the devices on the account and dump the current temperature and setpoint for one thermostat. Run it from php artisan tinker. If you get a list of devices with per-device IDs and current readings, the hard part is done — everything else is setpoint and mode writes.
Wrap it, don't sprinkle it. The package is a one-star, single-maintainer project on an undocumented consumer API. Treat it as an implementation detail behind your own interface so you can swap or fork it later:
interface ThermostatGateway
{
public function state(string $deviceId): ThermostatState; // a small DTO
public function setSetpoint(string $deviceId, float $celsius): void;
public function setMode(string $deviceId, Mode $mode): void;
}
Bind your HoneywellGateway to the interface. Every call site in your app then talks to ThermostatState, not to the vendor's array shapes.
Read path: poll, cache, persist. The portal does not push. The standard pattern is a scheduled command that fetches state every 5–10 minutes, writes it to a thermostat_readings table, and dispatches a broadcast event if it changed:
Schedule::command('thermostats:poll')->everyTenMinutes()->withoutOverlapping();
Cache the raw device list for a couple of minutes on top of that so a dashboard page refresh doesn't trigger a portal login. Never call the client from a request cycle that a user can spam.
Write path: queue, validate, log. Setpoint writes go through a queued job so a slow/flaky portal doesn't block a web request, and so you get retries. Clamp values before sending (TCC-style thermostats generally accept 0.5 °C / 1 °F steps and a bounded range), and log every write with old value, new value and user — you will want that audit trail when someone claims the heating went to 30 °C at 3 a.m.
Debounce UI controls. A slider or +/- stepper in Livewire/Inertia will fire on every tick. Collapse to the final value with a debounce or a ShouldBeUnique job keyed on the device ID, otherwise you'll hammer the portal and get throttled.
Testing. Fake the gateway at your interface boundary for feature tests. For contract tests against the real client, mock the HTTP layer — if the package uses Guzzle, that's a MockHandler with recorded portal HTML/JSON responses; if it uses the Laravel HTTP client, Http::fake(). Record one real login response and one real device response early; those fixtures are your only protection against the portal changing its markup.
Integration sweet spots. A Livewire dashboard with "current temp / target temp / mode" per room, an alert when a room drops below a threshold for N minutes, a holiday-mode artisan command, and a nightly setback schedule that sets off or a low setpoint at 22:00 and restores at 06:00.
composer.json, and budget for the reality that the next portal change breaks it with no upstream fix. Keep the fork path short by isolating it behind your own interface from day one.mytotalconnectcomfort.eu. If you get auth failures with correct credentials, check which host the client defaults to and whether you can override it.Heat the heat setpoint applies; in Cool the cool setpoint; in Auto both. Writing "the temperature" without setting the mode means the change may appear to do nothing. Always read-modify-write the full control payload rather than sending a single field.withoutOverlapping() and a RateLimited job middleware.How can I help you explore Laravel packages today?