> */ public static function all(): array { return [ 'ring' => [ 'key' => 'ring', 'name' => 'Ring', 'vendor' => 'Ring', 'icon' => 'doorbell', // Ring has no local API — the established bridge is ring-mqtt (cloud, token auth). // The BRIDGE does the Ring login (email/password/2FA) in its own web UI; HomeOS never // handles Ring credentials. So the addon's job is to guide setup + reflect the // bridge's real MQTT status — not to collect a password. 'cloud' => true, 'summary_key' => 'addons.ring.summary', 'body_key' => 'addons.ring.body', 'backend' => 'ring-mqtt', 'docs' => 'https://github.com/tsightler/ring-mqtt', 'setup_modal' => 'modals.ring-setup', ], 'smtp' => [ 'key' => 'smtp', 'name' => 'E-Mail (SMTP)', 'vendor' => 'SMTP', 'icon' => 'alert', 'cloud' => false, 'summary_key' => 'addons.smtp.summary', 'body_key' => 'addons.smtp.body', 'docs' => 'https://homeos.local', 'setup_modal' => 'modals.smtp-setup', ], ]; } public static function get(string $key): ?array { return static::all()[$key] ?? null; } public static function has(string $key): bool { return isset(static::all()[$key]); } /** * Merge each catalogue definition with its persisted state, creating rows lazily so the * page can render before anything is installed. * * @return array> */ public static function withState(): array { $rows = Addon::whereIn('key', array_keys(static::all()))->get()->keyBy('key'); return collect(static::all())->map(function (array $def) use ($rows) { $state = $rows->get($def['key']); return $def + [ 'installed' => (bool) ($state?->installed ?? false), 'status' => $state?->status ?? 'disconnected', 'status_detail' => $state?->status_detail, 'connected_at' => $state?->connected_at, 'uuid' => $state?->uuid, ]; })->values()->all(); } }