Device reachability fixes + alarm embed freshness - #39
Merged
Conversation
…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>
There was a problem hiding this comment.
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
SmartDeviceKindand threads it throughIRustServerConnection.GetSmartDeviceInfoAsyncso alarms are read viaGetAlarmInfoAsyncinstead 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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)NoResponseand the embed was stuck on "no response" forever (DB evidence:Reachability=3whileLastTriggeredUtckept updating). Reads are now kind-aware via a newSmartDeviceKindthreaded throughIRustServerConnection; alarms go throughGetAlarmInfoAsync.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.StorageMonitorStateRelaynow re-reads contents viaIRustServerQuerybefore rendering aReachabledevice.StorageMonitorTriggeredEvent, so embeds keep tracking even when noEntityChangedbroadcast 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)<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'sIClockdependency removed.SmartDeviceStateObservedEvent("a read observed this state") vsSmartDeviceTriggeredEvent("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.IRustServerQuery.GetSmartAlarmReadingAsyncand republishes through the same relay pipelines. Localized en/fr (key parity 260 → 261).Test plan
dotnet jb cleanupcode --profile=ReformatAndReorderapplied (CI format gate)🤖 Generated with Claude Code