Skip to content

Conversation

@DTTerastar
Copy link
Collaborator

@DTTerastar DTTerastar commented Sep 21, 2025

Summary

  • add a bayesian_probabilities config section and publish per-room probability vectors alongside the existing tracker state
  • expose retained MQTT topics plus Home Assistant discovery sensors for high-confidence rooms and document how to consume them
  • extend DeviceTracker and locator pipeline with probability smoothing helpers and a unit test covering the new payloads

Testing

  • dotnet test (fails: dotnet is not available in the container)

https://chatgpt.com/codex/tasks/task_e_68d00873dd8c8324bde1d937e7cd16f8

Summary by CodeRabbit

  • New Features

    • Publish per-room Bayesian probabilities over MQTT (normalized) with optional Home Assistant sensor auto-discovery; per-device probability topics and person-level fusion examples included.
    • New configuration: bayesian_probabilities.enabled, discovery_threshold, retain.
  • Documentation

    • README: overview, MQTT topics/payloads, discovery behavior, YAML examples, and automation usage.
  • Improvements

    • Discovery metadata extended with sensor metadata (device class, state class, unit, value template, icon).
    • Probability lifecycle: create/remove discovery sensors based on threshold; device probability/state reset when untracked.
  • Tests

    • Added test verifying probabilities are published when enabled.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 21, 2025

📝 Walkthrough

Walkthrough

Adds device-level Bayesian room-probability support: config options, model fields and cloning, device state and discovery records, MQTT publishing of per-room probabilities and attributes, Home Assistant autodiscovery metadata, locator logic to compute/publish probabilities, untracking reset, README docs, and a unit test.

Changes

Cohort / File(s) Summary
Documentation
README.md
Adds Bayesian probability section, config guidance, MQTT topics/payload examples, Home Assistant discovery and automation examples.
Config model & cloning
src/Models/Config.cs, src/Models/Config.Clone.cs
Adds Config.BayesianProbabilities and ConfigBayesianProbabilities (properties: enabled, discovery_threshold, retain) and a Clone() implementation for deep-copying Bayesian config.
Auto-discovery model
src/Models/AutoDiscovery.cs
Adds optional JSON-serialized Home Assistant metadata fields: device_class, state_class, unit_of_measurement, value_template, icon.
Device model & state
src/Models/Device.cs
Adds BayesianProbabilities and BayesianDiscoveries concurrent dictionaries and ResetBayesianState() to remove HA autodiscovery and clear probability state.
Locator service (probabilities)
src/Services/MultiScenarioLocator.cs
Implements building/normalizing per-room probability vectors, publishing per-room MQTT probability topics and device attributes (including probabilities), Home Assistant discovery creation/removal based on threshold, helpers for topics/sanitization, and conditional location reporting; constructor updated to accept MQTT, dispatcher, and history dependencies.
Device tracking lifecycle
src/Services/DeviceTracker.cs
Resets device Bayesian state when a device transitions to untracked (after deleting autodiscovery entries).
Config example
src/config.example.yaml
Adds top-level bayesian_probabilities section with enabled, discovery_threshold, and retain.
Tests
tests/MultiScenarioLocatorTests.cs
Adds FixedRoom locator helper and BayesianProbabilitiesPublishedWhenEnabled test asserting probability topic publish and device attributes include probabilities.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Dev as Device
  participant MSL as MultiScenarioLocator
  participant MQTT as MqttCoordinator
  participant HA as Home Assistant (Auto-Discovery)
  participant Hist as DeviceHistoryStore

  Note over Dev,MSL: Processing cycle (Bayesian enabled)
  Dev->>MSL: Provide scenarios & locators
  MSL->>MSL: Build and normalize per-room probability vector (epsilon)
  alt Probabilities available
    MSL->>MQTT: Publish per-room `probabilities/<room>` topics (retain per config)
    MSL->>MQTT: Publish device state/attributes including `probabilities` object
    MSL->>HA: Create/Update discovery sensors for rooms >= discovery_threshold
    opt Best location exists & reporting enabled
      MSL->>Hist: Record history / emit device-changed events
    end
  else Disabled or no data
    MSL->>MQTT: Clear probability topics/attributes (if configured)
    MSL->>HA: Remove probability discovery entities
  end

  Note over Dev,MSL: Untracking path
  Dev-->>MSL: Device becomes untracked
  MSL->>HA: Delete autodiscovery entries
  MSL->>Dev: ResetBayesianState()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas to focus during review:

  • Duplicate partial class declarations of ConfigBayesianProbabilities in src/Models/Config.cs.
  • Probability vector construction, normalization, epsilon handling, and edge cases in src/Services/MultiScenarioLocator.cs.
  • Correct serialization/YAML mapping for new AutoDiscovery fields (src/Models/AutoDiscovery.cs) and config YAML aliases.
  • Lifecycle sequencing: retain behavior, autodiscovery add/remove, and ResetBayesianState() interactions (src/Models/Device.cs, src/Services/DeviceTracker.cs).
  • Unit test assertions and mocked MQTT/topic payload contents in tests/MultiScenarioLocatorTests.cs.

Possibly related issues

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.55% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding Bayesian probability publishing functionality. It aligns well with the substantial additions to configuration, models, services, and documentation focused on Bayesian room probabilities.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/implement-changes-from-issue-#1278

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 212205f and 8ca8d2c.

📒 Files selected for processing (9)
  • README.md (2 hunks)
  • src/Models/AutoDiscovery.cs (1 hunks)
  • src/Models/Config.Clone.cs (2 hunks)
  • src/Models/Config.cs (2 hunks)
  • src/Models/Device.cs (4 hunks)
  • src/Services/DeviceTracker.cs (1 hunks)
  • src/Services/MultiScenarioLocator.cs (5 hunks)
  • src/config.example.yaml (1 hunks)
  • tests/MultiScenarioLocatorTests.cs (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Services/DeviceTracker.cs
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Place backend C# ASP.NET Core code under src/ (controllers, services, models, utils)

Files:

  • src/config.example.yaml
  • src/Models/AutoDiscovery.cs
  • src/Models/Config.cs
  • src/Models/Device.cs
  • src/Models/Config.Clone.cs
  • src/Services/MultiScenarioLocator.cs
src/config.example.yaml

📄 CodeRabbit inference engine (AGENTS.md)

Use src/config.example.yaml as a template and do not commit real secrets

Files:

  • src/config.example.yaml
{src,tests}/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

{src,tests}/**/*.cs: C#: Use spaces with an indent size of 4
C#: Use PascalCase for types and methods
C#: Use camelCase for local variables and parameters

Files:

  • src/Models/AutoDiscovery.cs
  • src/Models/Config.cs
  • src/Models/Device.cs
  • tests/MultiScenarioLocatorTests.cs
  • src/Models/Config.Clone.cs
  • src/Services/MultiScenarioLocator.cs
tests/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

Place backend NUnit tests under tests/

Files:

  • tests/MultiScenarioLocatorTests.cs
tests/**/*Tests.cs

📄 CodeRabbit inference engine (AGENTS.md)

Name backend NUnit test files with the *Tests.cs suffix (e.g., TimeSpanExtensionsTests.cs)

Files:

  • tests/MultiScenarioLocatorTests.cs
🧠 Learnings (1)
📚 Learning: 2025-09-16T00:51:01.127Z
Learnt from: CR
Repo: ESPresense/ESPresense-companion PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-16T00:51:01.127Z
Learning: Applies to src/**/Controllers/StateController.cs : StateController must populate Device.ConfiguredRefRssi from DeviceSettings during API calls

Applied to files:

  • src/Models/Device.cs
🧬 Code graph analysis (4)
src/Models/Config.cs (1)
src/Models/Config.Clone.cs (2)
  • ConfigBayesianProbabilities (120-131)
  • ConfigBayesianProbabilities (122-130)
tests/MultiScenarioLocatorTests.cs (2)
src/Models/Room.cs (2)
  • Room (6-32)
  • ToString (28-31)
src/Models/Scenario.cs (3)
  • Locate (39-42)
  • Scenario (8-59)
  • UpdateLocation (44-51)
src/Models/Config.Clone.cs (1)
src/Models/Config.cs (1)
  • ConfigBayesianProbabilities (149-159)
src/Services/MultiScenarioLocator.cs (3)
src/Models/Config.cs (2)
  • Config (8-61)
  • ConfigBayesianProbabilities (149-159)
src/Models/Device.cs (3)
  • Device (11-209)
  • Device (23-28)
  • ToString (30-37)
src/Models/AutoDiscovery.cs (6)
  • AutoDiscovery (9-156)
  • AutoDiscovery (17-40)
  • AutoDiscovery (42-47)
  • DiscoveryRecord (110-137)
  • DeviceRecord (139-150)
  • OriginRecord (152-155)
🪛 GitHub Actions: Build and test
src/Services/MultiScenarioLocator.cs

[error] 200-200: CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''

🪛 GitHub Actions: Deploy to Docker
src/Services/MultiScenarioLocator.cs

[error] 200-202: CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''

🪛 GitHub Check: build
src/Services/MultiScenarioLocator.cs

[warning] 141-141:
Dereference of a possibly null reference.


[failure] 202-202:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 201-201:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 200-200:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 202-202:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 201-201:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 200-200:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (14)
src/config.example.yaml (1)

53-56: LGTM!

The Bayesian probabilities configuration section is well-structured with sensible defaults. The feature is disabled by default (safe opt-in), the discovery threshold of 10% filters low-confidence rooms appropriately, and retain=true ensures sensors persist across restarts.

src/Models/Config.Clone.cs (2)

120-131: LGTM!

The Clone() implementation for ConfigBayesianProbabilities follows the established pattern in this file. Since all properties are value types (bool, double, bool), simple assignment produces a correct deep copy.


27-28: LGTM!

Config.Clone() correctly integrates BayesianProbabilities cloning, maintaining consistency with other configuration sections.

tests/MultiScenarioLocatorTests.cs (2)

81-166: LGTM!

The test comprehensively validates Bayesian probability publishing:

  • Sets up isolated config with proper YAML structure
  • Mocks MQTT to capture all published messages
  • Verifies both probability topics and attributes payload
  • Uses JObject to parse and assert JSON structure

The test design is solid and validates the key requirements.


21-35: The project targets .NET 8.0, which defaults to C# 12. C# 12 introduces primary constructors, which provide a concise syntax to declare constructors whose parameters are available anywhere in the body of the type. The code is properly configured.

src/Models/Device.cs (1)

85-89: LGTM!

The new concurrent dictionaries are well-designed:

  • ConcurrentDictionary provides thread-safe operations for probability tracking
  • Case-insensitive comparison is appropriate for room names
  • JsonIgnore is correct as these are internal state
src/Models/AutoDiscovery.cs (1)

122-130: LGTM!

The new DiscoveryRecord properties extend Home Assistant discovery metadata appropriately. All are optional (nullable) and correctly annotated with JsonProperty for snake_case serialization.

src/Models/Config.cs (2)

44-45: LGTM!

The BayesianProbabilities property is correctly declared with proper YamlMember alias and initialized to new().


149-159: No duplicate declarations exist; class is correctly split using partial keyword.

The ConfigBayesianProbabilities class is declared across two files as a partial class: the main definition in src/Models/Config.cs and a Clone method extension in src/Models/Config.Clone.cs. This is a valid C# pattern and not a duplication issue.

Likely an incorrect or invalid review comment.

src/Services/MultiScenarioLocator.cs (5)

7-9: LGTM!

The new using directives support the Bayesian probability features: CultureInfo for invariant formatting, StringBuilder for topic sanitization, and TextExtensions for snake_case conversion.


26-31: Constructor signature updated for Bayesian probability support.

The constructor now injects MqttCoordinator, GlobalEventDispatcher, DeviceHistoryStore, and ILeaseService dependencies required for probability publishing. Ensure all call sites are updated accordingly.


141-160: LGTM!

The Bayesian probability configuration flow is correct:

  • Safely handles null state.Config with ?. operator
  • Pattern matching probabilityConfig is { Enabled: true } properly guards null
  • Clears outputs when disabled (lines 157-160)

The static analysis warning at line 141 is a false positive—the nullable chain handles null Config correctly.


238-291: LGTM!

BuildProbabilityVector correctly:

  • Aggregates scenario probabilities by room name
  • Handles edge cases (no scenarios → "not_home", null rooms → "unknown")
  • Normalizes to sum=1.0 with proper clamping
  • Adds "other" category for remainder to ensure total=1.0

The probability math is sound and edge cases are well-handled.


365-419: LGTM!

The helper methods are well-implemented:

  • CreateProbabilityDiscovery properly constructs Home Assistant sensor discovery with appropriate metadata (StateClass, Icon)
  • BuildProbabilityTopic safely constructs MQTT topics using sanitization
  • SanitizeSegment robustly cleans room names for MQTT paths (snake_case, character filtering, fallback to "unknown")
  • IsSyntheticRoom correctly filters synthetic categories with case-insensitive comparison

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (9)
src/config.example.yaml (1)

55-59: Clarify threshold range in example config

Add explicit 0.0–1.0 range to avoid misconfiguration.

 bayesian_probabilities:
   enabled: false          # Publish per-room probability vectors (default disabled)
-  discovery_threshold: 0.1 # Minimum probability before creating Home Assistant sensor
+  discovery_threshold: 0.1 # Minimum probability before creating Home Assistant sensor (range: 0.0–1.0)
   retain: true             # Retain MQTT probability topics so sensors restore on restart
README.md (1)

23-58: Document probability range and topic naming

  • Mention values are 0.0–1.0 to set expectations.
  • Note room segment sanitization/lowercasing (as topics use .../probabilities/kitchen).
src/Models/Config.Clone.cs (1)

27-29: Make clone resilient to nulls

Defensive null-guards prevent NREs if any sub-config is ever deserialized as null.

-                Optimization = Optimization.Clone(),
-                BayesianProbabilities = BayesianProbabilities.Clone()
+                Optimization = Optimization?.Clone() ?? new ConfigOptimization(),
+                BayesianProbabilities = BayesianProbabilities?.Clone() ?? new ConfigBayesianProbabilities()
src/Models/Config.cs (1)

146-156: Clamp discovery_threshold to 0.0–1.0

Prevents invalid config values from propagating.

-    public partial class ConfigBayesianProbabilities
-    {
-        [YamlMember(Alias = "enabled")]
-        public bool Enabled { get; set; } = false;
-
-        [YamlMember(Alias = "discovery_threshold")]
-        public double DiscoveryThreshold { get; set; } = 0.1;
-
-        [YamlMember(Alias = "retain")]
-        public bool Retain { get; set; } = true;
-    }
+    public partial class ConfigBayesianProbabilities
+    {
+        [YamlMember(Alias = "enabled")]
+        public bool Enabled { get; set; } = false;
+
+        private double _discoveryThreshold = 0.1;
+        [YamlMember(Alias = "discovery_threshold")]
+        public double DiscoveryThreshold
+        {
+            get => _discoveryThreshold;
+            set => _discoveryThreshold = value < 0 ? 0 : (value > 1 ? 1 : value);
+        }
+
+        [YamlMember(Alias = "retain")]
+        public bool Retain { get; set; } = true;
+    }
tests/MultiScenarioLocatorTests.cs (1)

82-165: Great coverage; consider a couple more assertions

  • Assert that probability topics respect retain: true.
  • Assert a discovery config is published for rooms above discovery_threshold.
  • Add a test that untracking a device deletes probability sensors (config topic with null payload).

Example additions:

+        // Retain flag honored
+        Assert.That(published.Any(p => p.topic == probabilityTopic && p.retain == true), Is.True);
+
+        // Discovery config published for Kitchen
+        var disc = published.FirstOrDefault(p => p.topic.Contains($"/sensor/espresense-{device.Id}-kitchen-probability/config"));
+        Assert.That(disc.payload, Is.Not.Null.And.Not.Empty);
src/Services/MultiScenarioLocator.cs (4)

271-281: Reduce publish churn by comparing rounded values to what you send.

You publish with 4-decimal precision but compare using raw doubles with epsilon. Compare after rounding and persist the rounded value to avoid tiny oscillations.

Apply:

-            var payloadChanged = !device.BayesianProbabilities.TryGetValue(roomName, out var existing) || Math.Abs(existing - probability) > ProbabilityEpsilon;
+            var rounded = Math.Round(probability, 4, MidpointRounding.AwayFromZero);
+            var payloadChanged = !device.BayesianProbabilities.TryGetValue(roomName, out var existing) || Math.Abs(existing - rounded) > ProbabilityEpsilon;
             if (payloadChanged)
             {
-                var payload = probability.ToString("0.####", CultureInfo.InvariantCulture);
+                var payload = rounded.ToString("0.####", CultureInfo.InvariantCulture);
                 await mqtt.EnqueueAsync(BuildProbabilityTopic(device.Id, roomName), payload, retain: config.Retain);
-                device.BayesianProbabilities[roomName] = probability;
+                device.BayesianProbabilities[roomName] = rounded;
                 changed = true;
             }

338-363: HA discovery: add unit and suggested display for probabilities.

Publishing 0..1 benefits from a unit and suggested display. Consider percent for UX.

Example:

             var record = new AutoDiscovery.DiscoveryRecord
             {
                 Name = $"{device.Name ?? device.Id} {roomName} Probability",
                 UniqueId = $"espresense-companion-{device.Id}-{sanitizedRoom}",
                 StateTopic = BuildProbabilityTopic(device.Id, roomName),
                 EntityStatusTopic = "espresense/companion/status",
                 Device = new AutoDiscovery.DeviceRecord
                 {
                     Name = device.Name ?? device.Id,
                     Manufacturer = "ESPresense",
                     Model = "Companion",
                     SwVersion = "1.0.0",
                     Identifiers = new[] { $"espresense-{device.Id}" }
                 },
                 Origin = new AutoDiscovery.OriginRecord { Name = "ESPresense Companion" },
                 StateClass = "measurement",
-                Icon = "mdi:account-location"
+                Icon = "mdi:account-location",
+                UnitOfMeasurement = "%",
+                SuggestedDisplayPrecision = 1
             };

If you keep 0..1 on the wire, alternatively set DeviceClass or document scaling. Verify property names match AutoDiscovery.DiscoveryRecord.


211-264: Probability vector edge-cases look good; minor naming consistency nit.

Both “unknown” and “other” appear; consider consistently using one bucket for absent/overflow mass.

If desired, replace the initial "not_home" with "other" (or vice versa) for uniformity across sensors and payloads.


163-165: Make lat/lon/elevation typing explicit (use nullable locals)

gps.Add returns (double? lat, double? lon) — src/Extensions/GpsUtilExtensions.cs. The ternary with gps.Add(...): (null, null) can confuse the compiler; declare nullable locals and assign them inside the if.

File: src/Services/MultiScenarioLocator.cs (around lines 163–165)

Apply:

-                var (lat, lon) = includeLocation && gps?.Report == true ? gps.Add(location.X, location.Y) : (null, null);
-                var elevation = includeLocation && gps?.Report == true ? location.Z + gps?.Elevation : null;
+                double? lat = null, lon = null, elevation = null;
+                if (includeLocation && gps?.Report == true)
+                {
+                    var (latVal, lonVal) = gps.Add(location.X, location.Y);
+                    lat = latVal;
+                    lon = lonVal;
+                    elevation = location.Z + gps?.Elevation;
+                }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a883730 and 92fc9b4.

📒 Files selected for processing (9)
  • README.md (2 hunks)
  • src/Models/AutoDiscovery.cs (1 hunks)
  • src/Models/Config.Clone.cs (2 hunks)
  • src/Models/Config.cs (2 hunks)
  • src/Models/Device.cs (2 hunks)
  • src/Services/DeviceTracker.cs (1 hunks)
  • src/Services/MultiScenarioLocator.cs (4 hunks)
  • src/config.example.yaml (1 hunks)
  • tests/MultiScenarioLocatorTests.cs (3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Place backend C# ASP.NET Core code under src/ (controllers, services, models, utils)

Files:

  • src/Models/AutoDiscovery.cs
  • src/Models/Device.cs
  • src/Services/MultiScenarioLocator.cs
  • src/Services/DeviceTracker.cs
  • src/Models/Config.Clone.cs
  • src/config.example.yaml
  • src/Models/Config.cs
{src,tests}/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

{src,tests}/**/*.cs: C#: Use spaces with an indent size of 4
C#: Use PascalCase for types and methods
C#: Use camelCase for local variables and parameters

Files:

  • src/Models/AutoDiscovery.cs
  • src/Models/Device.cs
  • src/Services/MultiScenarioLocator.cs
  • src/Services/DeviceTracker.cs
  • src/Models/Config.Clone.cs
  • tests/MultiScenarioLocatorTests.cs
  • src/Models/Config.cs
tests/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

Place backend NUnit tests under tests/

Files:

  • tests/MultiScenarioLocatorTests.cs
tests/**/*Tests.cs

📄 CodeRabbit inference engine (AGENTS.md)

Name backend NUnit test files with the *Tests.cs suffix (e.g., TimeSpanExtensionsTests.cs)

Files:

  • tests/MultiScenarioLocatorTests.cs
src/config.example.yaml

📄 CodeRabbit inference engine (AGENTS.md)

Use src/config.example.yaml as a template and do not commit real secrets

Files:

  • src/config.example.yaml
🧬 Code graph analysis (5)
src/Models/Device.cs (2)
src/Models/AutoDiscovery.cs (3)
  • AutoDiscovery (9-156)
  • AutoDiscovery (17-40)
  • AutoDiscovery (42-47)
src/Services/MultiScenarioLocator.cs (1)
  • AutoDiscovery (338-363)
src/Services/MultiScenarioLocator.cs (4)
src/Models/Config.cs (2)
  • Config (8-58)
  • ConfigBayesianProbabilities (146-156)
src/Models/Device.cs (2)
  • Device (10-184)
  • Device (22-27)
src/Services/MqttCoordinator.cs (12)
  • Task (69-76)
  • Task (78-120)
  • Task (122-189)
  • Task (204-261)
  • Task (273-295)
  • Task (306-334)
  • Task (349-391)
  • Task (407-433)
  • Task (448-470)
  • Task (473-498)
  • Task (500-509)
  • Task (511-533)
src/Models/AutoDiscovery.cs (5)
  • AutoDiscovery (9-156)
  • AutoDiscovery (17-40)
  • AutoDiscovery (42-47)
  • DiscoveryRecord (110-137)
  • DeviceRecord (139-150)
src/Services/DeviceTracker.cs (1)
src/Models/Device.cs (1)
  • ResetBayesianState (139-148)
src/Models/Config.Clone.cs (1)
src/Models/Config.cs (1)
  • ConfigBayesianProbabilities (146-156)
src/Models/Config.cs (1)
src/Models/Config.Clone.cs (2)
  • ConfigBayesianProbabilities (120-131)
  • ConfigBayesianProbabilities (122-130)
🪛 GitHub Check: build
src/Services/MultiScenarioLocator.cs

[failure] 174-174:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 173-173:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 172-172:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 174-174:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 173-173:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 172-172:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''

🪛 GitHub Actions: Build and test
src/Services/MultiScenarioLocator.cs

[error] 172-172: dotnet build -c Release --no-restore failed: CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (12)
src/Models/AutoDiscovery.cs (1)

122-131: Extended HA discovery metadata looks good

Fields match HA discovery keys and are null-suppressed by serializer settings.

README.md (1)

12-12: Docs: nice addition

Bullet succinctly advertises the new capability.

src/Models/Config.Clone.cs (1)

120-132: BayesianProbabilities.Clone() is correct

Deep copy of flags and settings is appropriate.

src/Models/Config.cs (1)

49-51: Config surface addition LGTM

Top‑level bayesian_probabilities with sensible defaults is consistent with the example YAML.

src/Models/Device.cs (2)

76-81: Thread‑safe state containers for probabilities/discoveries

Good choice with case‑insensitive ConcurrentDictionary and JsonIgnore.


139-148: Ensure HA sensor deletion is emitted

ResetBayesianState() removes probability discoveries from HassAutoDiscovery and clears dictionaries but does not publish delete messages itself. Relying on the earlier loop over HassAutoDiscovery is fine only if probability sensors were inserted there. Please confirm; otherwise, emit deletes for BayesianDiscoveries before clearing.

tests/MultiScenarioLocatorTests.cs (4)

1-1: Import changes OK

Needed for types used below.


3-3: Using order change is fine

No functional impact.


8-9: New test deps are appropriate

JObject parsing and Point3D usage are justified.


21-35: FixedRoomLocator helper is clean

Sets room/floor/confidence and advances fixes; suitable for deterministic tests.

src/Services/DeviceTracker.cs (1)

215-215: Resolved — probability discoveries are registered to HassAutoDiscovery

PublishProbabilitySensorsAsync/CreateProbabilityDiscovery add the probability AutoDiscovery to device.HassAutoDiscovery before sending (see src/Services/MultiScenarioLocator.cs:284–286, 338–343), so ResetBayesianState after removing entities will not leave probability sensors behind.

src/Services/MultiScenarioLocator.cs (1)

371-389: Segment sanitizer: safe and HA/MQTT-friendly. LGTM.

Good normalization and fallback to “unknown”.

Comment on lines 168 to 175
source_type = includeLocation ? "espresense" : null,
latitude = includeLocation ? lat : null,
longitude = includeLocation ? lon : null,
elevation = includeLocation ? elevation : null,
x = includeLocation ? location.X : null,
y = includeLocation ? location.Y : null,
z = includeLocation ? location.Z : null,
confidence = bestScenario?.Confidence,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix CS0173: ternary mixes double with null (x/y/z).

Anonymous object initializers can’t infer nullable from double : null. Cast the doubles to double?.

Apply:

-                    x             = includeLocation ? location.X : null,
-                    y             = includeLocation ? location.Y : null,
-                    z             = includeLocation ? location.Z : null,
+                    x             = includeLocation ? (double?)location.X : null,
+                    y             = includeLocation ? (double?)location.Y : null,
+                    z             = includeLocation ? (double?)location.Z : null,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
source_type = includeLocation ? "espresense" : null,
latitude = includeLocation ? lat : null,
longitude = includeLocation ? lon : null,
elevation = includeLocation ? elevation : null,
x = includeLocation ? location.X : null,
y = includeLocation ? location.Y : null,
z = includeLocation ? location.Z : null,
confidence = bestScenario?.Confidence,
source_type = includeLocation ? "espresense" : null,
latitude = includeLocation ? lat : null,
longitude = includeLocation ? lon : null,
elevation = includeLocation ? elevation : null,
x = includeLocation ? (double?)location.X : null,
y = includeLocation ? (double?)location.Y : null,
z = includeLocation ? (double?)location.Z : null,
confidence = bestScenario?.Confidence,
🧰 Tools
🪛 GitHub Check: build

[failure] 174-174:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 173-173:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 172-172:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 174-174:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 173-173:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 172-172:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''

🪛 GitHub Actions: Build and test

[error] 172-172: dotnet build -c Release --no-restore failed: CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''.

🤖 Prompt for AI Agents
In src/Services/MultiScenarioLocator.cs around lines 168 to 175, the ternary
expressions for x, y, z (and similarly latitude/longitude/elevation if hitting
the same CS0173) mix plain double with null causing type inference errors; fix
by casting the double operands to nullable doubles, e.g. change includeLocation
? location.X : null to includeLocation ? (double?)location.X : null (and
likewise for y, z, latitude, longitude, elevation if needed).

DTTerastar pushed a commit that referenced this pull request Nov 17, 2025
…d test coverage

This commit addresses all the feedback from PR #1279 review:

**Compilation Fixes:**
- Fix CS0173 type inference errors in MultiScenarioLocator.cs by casting double values to nullable (double?)
- Properly handle x, y, z coordinate nullable types in JSON serialization

**Logic Improvements:**
- Fix MQTT retain logic for tombstone messages (always use retain:true when clearing topics)
- Add validation clamping for DiscoveryThreshold to ensure 0.0-1.0 range
- Round probability values to 4 decimal places before comparison to reduce publish churn

**Code Quality:**
- Add null-guards in Config.Clone.cs to prevent NullReferenceExceptions
- Use null-conditional operators for all Clone() calls

**Documentation:**
- Clarify threshold range (0.0-1.0) in README and example config
- Document room name normalization to lowercase for MQTT topics
- Add inline comments about configuration ranges

**Test Coverage:**
- Add assertions to verify retain flag is honored on probability topics
- Add assertions to verify discovery configs are published for rooms above threshold
- Add test scenario for untracking devices to verify probability sensors are deleted
- Add assertions to verify tombstone messages use retain:true
@DTTerastar DTTerastar force-pushed the codex/implement-changes-from-issue-#1278 branch from 92fc9b4 to 212205f Compare November 17, 2025 20:09
@DTTerastar DTTerastar had a problem deploying to CI - release environment November 17, 2025 20:09 — with GitHub Actions Failure
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/Services/MultiScenarioLocator.cs (2)

159-180: Fix CS0173 errors by using nullable doubles for GPS/location fields

The attribute payload currently mixes non‑nullable doubles with null in ternaries, which triggers CS0173 and breaks the build (as seen in the pipeline). Similar issues exist for the (lat, lon) tuple and the x/y/z properties.

Use nullable doubles and avoid tuple ternary with (null, null). For example:

-                var gps      = state?.Config?.Gps;
-                var location = device.Location ?? new Point3D();
-
-                // Only include GPS coordinates if reporting is enabled and we have a best scenario
-                var (lat, lon) = includeLocation && gps?.Report == true ? gps.Add(location.X, location.Y) : (null, null);
-                var elevation = includeLocation && gps?.Report == true ? location.Z + gps?.Elevation : null;
+                var gps      = state?.Config?.Gps;
+                var location = device.Location ?? new Point3D();
+
+                double? lat        = null;
+                double? lon        = null;
+                double? elevation  = null;
+
+                // Only include GPS coordinates if reporting is enabled and we have a best scenario
+                if (includeLocation && gps?.Report == true)
+                {
+                    var (rawLat, rawLon) = gps.Add(location.X, location.Y);
+                    lat       = rawLat;
+                    lon       = rawLon;
+                    elevation = location.Z + gps?.Elevation;
+                }
@@
-                    source_type   = includeLocation ? "espresense" : null,
-                    latitude      = includeLocation ? lat : null,
-                    longitude     = includeLocation ? lon : null,
-                    elevation     = includeLocation ? elevation : null,
-                    x             = includeLocation ? location.X : null,
-                    y             = includeLocation ? location.Y : null,
-                    z             = includeLocation ? location.Z : null,
+                    source_type   = includeLocation ? "espresense" : null,
+                    latitude      = lat,
+                    longitude     = lon,
+                    elevation     = elevation,
+                    x             = includeLocation ? (double?)location.X : null,
+                    y             = includeLocation ? (double?)location.Y : null,
+                    z             = includeLocation ? (double?)location.Z : null,

This keeps JSON semantics unchanged (nulls are still omitted via SerializerSettings.NullIgnore) while satisfying the compiler.

Please rerun your CI/build after applying this to ensure all CS0173 instances are resolved.


266-314: Always retain null “tombstone” messages when clearing probability topics

In the stale‑key cleanup loop, null payloads currently use retain: config.Retain:

await mqtt.EnqueueAsync(BuildProbabilityTopic(device.Id, key), null, retain: config.Retain);

If config.Retain is false, this will not clear an already‑retained probability value on the broker; consumers may continue to see stale probabilities.

Align this with ClearProbabilityOutputsAsync (which uses retain: true) so tombstones reliably clear retained state:

-            await mqtt.EnqueueAsync(BuildProbabilityTopic(device.Id, key), null, retain: config.Retain);
+            await mqtt.EnqueueAsync(BuildProbabilityTopic(device.Id, key), null, retain: true);

Retaining the null payload is the standard MQTT pattern for clearing retained topics, independent of the normal “retain” setting for non‑null updates.

Please confirm this matches your intended MQTT semantics, especially for users who flip retain off after running with it enabled.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92fc9b4 and 212205f.

📒 Files selected for processing (9)
  • README.md (2 hunks)
  • src/Models/AutoDiscovery.cs (1 hunks)
  • src/Models/Config.Clone.cs (2 hunks)
  • src/Models/Config.cs (2 hunks)
  • src/Models/Device.cs (2 hunks)
  • src/Services/DeviceTracker.cs (1 hunks)
  • src/Services/MultiScenarioLocator.cs (4 hunks)
  • src/config.example.yaml (1 hunks)
  • tests/MultiScenarioLocatorTests.cs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/MultiScenarioLocatorTests.cs
  • src/Models/AutoDiscovery.cs
  • src/Models/Device.cs
  • src/Models/Config.Clone.cs
  • src/config.example.yaml
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Place backend C# ASP.NET Core code under src/ (controllers, services, models, utils)

Files:

  • src/Services/DeviceTracker.cs
  • src/Services/MultiScenarioLocator.cs
  • src/Models/Config.cs
{src,tests}/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

{src,tests}/**/*.cs: C#: Use spaces with an indent size of 4
C#: Use PascalCase for types and methods
C#: Use camelCase for local variables and parameters

Files:

  • src/Services/DeviceTracker.cs
  • src/Services/MultiScenarioLocator.cs
  • src/Models/Config.cs
🧬 Code graph analysis (3)
src/Services/DeviceTracker.cs (1)
src/Models/Device.cs (1)
  • ResetBayesianState (139-148)
src/Services/MultiScenarioLocator.cs (5)
src/Models/Config.cs (2)
  • Config (8-58)
  • ConfigBayesianProbabilities (146-156)
src/Utils/JsonSerializerSettings.cs (1)
  • SerializerSettings (6-9)
src/Models/Device.cs (3)
  • Device (10-184)
  • Device (22-27)
  • ToString (29-36)
src/Services/MqttCoordinator.cs (12)
  • Task (73-80)
  • Task (82-130)
  • Task (132-219)
  • Task (251-304)
  • Task (306-334)
  • Task (349-406)
  • Task (423-446)
  • Task (457-485)
  • Task (500-542)
  • Task (558-584)
  • Task (599-621)
  • Task (624-649)
src/Models/AutoDiscovery.cs (6)
  • AutoDiscovery (9-156)
  • AutoDiscovery (17-40)
  • AutoDiscovery (42-47)
  • DiscoveryRecord (110-137)
  • DeviceRecord (139-150)
  • OriginRecord (152-155)
src/Models/Config.cs (1)
src/Models/Config.Clone.cs (2)
  • ConfigBayesianProbabilities (120-131)
  • ConfigBayesianProbabilities (122-130)
🪛 GitHub Actions: Build and test
src/Services/MultiScenarioLocator.cs

[error] 172-172: CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''

🪛 GitHub Actions: Deploy to Docker
src/Services/MultiScenarioLocator.cs

[error] 172-174: Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''

🪛 GitHub Check: build
src/Services/MultiScenarioLocator.cs

[failure] 174-174:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 173-173:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 172-172:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 174-174:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 173-173:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''


[failure] 172-172:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (6)
src/Services/DeviceTracker.cs (1)

212-216: Resetting Bayesian state on untrack is a good lifecycle cleanup

Clearing Bayesian discoveries and probabilities when a device becomes untracked avoids stale in‑memory state and keeps HassAutoDiscovery aligned with the MQTT deletions above.

README.md (1)

12-57: Bayesian probability docs match the implementation

The new description, config snippet, MQTT topics, and Home Assistant examples line up with ConfigBayesianProbabilities, the probability topics, and attribute payload shape in code.

src/Models/Config.cs (1)

49-51: ConfigBayesianProbabilities wiring and defaults look consistent

The bayesian_probabilities section, its mapping to ConfigBayesianProbabilities, and the Enabled/DiscoveryThreshold/Retain fields (with aliases and defaults) are consistent with the README and consumer code in MultiScenarioLocator.

If you adjust defaults or field names later, please double‑check Config.Clone.cs and any YAML docs stay in sync with this shape.

Also applies to: 146-156

src/Services/MultiScenarioLocator.cs (3)

7-9: Bayesian smoothing and config gating are well‑structured

The new imports, smoothing constants, and the BayesianProbabilities block (config check → vector → publish/clear) integrate cleanly into ProcessDevice and keep the feature nicely feature‑flagged, with attribute payloads only built when probabilities or state changed.

Also applies to: 30-33, 109-129


211-264: Probability vector construction and topic sanitization look solid

The BuildProbabilityVector logic (room key selection, normalization, other/not_home handling, and sum correction) plus SanitizeSegment/BuildProbabilityTopic give a coherent, stable probability distribution and safe MQTT topic segments. Skipping synthetic rooms via IsSyntheticRoom is a nice touch to avoid noisy entities.

If you later introduce additional synthetic room labels, remember to extend IsSyntheticRoom to keep discovery behavior consistent.

Also applies to: 365-392


316-336: ClearProbabilityOutputsAsync behavior is correct and consistent

The probability clear helper reliably sends retained null “tombstones” for all probability topics and deletes all Bayesian discovery configs, then clears the in‑memory dictionaries. This matches the expected behavior when the feature is disabled or turned off in config.

Once the stale‑key cleanup in PublishProbabilitySensorsAsync is aligned to also use retain: true, the clearing behavior will be consistent across both paths.

DTTerastar pushed a commit that referenced this pull request Nov 17, 2025
…nd test coverage

This commit addresses all the feedback from PR #1279 review:

**Compilation Fixes:**
- Fix CS0173 type inference errors in MultiScenarioLocator.cs by casting double values to nullable (double?)
- Properly handle x, y, z coordinate nullable types in JSON serialization

**Logic Improvements:**
- Fix MQTT retain logic for tombstone messages (always use retain:true when clearing topics)
- Add validation clamping for DiscoveryThreshold to ensure 0.0-1.0 range
- Round probability values to 4 decimal places before comparison to reduce publish churn

**Code Quality:**
- Add null-guards in Config.Clone.cs to prevent NullReferenceExceptions
- Use null-conditional operators for all Clone() calls

**Documentation:**
- Clarify threshold range (0.0-1.0) in README and example config
- Document room name normalization to lowercase for MQTT topics
- Add inline comments about configuration ranges

**Test Coverage:**
- Add assertions to verify retain flag is honored on probability topics
- Add assertions to verify discovery configs are published for rooms above threshold
- Add test scenario for untracking devices to verify probability sensors are deleted
- Add assertions to verify tombstone messages use retain:true
DTTerastar pushed a commit that referenced this pull request Nov 17, 2025
…d test coverage

This commit addresses all the feedback from PR #1279 review:

**Compilation Fixes:**
- Fix CS0173 type inference errors in MultiScenarioLocator.cs by casting double values to nullable (double?)
- Properly handle x, y, z coordinate nullable types in JSON serialization

**Logic Improvements:**
- Fix MQTT retain logic for tombstone messages (always use retain:true when clearing topics)
- Add validation clamping for DiscoveryThreshold to ensure 0.0-1.0 range
- Round probability values to 4 decimal places before comparison to reduce publish churn

**Code Quality:**
- Add null-guards in Config.Clone.cs to prevent NullReferenceExceptions
- Use null-conditional operators for all Clone() calls

**Documentation:**
- Clarify threshold range (0.0-1.0) in README and example config
- Document room name normalization to lowercase for MQTT topics
- Add inline comments about configuration ranges

**Test Coverage:**
- Add assertions to verify retain flag is honored on probability topics
- Add assertions to verify discovery configs are published for rooms above threshold
- Add test scenario for untracking devices to verify probability sensors are deleted
- Add assertions to verify tombstone messages use retain:true
@DTTerastar DTTerastar changed the title feat: publish bayesian room probabilities Codex: publish bayesian room probabilities Nov 17, 2025
@DTTerastar DTTerastar force-pushed the codex/implement-changes-from-issue-#1278 branch from 212205f to 8ca8d2c Compare December 16, 2025 21:09
@DTTerastar DTTerastar had a problem deploying to CI - release environment December 16, 2025 21:09 — with GitHub Actions Failure
DTTerastar pushed a commit that referenced this pull request Dec 16, 2025
…d test coverage

This commit addresses all the feedback from PR #1279 review:

**Compilation Fixes:**
- Fix CS0173 type inference errors in MultiScenarioLocator.cs by casting double values to nullable (double?)
- Properly handle x, y, z coordinate nullable types in JSON serialization

**Logic Improvements:**
- Fix MQTT retain logic for tombstone messages (always use retain:true when clearing topics)
- Add validation clamping for DiscoveryThreshold to ensure 0.0-1.0 range
- Round probability values to 4 decimal places before comparison to reduce publish churn

**Code Quality:**
- Add null-guards in Config.Clone.cs to prevent NullReferenceExceptions
- Use null-conditional operators for all Clone() calls

**Documentation:**
- Clarify threshold range (0.0-1.0) in README and example config
- Document room name normalization to lowercase for MQTT topics
- Add inline comments about configuration ranges

**Test Coverage:**
- Add assertions to verify retain flag is honored on probability topics
- Add assertions to verify discovery configs are published for rooms above threshold
- Add test scenario for untracking devices to verify probability sensors are deleted
- Add assertions to verify tombstone messages use retain:true
@DTTerastar DTTerastar closed this Dec 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants