diff --git a/Plugins/BTCPayServer.Plugins.BitcoinSwitch/BTCPayServer.Plugins.BitcoinSwitch.csproj b/Plugins/BTCPayServer.Plugins.BitcoinSwitch/BTCPayServer.Plugins.BitcoinSwitch.csproj
index f31d0ee..8e83521 100644
--- a/Plugins/BTCPayServer.Plugins.BitcoinSwitch/BTCPayServer.Plugins.BitcoinSwitch.csproj
+++ b/Plugins/BTCPayServer.Plugins.BitcoinSwitch/BTCPayServer.Plugins.BitcoinSwitch.csproj
@@ -9,7 +9,7 @@
Bitcoin SwitchControl harwdare using the POS as a switch
- 1.0.0
+ 1.0.1true
@@ -39,9 +39,4 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/Plugins/BTCPayServer.Plugins.BitcoinSwitch/BitcoinSwitchService.cs b/Plugins/BTCPayServer.Plugins.BitcoinSwitch/BitcoinSwitchService.cs
index c4b5c67..6aec58b 100644
--- a/Plugins/BTCPayServer.Plugins.BitcoinSwitch/BitcoinSwitchService.cs
+++ b/Plugins/BTCPayServer.Plugins.BitcoinSwitch/BitcoinSwitchService.cs
@@ -17,32 +17,29 @@ using Newtonsoft.Json.Linq;
namespace BTCPayServer.Plugins.FileSeller
{
-
-
-
public class BitcoinSwitchEvent
{
public string AppId { get; set; }
- public string Message { get; set; }
-
+ public string SwitchSettings { get; set; }
}
-
-
+
public class BitcoinSwitchService : EventHostedServiceBase
{
private readonly AppService _appService;
private readonly InvoiceRepository _invoiceRepository;
- public BitcoinSwitchService(EventAggregator eventAggregator,
+
+ public BitcoinSwitchService(
+ EventAggregator eventAggregator,
ILogger logger,
AppService appService,
- InvoiceRepository invoiceRepository) : base(eventAggregator, logger)
+ InvoiceRepository invoiceRepository)
+ : base(eventAggregator, logger)
{
_appService = appService;
_invoiceRepository = invoiceRepository;
}
public ConcurrentMultiDictionary AppToSockets { get; } = new();
-
protected override void SubscribeToEvents()
{
@@ -55,26 +52,12 @@ namespace BTCPayServer.Plugins.FileSeller
{
if (evt is BitcoinSwitchEvent bitcoinSwitchEvent)
{
- if (AppToSockets.TryGetValues(bitcoinSwitchEvent.AppId, out var sockets))
- {
- foreach (var socket in sockets)
- {
- try
- {
- await socket.SendAsync(
- new ArraySegment(System.Text.Encoding.UTF8.GetBytes(bitcoinSwitchEvent.Message)),
- WebSocketMessageType.Text, true, cancellationToken);
- }
- catch (Exception e)
- {
-
- }
- }
- }
+ _ = HandleGPIOMessages(cancellationToken, bitcoinSwitchEvent);
+ return;
}
if (evt is not InvoiceEvent invoiceEvent) return;
- List cartItems = null;
+List cartItems = null;
if (invoiceEvent.Name is not (InvoiceEvent.Completed or InvoiceEvent.MarkedCompleted
or InvoiceEvent.Confirmed))
{
@@ -124,27 +107,24 @@ namespace BTCPayServer.Plugins.FileSeller
return (null, null, null);
}
}).Where(tuple => tuple.Data != null && tuple.Items.Any(item =>
- item.AdditionalData?.ContainsKey("bitcoinswitch_gpio") is true &&
+ item.AdditionalData?.ContainsKey("bitcoinswitch") is true &&
items.Exists(cartItem => cartItem.Id == item.Id)));
foreach (var valueTuple in apps)
{
foreach (var item1 in valueTuple.Items.Where(item =>
- item.AdditionalData?.ContainsKey("bitcoinswitch_gpio") is true &&
+ item.AdditionalData?.ContainsKey("bitcoinswitch") is true &&
items.Exists(cartItem => cartItem.Id == item.Id)))
{
var appId = valueTuple.Data.Id;
- var gpio = item1.AdditionalData["bitcoinswitch_gpio"].Value();
- var duration = item1.AdditionalData.TryGetValue("bitcoinswitch_duration", out var durationObj) &&
- durationObj.Type == JTokenType.Integer
- ? durationObj.Value()
- : "5000";
+ var gpio = item1.AdditionalData["bitcoinswitch"].Value();
+
PushEvent(new BitcoinSwitchEvent()
{
AppId = appId,
- Message = $"{gpio}-{duration}.0"
+ SwitchSettings = gpio
});
}
@@ -160,5 +140,105 @@ namespace BTCPayServer.Plugins.FileSeller
await base.ProcessEvent(evt, cancellationToken);
}
+
+ private async Task HandleGPIOMessages(CancellationToken cancellationToken, BitcoinSwitchEvent bitcoinSwitchEvent)
+ {
+ // Parse switch settings into actions
+ var actions = ParseActions(bitcoinSwitchEvent.SwitchSettings);
+
+
+ try
+ {
+ // Execute each action sequentially
+ foreach (var action in actions)
+ {
+ if (action.IsDelay)
+ {
+ // Wait for specified delay
+ await Task.Delay(action.DelayMs, cancellationToken);
+ }
+ else
+ {
+ // Send pin-duration command
+ var message = $"{action.Pin}-{action.Duration}";
+ var buffer = System.Text.Encoding.UTF8.GetBytes(message);
+
+ if (!AppToSockets.TryGetValues(bitcoinSwitchEvent.AppId, out var sockets))
+ return;
+ foreach (var socket in sockets)
+ {
+ await socket.SendAsync(
+ new ArraySegment(buffer),
+ WebSocketMessageType.Text,
+ true,
+ cancellationToken);
+ }
+
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Logs.PayServer.LogError(ex, "Error sending BitcoinSwitchEvent to socket");
+ }
+
+
+ return;
+ }
+
+ ///
+ /// Parses a settings string like "25-5000.0,delay 1000,23-200.0" into a sequence of actions.
+ ///
+ private static List ParseActions(string settings)
+ {
+ var actions = new List();
+ var segments = settings.Split(',', StringSplitOptions.RemoveEmptyEntries);
+ foreach (var seg in segments.Select(s => s.Trim()))
+ {
+ if (seg.StartsWith("delay ", StringComparison.OrdinalIgnoreCase))
+ {
+ // Delay segment
+ var parts = seg.Split(' ', StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length == 2 && int.TryParse(parts[1], out var ms))
+ {
+ actions.Add(SwitchAction.Delay(ms));
+ }
+ }
+ else
+ {
+ // Pin-duration segment
+ var parts = seg.Split('-', StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length == 2
+ && int.TryParse(parts[0], out var pin)
+ && double.TryParse(parts[1], out var duration))
+ {
+ actions.Add(SwitchAction.Command(pin, duration));
+ }
+ }
+ }
+ return actions;
+ }
}
-}
\ No newline at end of file
+
+ ///
+ /// Represents either a delay or a pin-duration command.
+ ///
+ public class SwitchAction
+ {
+ public bool IsDelay { get; }
+ public int DelayMs { get; }
+ public int Pin { get; }
+ public double Duration { get; }
+
+ private SwitchAction(bool isDelay, int delayMs, int pin, double duration)
+ {
+ IsDelay = isDelay;
+ DelayMs = delayMs;
+ Pin = pin;
+ Duration = duration;
+ }
+
+ public static SwitchAction Delay(int ms) => new(true, ms, 0, 0);
+ public static SwitchAction Command(int pin, double duration) => new(false, 0, pin, duration);
+ }
+}
diff --git a/Plugins/BTCPayServer.Plugins.BitcoinSwitch/Views/Shared/BitcoinSwitch/BitcoinSwitchPluginTemplateEditorItemDetail.cshtml b/Plugins/BTCPayServer.Plugins.BitcoinSwitch/Views/Shared/BitcoinSwitch/BitcoinSwitchPluginTemplateEditorItemDetail.cshtml
index c99e29f..ca2005c 100644
--- a/Plugins/BTCPayServer.Plugins.BitcoinSwitch/Views/Shared/BitcoinSwitch/BitcoinSwitchPluginTemplateEditorItemDetail.cshtml
+++ b/Plugins/BTCPayServer.Plugins.BitcoinSwitch/Views/Shared/BitcoinSwitch/BitcoinSwitchPluginTemplateEditorItemDetail.cshtml
@@ -1,12 +1,23 @@
-
-
+
+
-
-
-
-
-
-
\ No newline at end of file
+
+
+ Each segment specifies a GPIO pin and its activation duration (in milliseconds) in the format pin-duration (e.g., 25-5000.0). You can also insert ,delay milliseconds (e.g., ,delay 5000) between segments to pause before the next activation. Separate all parts with commas.
+