firstOrFail(); $this->findingUuid = $model->uuid; $this->name = $model->name ?? $model->identifier; } #[Computed] public function finding(): DiscoveryFinding { return DiscoveryFinding::where('uuid', $this->findingUuid)->firstOrFail(); } public function assign() { $this->validate([ 'name' => 'required|string|max:100', 'roomId' => 'nullable|integer|exists:rooms,id', ]); $finding = $this->finding(); $isShelly = $finding->vendor === 'Shelly'; // Preferred path (like HA): if it's a Shelly we can reach over its local API, onboard it // there — control + status run over HTTP, no MQTT setup on the device. if ($isShelly && filled($finding->ip)) { try { $device = app(ShellyLocalOnboarder::class)->onboard($finding->ip, $this->name, $this->roomId ?: null); $finding->update(['status' => 'assigned', 'device_id' => $device->id]); $this->closeModal(); return redirect()->route('devices.show', $device); } catch (\Throwable) { // Unreachable over HTTP — fall through to the MQTT-style record below. } } // The Shelly MQTT topic prefix is its device id — the mDNS service INSTANCE name // (finding->name), not the slugified topic identifier which carries the _shelly._tcp suffix. $prefix = $isShelly ? ($finding->name ?: $finding->identifier) : null; // Reuse an already-onboarded row by prefix so assigning just names it (no duplicate). $device = ($prefix ? Device::where('config->mqtt_prefix', $prefix)->first() : null) ?? new Device; $device->fill([ 'name' => $this->name, 'vendor' => $finding->vendor, 'model' => $finding->model ?: $device->model, 'protocol' => $isShelly ? 'mqtt' : $device->protocol, 'room_id' => $this->roomId ?: null, 'status' => 'active', ]); $device->config = array_merge($device->config ?? [], $prefix ? ['mqtt_prefix' => $prefix] : []); $device->save(); $finding->update(['status' => 'assigned', 'device_id' => $device->id]); $this->closeModal(); return redirect()->route('devices.show', $device); } public function render() { return view('livewire.modals.assign-device', [ 'finding' => $this->finding(), 'rooms' => Room::orderBy('sort')->orderBy('name')->get(), ]); } }