From cbefe0bfd81925b4d2cc20b59d77bb34711c2387 Mon Sep 17 00:00:00 2001 From: LucHeart Date: Mon, 24 Feb 2025 14:25:02 +0100 Subject: [PATCH] Add pause example --- SDK.CSharp.Example/ExampleConfig.cs | 9 ++ SDK.CSharp.Example/Http/PauseShocker.cs | 41 +++++++++ SDK.CSharp.Example/IExample.cs | 6 ++ SDK.CSharp.Example/LiveControl.cs | 60 +++++++++++++ SDK.CSharp.Example/Program.cs | 111 ++++++++++-------------- SDK.CSharp.Example/SignalrHub.cs | 40 +++++++++ SDK.CSharp/IOpenShockApiClient.cs | 3 +- SDK.CSharp/OpenShockApiClient.cs | 2 +- SDK.CSharp/OpenShockApiError.cs | 11 ++- 9 files changed, 215 insertions(+), 68 deletions(-) create mode 100644 SDK.CSharp.Example/ExampleConfig.cs create mode 100644 SDK.CSharp.Example/Http/PauseShocker.cs create mode 100644 SDK.CSharp.Example/IExample.cs create mode 100644 SDK.CSharp.Example/LiveControl.cs create mode 100644 SDK.CSharp.Example/SignalrHub.cs diff --git a/SDK.CSharp.Example/ExampleConfig.cs b/SDK.CSharp.Example/ExampleConfig.cs new file mode 100644 index 0000000..bc146f2 --- /dev/null +++ b/SDK.CSharp.Example/ExampleConfig.cs @@ -0,0 +1,9 @@ +namespace SDK.CSharp.Example; + +public sealed class ExampleConfig +{ + public Uri ApiUrl { get; set; } = new("https://api.openshock.app"); + public required string ApiToken { get; set; } + public Guid? Hub { get; set; } + public IReadOnlyCollection Shockers { get; set; } = Array.Empty(); +} \ No newline at end of file diff --git a/SDK.CSharp.Example/Http/PauseShocker.cs b/SDK.CSharp.Example/Http/PauseShocker.cs new file mode 100644 index 0000000..d91e129 --- /dev/null +++ b/SDK.CSharp.Example/Http/PauseShocker.cs @@ -0,0 +1,41 @@ +using OpenShock.SDK.CSharp; + +namespace SDK.CSharp.Example.Http; + +public sealed class PauseShocker : IExample +{ + private readonly ExampleConfig _config; + + public PauseShocker(ExampleConfig config) + { + _config = config; + } + + + public async Task Start() + { + var apiClient = new OpenShockApiClient(new ApiClientOptions + { + Token = _config.ApiToken + }); + + var firstShocker = _config.Shockers.First(); + + var response = await apiClient.PauseShocker(firstShocker, true); + + response.Switch( + success => Console.WriteLine("Shocker paused: " + success.Value), + error => Console.WriteLine("Shocker not found") + ); + + Console.WriteLine("Press enter to unpause again"); + Console.ReadLine(); + + response = await apiClient.PauseShocker(firstShocker, false); + + response.Switch( + success => Console.WriteLine("Shocker paused: " + success.Value), + error => Console.WriteLine("Shocker not found") + ); + } +} \ No newline at end of file diff --git a/SDK.CSharp.Example/IExample.cs b/SDK.CSharp.Example/IExample.cs new file mode 100644 index 0000000..1939746 --- /dev/null +++ b/SDK.CSharp.Example/IExample.cs @@ -0,0 +1,6 @@ +namespace SDK.CSharp.Example; + +public interface IExample +{ + public Task Start(); +} \ No newline at end of file diff --git a/SDK.CSharp.Example/LiveControl.cs b/SDK.CSharp.Example/LiveControl.cs new file mode 100644 index 0000000..e83d146 --- /dev/null +++ b/SDK.CSharp.Example/LiveControl.cs @@ -0,0 +1,60 @@ +using Microsoft.Extensions.Logging; +using OpenShock.SDK.CSharp; +using OpenShock.SDK.CSharp.Live; +using OpenShock.SDK.CSharp.Models; + +namespace SDK.CSharp.Example; + +public sealed class LiveControl +{ + private readonly ILoggerFactory _loggerFactory; + private readonly ILogger _logger; + + public LiveControl(ILoggerFactory loggerFactory) + { + _loggerFactory = loggerFactory; + _logger = loggerFactory.CreateLogger(); + } + + public async Task ControlExample(string apiToken, Guid hubId, Guid shockerId) + { + var apiClient = new OpenShockApiClient(new ApiClientOptions + { + Token = apiToken + }); + + var gatewayRequest = await apiClient.GetDeviceGateway(hubId); + + if (gatewayRequest.IsT1) + { + _logger.LogError("Failed to get gateway, make sure you used a valid device id"); + return; + } + + if (gatewayRequest.IsT2) + { + _logger.LogError("Device is offline"); + return; + } + + if (gatewayRequest.IsT3) + { + _logger.LogError("Device is not connected to a gateway"); + return; + } + + var gateway = gatewayRequest.AsT0.Value; + + _logger.LogInformation("Device is connected to gateway {GatewayId} in region {Region}", gateway.Gateway, gateway.Country); + + OpenShockLiveControlClient controlClient = new(gateway.Gateway, hubId, apiToken, _loggerFactory.CreateLogger()); + await controlClient.InitializeAsync(); + + while (true) + { + Console.ReadLine(); + controlClient.IntakeFrame(shockerId, ControlType.Vibrate, 100); + Console.WriteLine("Sent frame"); + } + } +} \ No newline at end of file diff --git a/SDK.CSharp.Example/Program.cs b/SDK.CSharp.Example/Program.cs index 28d3372..e7f1ff8 100644 --- a/SDK.CSharp.Example/Program.cs +++ b/SDK.CSharp.Example/Program.cs @@ -1,17 +1,15 @@ -using Microsoft.Extensions.DependencyInjection; +using System.Collections.Immutable; +using System.Text.Json; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using OpenShock.SDK.CSharp; -using OpenShock.SDK.CSharp.Hub; -using OpenShock.SDK.CSharp.Live; -using OpenShock.SDK.CSharp.Models; +using SDK.CSharp.Example; using Serilog; -using Control = OpenShock.SDK.CSharp.Hub.Models.Control; -const string apiToken = ""; -var deviceId = Guid.Parse("bc849182-89e0-43ff-817b-32400be3f97d"); -var hostBuilder = Host.CreateDefaultBuilder(); +var hostBuilder = Host.CreateApplicationBuilder(); var loggerConfiguration = new LoggerConfiguration() .MinimumLevel.Verbose() @@ -21,74 +19,57 @@ Log.Logger = loggerConfiguration.CreateLogger(); -hostBuilder.UseSerilog(Log.Logger); +hostBuilder.Logging.ClearProviders(); +hostBuilder.Logging.AddSerilog(); -var host = hostBuilder.Build(); - -var logger = host.Services.GetRequiredService>(); - -var apiClient = new OpenShockApiClient(new ApiClientOptions +var exampleType = typeof(IExample); +typeof(Program).Assembly.GetTypes().Where(x => x.IsClass && x.IsAssignableTo(exampleType)).ToList().ForEach(x => { - Token = apiToken + hostBuilder.Services.TryAddEnumerable(new ServiceDescriptor(exampleType, x, ServiceLifetime.Singleton)); }); -var shockers = await apiClient.GetOwnShockers(); +var config = hostBuilder.Configuration.Get()!; +hostBuilder.Services.AddSingleton(config); +Console.WriteLine(JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true })); -if (!shockers.IsT0) -{ - logger.LogError("Failed to get own shockers, make sure you used a valid api token"); - return; -} +var host = hostBuilder.Build(); -var apiSignalRHubClient = new OpenShockHubClient(new HubClientOptions +var console = new Thread(async () => { - Token = apiToken, - ConfigureLogging = builder => builder.AddSerilog(Log.Logger) -}); - -await apiSignalRHubClient.StartAsync(); + Console.WriteLine("OpenShock Example Program..."); + + var examples = host.Services.GetServices().ToImmutableArray(); + + Console.WriteLine($"Available Examples:"); -await apiSignalRHubClient.Control([ - new Control + for (int i = 0; i < examples.Length; i++) { - Id = Guid.Parse("d9267ca6-d69b-4b7a-b482-c455f75a4408"), - Type = ControlType.Shock, - Intensity = 10, - Duration = 1000, - Exclusive = true + var example = examples[i]; + Console.WriteLine($"[{i}] {example.GetType().Name}"); } -]); -var gatewayRequest = await apiClient.GetDeviceGateway(deviceId); - -if (gatewayRequest.IsT1) -{ - logger.LogError("Failed to get gateway, make sure you used a valid device id"); - return; -} - -if (gatewayRequest.IsT2) -{ - logger.LogError("Device is offline"); - return; -} - -if (gatewayRequest.IsT3) -{ - logger.LogError("Device is not connected to a gateway"); - return; -} + while (true) + { + var input = Console.ReadLine(); + if (!int.TryParse(input, out var choice)) + { + Console.WriteLine("Invalid choice"); + continue; + } + + var example = examples[choice]; + + if (examples == null) Console.WriteLine("Invalid choice, example doesnt exist"); + + Console.WriteLine($"Starting example {example.GetType().Name}"); + + example.Start().Wait(); + } -var gateway = gatewayRequest.AsT0.Value; +}); -logger.LogInformation("Device is connected to gateway {GatewayId} in region {Region}", gateway.Gateway, gateway.Country); +console.IsBackground = true; -OpenShockLiveControlClient controlClient = new(gateway.Gateway, deviceId, apiToken, host.Services.GetRequiredService>()); -await controlClient.InitializeAsync(); +console.Start(); -while (true) -{ - Console.ReadLine(); - controlClient.IntakeFrame(Guid.Parse("d9267ca6-d69b-4b7a-b482-c455f75a4408"), ControlType.Vibrate, 100); - Console.WriteLine("Sent frame"); -} \ No newline at end of file +await host.RunAsync(); \ No newline at end of file diff --git a/SDK.CSharp.Example/SignalrHub.cs b/SDK.CSharp.Example/SignalrHub.cs new file mode 100644 index 0000000..46c4b09 --- /dev/null +++ b/SDK.CSharp.Example/SignalrHub.cs @@ -0,0 +1,40 @@ +using OpenShock.SDK.CSharp.Hub; +using OpenShock.SDK.CSharp.Models; +using Serilog; + +namespace SDK.CSharp.Example; + +public sealed class SignalrHub : IExample +{ + private readonly ExampleConfig _config; + + public SignalrHub(ExampleConfig config) + { + _config = config; + } + + public async Task Start() + { + var apiSignalRHubClient = new OpenShockHubClient(new HubClientOptions + { + Server = _config.ApiUrl, + Token = _config.ApiToken, + ConfigureLogging = builder => builder.AddSerilog(Log.Logger) + }); + + await apiSignalRHubClient.StartAsync(); + + + + await apiSignalRHubClient.Control(_config.Shockers.Select(x => new OpenShock.SDK.CSharp.Hub.Models.Control + { + Id = x, + Type = ControlType.Shock, + Intensity = 10, + Duration = 1000, + Exclusive = true + })); + + + } +} \ No newline at end of file diff --git a/SDK.CSharp/IOpenShockApiClient.cs b/SDK.CSharp/IOpenShockApiClient.cs index 7df7f74..073213c 100644 --- a/SDK.CSharp/IOpenShockApiClient.cs +++ b/SDK.CSharp/IOpenShockApiClient.cs @@ -61,7 +61,8 @@ public Task, NotFound, DeviceOffline, DeviceNotConnec /// True when the shocker needs to be paused /// /// bool that indicates the current state of the shocker pause - public Task, NotFound>> PauseShocker(Guid shockerId, bool paused, CancellationToken cancellationToken = default); + public Task, NotFound>> PauseShocker(Guid shockerId, bool paused, + CancellationToken cancellationToken = default); } public struct DeviceOffline; diff --git a/SDK.CSharp/OpenShockApiClient.cs b/SDK.CSharp/OpenShockApiClient.cs index 2a7c978..b9ba0a8 100644 --- a/SDK.CSharp/OpenShockApiClient.cs +++ b/SDK.CSharp/OpenShockApiClient.cs @@ -190,7 +190,7 @@ await pauseResponse.Content.ReadBaseResponseAsJsonAsync(cancellationToken, var problem = await pauseResponse.Content.ReadAsJsonAsync(cancellationToken, JsonSerializerOptions); if (problem.Type == "Shocker.NotFound") return new NotFound(); - throw new OpenShockApiError("Failed to pause shocker", pauseResponse.StatusCode); + throw new OpenShockApiError("Failed to pause shocker", problem); } private string GetUserAgent() diff --git a/SDK.CSharp/OpenShockApiError.cs b/SDK.CSharp/OpenShockApiError.cs index c02d22d..b81bcb2 100644 --- a/SDK.CSharp/OpenShockApiError.cs +++ b/SDK.CSharp/OpenShockApiError.cs @@ -1,10 +1,19 @@ using System.Net; +using OpenShock.SDK.CSharp.Problems; namespace OpenShock.SDK.CSharp; public sealed class OpenShockApiError : Exception { - public OpenShockApiError(string message, HttpStatusCode statusCode) : base(message) + public OpenShockApiError(string message, HttpStatusCode statusCode) : base($"{message} (HTTP {(int)statusCode})") { } + + public OpenShockApiError(string message, ProblemDetails problemDetails) : base( + $"{message} (HTTP {problemDetails.Status}; {problemDetails.Title}; {problemDetails.Detail})") + { + ProblemDetails = problemDetails; + } + + public ProblemDetails? ProblemDetails { get; } } \ No newline at end of file