UI and localization
Switch the bundled UI on or off
The pages are registered by default, and closed to everyone outside the local environment until you open them (next section). Switch them off to use the package as a NUKI Web API client only:
NUKI_UI_ENABLED=false
When enabled, routes/web.php is loaded and Livewire components are registered under nuki.* aliases.
URL prefix: nuki.ui.prefix (default nuki). Layout: nuki.ui.layout (default nuki::layouts.app). See Configuration → ui.*.
Who may open the UI
The pages lock and unlock doors and hold API tokens, so they are closed unless you open them, the same way Horizon, Telescope and Pulse are. Which rule applies depends on auth_users.enabled:
-
auth_users.enabled = false(default). Every UI route runs the AuthorizeUi middleware, which asks theviewNukigate and answers403when it says no. The package defines that gate only when your application has not, and its default allows thelocalenvironment and nothing else. To reach the UI anywhere else, define the gate yourself, for example inAppServiceProvider::boot():use App\Models\User; use Illuminate\Support\Facades\Gate; public function boot(): void { Gate::define('viewNuki', fn (?User $user) => $user?->is_admin === true); }The gate receives the user of your application’s default guard, or
nullfor a guest. Keep the parameter nullable (?User $user): Laravel does not call a gate for a guest otherwise, and the answer is then always no. Put your ownauthmiddleware inui.middlewarewhen a guest should be sent to your login page instead of getting a403. -
auth_users.enabled = true. The package’s owndarvis-nukiguard protects the pages and theviewNukigate is not asked. See Users and permissions.
The middleware is also registered as Livewire persistent middleware, so the requests a page sends after it was loaded are checked the same way. ui.enabled and ui.middleware work as before; AuthorizeUi runs after the middleware you list there.
Bundled Livewire components
Auto-registered with nuki.* aliases by NukiServiceProvider.
Main UI
| Alias | Class | Route | Purpose |
|---|---|---|---|
nuki.dashboard | Dashboard | /dashboard | KPI cards (total locks, locked, critical battery, open doors), recent activity feed, per-lock battery bars. For a sub user: only their own locks, and activity only of locks with view_logs. |
nuki.smartlocks-index | SmartlocksIndex | / | List of smartlocks; filtered for sub users. Quick lock/unlock actions. |
nuki.smartlock-show | SmartlockShow | /smartlocks/{smartlockId} | Single-lock detail: state, recent logs, authorizations, rename + sync buttons. |
nuki.activity-timeline | ActivityTimeline | /activity | Visual timeline grouped per day; filter by lock or period. For a sub user: only locks with view_logs; a filter on another lock is a 403. |
nuki.webhooks-index | WebhooksIndex | /webhooks | List + manage NUKI webhook subscriptions. |
nuki.oauth-connect | OAuthConnect | /oauth/connect | Shows the stored OAuth token of the current account, generates an authorization URL and disconnects. It does not receive the redirect from NUKI; that route is yours. Only meaningful when NUKI_AUTH=oauth. |
nuki.accounts-index | AccountsIndex | /accounts | CRUD for nuki_accounts (token mode, multi-account). |
nuki.account-switcher | AccountSwitcher | — | Dropdown used in the top navigation; dispatches the nuki-account-changed Livewire event. |
Auth UI (only when NUKI_AUTH_USERS_ENABLED=true)
| Alias | Class | Route |
|---|---|---|
nuki.auth.login | LoginPage | /login |
nuki.auth.otp | LoginOtpPage | /login/otp |
nuki.auth.register | RegisterPage | /register |
nuki.auth.forgot-password | ForgotPasswordPage | /password/forgot |
nuki.auth.reset-password | ResetPasswordPage | /password/reset/{token} |
nuki.profile | ProfilePage | /profile |
nuki.sub-users-index | SubUsersIndex | /sub-users |
nuki.sub-user-show | SubUserShow | /sub-users/{id} |
The main components are registered while ui.enabled is true, the auth components while auth_users.enabled is true. nuki.auth.verify-email (VerifyEmailNoticePage, /email/verify) is registered as well.
You can render one in a Blade file of your own:
<livewire:nuki.smartlocks-index />
The AuthorizeUi middleware is on the package’s routes, not on the component. A component you place on your own page is as open as that page, so put the page behind your own middleware.
Account-aware components
Use the UsesNukiAccount trait:
use Livewire\Attributes\On;
use Darvis\Nuki\Concerns\UsesNukiAccount;
use Darvis\Nuki\Facades\Nuki;
use Livewire\Component;
class MyDashboard extends Component
{
use UsesNukiAccount;
#[On('nuki-account-changed')]
public function handleAccountChanged(string $accountKey): void
{
// Never assign the raw argument: the browser can send this event too.
$this->accountKey = $this->authorizedAccountKey($accountKey);
}
public function render()
{
return view('livewire.my-dashboard', [
'locks' => Nuki::as($this->accountKey)->smartlocks()->all(),
]);
}
}
This class belongs in app/Livewire/MyDashboard.php, with a view of your own in resources/views/livewire/my-dashboard.blade.php.
The trait:
- Sets
$accountKeywhen the component mounts, fromsession('nuki.current_account'). For a package user it falls back to the user’s first accessible account, or todefaultwhen there is none. The property is#[Locked]: the browser can read it and cannot change it. - Has
authorizedAccountKey(string $accountKey), which returns the key when the current user may use it (default, or one of the user’s accessible accounts; any key without package users) and aborts with403otherwise. - Exposes
$availableAccountsand$currentAccountLabelcomputed properties for use in Blade. AccountSwitcherwrites the new value to the session (for a package user only when the account isdefaultor one of the user’s accessible accounts, otherwise403) and broadcastsnuki-account-changed; any component listening with the attribute above re-renders.
Flux UI requirement
The bundled views use Flux components exclusively (<flux:card>, <flux:button>, <flux:badge>, <flux:callout> and so on). Flux 2 is a Composer requirement of the package, and the free edition has every component the views use. Your layout loads the Flux and Tailwind assets. If you publish the views (--tag=nuki-views) and customise them, keep the Flux components in place — don’t drop in hand-rolled Tailwind buttons or form controls.
Custom layout
Override nuki.ui.layout to wrap the pages in your own chrome:
// config/nuki.php
'ui' => [
'layout' => 'layouts.app', // your own layout
],
The layout is a Blade component layout: it echoes the $slot variable where the page goes, and calls @fluxAppearance in the <head> and @fluxScripts before </body>. The package’s own layouts/app.blade.php is the example to copy; it loads your application’s resources/css/app.css and resources/js/app.js through Vite, so Tailwind in your build has to cover the package views.
Localization
Four locales ship: en, nl, de, es. Resolution happens per request inside SetLocale, in this order:
- The
localeof the signed inNukiUser(whenNUKI_AUTH_USERS_ENABLED=true). The user sets it on the/nuki/profilepage. session('nuki.locale'). The package never writes this value; set it from your own code, for example from a language menu of your application.- The host application’s
app()->getLocale(), if it appears innuki.ui.locales. nuki.ui.default_locale(defaulten).
A value that is not a key of nuki.ui.locales is skipped. Carbon::setLocale() is set alongside Laravel’s locale, so dates such as diffForHumans() follow the language.
Set the package default:
NUKI_DEFAULT_LOCALE=nl
Add or remove locales by editing nuki.ui.locales in your published config.
Overriding individual strings
Publish the language files into your app:
php artisan vendor:publish --tag=nuki-lang
This copies into lang/vendor/nuki/{en,nl,de,es}/*.php. Laravel resolves __('nuki::...') lookups from this path before falling back to the package defaults, so you can change a single phrase without forking the whole file.
Layouts shipped
Two Blade layouts are bundled under nuki::layouts.*:
nuki::layouts.app— the shell of the pages: navigation, account switcher and a menu with the profile and logout links. There is no language menu; see Localization.nuki::layouts.auth— a split-screen layout for the login, OTP, register, password reset and email verification pages. Form column on the left, brand panel with gradient and feature bullets on the right (lg+). Collapses to single-column on mobile.
Branding the auth pages
The auth layout reads four optional ui.* keys to drop in your own brand without forking the views. All have sensible defaults so a fresh install renders cleanly without any of them set.
# Logo above the form. Both light/dark optional; if unset, a neutral lock
# icon plus NUKI_UI_BRAND is rendered.
NUKI_UI_LOGO_LIGHT=/img/brand-light.svg
NUKI_UI_LOGO_DARK=/img/brand-dark.svg
# One-line tagline on the right-hand brand panel. Falls back to the
# localised nuki::nuki.auth.panel.subheading string.
NUKI_UI_TAGLINE="Smartlock management for Acme B.V."
# Toggle the right-hand brand panel. False = form fills the full width.
NUKI_UI_AUTH_PANEL=true
Footer links (privacy, terms, support) live next to the copyright. Set them in your published config:
// config/nuki.php
'ui' => [
'footer' => [
'links' => [
['label' => 'Privacy', 'url' => '/privacy'],
['label' => 'Terms', 'url' => '/terms'],
],
],
],
The right-hand panel’s heading, subheading and three feature bullets come from nuki::nuki.auth.panel.* in each of the four shipped locales. Override them like any other string by publishing the language files (php artisan vendor:publish --tag=nuki-lang) and editing lang/vendor/nuki/{en,nl,de,es}/nuki.php.