diff --git a/app/Livewire/Devices/Show.php b/app/Livewire/Devices/Show.php
index fab834e..b65a626 100644
--- a/app/Livewire/Devices/Show.php
+++ b/app/Livewire/Devices/Show.php
@@ -56,8 +56,8 @@ class Show extends Component
$this->mqttCredential = [
'username' => $prefix,
'password' => $password,
- 'host' => config('homeos.mqtt.device_host') ?: __('devices.mqtt_host_hint'),
- 'port' => (int) config('mqtt-client.connections.default.port', 1883),
+ 'host' => \App\Support\HostAddress::broker(),
+ 'port' => \App\Support\HostAddress::port(),
];
}
@@ -110,6 +110,22 @@ class Show extends Component
$this->flashCommand($command->result);
}
+ /** Delete the device (cascades entities/state) and free its discovery finding to be re-added. */
+ #[On('deleteDevice')]
+ public function deleteDevice()
+ {
+ $prefix = data_get($this->device->config, 'mqtt_prefix');
+
+ \App\Models\DiscoveryFinding::query()
+ ->where('device_id', $this->device->id)
+ ->when($prefix, fn ($q) => $q->orWhere('name', $prefix)->orWhere('identifier', $prefix))
+ ->update(['status' => 'new', 'device_id' => null]);
+
+ $this->device->delete();
+
+ return redirect()->route('devices.index');
+ }
+
/**
* Assign a Shelly input a role: '' = plain input, 'window'/'door' = a contact sensor.
* Stored in device config so the ingest maps future messages accordingly; the entity is
diff --git a/app/Livewire/Discovery/Index.php b/app/Livewire/Discovery/Index.php
index 50ccbb0..f93ba96 100644
--- a/app/Livewire/Discovery/Index.php
+++ b/app/Livewire/Discovery/Index.php
@@ -2,14 +2,18 @@
namespace App\Livewire\Discovery;
+use App\Models\Device;
use App\Models\DiscoveryFinding;
use Livewire\Attributes\Layout;
use Livewire\Attributes\On;
use Livewire\Component;
+use PhpMqtt\Client\Facades\MQTT;
#[Layout('layouts.app')]
class Index extends Component
{
+ public bool $rescanning = false;
+
#[On('echo-private:discovery,.DeviceDiscovered')]
public function onDiscovered(): void {}
@@ -23,12 +27,32 @@ class Index extends Component
DiscoveryFinding::where('uuid', $uuid)->update(['status' => 'new']);
}
+ /** Ask the discovery sidecar to re-query the network for participants. */
+ public function rescan(): void
+ {
+ try {
+ MQTT::connection()->publish('homeos/sidecar/rescan', (string) now()->getTimestamp(), 0);
+ $this->rescanning = true;
+ } catch (\Throwable $e) {
+ report($e);
+ }
+ }
+
public function render()
{
$findings = DiscoveryFinding::query()->orderByDesc('last_seen_at')->orderByDesc('id')->get();
+ // A finding is only "new" if it hasn't already become a device — either linked at assign
+ // (device_id) or auto-onboarded by MQTT (matched by topic prefix). This stops a device
+ // showing up both under "Neue Geräte" and under Geräte.
+ $knownPrefixes = Device::query()->whereNotNull('config->mqtt_prefix')->get()
+ ->map(fn ($d) => data_get($d->config, 'mqtt_prefix'))->filter()->values()->all();
+
+ $isAdded = fn (DiscoveryFinding $f) => $f->device_id !== null
+ || in_array($f->name ?: $f->identifier, $knownPrefixes, true);
+
return view('livewire.discovery.index', [
- 'new' => $findings->where('status', 'new')->values(),
+ 'new' => $findings->where('status', 'new')->reject($isAdded)->values(),
'ignored' => $findings->where('status', 'ignored')->values(),
]);
}
diff --git a/app/Livewire/Settings/Index.php b/app/Livewire/Settings/Index.php
index 8f57e5f..71d5950 100644
--- a/app/Livewire/Settings/Index.php
+++ b/app/Livewire/Settings/Index.php
@@ -32,7 +32,7 @@ class Index extends Component
'integrations' => $integrations,
'version' => app()->version(),
'deviceMqtt' => [
- 'host' => config('homeos.mqtt.device_host'),
+ 'host' => \App\Support\HostAddress::broker(),
'username' => config('homeos.mqtt.device_username'),
'password' => config('homeos.mqtt.device_password'),
'port' => config('homeos.mqtt.port'),
diff --git a/app/Support/HostAddress.php b/app/Support/HostAddress.php
new file mode 100644
index 0000000..b950348
--- /dev/null
+++ b/app/Support/HostAddress.php
@@ -0,0 +1,30 @@
+getHost().':'.self::port();
+ }
+}
diff --git a/config/homeos.php b/config/homeos.php
index 5a437d1..184b0a4 100644
--- a/config/homeos.php
+++ b/config/homeos.php
@@ -10,8 +10,9 @@ return [
| the user under Settings → Geräte-MQTT so they can enter them into a device.
*/
'mqtt' => [
- // Address the DEVICES connect to = this server's LAN IP:port (not the internal
- // `mosquitto` hostname). Empty → the settings screen shows a hint instead.
+ // Optional override for the address DEVICES connect to. Normally left null and
+ // auto-detected from the request host (the LAN IP you open HomeOS at) — see
+ // App\Support\HostAddress. Set MQTT_DEVICE_HOST only to force a specific value.
'device_host' => env('MQTT_DEVICE_HOST'),
// The shared device account. Username is fixed (matches the `shelly` ACL block).
diff --git a/docker/mosquitto/config/acl b/docker/mosquitto/config/acl
index fc9c34b..d92aa35 100644
--- a/docker/mosquitto/config/acl
+++ b/docker/mosquitto/config/acl
@@ -4,9 +4,10 @@
user laravel
topic readwrite #
-# Discovery sidecar (Phase 4): only publishes discovery findings.
+# Discovery sidecar (Phase 4): publishes discovery findings + listens for a rescan command.
user sidecar
topic write homeos/discovery/#
+topic read homeos/sidecar/rescan
# Ring bridge (ring-mqtt add-on): owns the ring/ namespace only. Cloud-bridged devices —
# it publishes their state and reads its own command topics, nothing else on the bus.
diff --git a/lang/de/devices.php b/lang/de/devices.php
index efb56eb..beeae96 100644
--- a/lang/de/devices.php
+++ b/lang/de/devices.php
@@ -16,6 +16,9 @@ return [
'role_window' => 'Fensterkontakt',
'role_door' => 'Türkontakt',
'role_invert' => 'Invertiert',
+ 'delete' => 'Gerät entfernen',
+ 'delete_confirm_title' => 'Gerät entfernen?',
+ 'delete_confirm_body' => ':name wird mit allen Werten gelöscht. Meldet sich das Gerät erneut, taucht es wieder unter „Neue Geräte“ auf.',
'battery' => 'Batterie',
'motion' => 'Bewegung',
'no_motion' => 'Ruhe',
diff --git a/lang/de/discovery.php b/lang/de/discovery.php
index 9cbabba..2f028dd 100644
--- a/lang/de/discovery.php
+++ b/lang/de/discovery.php
@@ -4,6 +4,8 @@ return [
'subtitle' => 'Neue Teilnehmer im Netzwerk.',
'new_title' => 'Neue Geräte',
'scanning' => 'Netzwerk wird durchsucht… neue Geräte erscheinen hier automatisch.',
+ 'rescan' => 'Neu scannen',
+ 'rescanning' => 'Netzwerk wird neu durchsucht…',
'assign' => 'Zuweisen',
'ignore' => 'Ignorieren',
'ignored_title' => 'Ignoriert',
diff --git a/lang/en/devices.php b/lang/en/devices.php
index 0313913..c8cdea0 100644
--- a/lang/en/devices.php
+++ b/lang/en/devices.php
@@ -16,6 +16,9 @@ return [
'role_window' => 'Window contact',
'role_door' => 'Door contact',
'role_invert' => 'Inverted',
+ 'delete' => 'Remove device',
+ 'delete_confirm_title' => 'Remove device?',
+ 'delete_confirm_body' => ':name and all its values are deleted. If the device reports again it reappears under “New devices”.',
'battery' => 'Battery',
'motion' => 'Motion',
'no_motion' => 'Idle',
diff --git a/lang/en/discovery.php b/lang/en/discovery.php
index 42ee26e..98de375 100644
--- a/lang/en/discovery.php
+++ b/lang/en/discovery.php
@@ -4,6 +4,8 @@ return [
'subtitle' => 'New participants on the network.',
'new_title' => 'New devices',
'scanning' => 'Scanning the network… new devices appear here automatically.',
+ 'rescan' => 'Rescan',
+ 'rescanning' => 'Rescanning the network…',
'assign' => 'Assign',
'ignore' => 'Ignore',
'ignored_title' => 'Ignored',
diff --git a/resources/views/livewire/devices/show.blade.php b/resources/views/livewire/devices/show.blade.php
index bb79396..808cb8a 100644
--- a/resources/views/livewire/devices/show.blade.php
+++ b/resources/views/livewire/devices/show.blade.php
@@ -137,6 +137,11 @@
class="flex items-center gap-2.5 rounded-lg border border-line-soft bg-raised px-3 py-2.5 text-[13px] font-semibold text-ink hover:border-line transition-colors">
{{ __('devices.actions_demo_hint') }}
diff --git a/resources/views/livewire/discovery/index.blade.php b/resources/views/livewire/discovery/index.blade.php index bf36f57..3587869 100644 --- a/resources/views/livewire/discovery/index.blade.php +++ b/resources/views/livewire/discovery/index.blade.php @@ -1,7 +1,22 @@