Skip to content

Commit

Permalink
feat: Adding Lovehoney Desire device support
Browse files Browse the repository at this point in the history
  • Loading branch information
blackspherefollower authored and qdot committed Nov 27, 2019
1 parent bb55a30 commit 7040e35
Show file tree
Hide file tree
Showing 4 changed files with 320 additions and 0 deletions.
99 changes: 99 additions & 0 deletions Buttplug.Test/Devices/Protocols/LovehoneyDesireKnickerVibeTests.cs
@@ -0,0 +1,99 @@
// <copyright file="LovehoneyDesireKnickerVibeTests.cs" company="Nonpolynomial Labs LLC">
// Buttplug C# Source Code File - Visit https://buttplug.io for more info about the project.
// Copyright (c) Nonpolynomial Labs LLC. All rights reserved.
// Licensed under the BSD 3-Clause license. See LICENSE file in the project root for full license information.
// </copyright>

// Test file, disable ConfigureAwait checking.
// ReSharper disable ConsiderUsingConfigureAwait

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Buttplug.Core.Messages;
using Buttplug.Devices;
using Buttplug.Devices.Protocols;
using Buttplug.Test.Devices.Protocols.Utils;
using JetBrains.Annotations;
using NUnit.Framework;

namespace Buttplug.Test.Devices.Protocols
{
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Test classes can skip documentation requirements")]
[TestFixture]
public class LovehoneyDesireKnickerVibeTests
{
[NotNull]
private ProtocolTestUtils testUtil;

[SetUp]
public async Task Init()
{
testUtil = new ProtocolTestUtils();
await testUtil.SetupTest<LovehoneyDesireProtocol>("KNICKER VIBE");
}

[Test]
public void TestAllowedMessages()
{
testUtil.TestDeviceAllowedMessages(new Dictionary<System.Type, uint>()
{
{ typeof(StopDeviceCmd), 0 },
{ typeof(SingleMotorVibrateCmd), 0 },
{ typeof(VibrateCmd), 1 },
});
}

// StopDeviceCmd noop test handled in GeneralDeviceTests

[Test]
public async Task TestStopDeviceCmd()
{
var expected =
new List<(byte[], string)>()
{
(new byte[] { 0xF3, 0x01, 0x40 }, Endpoints.Tx),
};

await testUtil.TestDeviceMessage(new SingleMotorVibrateCmd(4, 0.5), expected, false);

expected =
new List<(byte[], string)>()
{
(new byte[] { 0xF3, 0x01, 0x00 }, Endpoints.Tx),
};

await testUtil.TestDeviceMessage(new StopDeviceCmd(4), expected, false);
}

[Test]
public async Task TestSingleMotorVibrateCmd()
{
var expected =
new List<(byte[], string)>()
{
(new byte[] { 0xF3, 0x01, 0x40 }, Endpoints.Tx),
};

await testUtil.TestDeviceMessage(new SingleMotorVibrateCmd(4, 0.5), expected, false);
}

[Test]
public async Task TestVibrateCmd()
{
var expected =
new List<(byte[], string)>()
{
(new byte[] { 0xF3, 0x01, 0x40 }, Endpoints.Tx),
};

await testUtil.TestDeviceMessage(VibrateCmd.Create(4, 1, 0.5, 1), expected, false);
}

[Test]
public void TestInvalidVibrateCmd()
{
testUtil.TestInvalidVibrateCmd(1);
}
}
}
100 changes: 100 additions & 0 deletions Buttplug.Test/Devices/Protocols/LovehoneyDesireProstateVibeTests.cs
@@ -0,0 +1,100 @@
// <copyright file="LovehoneyDesireProstateVibeTests.cs" company="Nonpolynomial Labs LLC">
// Buttplug C# Source Code File - Visit https://buttplug.io for more info about the project.
// Copyright (c) Nonpolynomial Labs LLC. All rights reserved.
// Licensed under the BSD 3-Clause license. See LICENSE file in the project root for full license information.
// </copyright>

// Test file, disable ConfigureAwait checking.
// ReSharper disable ConsiderUsingConfigureAwait

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Buttplug.Core.Messages;
using Buttplug.Devices;
using Buttplug.Devices.Protocols;
using Buttplug.Test.Devices.Protocols.Utils;
using JetBrains.Annotations;
using NUnit.Framework;

namespace Buttplug.Test.Devices.Protocols
{
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Test classes can skip documentation requirements")]
[TestFixture]
public class LovehoneyDesireProstateVibeTests
{
[NotNull]
private ProtocolTestUtils testUtil;

[SetUp]
public async Task Init()
{
testUtil = new ProtocolTestUtils();
await testUtil.SetupTest<LovehoneyDesireProtocol>("PROSTATE VIBE");
}

[Test]
public void TestAllowedMessages()
{
testUtil.TestDeviceAllowedMessages(new Dictionary<System.Type, uint>()
{
{ typeof(StopDeviceCmd), 0 },
{ typeof(SingleMotorVibrateCmd), 0 },
{ typeof(VibrateCmd), 2 },
});
}

// StopDeviceCmd noop test handled in GeneralDeviceTests

[Test]
public async Task TestStopDeviceCmd()
{
var expected =
new List<(byte[], string)>()
{
(new byte[] { 0xF3, 0x00, 0x40 }, Endpoints.Tx),
};

await testUtil.TestDeviceMessage(new SingleMotorVibrateCmd(4, 0.5), expected, false);

expected =
new List<(byte[], string)>()
{
(new byte[] { 0xF3, 0x00, 0x00 }, Endpoints.Tx),
};

await testUtil.TestDeviceMessage(new StopDeviceCmd(4), expected, false);
}

[Test]
public async Task TestSingleMotorVibrateCmd()
{
var expected =
new List<(byte[], string)>()
{
(new byte[] { 0xF3, 0x00, 0x40 }, Endpoints.Tx),
};

await testUtil.TestDeviceMessage(new SingleMotorVibrateCmd(4, 0.5), expected, false);
}

[Test]
public async Task TestVibrateCmd()
{
var expected =
new List<(byte[], string)>()
{
(new byte[] { 0xF3, 0x01, 0x40 }, Endpoints.Tx),
(new byte[] { 0xF3, 0x02, 0x00 }, Endpoints.Tx),
};

await testUtil.TestDeviceMessage(VibrateCmd.Create(4, 1, 0.5, 1), expected, false);
}

[Test]
public void TestInvalidVibrateCmd()
{
testUtil.TestInvalidVibrateCmd(2);
}
}
}
Expand Up @@ -53,6 +53,7 @@ protected DeviceConfigurationManager()
AddProtocol("kiiroo-v21-vibrator", typeof(KiirooGen21VibeProtocol));
AddProtocol("lelo-f1s", typeof(LeloF1sProtocol));
AddProtocol("libo", typeof(LiBoProtocol));
AddProtocol("lovehoney-desire", typeof(LovehoneyDesireProtocol));
AddProtocol("magic-motion", typeof(MagicMotionProtocol));
AddProtocol("mysteryvibe", typeof(MysteryVibeProtocol));
AddProtocol("picobong", typeof(PicobongProtocol));
Expand Down
120 changes: 120 additions & 0 deletions Buttplug/Devices/Protocols/LovehoneyDesireProtocol.cs
@@ -0,0 +1,120 @@
// <copyright file="LovehoneyDesireProtocol.cs" company="Nonpolynomial Labs LLC">
// Buttplug C# Source Code File - Visit https://buttplug.io for more info about the project.
// Copyright (c) Nonpolynomial Labs LLC. All rights reserved.
// Licensed under the BSD 3-Clause license. See LICENSE file in the project root for full license information.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Buttplug.Core.Logging;
using Buttplug.Core.Messages;

namespace Buttplug.Devices.Protocols
{
internal class LovehoneyDesireProtocol : ButtplugDeviceProtocol
{
private readonly double[] _vibratorSpeeds = { 0, 0 };

internal struct LovehoneyDesireType
{
public string Name;
public uint VibeCount;
}

internal readonly Dictionary<string, LovehoneyDesireType> _deviceMap = new Dictionary<string, LovehoneyDesireType>()
{
{ "PROSTATE VIBE", new LovehoneyDesireType() { Name = "Prostate Vibrator", VibeCount = 2 } },
{ "KNICKER VIBE", new LovehoneyDesireType() { Name = "Knicker Vibrator", VibeCount = 1 } },
};

private readonly LovehoneyDesireType _devInfo;

public LovehoneyDesireProtocol(IButtplugLogManager aLogManager,
IButtplugDeviceImpl aInterface)
: base(aLogManager,
"Lovehoney Desire Device",
aInterface)
{
if (_deviceMap.TryGetValue(aInterface.Name, out var dev))
{
_devInfo = dev;
Name = $"Lovehoney Desire {dev.Name}";
}
else
{
_devInfo = new LovehoneyDesireType()
{
Name = "Unknown Lovehoney Desire Device",
VibeCount = 1,
};
}

AddMessageHandler<SingleMotorVibrateCmd>(HandleSingleMotorVibrateCmd);
AddMessageHandler<VibrateCmd>(HandleVibrateCmd, new MessageAttributes() { FeatureCount = _devInfo.VibeCount });
AddMessageHandler<StopDeviceCmd>(HandleStopDeviceCmd);
}

private async Task<ButtplugMessage> HandleStopDeviceCmd(ButtplugDeviceMessage aMsg, CancellationToken aToken)
{
BpLogger.Debug("Stopping Device " + Name);
return await HandleSingleMotorVibrateCmd(new SingleMotorVibrateCmd(aMsg.DeviceIndex, 0, aMsg.Id), aToken).ConfigureAwait(false);
}

private async Task<ButtplugMessage> HandleSingleMotorVibrateCmd(ButtplugDeviceMessage aMsg, CancellationToken aToken)
{
var cmdMsg = CheckMessageHandler<SingleMotorVibrateCmd>(aMsg);

return await HandleVibrateCmd(VibrateCmd.Create(cmdMsg.DeviceIndex, cmdMsg.Id, cmdMsg.Speed, _devInfo.VibeCount), aToken).ConfigureAwait(false);
}

private async Task<ButtplugMessage> HandleVibrateCmd(ButtplugDeviceMessage aMsg, CancellationToken aToken)
{
var cmdMsg = CheckGenericMessageHandler<VibrateCmd>(aMsg, _devInfo.VibeCount);
var changed = new[] { false, false };

foreach (var v in cmdMsg.Speeds)
{
if (Math.Abs(v.Speed - _vibratorSpeeds[v.Index]) < 0.001)
{
continue;
}

changed[v.Index] = true;
_vibratorSpeeds[v.Index] = v.Speed;
}

if (_devInfo.VibeCount >= 2 &&
Math.Abs(_vibratorSpeeds[0] - _vibratorSpeeds[1]) < 0.001 &&
(changed[0] || changed[1] || !SentVibration))
{
// Values are the same, so use global setter
await Interface.WriteValueAsync(
new byte[] { 0xF3, 0, (byte)Convert.ToUInt32(_vibratorSpeeds[0] * 0x7F) }, aToken)
.ConfigureAwait(false);

SentVibration = true;
return new Ok(aMsg.Id);
}

if (changed[0] || !SentVibration)
{
await Interface.WriteValueAsync(
new byte[] { 0xF3, 1, (byte)Convert.ToUInt32(_vibratorSpeeds[0] * 0x7F) }, aToken)
.ConfigureAwait(false);
}

if (_devInfo.VibeCount >= 2 && (changed[1] || !SentVibration))
{
await Interface.WriteValueAsync(
new byte[] { 0xF3, 2, (byte)Convert.ToUInt32(_vibratorSpeeds[1] * 0x7F) }, aToken)
.ConfigureAwait(false);
}

SentVibration = true;
return new Ok(aMsg.Id);
}
}
}

0 comments on commit 7040e35

Please sign in to comment.