Skip to content

Device reachability fixes + alarm embed freshness - #39

Merged
HandyS11 merged 2 commits into
developfrom
fix/device-reachability
Jul 5, 2026
Merged

Device reachability fixes + alarm embed freshness#39
HandyS11 merged 2 commits into
developfrom
fix/device-reachability

Conversation

@HandyS11

@HandyS11 HandyS11 commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the three bugs found while running the bot live (devices always "unreachable" despite working triggers), then makes alarm embeds stay fresh between triggers.

Reachability fixes (4361dde)

  • Alarms were read as switches. RustPlusApi beta.3 type-checks the server-reported entity type and throws for alarms, so every alarm prime/sweep read surfaced as NoResponse and the embed was stuck on "no response" forever (DB evidence: Reachability=3 while LastTriggeredUtc kept updating). Reads are now kind-aware via a new SmartDeviceKind threaded through IRustServerConnection; alarms go through GetAlarmInfoAsync.
  • Reachable storage monitors rendered as unreachable. The reachability event handler re-rendered with contents: null, which shows the unreachable banner and disables Refresh/Rename; it raced the prime's contents render on a second bus loop and reliably lost. StorageMonitorStateRelay now re-reads contents via IRustServerQuery before rendering a Reachable device.
  • Storage contents didn't track changes. The 5-minute reachability sweep already read contents and threw them away; it now republishes them as StorageMonitorTriggeredEvent, so embeds keep tracking even when no EntityChanged broadcast arrives. Device triggers are deliberately NOT republished (an active alarm would re-ping every sweep).

Library-side defects (mapper type-check, capacity-based broadcast routing) are documented separately in the RustPlusApi repo for beta.4.

Alarm embed freshness (bde1edc)

  • Live "Last triggered" timer: the embed now renders Discord's native <t:unix:R> relative timestamp, which clients keep updating forever — the baked-in "<1m ago" text could go stale within a minute. CompactDuration + the renderer's IClock dependency removed.
  • Silent observed-state sync: new SmartDeviceStateObservedEvent ("a read observed this state") vs SmartDeviceTriggeredEvent ("the device broadcast a change"). Published by the sweep and the connect prime for alarms; the relay persists + re-renders only on drift and never pings/relays. Also fixes a latent wart: reconnecting while an alarm was active re-pinged @everyone.
  • Refresh button on alarm embeds (mirrors the storage monitor one): re-reads live state via the new kind-aware IRustServerQuery.GetSmartAlarmReadingAsync and republishes through the same relay pipelines. Localized en/fr (key parity 260 → 261).

Test plan

  • TDD throughout — every behavior change had a verified failing test first
  • Full suite: 808 tests green
  • dotnet jb cleanupcode --profile=ReformatAndReorder applied (CI format gate)
  • Verified live by the bot owner: alarm status + storage contents now render correctly

🤖 Generated with Claude Code

HandyS11 and others added 2 commits July 5, 2026 17:03
…sweep contents refresh

Live-run diagnosis (devices always 'unreachable' despite working triggers):

1. Alarms were primed/swept via GetSmartSwitchInfoAsync; RustPlusApi beta.3
   type-checks the server-reported entity type and throws for alarms, so every
   alarm read surfaced as NoResponse on each connect. Reads are now kind-aware
   (new SmartDeviceKind threaded through IRustServerConnection): alarms go
   through GetAlarmInfoAsync.

2. A storage monitor that (re)became Reachable was re-rendered with null
   contents, which displays the unreachable banner and disables Refresh/Rename.
   The prime's contents render raced this on a second bus loop and reliably
   lost. StorageMonitorStateRelay now re-reads contents via IRustServerQuery
   before rendering a Reachable device.

3. The 5-minute reachability sweep read storage contents and discarded them;
   it now republishes them as StorageMonitorTriggeredEvent so embeds keep
   tracking in-game changes even when no EntityChanged broadcast arrives.
   Device triggers are deliberately NOT republished (an active alarm would
   re-ping every sweep).

Library-side defects (mapper type-check, capacity-based broadcast routing)
are documented in RustPlusApi docs/development/beta4-entity-info-fixes.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fresh button

The "Last triggered <1m ago" text was baked into the embed at render time and
froze until the next re-render. Three changes keep alarm embeds honest:

1. The last-triggered line now renders Discord's native relative timestamp
   (<t:unix:R>), which clients keep updating live — no server-side re-renders
   needed. CompactDuration and the renderer's IClock dependency are gone.

2. New SmartDeviceStateObservedEvent: a poll/prime/refresh OBSERVED this state,
   as opposed to a broadcast trigger. AlarmStateRelay syncs silently — persists
   drifted LastIsActive (leaving LastTriggeredUtc untouched) and re-renders;
   never pings or relays; no-ops (no Discord edit) when unchanged. Published by
   the 5-minute reachability sweep (which already read the state and discarded
   it) and by the connect prime for alarms — the latter also fixes a latent
   wart where reconnecting while an alarm was active re-pinged @everyone.

3. Refresh button on the alarm embed (mirrors the storage monitor one):
   re-reads the alarm live via the new kind-aware
   IRustServerQuery.GetSmartAlarmReadingAsync and republishes reachability +
   observed state through the same relay pipelines.

Localization: alarm.button.refresh (en/fr), key parity 260 → 261.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 5, 2026 15:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes live-reported device reachability issues (especially alarms being read with the wrong Rust+ API call) and improves Discord embed correctness/freshness by adding kind-aware reads, periodic state/contents republishing, and a manual refresh path for alarms.

Changes:

  • Introduces SmartDeviceKind and threads it through IRustServerConnection.GetSmartDeviceInfoAsync so alarms are read via GetAlarmInfoAsync instead of switch reads.
  • Fixes storage monitor reachability re-rendering by re-fetching contents when a device becomes reachable, and republishes reachable storage contents during periodic sweeps.
  • Improves alarm embed freshness by switching to Discord native relative timestamps (<t:unix:R>), adds a Refresh button, and introduces a silent “observed state” event pipeline to correct drift without ping/relay effects.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/RustPlusBot.Localization.Tests/StringsResourceParityTests.cs Updates expected localization key count for new alarm refresh string.
tests/RustPlusBot.Features.StorageMonitors.Tests/StorageMonitorStateRelayTests.cs Adds coverage for reachable re-render fetching live contents; wires IRustServerQuery into relay tests.
tests/RustPlusBot.Features.StorageMonitors.Tests/Hosting/StorageMonitorsHostedServiceTests.cs Updates hosted service test wiring for new relay dependency.
tests/RustPlusBot.Features.Connections.Tests/StorageSweepTests.cs New test ensuring periodic sweep republishes reachable storage contents.
tests/RustPlusBot.Features.Connections.Tests/Fakes/FakeRustSocketSource.cs Enhances fake socket to stage device state pre-connect and record kind-aware reads.
tests/RustPlusBot.Features.Connections.Tests/AlarmSweepTests.cs New tests for kind-aware alarm reads and observed-state publishing from sweeps.
tests/RustPlusBot.Features.Connections.Tests/AlarmPrimingTests.cs Updates priming to observed-state semantics and asserts alarm-kind reads.
tests/RustPlusBot.Features.Alarms.Tests/Hosting/AlarmsHostedServiceTests.cs Adds coverage for observed-state drift syncing via hosted service routing.
tests/RustPlusBot.Features.Alarms.Tests/AlarmStateRelayTests.cs Adds observed-state relay behavior tests (silent drift sync, no-op, foreign ignore).
tests/RustPlusBot.Features.Alarms.Tests/AlarmRefresherTests.cs Updates renderer construction after removing IClock dependency.
tests/RustPlusBot.Features.Alarms.Tests/AlarmPairingCoordinatorTests.cs Updates renderer construction after removing IClock dependency.
tests/RustPlusBot.Features.Alarms.Tests/AlarmEmbedRendererTests.cs Updates assertions for Discord native relative timestamps + refresh button presence.
src/RustPlusBot.Localization/Strings.resx Adds English alarm.button.refresh string.
src/RustPlusBot.Localization/Strings.fr.resx Adds French alarm.button.refresh string.
src/RustPlusBot.Features.StorageMonitors/Relaying/StorageMonitorStateRelay.cs Injects IRustServerQuery and re-reads contents when reachability becomes reachable to avoid stale “unreachable” renders.
src/RustPlusBot.Features.Connections/Supervisor/ConnectionSupervisor.cs Implements alarm-kind reads, observed-state publishing for alarms, and periodic contents republishing for storage monitors.
src/RustPlusBot.Features.Connections/Listening/RustPlusSocketSource.cs Makes smart device reads kind-aware (alarm vs switch API call).
src/RustPlusBot.Features.Connections/Listening/IRustServerConnection.cs Extends device-read contract to require SmartDeviceKind.
src/RustPlusBot.Features.Alarms/Rendering/AlarmEmbedRenderer.cs Removes clock-based relative duration formatting; uses <t:unix:R> and adds refresh button.
src/RustPlusBot.Features.Alarms/Rendering/AlarmComponentIds.cs Adds component ID prefix for alarm refresh button.
src/RustPlusBot.Features.Alarms/Relaying/AlarmStateRelay.cs Adds observed-state handling for silent drift correction without triggering notifications.
src/RustPlusBot.Features.Alarms/Modules/AlarmComponentModule.cs Adds Refresh interaction to re-read alarm state via IRustServerQuery and publish observed/reachability events.
src/RustPlusBot.Features.Alarms/Hosting/AlarmsHostedService.cs Adds an observed-state consumer loop routing SmartDeviceStateObservedEvent into the relay.
src/RustPlusBot.Abstractions/Events/SmartDeviceStateObservedEvent.cs New event for “read-observed” device state used for silent sync.
src/RustPlusBot.Abstractions/Connections/SmartDeviceKind.cs New enum distinguishing alarm vs switch reads to satisfy Rust+ type checking.
src/RustPlusBot.Abstractions/Connections/IRustServerQuery.cs Adds GetSmartAlarmReadingAsync API for kind-aware alarm refresh reads.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@HandyS11
HandyS11 merged commit 8fd9f81 into develop Jul 5, 2026
4 checks passed
@HandyS11
HandyS11 deleted the fix/device-reachability branch July 5, 2026 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants