Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1191,7 +1191,8 @@ private static bool IsCloudSecurityVendor(string vendorLower)
System.Text.RegularExpressions.Regex.IsMatch(vendorLower, @"\barlo\b") ||
System.Text.RegularExpressions.Regex.IsMatch(vendorLower, @"\bsimplisafe\b") ||
System.Text.RegularExpressions.Regex.IsMatch(vendorLower, @"\btp-link\b") ||
System.Text.RegularExpressions.Regex.IsMatch(vendorLower, @"\bcanary\b");
System.Text.RegularExpressions.Regex.IsMatch(vendorLower, @"\bcanary\b") ||
System.Text.RegularExpressions.Regex.IsMatch(vendorLower, @"\bfurbo\b");
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,25 @@ public void DetectDeviceType_RingVendor_ReturnsCloudCamera()
result.Category.Should().Be(ClientDeviceCategory.CloudCamera);
}

[Fact]
public void DetectDeviceType_FurboVendor_ReturnsCloudCamera()
{
// Arrange - Furbo is a cloud camera vendor (dog camera with treat tossing)
var client = new UniFiClientResponse
{
Mac = "11:22:33:44:55:66",
Name = "Furbo Dog Camera",
Oui = "Furbo",
DevCat = 9 // Camera fingerprint (9 = IP Network Camera)
};

// Act
var result = _service.DetectDeviceType(client);

// Assert - Furbo cameras are cloud cameras (require internet for remote access)
result.Category.Should().Be(ClientDeviceCategory.CloudCamera);
}

#endregion

#region Camera Name Override Tests (Nest/Google cameras)
Expand Down Expand Up @@ -258,6 +277,29 @@ public void DetectDeviceType_NestOrGoogleWithCameraName_ReturnsCloudCamera(strin
result.Category.Should().Be(ClientDeviceCategory.CloudCamera);
}

[Theory]
[InlineData("Furbo", "Living Room")]
[InlineData("FURBO", "Dog Camera")]
[InlineData("Furbo Inc", "Pet Camera")]
[InlineData("Furbo Dog Camera", "Kitchen Cam")]
public void DetectDeviceType_FurboVendorVariations_ReturnsCloudCamera(string vendor, string deviceName)
{
// Arrange - Test various Furbo vendor name formats (case-insensitive, with suffixes)
var client = new UniFiClientResponse
{
Mac = "11:22:33:44:55:66",
Name = deviceName,
Oui = vendor,
DevCat = 9 // Camera fingerprint
};

// Act
var result = _service.DetectDeviceType(client);

// Assert - All Furbo variations should be detected as cloud cameras
result.Category.Should().Be(ClientDeviceCategory.CloudCamera);
}

[Theory]
[InlineData("Nest Labs Inc.", "Living Room Thermostat", ClientDeviceCategory.SmartThermostat)]
[InlineData("Nest Labs Inc.", "Hallway Ecobee", ClientDeviceCategory.SmartThermostat)]
Expand Down Expand Up @@ -976,6 +1018,7 @@ public void SetClientHistory_FiltersEntriesWithEmptyMac()
[InlineData("Blink", ClientDeviceCategory.CloudCamera)]
[InlineData("TP-Link", ClientDeviceCategory.CloudCamera)]
[InlineData("Canary", ClientDeviceCategory.CloudCamera)]
[InlineData("Furbo", ClientDeviceCategory.CloudCamera)]
public void DetectFromMac_HistoryCameraFingerprint_WithCloudVendor_ReturnsCloudCamera(string vendor, ClientDeviceCategory expected)
{
// Arrange - history with camera fingerprint (DevCat 9) and cloud vendor via OUI
Expand Down Expand Up @@ -1733,6 +1776,7 @@ public void DetectDeviceType_VendorWithCloudKeywordSubstring_DoesNotFalsePositiv
[InlineData("SimpliSafe Inc")]
[InlineData("TP-Link Technologies")]
[InlineData("Canary Connect")]
[InlineData("Furbo Inc")]
// Bare vendor names (word boundary should still match)
[InlineData("Ring")]
[InlineData("Nest")]
Expand All @@ -1743,6 +1787,7 @@ public void DetectDeviceType_VendorWithCloudKeywordSubstring_DoesNotFalsePositiv
[InlineData("SimpliSafe")]
[InlineData("TP-Link")]
[InlineData("Canary")]
[InlineData("Furbo")]
public void DetectDeviceType_ActualCloudVendor_BecomesCloudCamera(string oui)
{
// Arrange - Actual cloud camera vendor OUI
Expand Down