Claude's Publish Bayesian Room Probabilities - #1345
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR introduces a new Bayesian probability calculation and publishing feature for device location tracking. It adds configuration support, a new service to publish per-room probability sensors to Home Assistant via MQTT, extends data models to track Bayesian state, and integrates probability publishing into the locator service with comprehensive test coverage. Changes
Sequence DiagramsequenceDiagram
participant ML as MultiScenarioLocator
participant BP as BayesianProbabilityPublisher
participant Dev as Device
participant MQTT as MQTT/Home Assistant
ML->>BP: BuildProbabilityVector(device, scenario)
Note over BP: Aggregate scenarios by room<br/>Normalize to [0,1]
BP-->>ML: probabilities dict
alt Bayesian Enabled
ML->>BP: PublishProbabilitySensorsAsync(device, probabilities, config)
BP->>Dev: Update BayesianProbabilities
loop For each room above threshold
BP->>BP: CreateProbabilityDiscovery(room)
BP->>Dev: Add to BayesianDiscoveries
BP->>MQTT: Publish discovery config
end
BP->>MQTT: Publish probability state
BP-->>ML: changes occurred
else Bayesian Disabled
ML->>BP: ClearProbabilityOutputsAsync(device)
BP->>Dev: ResetBayesianState()
BP->>MQTT: Remove discovery entries
end
ML->>MQTT: Publish device attributes with probabilities
Estimated code review effort🎯 4 (Complex) | ⏱️ ~65 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
00238f8 to
93cc908
Compare
93cc908 to
84c3bb6
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/Services/MultiScenarioLocator.cs (3)
306-306: Consider using ProbabilityEpsilon constant for consistency.The hardcoded value
0.0001differs from theProbabilityEpsilonconstant (0.001) defined at line 34. For consistency, consider using the constant or documenting why a different threshold is needed here.Apply this diff if you want to use the existing constant:
- if (remainder > 0.0001) + if (remainder > ProbabilityEpsilon)
339-351: Minor: Else-if clause catches both conditions.The
else ifat line 346 will execute for both synthetic rooms and rooms below the discovery threshold, which is correct but slightly less explicit than separate conditions. The current implementation works correctly sinceTryRemovehandles missing keys gracefully.If you prefer more explicit logic, consider:
- if (!IsSyntheticRoom(roomName) && probability >= config.DiscoveryThreshold) - { - var discovery = device.BayesianDiscoveries.GetOrAdd(roomName, key => CreateProbabilityDiscovery(device, key)); - if (!device.HassAutoDiscovery.Contains(discovery)) - device.HassAutoDiscovery.Add(discovery); - await discovery.Send(mqtt); - } - else if (device.BayesianDiscoveries.TryRemove(roomName, out var staleDiscovery)) - { - device.HassAutoDiscovery.Remove(staleDiscovery); - await staleDiscovery.Delete(mqtt); - changed = true; - } + var shouldHaveDiscovery = !IsSyntheticRoom(roomName) && probability >= config.DiscoveryThreshold; + if (shouldHaveDiscovery) + { + var discovery = device.BayesianDiscoveries.GetOrAdd(roomName, key => CreateProbabilityDiscovery(device, key)); + if (!device.HassAutoDiscovery.Contains(discovery)) + device.HassAutoDiscovery.Add(discovery); + await discovery.Send(mqtt); + } + else if (device.BayesianDiscoveries.TryRemove(roomName, out var staleDiscovery)) + { + device.HassAutoDiscovery.Remove(staleDiscovery); + await staleDiscovery.Delete(mqtt); + changed = true; + }
411-411: Centralize the version constant to avoid hardcoded duplication.
SwVersionis hardcoded as"1.0.0"in this file and also insrc/Models/AutoDiscovery.cs(line 34), with the same version also appearing insrc/Program.cs(line 98). Consider creating a single version constant or leveraging a configuration value to ensure all locations stay synchronized and simplify future version updates.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
README.md(2 hunks)src/Models/AutoDiscovery.cs(1 hunks)src/Models/Config.Clone.cs(4 hunks)src/Models/Config.cs(2 hunks)src/Models/Device.cs(2 hunks)src/Services/DeviceTracker.cs(1 hunks)src/Services/MultiScenarioLocator.cs(5 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/Config.cssrc/Models/AutoDiscovery.cssrc/Services/DeviceTracker.cssrc/config.example.yamlsrc/Services/MultiScenarioLocator.cssrc/Models/Config.Clone.cssrc/Models/Device.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/Config.cssrc/Models/AutoDiscovery.cssrc/Services/DeviceTracker.cssrc/Services/MultiScenarioLocator.cssrc/Models/Config.Clone.cstests/MultiScenarioLocatorTests.cssrc/Models/Device.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
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
🧬 Code graph analysis (6)
src/Models/Config.cs (1)
src/Models/Config.Clone.cs (2)
ConfigBayesianProbabilities(120-131)ConfigBayesianProbabilities(122-130)
src/Services/DeviceTracker.cs (1)
src/Models/Device.cs (1)
ResetBayesianState(152-161)
src/Services/MultiScenarioLocator.cs (3)
src/Models/Config.cs (2)
Config(8-61)ConfigBayesianProbabilities(149-165)src/Models/Device.cs (2)
Device(11-209)Device(23-28)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.Clone.cs (1)
src/Models/Config.cs (1)
ConfigBayesianProbabilities(149-165)
tests/MultiScenarioLocatorTests.cs (2)
src/Models/Room.cs (2)
Room(6-32)ToString(28-31)src/Models/Floor.cs (2)
Floor(7-47)ToString(36-39)
src/Models/Device.cs (2)
src/Services/MultiScenarioLocator.cs (1)
AutoDiscovery(395-420)src/Models/AutoDiscovery.cs (3)
AutoDiscovery(9-156)AutoDiscovery(17-40)AutoDiscovery(42-47)
🪛 GitHub Actions: Build and test
src/Services/MultiScenarioLocator.cs
[error] 228-228: CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''
src/Models/Config.Clone.cs
[warning] 39-39: CS8601: Possible null reference assignment.
[warning] 40-40: CS8601: Possible null reference assignment.
[warning] 41-41: CS8601: Possible null reference assignment.
[warning] 68-68: CS8601: Possible null reference assignment.
[warning] 15-15: CS8601: Possible null reference assignment.
[warning] 16-16: CS8601: Possible null reference assignment.
[warning] 19-19: CS8601: Possible null reference assignment.
[warning] 20-20: CS8601: Possible null reference assignment.
[warning] 21-21: CS8601: Possible null reference assignment.
[warning] 22-22: CS8601: Possible null reference assignment.
[warning] 23-23: CS8601: Possible null reference assignment.
[warning] 24-24: CS8601: Possible null reference assignment.
🪛 GitHub Actions: Deploy to Docker
src/Services/MultiScenarioLocator.cs
[error] 228-230: CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''.
🪛 GitHub Check: build
src/Services/MultiScenarioLocator.cs
[failure] 230-230:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''
[failure] 229-229:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''
[failure] 228-228:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''
[failure] 230-230:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''
[failure] 229-229:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''
[failure] 228-228:
Type of conditional expression cannot be determined because there is no implicit conversion between 'double' and ''
src/Models/Config.Clone.cs
[warning] 41-41:
Possible null reference assignment.
[warning] 40-40:
Possible null reference assignment.
[warning] 39-39:
Possible null reference assignment.
[warning] 68-68:
Possible null reference assignment.
⏰ 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 (23)
src/config.example.yaml (1)
53-57: LGTM! Clear configuration structure.The new
bayesian_probabilitiesconfiguration block follows the established YAML structure and provides clear inline documentation for each field.README.md (1)
23-58: Excellent documentation with practical examples.The documentation clearly explains the feature, configuration options, topic structure, and provides a concrete Home Assistant integration example. This will help users understand and implement the feature effectively.
src/Services/DeviceTracker.cs (1)
246-246: LGTM! Proper cleanup on untrack.The call to
ResetBayesianState()is correctly placed in the untrack path after removingHassAutoDiscoveryentries, ensuring complete cleanup of Bayesian-related state when a device stops being tracked.src/Models/Device.cs (2)
85-90: LGTM! Well-designed state management.The concurrent dictionaries with case-insensitive comparers are appropriate for thread-safe room name lookups. The
JsonIgnoreattributes correctly prevent serialization of internal state.
152-161: LGTM! Correct cleanup implementation.The method properly uses
ToList()to avoid collection modification during enumeration, removes discoveries fromHassAutoDiscovery, and clears both dictionaries. Good defensive programming.src/Models/Config.cs (2)
59-60: LGTM! Consistent configuration structure.The property follows the established pattern for configuration sections and is properly initialized.
149-165: Excellent input validation!The
Math.Clampin theDiscoveryThresholdsetter ensures values are always within the valid [0.0, 1.0] range, preventing invalid configuration from causing runtime issues. The defaults are sensible (feature disabled by default, retain enabled).src/Models/AutoDiscovery.cs (1)
122-130: LGTM! Proper Home Assistant discovery extensions.The new properties correctly follow Home Assistant's MQTT discovery specification with snake_case JSON property names and nullable types for optional fields.
tests/MultiScenarioLocatorTests.cs (2)
22-36: LGTM! Clean test helper implementation.The
FixedRoomLocatoruses modern C# primary constructor syntax and provides deterministic scenario setup for reliable testing.
85-193: Excellent test coverage!This comprehensive test verifies all critical aspects of the Bayesian probability feature:
- Probability topic publishing with correct retain flags
- Home Assistant discovery message creation
- Attributes payload structure
- Cleanup behavior with tombstone messages on untrack
The test setup is thorough and assertions are specific, providing strong confidence in the implementation.
src/Models/Config.Clone.cs (3)
19-28: LGTM! Null-conditional operators are used correctly.The static analysis warnings (CS8601) about "Possible null reference assignment" are false positives. The null-conditional operator
?.correctly handles null cases, and the properties have non-null defaults in their declarations. This is the proper pattern for deep cloning nullable nested configuration objects.
39-41: LGTM! Consistent null-safe cloning pattern.The null-conditional operators follow the same correct pattern as the parent
Config.Clonemethod. The static analysis warnings are false positives.Also applies to: 68-68
120-131: LGTM! Proper shallow clone for value types.The
ConfigBayesianProbabilities.Clonemethod correctly implements shallow copying of value-type properties, consistent with other simple configuration Clone methods in the codebase.src/Services/MultiScenarioLocator.cs (10)
7-9: LGTM: New imports support the Bayesian probability features.The added using directives are all utilized in the new functionality.
34-34: LGTM: Epsilon value is appropriate for probability comparison.The value aligns well with the 4-decimal rounding used in probability calculations.
58-62: LGTM: Correctly clears Bayesian state for anchored devices.Anchored devices have fixed locations, so clearing probability outputs is appropriate. The guard condition avoids unnecessary async operations.
165-184: LGTM: Bayesian probability handling is well-structured.The logic correctly gates probability publishing based on configuration and manages state appropriately for both enabled and disabled scenarios.
206-264: LGTM: Conditional location reporting logic is sound.The approach of publishing attributes when either movement or probability changes occur is correct. Gating location data, GPS coordinates, and device history recording based on
bestScenariopresence ensures consistency and avoids publishing incomplete data.
267-320: LGTM: Probability vector construction is comprehensive.The method correctly handles multiple edge cases including inactive scenarios, zero-sum probabilities, normalization, and floating-point precision issues. The case-insensitive dictionary and defensive null/whitespace handling are appropriate.
322-371: LGTM: Probability sensor publishing logic is thorough.The method correctly manages the full lifecycle of probability sensors: creation, updates, discovery management, and cleanup. The use of epsilon for change detection prevents unnecessary MQTT traffic, and the tombstone pattern (null with retain) properly clears stale data.
373-393: LGTM: Probability cleanup is implemented correctly.The method properly clears all Bayesian state using MQTT tombstones (null with retain) and removes Home Assistant discoveries. The use of
ToArray()prevents collection modification exceptions during enumeration.
395-420: LGTM: Home Assistant discovery configuration is well-formed.The discovery record includes all essential fields for proper Home Assistant integration. The state class "measurement" is appropriate for probability values.
422-449: LGTM: Utility methods are well-implemented.All three helper methods are defensive and handle edge cases appropriately:
BuildProbabilityTopicconstructs proper MQTT topic pathsSanitizeSegmentthoroughly sanitizes room names for use in topics/IDsIsSyntheticRoomprovides clear identification of special room namesThe use of
staticis appropriate for these pure utility functions.
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
tests/MultiScenarioLocatorTests.cs(3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
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,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:
tests/MultiScenarioLocatorTests.cs
⏰ 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). (3)
- GitHub Check: build
- GitHub Check: Deploy to Docker add-on with tag
- GitHub Check: Analyze (csharp)
🔇 Additional comments (1)
tests/MultiScenarioLocatorTests.cs (1)
22-36: LGTM! Well-designed test helper.The
FixedRoomLocatorclass provides deterministic room assignment for testing Bayesian probability computation. Using a primary constructor and readonly fields is idiomatic modern C#, and the implementation correctly updates all scenario properties needed for the test.
a071bf2 to
e8a6dc8
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
tests/MultiScenarioLocatorTests.cs (1)
11-11: Duplicate using directive remains unresolved.This duplicate
using MathNet.Spatial.Euclidean;(also present on line 9) was flagged in a previous review and should be removed.🔎 Proposed fix
-using MathNet.Spatial.Euclidean;
🧹 Nitpick comments (2)
src/Services/MultiScenarioLocator.cs (2)
267-320: Consider removing redundant clamp operation.The normalization logic divides each probability by the sum of all probabilities (line 298), which guarantees the result is in the range [0, 1] for positive inputs. The
Math.Clamp(normalized, 0, 1)on line 299 is therefore redundant, though harmless.🔎 Proposed refactor
foreach (var key in result.Keys.ToList()) { var normalized = result[key] / sum; - result[key] = Math.Clamp(normalized, 0, 1); + result[key] = normalized; }
339-345: Consider simplifying the discovery management pattern.The
GetOrAddfollowed byContainscheck andAddworks correctly, but since discoveries are created once and reused, you could track whether it's newly created to avoid the redundantContainscheck.🔎 Proposed refactor
- if (!IsSyntheticRoom(roomName) && probability >= config.DiscoveryThreshold) - { - var discovery = device.BayesianDiscoveries.GetOrAdd(roomName, key => CreateProbabilityDiscovery(device, key)); - if (!device.HassAutoDiscovery.Contains(discovery)) - device.HassAutoDiscovery.Add(discovery); - await discovery.Send(mqtt); - } + if (!IsSyntheticRoom(roomName) && probability >= config.DiscoveryThreshold) + { + var isNew = !device.BayesianDiscoveries.ContainsKey(roomName); + var discovery = device.BayesianDiscoveries.GetOrAdd(roomName, key => CreateProbabilityDiscovery(device, key)); + if (isNew) + device.HassAutoDiscovery.Add(discovery); + await discovery.Send(mqtt); + }
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
README.mdsrc/Models/AutoDiscovery.cssrc/Models/Config.Clone.cssrc/Models/Config.cssrc/Models/Device.cssrc/Services/DeviceTracker.cssrc/Services/MultiScenarioLocator.cssrc/config.example.yamltests/MultiScenarioLocatorTests.cs
🚧 Files skipped from review as they are similar to previous changes (3)
- src/config.example.yaml
- src/Models/AutoDiscovery.cs
- README.md
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Place backend C# ASP.NET Core code under src/ (controllers, services, models, utils)
Files:
src/Services/DeviceTracker.cssrc/Services/MultiScenarioLocator.cssrc/Models/Config.cssrc/Models/Config.Clone.cssrc/Models/Device.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.cssrc/Services/MultiScenarioLocator.cssrc/Models/Config.cstests/MultiScenarioLocatorTests.cssrc/Models/Config.Clone.cssrc/Models/Device.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
🧬 Code graph analysis (5)
src/Services/DeviceTracker.cs (1)
src/Models/Device.cs (1)
ResetBayesianState(152-161)
src/Services/MultiScenarioLocator.cs (3)
src/Models/Config.cs (2)
Config(8-61)ConfigBayesianProbabilities(149-165)src/Models/Device.cs (2)
Device(11-209)Device(23-28)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)
src/Models/Config.Clone.cs (1)
src/Models/Config.cs (1)
ConfigBayesianProbabilities(149-165)
src/Models/Device.cs (2)
src/Services/MultiScenarioLocator.cs (1)
AutoDiscovery(395-420)src/Models/AutoDiscovery.cs (3)
AutoDiscovery(9-156)AutoDiscovery(17-40)AutoDiscovery(42-47)
⏰ 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). (3)
- GitHub Check: build
- GitHub Check: Analyze (csharp)
- GitHub Check: Deploy to Docker add-on with tag
🔇 Additional comments (17)
src/Services/DeviceTracker.cs (1)
246-246: LGTM! Bayesian state cleanup is correctly integrated.The call to
ResetBayesianState()is appropriately placed after removing HassAutoDiscovery entries when a device is untracked, ensuring all Bayesian-related resources are cleaned up.src/Models/Config.cs (2)
59-60: LGTM! Property addition is consistent with existing patterns.The new
BayesianProbabilitiesproperty follows the same pattern as other configuration sections, with proper YAML aliasing and initialization.
149-165: LGTM! Well-implemented configuration class with proper validation.The
ConfigBayesianProbabilitiesclass correctly implements:
- Clamping of
DiscoveryThresholdto the valid probability range [0.0, 1.0]- Sensible defaults (enabled=false, threshold=0.1, retain=true)
- Proper YAML serialization attributes
src/Models/Config.Clone.cs (4)
19-28: LGTM! Null-safe cloning improvements are well-implemented.The addition of null-conditional operators across all property clones prevents potential
NullReferenceExceptionissues. The newBayesianProbabilities?.Clone()follows the established pattern consistently.
39-41: LGTM! Null-safe cloning for nested locator components.The null-conditional operators ensure safe cloning of locator configurations when they may be absent.
68-68: LGTM! Null-safe Weighting clone.Consistent with the null-safety improvements applied throughout this file.
120-131: LGTM! Clone implementation correctly copies all properties.The
ConfigBayesianProbabilities.Clone()method properly duplicates all three properties (Enabled,DiscoveryThreshold,Retain), matching the class definition inConfig.cs.src/Models/Device.cs (2)
85-89: LGTM! Thread-safe Bayesian state storage.The new properties use
ConcurrentDictionarywith case-insensitive comparers, which is:
- Consistent with the existing
Nodesproperty pattern (line 51)- Thread-safe for concurrent access
- Properly marked with
[STJ.JsonIgnore]to exclude from serialization
152-161: LGTM! Safe cleanup implementation with proper ordering.The
ResetBayesianState()method correctly:
- Calls
.ToList()before iteration to prevent collection modification exceptions- Removes discoveries from
HassAutoDiscoverybefore clearing local dictionaries- Ensures complete cleanup of Bayesian-related state
tests/MultiScenarioLocatorTests.cs (2)
22-36: LGTM! Clean and focused test helper.The
FixedRoomLocatorclass provides a deterministic way to assign room/floor/confidence in tests, making the Bayesian probability tests predictable and maintainable.
85-204: LGTM! Comprehensive test validates complete Bayesian probability workflow.The
BayesianProbabilitiesPublishedWhenEnabledtest thoroughly validates:
- ✅ Probability message publishing with non-empty payloads
- ✅ Retain flag behavior on probability topics
- ✅ Discovery message generation for rooms above threshold
- ✅ Attributes payload structure with probability data
- ✅ Proper cleanup (tombstone messages) when device is untracked
The test uses proper assertions with descriptive messages and validates both the happy path and cleanup behavior.
src/Services/MultiScenarioLocator.cs (6)
58-62: LGTM!Properly clears Bayesian state for anchored devices, ensuring no stale probability data persists.
165-184: LGTM!Clean integration of Bayesian probability logic with proper handling of enabled/disabled configuration states.
206-264: LGTM!The conditional payload construction properly handles both location-based updates and probability-only updates. The null-conditional logic ensures that location data is only included when a best scenario exists, while probability data can be published independently.
Note: The compilation error from the previous review (lines 228-230) has been correctly addressed with
(double?)casts.
373-393: LGTM!Properly clears all Bayesian probability outputs by publishing tombstone messages and cleaning up discovery entities.
395-420: LGTM!Properly constructs Home Assistant auto-discovery entities for probability sensors with appropriate device class and icon.
422-449: LGTM!Well-implemented helper methods with proper defensive coding and case-insensitive string handling for room names.
8c5a295 to
299c7b8
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/Program.cssrc/Services/BayesianProbabilityPublisher.cssrc/Services/MultiScenarioLocator.cstests/FilteringTests.cstests/MultiScenarioLocatorTests.cs
🚧 Files skipped from review as they are similar to previous changes (2)
- tests/MultiScenarioLocatorTests.cs
- src/Program.cs
🧰 Additional context used
📓 Path-based instructions (4)
tests/**/*.cs
📄 CodeRabbit inference engine (AGENTS.md)
Place backend NUnit tests under tests/
Files:
tests/FilteringTests.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/FilteringTests.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:
tests/FilteringTests.cssrc/Services/BayesianProbabilityPublisher.cssrc/Services/MultiScenarioLocator.cs
src/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Place backend C# ASP.NET Core code under src/ (controllers, services, models, utils)
Files:
src/Services/BayesianProbabilityPublisher.cssrc/Services/MultiScenarioLocator.cs
🧬 Code graph analysis (3)
tests/FilteringTests.cs (2)
src/Services/BayesianProbabilityPublisher.cs (2)
BayesianProbabilityPublisher(12-232)BayesianProbabilityPublisher(19-22)src/Services/MultiScenarioLocator.cs (1)
MultiScenarioLocator(23-333)
src/Services/BayesianProbabilityPublisher.cs (2)
src/Models/Scenario.cs (1)
Scenario(8-59)src/Models/AutoDiscovery.cs (3)
DiscoveryRecord(110-137)DeviceRecord(139-150)OriginRecord(152-155)
src/Services/MultiScenarioLocator.cs (2)
src/Services/BayesianProbabilityPublisher.cs (3)
BayesianProbabilityPublisher(12-232)BayesianProbabilityPublisher(19-22)Dictionary(28-81)src/Events/GlobalEventDispatcher.cs (1)
OnDeviceChanged(20-23)
⏰ 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). (3)
- GitHub Check: Deploy to Docker add-on with tag
- GitHub Check: build
- GitHub Check: Analyze (csharp)
🔇 Additional comments (9)
tests/FilteringTests.cs (1)
108-109: LGTM! Test setup correctly updated.The BayesianProbabilityPublisher is properly instantiated with the mocked MqttCoordinator and passed to the MultiScenarioLocator constructor, aligning with the expanded constructor signature.
src/Services/BayesianProbabilityPublisher.cs (4)
28-81: LGTM! Robust probability normalization logic.The method correctly handles edge cases:
- Returns
not_home= 1 when no active scenarios exist- Uses a reasonable fallback chain (Room → Floor → Scenario → "unknown")
- Normalizes probabilities to sum to 1.0
- Distributes remainder to an "other" bucket to ensure exact sum of 1.0
- Handles floating-point precision issues with re-normalization
87-143: LGTM! Well-designed hysteresis prevents sensor flapping.The publishing logic correctly:
- Detects and publishes changed probability values (rounded to 4 decimals)
- Implements hysteresis (create at threshold, remove at 80% threshold) to prevent rapid discovery add/remove cycles
- Filters synthetic rooms ("other", "not_home") from Home Assistant discovery
- Cleans up removed rooms by publishing null with retain flag and deleting discovery entries
149-169: LGTM! Comprehensive cleanup implementation.The method properly clears all probability state by:
- Publishing null with retain flag to remove MQTT topics
- Removing entries from device state collections
- Deleting all Home Assistant discovery entries
171-231: LGTM! Helper methods are well-implemented.The helper methods provide robust support for:
- Creating properly structured Home Assistant discovery records with device metadata
- Building sanitized MQTT topic paths using snake_case conversion and character filtering
- Identifying synthetic rooms to exclude from discovery (well-documented rationale)
src/Services/MultiScenarioLocator.cs (4)
28-29: LGTM! Constructor signature properly expanded.The BayesianProbabilityPublisher dependency is correctly added to the constructor using primary constructor syntax, enabling dependency injection of the new service.
55-59: LGTM! Proper cleanup for anchored devices.Bayesian probability state is correctly cleared when a device is anchored, preventing stale probability data. The conditional check avoids unnecessary cleanup calls when the state is already empty.
162-181: LGTM! Bayesian integration follows clean enable/disable pattern.The probability publishing logic correctly:
- Builds and publishes probability vectors when the feature is enabled
- Stores rounded probabilities for inclusion in MQTT attribute payloads
- Clears all probability state when the feature is disabled
- Tracks changes to trigger attribute updates
204-266: LGTM! Conditional publishing logic handles edge cases correctly.The modified publishing logic properly:
- Publishes attributes when either location changes or probabilities change
- Uses
includeLocationflag to conditionally include location data based onbestScenariopresence- Properly casts coordinates to nullable doubles (lines 230-232) to avoid compilation errors
- Includes probability data in the attributes payload
- Only triggers events and history recording when actual location data exists
- Documents the behavior with clear inline comments
299c7b8 to
204d407
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
tests/MultiScenarioLocatorTests.cs (1)
9-11: Remove duplicate import statement.The
using MathNet.Spatial.Euclidean;directive appears on both lines 9 and 11.using Moq; using Newtonsoft.Json.Linq; using MathNet.Spatial.Euclidean; using SQLite; -using MathNet.Spatial.Euclidean;
🧹 Nitpick comments (1)
src/Services/BayesianProbabilityPublisher.cs (1)
172-197: Consider using dynamic version for discovery metadata.The
SwVersion = "1.0.0"is hardcoded. For better maintainability, consider using the assembly version or a shared constant that can be updated centrally.🔎 Suggested improvement
+ private static readonly string AssemblyVersion = + typeof(BayesianProbabilityPublisher).Assembly.GetName().Version?.ToString() ?? "1.0.0"; + private AutoDiscovery CreateProbabilityDiscovery(Device device, string roomName) { // ... Device = new AutoDiscovery.DeviceRecord { // ... - SwVersion = "1.0.0", + SwVersion = AssemblyVersion, // ... },
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/Program.cssrc/Services/BayesianProbabilityPublisher.cssrc/Services/MultiScenarioLocator.cstests/FilteringTests.cstests/MultiScenarioLocatorTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/FilteringTests.cs
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Place backend C# ASP.NET Core code under src/ (controllers, services, models, utils)
Files:
src/Program.cssrc/Services/BayesianProbabilityPublisher.cssrc/Services/MultiScenarioLocator.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/Program.cssrc/Services/BayesianProbabilityPublisher.cssrc/Services/MultiScenarioLocator.cstests/MultiScenarioLocatorTests.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
🧬 Code graph analysis (3)
src/Program.cs (1)
src/Services/BayesianProbabilityPublisher.cs (2)
BayesianProbabilityPublisher(12-233)BayesianProbabilityPublisher(19-22)
src/Services/BayesianProbabilityPublisher.cs (2)
src/Models/Scenario.cs (1)
Scenario(8-59)src/Models/AutoDiscovery.cs (3)
DiscoveryRecord(110-137)DeviceRecord(139-150)OriginRecord(152-155)
src/Services/MultiScenarioLocator.cs (3)
src/Services/BayesianProbabilityPublisher.cs (3)
BayesianProbabilityPublisher(12-233)BayesianProbabilityPublisher(19-22)Dictionary(28-81)src/Utils/JsonSerializerSettings.cs (1)
SerializerSettings(6-9)src/Events/GlobalEventDispatcher.cs (1)
OnDeviceChanged(20-23)
⏰ 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). (3)
- GitHub Check: Deploy to Docker add-on with tag
- GitHub Check: build
- GitHub Check: Analyze (csharp)
🔇 Additional comments (15)
src/Program.cs (1)
78-78: LGTM!The singleton registration follows the established pattern and is correctly placed after
MqttCoordinator(its dependency) in the DI container setup.tests/MultiScenarioLocatorTests.cs (4)
22-36: LGTM!The
FixedRoomLocatorhelper is well-designed for deterministic test scenarios. It correctly handles nullableFixeswith null coalescing and sets all required scenario properties.
59-61: LGTM!The test correctly integrates the new
BayesianProbabilityPublisherdependency while maintaining its original purpose of verifying the "not_home" state transition.
86-206: Comprehensive test coverage for Bayesian probability publishing.This test thoroughly validates the Bayesian probability workflow including:
- Probability topic publishing with correct retain flags
- Home Assistant discovery message creation
- Probability attributes in the payload
- Tombstone cleanup when untracking devices
The use of
Guid.NewGuid()for the work directory (line 89) is a good practice to avoid test isolation issues.
229-230: LGTM!Correctly updated to include the new
BayesianProbabilityPublisherdependency.src/Services/BayesianProbabilityPublisher.cs (6)
14-22: LGTM!Constants are well-documented and the hysteresis ratio (0.8) is a reasonable choice to prevent sensor flapping. The constructor follows standard DI patterns.
28-81: LGTM!The probability vector building logic handles edge cases well:
- No active scenarios →
not_home- Zero sum →
other- Proper normalization with remainder allocation to "other" bucket
The case-insensitive dictionary is appropriate for room name aggregation.
92-103: LGTM - Previous concern addressed.The direct equality comparison at line 96 (
Math.Round(existing, 4) != roundedProbability) correctly resolves the earlier epsilon precision issue. Both values are rounded to 4 decimals before comparison, ensuring all changes are detected.
105-124: LGTM!The hysteresis logic effectively prevents sensor flapping by using different thresholds for creation vs. removal. The synthetic room exclusion keeps the Home Assistant UI clean.
150-170: LGTM!Proper cleanup implementation using MQTT tombstone pattern (null payload with retain). The
ToArray()call correctly handles modification during iteration of the concurrent dictionary.
199-232: LGTM!The helper methods are well-implemented:
SanitizeSegmentprovides robust sanitization with appropriate fallbacksIsSyntheticRoomcorrectly identifies synthetic rooms excluded from discovery- The XML documentation on
IsSyntheticRoomhelpfully explains the design decision about HA sensor sums.src/Services/MultiScenarioLocator.cs (4)
28-29: LGTM!The
BayesianProbabilityPublisherdependency is appropriately added to the primary constructor, following the existing pattern.
55-59: LGTM!Good optimization to check for existing outputs before calling the async cleanup method. This ensures anchored devices have clean Bayesian state.
162-181: LGTM!Clean integration of Bayesian probability publishing:
- Properly gated by configuration
- Tracks changes for attribute publication decisions
- Cleans up outputs when feature is disabled (important for configuration changes at runtime)
204-266: LGTM - Previous compilation error addressed.The conditional attribute publishing logic is well-designed:
- Allows probability-only updates when no location exists (lines 208, 226-232)
- The
(double?)casts on lines 230-232 correctly resolve the previous compilation error- History recording is appropriately gated by
includeLocationto only log real location updatesSerializerSettings.NullIgnoreensures clean JSON output without null fields
204d407 to
eccda05
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
tests/MultiScenarioLocatorTests.cs (1)
9-11: Remove duplicate import statement.The
using MathNet.Spatial.Euclidean;directive appears on both lines 9 and 11.🔎 Proposed fix
using Moq; using Newtonsoft.Json.Linq; using MathNet.Spatial.Euclidean; using SQLite; -using MathNet.Spatial.Euclidean;
🧹 Nitpick comments (1)
src/Services/BayesianProbabilityPublisher.cs (1)
183-190: Consider extracting hardcoded version string.The
SwVersion = "1.0.0"is hardcoded. For consistency and maintainability, consider deriving this from the assembly version or a shared constant.🔎 Proposed improvement
+ private static readonly string SwVersion = typeof(BayesianProbabilityPublisher).Assembly + .GetName().Version?.ToString() ?? "1.0.0"; + private AutoDiscovery CreateProbabilityDiscovery(Device device, string roomName) { // ... Device = new AutoDiscovery.DeviceRecord { Name = device.Name ?? device.Id, Manufacturer = "ESPresense", Model = "Companion", - SwVersion = "1.0.0", + SwVersion = SwVersion, Identifiers = new[] { $"espresense-{device.Id}" } },
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
README.mdsrc/Models/AutoDiscovery.cssrc/Models/Config.Clone.cssrc/Models/Config.cssrc/Models/Device.cssrc/Program.cssrc/Services/BayesianProbabilityPublisher.cssrc/Services/DeviceTracker.cssrc/Services/MultiScenarioLocator.cssrc/config.example.yamltests/FilteringTests.cstests/MultiScenarioLocatorTests.cs
🚧 Files skipped from review as they are similar to previous changes (5)
- tests/FilteringTests.cs
- src/Models/Config.cs
- README.md
- src/config.example.yaml
- src/Services/DeviceTracker.cs
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Place backend C# ASP.NET Core code under src/ (controllers, services, models, utils)
Files:
src/Models/Config.Clone.cssrc/Models/AutoDiscovery.cssrc/Program.cssrc/Services/MultiScenarioLocator.cssrc/Services/BayesianProbabilityPublisher.cssrc/Models/Device.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/Config.Clone.cssrc/Models/AutoDiscovery.cstests/MultiScenarioLocatorTests.cssrc/Program.cssrc/Services/MultiScenarioLocator.cssrc/Services/BayesianProbabilityPublisher.cssrc/Models/Device.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
🧬 Code graph analysis (4)
src/Models/Config.Clone.cs (1)
src/Models/Config.cs (1)
ConfigBayesianProbabilities(149-165)
src/Program.cs (1)
src/Services/BayesianProbabilityPublisher.cs (2)
BayesianProbabilityPublisher(12-233)BayesianProbabilityPublisher(19-22)
src/Services/BayesianProbabilityPublisher.cs (2)
src/Models/Scenario.cs (1)
Scenario(8-59)src/Models/AutoDiscovery.cs (3)
DiscoveryRecord(110-137)DeviceRecord(139-150)OriginRecord(152-155)
src/Models/Device.cs (1)
src/Models/AutoDiscovery.cs (3)
AutoDiscovery(9-156)AutoDiscovery(17-40)AutoDiscovery(42-47)
⏰ 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). (3)
- GitHub Check: Deploy to Docker add-on with tag
- GitHub Check: build
- GitHub Check: Analyze (csharp)
🔇 Additional comments (19)
src/Models/Config.Clone.cs (2)
19-28: LGTM! Defensive null-conditional cloning pattern.The switch to null-conditional cloning (
?.Clone()) across all properties prevents potentialNullReferenceExceptionissues and properly integrates the newBayesianProbabilitiesconfiguration into the clone flow.
120-131: LGTM! Clone implementation is correct and consistent.The
ConfigBayesianProbabilities.Clone()method properly copies all properties and follows the established pattern used by other configuration Clone methods in this file.src/Models/AutoDiscovery.cs (1)
122-130: LGTM! Standard Home Assistant discovery attributes.The added properties (
DeviceClass,StateClass,UnitOfMeasurement,ValueTemplate,Icon) are standard Home Assistant auto-discovery attributes, properly declared as nullable strings with correct JSON serialization attributes.src/Program.cs (1)
78-78: LGTM! Proper DI registration.The
BayesianProbabilityPublisheris correctly registered as a singleton and positioned before the hosted services that depend on it.src/Models/Device.cs (2)
85-89: LGTM! Thread-safe dictionaries with proper configuration.The
BayesianProbabilitiesandBayesianDiscoveriesdictionaries useConcurrentDictionaryfor thread safety andStringComparer.OrdinalIgnoreCasefor case-insensitive room name matching, which is appropriate for this use case.
152-161: LGTM! Proper cleanup of Bayesian state.The
ResetBayesianState()method correctly:
- Uses
ToList()to avoid collection modification during enumeration- Removes discoveries from
HassAutoDiscoverybefore clearing- Clears both dictionaries to fully reset the Bayesian state
tests/MultiScenarioLocatorTests.cs (4)
22-36: LGTM! Well-designed test helper.The
FixedRoomLocatorclass provides deterministic localization behavior for testing Bayesian probability outputs. The use of primary constructor syntax (C# 12) is concise and appropriate for a test helper.
59-61: LGTM! Test updated for new dependency.The test properly instantiates and injects
BayesianProbabilityPublisherto match the updatedMultiScenarioLocatorconstructor signature.
86-206: LGTM! Comprehensive test coverage.The
BayesianProbabilitiesPublishedWhenEnabledtest thoroughly validates:
- Probability topic publishing with correct payloads
- Retain flag behavior (
retain=truefor all probability messages)- Discovery message creation for rooms above threshold
- Attributes message structure and probability values
- Cleanup behavior on untracking (tombstone messages and discovery deletion)
The test setup is extensive but appropriate for integration testing of the Bayesian probability publishing flow.
229-230: LGTM! Test updated for new dependency.Consistent with the other test updates, properly injects
BayesianProbabilityPublisherto match the updated constructor signature.src/Services/BayesianProbabilityPublisher.cs (5)
1-22: LGTM!The class structure, constants, and constructor are well-designed. The
NormalizationEpsilonandDiscoveryHysteresisRatioconstants are appropriately documented, and the dependency injection pattern is correctly implemented.
28-81: LGTM!The
BuildProbabilityVectormethod handles edge cases well: empty scenarios fallback to "not_home", zero-sum protection returns "other", and the normalization logic correctly handles both under-sum (adding "other") and over-sum (re-normalizing) cases.
92-124: LGTM!The publishing logic is well-implemented:
- Direct equality comparison for 4-decimal rounded values (addressing the previous epsilon concern)
- Hysteresis pattern (80% threshold for removal) prevents sensor flapping
- Proper guard against duplicate discovery registrations at lines 112-113
150-170: LGTM!The cleanup method correctly handles collection modification by using
ToArray(), properly tombstones retained MQTT messages, and cleans up both probability state and discovery entities.
205-233: LGTM!The helper methods are well-implemented:
SanitizeSegmentproperly handles edge cases (null, whitespace, invalid chars) and produces consistent MQTT-safe topic segmentsIsSyntheticRoomis clearly documented explaining why synthetic rooms are excluded from HA discoverysrc/Services/MultiScenarioLocator.cs (4)
23-30: LGTM!The constructor correctly adds the
BayesianProbabilityPublisherdependency following the existing primary constructor pattern.
55-59: LGTM!Properly clears Bayesian state for anchored devices, with an efficient guard to avoid unnecessary async operations when there's no state to clear.
162-181: LGTM!The Bayesian probability integration is well-structured:
- Conditional execution based on configuration
- Probability vector is built and published only when enabled
- Cleanup is triggered when the feature is disabled, preventing stale data
204-266: LGTM!The attribute publication logic is well-designed:
- Correctly separates location-dependent from probability-only updates
- The
includeLocationflag properly gates GPS coordinates, device events, and history entries- The nullable double cast issue from previous reviews is addressed at lines 230-232
- Probabilities are included in the attributes payload for Home Assistant integration
eccda05 to
07441cd
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@README.md`:
- Around line 44-58: The example uses invalid Bayesian sensor syntax and a wrong
entity type: replace the top-level "sensor" with "binary_sensor" and add a
probability_threshold; in each observation change "probability" to the required
"prob_given_true" and "prob_given_false" fields and make the observation
value_template return a boolean (e.g., compare the probability sensor with >
0.5) instead of returning a raw float; finally update the automation example to
trigger on the created binary_sensor's state ("binary_sensor.pat_in_kitchen" is
"on"/"off") or, if you need numeric comparisons, use the original probability
sensor with a numeric_state condition.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Services/MultiScenarioLocator.cs (1)
178-208:⚠️ Potential issue | 🟡 MinorDisabling probabilities can leave retained attributes stale in not_home.
When Bayesian probabilities are disabled and
bestScenariois null, no attributes are republished, so retained payloads can still include old probabilities. Consider forcing a publish with an emptyprobabilitiespayload when a clear occurred.🔧 Proposed fix
else { probabilityChanged = await bayesianPublisher.ClearProbabilityOutputsAsync(device); + if (probabilityChanged && bestScenario == null) + probabilityAttributes = new Dictionary<string, double>(); }
🤖 Fix all issues with AI agents
In `@src/Services/BayesianProbabilityPublisher.cs`:
- Around line 91-110: The discovery check uses the unrounded probability causing
inconsistencies with the rounded payloads; update the condition in the foreach
inside BayesianProbabilityPublisher (the loop that computes roundedProbability)
to compare roundedProbability (or a value rounded to the same 4-decimal
precision) against config.DiscoveryThreshold instead of the raw probability, so
IsSyntheticRoom(... ) && roundedProbability >= config.DiscoveryThreshold
triggers discovery and keeps payload and discovery logic consistent.
In `@src/Services/MultiScenarioLocator.cs`:
- Around line 171-175: probabilityVector keys are being sanitized via
BayesianProbabilityPublisher.SanitizeSegment before building
probabilityAttributes with ToDictionary, which throws if distinct original keys
collide after sanitization; change the construction of probabilityAttributes to
first group probabilityVector by the sanitized key (use
BayesianProbabilityPublisher.SanitizeSegment for grouping) and then coalesce
each group's values (e.g., sum or otherwise aggregate the probabilities) and
round the result (Math.Round(..., 4)) so duplicate sanitized keys do not cause
exceptions.
| foreach (var (roomName, probability) in probabilities) | ||
| { | ||
| var roundedProbability = Math.Round(probability, 4); | ||
| // Direct equality comparison since both values are rounded to 4 decimals | ||
| var payloadChanged = !device.BayesianProbabilities.TryGetValue(roomName, out var existing) || Math.Round(existing, 4) != roundedProbability; | ||
| if (payloadChanged) | ||
| { | ||
| device.BayesianProbabilities[roomName] = roundedProbability; | ||
| changed = true; | ||
| } | ||
|
|
||
| var hasDiscovery = device.BayesianDiscoveries.ContainsKey(roomName); | ||
|
|
||
| if (!IsSyntheticRoom(roomName) && probability >= config.DiscoveryThreshold) | ||
| { | ||
| var discovery = device.BayesianDiscoveries.GetOrAdd(roomName, key => CreateProbabilityDiscovery(device, key)); | ||
| if (!device.HassAutoDiscovery.Contains(discovery)) | ||
| device.HassAutoDiscovery.Add(discovery); | ||
| await discovery.Send(_mqtt); | ||
| } |
There was a problem hiding this comment.
Align discovery threshold with rounded values.
Discovery uses the raw probability while payloads use rounded values, which can omit sensors that appear to meet the threshold. Consider comparing against the rounded value for consistency.
🔧 Proposed fix
- if (!IsSyntheticRoom(roomName) && probability >= config.DiscoveryThreshold)
+ if (!IsSyntheticRoom(roomName) && roundedProbability >= config.DiscoveryThreshold)📝 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.
| foreach (var (roomName, probability) in probabilities) | |
| { | |
| var roundedProbability = Math.Round(probability, 4); | |
| // Direct equality comparison since both values are rounded to 4 decimals | |
| var payloadChanged = !device.BayesianProbabilities.TryGetValue(roomName, out var existing) || Math.Round(existing, 4) != roundedProbability; | |
| if (payloadChanged) | |
| { | |
| device.BayesianProbabilities[roomName] = roundedProbability; | |
| changed = true; | |
| } | |
| var hasDiscovery = device.BayesianDiscoveries.ContainsKey(roomName); | |
| if (!IsSyntheticRoom(roomName) && probability >= config.DiscoveryThreshold) | |
| { | |
| var discovery = device.BayesianDiscoveries.GetOrAdd(roomName, key => CreateProbabilityDiscovery(device, key)); | |
| if (!device.HassAutoDiscovery.Contains(discovery)) | |
| device.HassAutoDiscovery.Add(discovery); | |
| await discovery.Send(_mqtt); | |
| } | |
| foreach (var (roomName, probability) in probabilities) | |
| { | |
| var roundedProbability = Math.Round(probability, 4); | |
| // Direct equality comparison since both values are rounded to 4 decimals | |
| var payloadChanged = !device.BayesianProbabilities.TryGetValue(roomName, out var existing) || Math.Round(existing, 4) != roundedProbability; | |
| if (payloadChanged) | |
| { | |
| device.BayesianProbabilities[roomName] = roundedProbability; | |
| changed = true; | |
| } | |
| var hasDiscovery = device.BayesianDiscoveries.ContainsKey(roomName); | |
| if (!IsSyntheticRoom(roomName) && roundedProbability >= config.DiscoveryThreshold) | |
| { | |
| var discovery = device.BayesianDiscoveries.GetOrAdd(roomName, key => CreateProbabilityDiscovery(device, key)); | |
| if (!device.HassAutoDiscovery.Contains(discovery)) | |
| device.HassAutoDiscovery.Add(discovery); | |
| await discovery.Send(_mqtt); | |
| } |
🤖 Prompt for AI Agents
In `@src/Services/BayesianProbabilityPublisher.cs` around lines 91 - 110, The
discovery check uses the unrounded probability causing inconsistencies with the
rounded payloads; update the condition in the foreach inside
BayesianProbabilityPublisher (the loop that computes roundedProbability) to
compare roundedProbability (or a value rounded to the same 4-decimal precision)
against config.DiscoveryThreshold instead of the raw probability, so
IsSyntheticRoom(... ) && roundedProbability >= config.DiscoveryThreshold
triggers discovery and keeps payload and discovery logic consistent.
| if (probabilityVector.Count > 0) | ||
| { | ||
| probabilityAttributes = probabilityVector.ToDictionary( | ||
| kvp => BayesianProbabilityPublisher.SanitizeSegment(kvp.Key), | ||
| kvp => Math.Round(kvp.Value, 4)); |
There was a problem hiding this comment.
Sanitized-key collisions can throw when building probability attributes.
Distinct room names can sanitize to the same key (e.g., spaces vs dashes), which makes ToDictionary throw and can break processing. Consider grouping by sanitized key (or pre-sanitizing upstream) to coalesce duplicates safely.
🛠️ Proposed fix
- probabilityAttributes = probabilityVector.ToDictionary(
- kvp => BayesianProbabilityPublisher.SanitizeSegment(kvp.Key),
- kvp => Math.Round(kvp.Value, 4));
+ probabilityAttributes = probabilityVector
+ .GroupBy(kvp => BayesianProbabilityPublisher.SanitizeSegment(kvp.Key), StringComparer.OrdinalIgnoreCase)
+ .ToDictionary(
+ g => g.Key,
+ g => Math.Round(g.Sum(x => x.Value), 4),
+ StringComparer.OrdinalIgnoreCase);📝 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.
| if (probabilityVector.Count > 0) | |
| { | |
| probabilityAttributes = probabilityVector.ToDictionary( | |
| kvp => BayesianProbabilityPublisher.SanitizeSegment(kvp.Key), | |
| kvp => Math.Round(kvp.Value, 4)); | |
| if (probabilityVector.Count > 0) | |
| { | |
| probabilityAttributes = probabilityVector | |
| .GroupBy(kvp => BayesianProbabilityPublisher.SanitizeSegment(kvp.Key), StringComparer.OrdinalIgnoreCase) | |
| .ToDictionary( | |
| g => g.Key, | |
| g => Math.Round(g.Sum(x => x.Value), 4), | |
| StringComparer.OrdinalIgnoreCase); |
🤖 Prompt for AI Agents
In `@src/Services/MultiScenarioLocator.cs` around lines 171 - 175,
probabilityVector keys are being sanitized via
BayesianProbabilityPublisher.SanitizeSegment before building
probabilityAttributes with ToDictionary, which throws if distinct original keys
collide after sanitization; change the construction of probabilityAttributes to
first group probabilityVector by the sanitized key (use
BayesianProbabilityPublisher.SanitizeSegment for grouping) and then coalesce
each group's values (e.g., sum or otherwise aggregate the probabilities) and
round the result (Math.Round(..., 4)) so duplicate sanitized keys do not cause
exceptions.
* fix: Handle MQTT DNS failures gracefully (fixes #1463) When MQTT broker DNS resolution fails (e.g., mqtt.z13.org cannot resolve), ESPresense Companion was treating publish attempts as fatal exceptions that escaped background services and triggered host shutdown per default HostOptions.BackgroundServiceExceptionBehavior. Root cause: - MqttCoordinator.EnqueueAsync logs and rethrows exceptions - TelemetryService.ExecuteAsync publishes every 30s without try/catch - MultiScenarioLocator.ProcessDevice publishes state updates without protection - Unhandled MqttCommunicationException → BackgroundService failed → host shutdown Changes: 1. Added IMqttCoordinator.TryEnqueueAsync method for best-effort publishes that don't throw exceptions (returns bool success instead) 2. Updated TelemetryService to use TryEnqueueAsync - telemetry is best-effort and should never crash the host 3. Updated MultiScenarioLocator to use TryEnqueueAsync for state/attribute publishes - ensures a single MQTT failure doesn't abort device processing 4. EnqueueAsync still throws for critical operations that need error handling Result: MQTT failures (DNS, network, broker down) are now logged but don't crash background services or trigger host shutdown. Services continue running and will retry when connection is restored. * test: Update NotHomeStateWhenAllScenariosExpire to use TryEnqueueAsync Fix test failure due to EnqueueAsync -> TryEnqueueAsync migration. TryEnqueueAsync returns Task<bool> instead of Task. --------- Co-authored-by: Darrell <DT@Terastar.biz>
This change adds an additional condition in InitializeScenario to only include nodes whose Z coordinate lies within the floor's bounds when bounds are defined. This prevents nodes from other floors (especially when assigned to multiple floors) from participating in a floor's localization, which caused room flip-flopping in vertically stacked rooms. Fixes #2220 (ESPresense/ESPresense#2220) Co-authored-by: Sensie <sensie@openclaw.ai>
* Bump the nuget group with 1 update Bumps AutoMapper from 16.1.0 to 16.1.1 --- updated-dependencies: - dependency-name: AutoMapper dependency-version: 16.1.1 dependency-type: direct:production dependency-group: nuget ... Signed-off-by: dependabot[bot] <support@github.com> * fix: update AutoMapper in test project to match main The test project still referenced AutoMapper 16.1.0 while main was updated to 16.1.1, causing a package downgrade error (NU1605). This blocked the Dependabot PR from merging. Fixes #1507 Therefore: update test project to 16.1.1. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sensie <sensie@openclaw.ai>
Bumps the npm_and_yarn group with 1 update in the /src/ui directory: [devalue](https://github.com/sveltejs/devalue). Updates `devalue` from 5.6.3 to 5.6.4 - [Release notes](https://github.com/sveltejs/devalue/releases) - [Changelog](https://github.com/sveltejs/devalue/blob/main/CHANGELOG.md) - [Commits](sveltejs/devalue@v5.6.3...v5.6.4) --- updated-dependencies: - dependency-name: devalue dependency-version: 5.6.4 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Slider for calibration * Trigger CI rebuild - retrigger
…ui (#1517) Bumps [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) from 4.1.18 to 4.2.1. - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.2.1/packages/@tailwindcss-vite) --- updated-dependencies: - dependency-name: "@tailwindcss/vite" dependency-version: 4.2.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
--- updated-dependencies: - dependency-name: Swashbuckle.AspNetCore dependency-version: 10.1.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…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
…tion Fixes CS0173 compilation errors where ternary operator couldn't determine type between double and null. Added explicit (double?) casts for x, y, z coordinates in the attributes payload.
…ve room name matching The test was failing because it expected exact case-sensitive room name matches in the JSON probabilities object. Added case-insensitive lookup and better error messages to show available keys when assertions fail.
…tiScenarioLocator
…eric_state` observations and add an automation example.
…s and enable sticky Home Assistant sensors via `value_template`.
…y names - Keep HA sensor discoveries when device leaves a room (set probability to 0 instead of deleting) - Remove device name from sensor Name to prevent duplicate names like `sensor.device_name_device_name_room_probability` - Include sticky rooms with 0 probability in attributes payload - Update tests to verify sticky behavior Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- NearestNode now finds rooms by location or by matching node ID/name - Filter out low-confidence fallback scenarios from Bayesian probabilities when better locators are working (confidence threshold = 5) - Prevents "NearestNode" from appearing as a fake room in probabilities Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
97806fd to
cc0c4ce
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/Models/Config.Clone.cs (1)
35-42:⚠️ Potential issue | 🟠 MajorClone the remaining locator sub-configs here.
Lines 37-41 only copy
NadarayaWatson,NelderMead, andNearestNode.ConfigLocatorsstill hasBfgs,Mle, andMultiFloorinsrc/Models/Config.cs:63-82, so cloning now resets those settings to defaults instead of preserving the loaded config.🛠️ Suggested fix
return new ConfigLocators { NadarayaWatson = NadarayaWatson?.Clone(), NelderMead = NelderMead?.Clone(), + Bfgs = Bfgs?.Clone(), + Mle = Mle?.Clone(), + MultiFloor = MultiFloor?.Clone(), NearestNode = NearestNode?.Clone() };public partial class BfgsConfig { public BfgsConfig Clone() => new() { Enabled = Enabled, Floors = Floors?.ToArray(), Weighting = Weighting?.Clone() }; }Apply the same pattern to
MleConfigandMultiFloorConfig.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Models/Config.Clone.cs` around lines 35 - 42, ConfigLocators.Clone currently only copies NadarayaWatson, NelderMead, and NearestNode, which drops Bfgs, Mle, and MultiFloor settings; add cloning for the Bfgs, Mle, and MultiFloor properties in ConfigLocators.Clone and implement Clone methods on BfgsConfig, MleConfig, and MultiFloorConfig (e.g., BfgsConfig.Clone, MleConfig.Clone, MultiFloorConfig.Clone) following the existing pattern: return a new instance copying simple fields, cloning nested objects (like Weighting.Clone()) and copying arrays with .ToArray() (e.g., Floors?.ToArray()) so loaded config values are preserved rather than reset.src/Services/MultiScenarioLocator.cs (1)
180-210:⚠️ Potential issue | 🟠 MajorClearing Bayesian state can leave stale retained attributes behind.
When
ClearProbabilityOutputsAsync()returns true andbestScenariois null, Line 210 skips the/attributesrepublish becauseprobabilityAttributesis still null. The broker keeps the previous retainedprobabilitiesobject, so disabling Bayesian publishing or clearing state while a device isnot_homeleaks stale probabilities to subscribers.🧹 Suggested fix
else { probabilityChanged = await bayesianPublisher.ClearProbabilityOutputsAsync(device); + if (probabilityChanged) + { + probabilityAttributes = new Dictionary<string, double>(); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Services/MultiScenarioLocator.cs` around lines 180 - 210, When ClearProbabilityOutputsAsync (referenced as bayesianPublisher.ClearProbabilityOutputsAsync) returns true but bestScenario is null and probabilityAttributes is null, the existing condition prevents sending an /attributes republish and leaves stale retained probabilities on the broker; update the logic so that when probabilityChanged is true you still enqueue an attributes publish (using mqtt.TryEnqueueAsync for the "espresense/companion/{device.Id}/attributes" topic) containing null/empty probability and location fields to clear retained values even if probabilityAttributes is null and bestScenario is null, ensuring retained probabilities are removed when Bayesian outputs are cleared.
♻️ Duplicate comments (2)
src/Services/BayesianProbabilityPublisher.cs (1)
100-118:⚠️ Potential issue | 🟡 MinorUse the rounded probability for discovery gating.
Line 113 still compares the raw value even though Lines 102-107 round and persist the 4-decimal payload value. A room at
0.09996is exposed as0.1000indevice.BayesianProbabilities, but it still misses discovery when the threshold is0.1.🔧 Suggested fix
- if (!IsSyntheticRoom(roomName) && probability >= config.DiscoveryThreshold) + if (!IsSyntheticRoom(roomName) && roundedProbability >= config.DiscoveryThreshold)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Services/BayesianProbabilityPublisher.cs` around lines 100 - 118, The discovery gating currently uses the raw probability variable (probability) but you round and store the 4-decimal payload (roundedProbability); change the gating to use roundedProbability when checking against config.DiscoveryThreshold so values like 0.09996 that become 0.1000 will pass the threshold; update the condition in the block that calls IsSyntheticRoom, CreateProbabilityDiscovery, and discovery.Send to compare roundedProbability >= config.DiscoveryThreshold (leave the rest of the logic with device.BayesianDiscoveries and device.HassAutoDiscovery unchanged).src/Services/MultiScenarioLocator.cs (1)
171-177:⚠️ Potential issue | 🟠 MajorSanitized-key collisions can still break probability publishing.
SanitizeSegmentis not one-to-one:Living RoomandLiving-Roomboth collapse to the same key.ToDictionarywill throw here, andsrc/Services/BayesianProbabilityPublisher.cs:185-210derives discovery IDs from the same sanitized segment, so this becomes both a runtime failure and an entity-ID collision. Group or disambiguate by the sanitized key before materializing the payload, and use that same normalized key for discovery creation.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Services/MultiScenarioLocator.cs` around lines 171 - 177, The current ToDictionary call can throw when BayesianProbabilityPublisher.SanitizeSegment produces duplicate keys (e.g., "Living Room" vs "Living-Room"); fix by grouping device.BayesianProbabilities by the sanitized key (use BayesianProbabilityPublisher.SanitizeSegment(kvp.Key)), then materialize probabilityAttributes from the groups: if a group has a single member use the sanitized key, if multiple members disambiguate by appending a stable suffix (e.g., incrementing index or short hash of the original key) to produce unique normalized keys, assign the aggregated/selected probability value (e.g., sum or chosen strategy) rounded as before, and ensure the same normalized keys are used when creating discoveries in BayesianProbabilityPublisher so IDs remain consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Locators/NearestNode.cs`:
- Around line 37-38: The code currently sets scenario.Floor =
node.Floors?.FirstOrDefault(), which chooses by array order and can pick the
wrong floor when multiple floors attach to a node; replace this with geometric
floor resolution: iterate node.Floors and select the floor whose geometry
contains the node's position (e.g., using a Floor.Contains(node.Position) or
checking a polygon/mesh containment test), and if containment is ambiguous
choose a deterministic tiebreaker (closest vertical distance or highest
intersection confidence); update the assignment to set scenario.Floor to that
contained floor (fall back to FirstOrDefault only if no containment test
passes).
In `@src/Services/MultiScenarioLocator.cs`:
- Around line 162-183: The new ConfigBayesianProbabilities.Retain flag is never
applied in MultiScenarioLocator: thread its value into the bayesianPublisher
publish/clear calls instead of only checking Enabled. Update
MultiScenarioLocator to read state.Config.BayesianProbabilities?.Retain and pass
that boolean into bayesianPublisher.PublishProbabilitySensorsAsync and
bayesianPublisher.ClearProbabilityOutputsAsync (or add overloads/parameters on
those methods if needed), and ensure the subsequent shared attributes publish
uses that retain value when publishing MQTT attributes; alternatively remove
Retain from the config contract if you decide not to support non-retained
probability publishing yet.
---
Outside diff comments:
In `@src/Models/Config.Clone.cs`:
- Around line 35-42: ConfigLocators.Clone currently only copies NadarayaWatson,
NelderMead, and NearestNode, which drops Bfgs, Mle, and MultiFloor settings; add
cloning for the Bfgs, Mle, and MultiFloor properties in ConfigLocators.Clone and
implement Clone methods on BfgsConfig, MleConfig, and MultiFloorConfig (e.g.,
BfgsConfig.Clone, MleConfig.Clone, MultiFloorConfig.Clone) following the
existing pattern: return a new instance copying simple fields, cloning nested
objects (like Weighting.Clone()) and copying arrays with .ToArray() (e.g.,
Floors?.ToArray()) so loaded config values are preserved rather than reset.
In `@src/Services/MultiScenarioLocator.cs`:
- Around line 180-210: When ClearProbabilityOutputsAsync (referenced as
bayesianPublisher.ClearProbabilityOutputsAsync) returns true but bestScenario is
null and probabilityAttributes is null, the existing condition prevents sending
an /attributes republish and leaves stale retained probabilities on the broker;
update the logic so that when probabilityChanged is true you still enqueue an
attributes publish (using mqtt.TryEnqueueAsync for the
"espresense/companion/{device.Id}/attributes" topic) containing null/empty
probability and location fields to clear retained values even if
probabilityAttributes is null and bestScenario is null, ensuring retained
probabilities are removed when Bayesian outputs are cleared.
---
Duplicate comments:
In `@src/Services/BayesianProbabilityPublisher.cs`:
- Around line 100-118: The discovery gating currently uses the raw probability
variable (probability) but you round and store the 4-decimal payload
(roundedProbability); change the gating to use roundedProbability when checking
against config.DiscoveryThreshold so values like 0.09996 that become 0.1000 will
pass the threshold; update the condition in the block that calls
IsSyntheticRoom, CreateProbabilityDiscovery, and discovery.Send to compare
roundedProbability >= config.DiscoveryThreshold (leave the rest of the logic
with device.BayesianDiscoveries and device.HassAutoDiscovery unchanged).
In `@src/Services/MultiScenarioLocator.cs`:
- Around line 171-177: The current ToDictionary call can throw when
BayesianProbabilityPublisher.SanitizeSegment produces duplicate keys (e.g.,
"Living Room" vs "Living-Room"); fix by grouping device.BayesianProbabilities by
the sanitized key (use BayesianProbabilityPublisher.SanitizeSegment(kvp.Key)),
then materialize probabilityAttributes from the groups: if a group has a single
member use the sanitized key, if multiple members disambiguate by appending a
stable suffix (e.g., incrementing index or short hash of the original key) to
produce unique normalized keys, assign the aggregated/selected probability value
(e.g., sum or chosen strategy) rounded as before, and ensure the same normalized
keys are used when creating discoveries in BayesianProbabilityPublisher so IDs
remain consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: bc466e86-5fb9-4d99-b9b2-bde8ed1efbc7
📒 Files selected for processing (13)
README.mdsrc/Locators/NearestNode.cssrc/Models/AutoDiscovery.cssrc/Models/Config.Clone.cssrc/Models/Config.cssrc/Models/Device.cssrc/Program.cssrc/Services/BayesianProbabilityPublisher.cssrc/Services/DeviceTracker.cssrc/Services/MultiScenarioLocator.cssrc/config.example.yamltests/ESPresense.Companion.Tests/FilteringTests.cstests/ESPresense.Companion.Tests/MultiScenarioLocatorTests.cs
🚧 Files skipped from review as they are similar to previous changes (6)
- src/Models/Config.cs
- src/Services/DeviceTracker.cs
- src/config.example.yaml
- src/Program.cs
- src/Models/Device.cs
- README.md
| // Find the floor containing the node | ||
| scenario.Floor = node.Floors?.FirstOrDefault(); |
There was a problem hiding this comment.
Restore geometric floor resolution instead of array-order selection
FirstOrDefault() on node.Floors can assign the wrong floor when multiple floors are attached to a node. Line 37 says “find the floor containing the node,” but Line 38 does not perform containment at all. This can propagate incorrect floor state into MultiScenarioLocator and Bayesian probability aggregation.
Proposed fix
- // Find the floor containing the node
- scenario.Floor = node.Floors?.FirstOrDefault();
+ // Find the floor containing the node
+ scenario.Floor = node.Floors == null
+ ? null
+ : SpatialUtils.FindFloorContaining(location, node.Floors) ?? node.Floors.FirstOrDefault();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Locators/NearestNode.cs` around lines 37 - 38, The code currently sets
scenario.Floor = node.Floors?.FirstOrDefault(), which chooses by array order and
can pick the wrong floor when multiple floors attach to a node; replace this
with geometric floor resolution: iterate node.Floors and select the floor whose
geometry contains the node's position (e.g., using a
Floor.Contains(node.Position) or checking a polygon/mesh containment test), and
if containment is ambiguous choose a deterministic tiebreaker (closest vertical
distance or highest intersection confidence); update the assignment to set
scenario.Floor to that contained floor (fall back to FirstOrDefault only if no
containment test passes).
| var probabilityConfig = state.Config?.BayesianProbabilities; | ||
| Dictionary<string, double>? probabilityAttributes = null; | ||
| var probabilityChanged = false; | ||
|
|
||
| if (probabilityConfig is { Enabled: true }) | ||
| { | ||
| var probabilityVector = bayesianPublisher.BuildProbabilityVector(device, bestScenario); | ||
| probabilityChanged = await bayesianPublisher.PublishProbabilitySensorsAsync(device, probabilityVector, probabilityConfig); | ||
|
|
||
| // Use device.BayesianProbabilities (not probabilityVector) to include sticky rooms with 0 probability | ||
| // This ensures all discovered sensors appear in the attributes payload | ||
| if (device.BayesianProbabilities.Count > 0) | ||
| { | ||
| probabilityAttributes = device.BayesianProbabilities.ToDictionary( | ||
| kvp => BayesianProbabilityPublisher.SanitizeSegment(kvp.Key), | ||
| kvp => Math.Round(kvp.Value, 4)); | ||
| } | ||
| } | ||
| else | ||
| { | ||
| probabilityChanged = await bayesianPublisher.ClearProbabilityOutputsAsync(device); | ||
| } |
There was a problem hiding this comment.
bayesian_probabilities.retain is a no-op right now.
This branch only looks at Enabled. The new Retain field on ConfigBayesianProbabilities is never applied here, discovery configs are still retained in src/Models/AutoDiscovery.cs:49-56, and the shared /attributes payload is still published with retain: true at Line 242. Setting bayesian_probabilities.retain: false therefore won't change MQTT behavior.
Either thread the setting into the actual publish path or remove it from the config contract until non-retained probability publishing is supported.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Services/MultiScenarioLocator.cs` around lines 162 - 183, The new
ConfigBayesianProbabilities.Retain flag is never applied in
MultiScenarioLocator: thread its value into the bayesianPublisher publish/clear
calls instead of only checking Enabled. Update MultiScenarioLocator to read
state.Config.BayesianProbabilities?.Retain and pass that boolean into
bayesianPublisher.PublishProbabilitySensorsAsync and
bayesianPublisher.ClearProbabilityOutputsAsync (or add overloads/parameters on
those methods if needed), and ensure the subsequent shared attributes publish
uses that retain value when publishing MQTT attributes; alternatively remove
Retain from the config contract if you decide not to support non-retained
probability publishing yet.
e2b2560 to
8231de3
Compare
|
Sensie: This PR is currently conflicting with main and needs to be rebased. The feature looks good but requires conflict resolution before merge. Deferring for maintainer action. 📡 |
|
@dependabot rebase |
Build Status: 2 Tests Failing ❌The latest build shows 2 unit test failures that need to be addressed before merge: 1. (NearestNodeTests.cs:144)
2. (MultiScenarioLocatorTests.cs:176)
Additionally, CodeRabbit has flagged these unresolved issues:
Please address the test failures and CodeRabbit issues, then the PR should be ready to merge. 146/150 tests pass — just these 2 need fixing. |
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Summary by CodeRabbit
Release Notes
New Features
bayesian_probabilities.enabled,discovery_threshold, andretainDocumentation