├── Blazm.Bluetooth ├── _Imports.razor ├── ServiceExtensions.cs ├── Blazm.Bluetooth.csproj ├── IBluetoothNavigator.cs ├── RequestDeviceQuery.cs ├── Device.cs ├── wwwroot │ └── Blazm.Bluetooth.js └── BluetoothNavigator.cs ├── .github └── FUNDING.yml ├── BlazorWebAssemblySample ├── wwwroot │ ├── favicon.ico │ ├── icon-192.png │ ├── Robosapien.png │ ├── css │ │ ├── open-iconic │ │ │ ├── font │ │ │ │ ├── fonts │ │ │ │ │ ├── open-iconic.eot │ │ │ │ │ ├── open-iconic.otf │ │ │ │ │ ├── open-iconic.ttf │ │ │ │ │ ├── open-iconic.woff │ │ │ │ │ └── open-iconic.svg │ │ │ │ └── css │ │ │ │ │ └── open-iconic-bootstrap.min.css │ │ │ ├── ICON-LICENSE │ │ │ ├── README.md │ │ │ └── FONT-LICENSE │ │ └── app.css │ ├── index.html │ └── Script │ │ └── joy.js ├── Devices │ └── Wowwee │ │ ├── Mip │ │ ├── Models │ │ │ ├── MiP │ │ │ │ └── PositionEnum.cs │ │ │ ├── MiPosaur │ │ │ │ └── PositionEnum.cs │ │ │ ├── GetUpEnum.cs │ │ │ ├── ClapStatus.cs │ │ │ ├── EepromData.cs │ │ │ ├── MiPDetection.cs │ │ │ ├── MiPHardwareVersion.cs │ │ │ ├── ChestLED.cs │ │ │ ├── DirectionEnum.cs │ │ │ ├── TurnEnum.cs │ │ │ ├── HeadLED.cs │ │ │ ├── StatusEnum.cs │ │ │ ├── LightEnum.cs │ │ │ ├── MiPStatus.cs │ │ │ ├── GestureRadarEnum.cs │ │ │ ├── MiPSound.cs │ │ │ ├── GestureEum.cs │ │ │ └── GameModeEmum.cs │ │ └── MiPRobot.cs │ │ ├── Robosapien.cs │ │ └── MiPBase.cs ├── Pages │ ├── Counter.razor │ ├── BatteryServiceDemo.razor │ ├── DeviceInformationDemo.razor │ ├── AnderssonScaleDemo.razor │ ├── ChristmasDemo.razor │ ├── MiPDemo.razor │ ├── RobosapienDemo.razor │ └── DeadpoolsHeadDemo.razor ├── Shared │ ├── MainLayout.razor │ ├── NavMenu.razor.css │ ├── MainLayout.razor.css │ └── NavMenu.razor ├── _Imports.razor ├── App.razor ├── Program.cs ├── BlazorWebAssemblySample.csproj ├── Properties │ └── launchSettings.json ├── Components │ └── VirtualJoystick.razor └── Script │ └── joy.js ├── Blazm.Bluetooth.sln ├── README.md ├── Demo.md ├── .gitignore └── DeadpoolsHead.md /Blazm.Bluetooth/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: EngstromJimmy 4 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EngstromJimmy/Blazm.Bluetooth/main/BlazorWebAssemblySample/wwwroot/favicon.ico -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EngstromJimmy/Blazm.Bluetooth/main/BlazorWebAssemblySample/wwwroot/icon-192.png -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/Robosapien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EngstromJimmy/Blazm.Bluetooth/main/BlazorWebAssemblySample/wwwroot/Robosapien.png -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EngstromJimmy/Blazm.Bluetooth/main/BlazorWebAssemblySample/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EngstromJimmy/Blazm.Bluetooth/main/BlazorWebAssemblySample/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EngstromJimmy/Blazm.Bluetooth/main/BlazorWebAssemblySample/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EngstromJimmy/Blazm.Bluetooth/main/BlazorWebAssemblySample/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/MiP/PositionEnum.cs: -------------------------------------------------------------------------------- 1 | namespace MiPWinRTSDK.MiP.Models.MiP 2 | { 3 | public enum PositionEnum 4 | { 5 | OnBack = 0, 6 | FaceDown = 1, 7 | } 8 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/MiPosaur/PositionEnum.cs: -------------------------------------------------------------------------------- 1 | namespace MiPWinRTSDK.MiP.Models.MiPosaur 2 | { 3 | public enum PositionEnum 4 | { 5 | OnBack =0, 6 | FaceDown =1, 7 | UpRight=2 8 | } 9 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/GetUpEnum.cs: -------------------------------------------------------------------------------- 1 | namespace MiPWinRTSDK.MiP.Models 2 | { 3 | public enum GetUpEnum 4 | { 5 | Front =0x00, 6 | Back =0x01, 7 | FrontOrBack =0x03 8 | } 9 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/ClapStatus.cs: -------------------------------------------------------------------------------- 1 | namespace MiPWinRTSDK.MiP.Models 2 | { 3 | public sealed class ClapStatus 4 | { 5 | public int DelayTime { get; internal set; } 6 | public bool Enabled { get; internal set; } 7 | } 8 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/EepromData.cs: -------------------------------------------------------------------------------- 1 | namespace MiPWinRTSDK.MiP.Models 2 | { 3 | public sealed class EepromData 4 | { 5 | public object Address { get; internal set; } 6 | public byte Data { get; internal set; } 7 | } 8 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/MiPDetection.cs: -------------------------------------------------------------------------------- 1 | namespace MiPWinRTSDK.MiP.Models 2 | { 3 | public sealed class MiPDetectionStatus 4 | { 5 | public byte IRTxPower { get; internal set; } 6 | public byte Mode { get; internal set; } 7 | } 8 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/MiPHardwareVersion.cs: -------------------------------------------------------------------------------- 1 | namespace MiPWinRTSDK.MiP.Models 2 | { 3 | public sealed class MiPHardwareVersion 4 | { 5 | public byte HardwareVersion { get; internal set; } 6 | public byte VoiceChipVersion { get; internal set; } 7 | } 8 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/ChestLED.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace MiPWinRTSDK.MiP.Models 4 | { 5 | public sealed class ChestLED 6 | { 7 | public Color Color { get; internal set; } 8 | public byte TimeOff { get; internal set; } 9 | public byte TimeOn { get; internal set; } 10 | } 11 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/DirectionEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MiPWinRTSDK.MiP.Models 8 | { 9 | public enum DirectionEnum 10 | { 11 | Forward=0x00, 12 | Backward=0x01 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/TurnEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MiPWinRTSDK.MiP.Models 8 | { 9 | public enum TurnEnum 10 | { 11 | TurnClockwise =0x01, 12 | AntiClockwise =0x00 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Blazm.Bluetooth/ServiceExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Blazm.Bluetooth; 4 | 5 | public static class ServiceExtensions 6 | { 7 | public static IServiceCollection AddBlazmBluetooth(this IServiceCollection services) 8 | { 9 | return services.AddTransient(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/HeadLED.cs: -------------------------------------------------------------------------------- 1 | namespace MiPWinRTSDK.MiP.Models 2 | { 3 | public sealed class HeadLED 4 | { 5 | public LightEnum Light1 { get; internal set; } 6 | public LightEnum Light2 { get; internal set; } 7 | public LightEnum Light3 { get; internal set; } 8 | public LightEnum Light4 { get; internal set; } 9 | } 10 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/StatusEnum.cs: -------------------------------------------------------------------------------- 1 | namespace MiPWinRTSDK.MiP.Models 2 | { 3 | public enum StatusEnum 4 | { 5 | OnBack =0x00, 6 | FaceDown =0x01, 7 | UpRight =0x02, 8 | PickedUp =0x03, 9 | HandStand =0x04, 10 | FaceDownOnTray =0x05, 11 | OnBackWithKickStand =0x06 12 | } 13 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/LightEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MiPWinRTSDK.MiP.Models 8 | { 9 | public enum LightEnum 10 | { 11 | Off =0x00, 12 | On =0x01, 13 | BlinkSlow =0x02, 14 | BlinkFast =0x03 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/MiPStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MiPWinRTSDK.MiP.Models 8 | { 9 | public sealed class MiPStatus 10 | { 11 | public byte BatteryLevel { get; internal set; } 12 | public StatusEnum Status { get; internal set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | Counter 4 | 5 |

Counter

6 | 7 |

Current count: @currentCount

8 | 9 | 10 | 11 | @code { 12 | private int currentCount = 0; 13 | 14 | private void IncrementCount() 15 | { 16 | currentCount++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/GestureRadarEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MiPWinRTSDK.MiP.Models 8 | { 9 | public enum GestureRadarEnum 10 | { 11 | DisableGestureAndRadar =0x00, 12 | GestureModeOn =0x02, 13 | RadarModeOn =0x04 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/MiPSound.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MiPWinRTSDK.MiP.Models 8 | { 9 | public sealed class MiPSound 10 | { 11 | public byte? Sound { get; set; } 12 | public byte? Volume { get; set; } 13 | public byte Delay { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using BlazorWebAssemblySample 10 | @using BlazorWebAssemblySample.Shared 11 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/GestureEum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace MiPWinRTSDK.MiP.Models 8 | { 9 | public enum GestureEnum 10 | { 11 | Left =0x0a, 12 | Right =0x0b, 13 | CenterSweepLeft =0x0c, 14 | CenterSweepRight =0x0D, 15 | CenterHold =0x0E, 16 | Forward =0x0f, 17 | Back =0x10 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Program.cs: -------------------------------------------------------------------------------- 1 | using Blazm.Bluetooth; 2 | using BlazorWebAssemblySample; 3 | using Microsoft.AspNetCore.Components.Web; 4 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 5 | using MiPWinRTSDK.MiP; 6 | 7 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 8 | builder.RootComponents.Add("#app"); 9 | builder.RootComponents.Add("head::after"); 10 | 11 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 12 | builder.Services.AddScoped(); 13 | builder.Services.AddBlazmBluetooth(); 14 | 15 | await builder.Build().RunAsync(); 16 | -------------------------------------------------------------------------------- /Blazm.Bluetooth/Blazm.Bluetooth.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | disable 6 | True 7 | MIT 8 | 1.0.1 9 | https://github.com/EngstromJimmy/Blazm.Bluetooth 10 | blazor;bluetooth 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BlazorWebAssemblySample 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
Loading...
16 | 17 |
18 | An unhandled error has occurred. 19 | Reload 20 | 🗙 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/BlazorWebAssemblySample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | disable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | true 16 | PreserveNewest 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Pages/BatteryServiceDemo.razor: -------------------------------------------------------------------------------- 1 | @page "/BatteryServiceDemo" 2 | @using Blazm.Bluetooth 3 | @inject IBluetoothNavigator navigator 4 | 5 | 6 | 7 |

BatteryService

8 | 9 | @batterylevel 10 | 11 | 12 | 13 | @code { 14 | 15 | int batterylevel = 0; 16 | 17 | protected async Task Connect() 18 | { 19 | var serviceid = "0000180f-0000-1000-8000-00805f9b34fb"; 20 | var characteristics= "00002a19-0000-1000-8000-00805f9b34fb"; 21 | 22 | var q = new RequestDeviceQuery(); 23 | q.Filters.Add(new Filter() { Name = "battery_service" }); 24 | var device=await navigator.RequestDeviceAsync(q); 25 | 26 | await device.SetupNotifyAsync(serviceid, characteristics); 27 | device.Notification += Value_Notification; 28 | } 29 | 30 | 31 | private void Value_Notification(object sender, CharacteristicEventArgs e) 32 | { 33 | batterylevel = e.Value.FirstOrDefault(); 34 | StateHasChanged(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Blazm.Bluetooth/IBluetoothNavigator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Blazm.Bluetooth; 5 | 6 | public interface IBluetoothNavigator 7 | { 8 | Task RequestDeviceAsync(RequestDeviceQuery query); 9 | Task WriteValueAsync(string deviceId, Guid serviceId, Guid characteristicId, byte[] value); 10 | Task WriteValueAsync(string deviceId, Guid serviceId, string characteristicId, byte[] value); 11 | Task WriteValueAsync(string deviceId, string serviceId, Guid characteristicId, byte[] value); 12 | Task WriteValueAsync(string deviceId, string serviceId, string characteristicId, byte[] value); 13 | Task ReadValueAsync(string deviceId, string serviceId, string characteristicId); 14 | Task ReadValueAsync(string deviceId, Guid serviceId, string characteristicId); 15 | Task ReadValueAsync(string deviceId, string serviceId, Guid characteristicId); 16 | Task ReadValueAsync(string deviceId, Guid serviceId, Guid characteristicId); 17 | Task SetupNotifyAsync(Device device, string serviceId, string characteristicId); 18 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:22866", 7 | "sslPort": 44333 8 | } 9 | }, 10 | "profiles": { 11 | "BlazorWebAssemblySample": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:7101;http://localhost:5101", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Components/VirtualJoystick.razor: -------------------------------------------------------------------------------- 1 | @inject IJSRuntime jsruntime 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | @code{ 10 | 11 | 12 | protected async override Task OnAfterRenderAsync(bool firstRender) 13 | { 14 | await jsruntime.InvokeVoidAsync("blazmjoystick.init", "joycontainer"); 15 | 16 | 17 | 18 | System.Threading.Timer timer = new System.Threading.Timer(async (object state) => 19 | { 20 | var pos = await jsruntime.InvokeAsync("blazmjoystick.getposition"); 21 | if (pos.X != 0 || pos.Y != 0) 22 | { 23 | JoystickPositionChanged?.Invoke(this, pos); 24 | } 25 | 26 | }, null, 0, 50); 27 | 28 | await base.OnAfterRenderAsync(firstRender); 29 | } 30 | 31 | public event EventHandler JoystickPositionChanged; 32 | 33 | 34 | public class JoyPosition 35 | { 36 | public int X { get; set; } 37 | public int Y { get; set; } 38 | } 39 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Pages/DeviceInformationDemo.razor: -------------------------------------------------------------------------------- 1 | @page "/deviceinformationdemo" 2 | 3 | @using Blazm.Bluetooth 4 | @inject IBluetoothNavigator navigator 5 | 6 | 7 |

Device Information

8 | 9 | 10 | 11 | @firmware 12 | @code { 13 | 14 | string firmware = ""; 15 | protected async Task Connect() 16 | { 17 | 18 | var serviceid = "device_information"; 19 | 20 | var q = new RequestDeviceQuery(); 21 | //Filtering on Sensun scale service 22 | q.Filters.Add(new Filter() { Services = { "0000ffb0-0000-1000-8000-00805f9b34fb" } }); 23 | q.OptionalServices.Add(serviceid); 24 | var device = await navigator.RequestDeviceAsync(q); 25 | 26 | //<1. Add Read> 27 | var bytes = await device.ReadValueAsync(serviceid, "firmware_revision_string"); 28 | firmware = System.Text.UTF8Encoding.UTF8.GetString(bytes); 29 | // 30 | 31 | int i = firmware.IndexOf('\0'); 32 | if (i >= 0) 33 | { 34 | firmware = firmware.Substring(0, i); 35 | } 36 | 37 | StateHasChanged(); 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Blazm.Bluetooth/RequestDeviceQuery.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Blazm.Bluetooth; 6 | 7 | [Serializable] 8 | public class RequestDeviceQuery 9 | { 10 | [JsonProperty(propertyName: "filters")] 11 | public List Filters { get; set; } = new List(); 12 | [JsonProperty(propertyName: "acceptAllDevices")] 13 | public bool? AcceptAllDevices { get; set; } = null; 14 | [JsonProperty(propertyName: "optionalServices")] 15 | public List OptionalServices { get; set; } = new List(); 16 | public bool ShouldSerializeFilters() 17 | { 18 | return Filters.Count > 0; 19 | } 20 | public bool ShouldSerializeOptionalServices() 21 | { 22 | return OptionalServices.Count > 0; 23 | } 24 | } 25 | 26 | [Serializable] 27 | public class Filter 28 | { 29 | [JsonProperty(propertyName: "services")] 30 | public List Services { get; set; } = new List(); 31 | [JsonProperty(propertyName: "name")] 32 | public string Name { get; set; } 33 | [JsonProperty(propertyName: "namePrefix")] 34 | public string NamePrefix { get; set; } 35 | public bool ShouldSerializeServices() 36 | { 37 | return Services.Count > 0; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Blazm.Bluetooth/Device.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | namespace Blazm.Bluetooth; 6 | 7 | [Serializable] 8 | public class Device 9 | { 10 | private IBluetoothNavigator _webBluetoothNavigator; 11 | public void InitDevice(IBluetoothNavigator bluetoothNavigator) 12 | { 13 | _webBluetoothNavigator = bluetoothNavigator; 14 | } 15 | 16 | public string Id { get; set; } 17 | public string Name { get; set; } 18 | 19 | public async Task WriteValueAsync(string serviceId, string characteristicId, byte[] value) 20 | { 21 | await _webBluetoothNavigator.WriteValueAsync(this.Id, serviceId, characteristicId, value); 22 | } 23 | 24 | public async Task ReadValueAsync(string serviceId, string characteristicId) 25 | { 26 | return await _webBluetoothNavigator.ReadValueAsync(this.Id, serviceId, characteristicId); 27 | } 28 | 29 | public async Task SetupNotifyAsync(string serviceId, string characteristicId) 30 | { 31 | await _webBluetoothNavigator.SetupNotifyAsync(this, serviceId, characteristicId); 32 | } 33 | 34 | [JSInvokable] 35 | public void HandleCharacteristicValueChanged(string serviceId, string characteristicsId, byte[] data) 36 | { 37 | Notification?.Invoke(this, new CharacteristicEventArgs() { CharacteristicId = Guid.Parse(characteristicsId), ServiceId = Guid.Parse(serviceId), Value = data }); 38 | } 39 | public event EventHandler Notification; 40 | } 41 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/Models/GameModeEmum.cs: -------------------------------------------------------------------------------- 1 | namespace MiPWinRTSDK.MiP.Models 2 | { 3 | public enum GameModeEmum 4 | { 5 | App =0x01, 6 | CagePlayBack =0x02, 7 | Tracking =0x03, 8 | DancePlayBack =0x04, 9 | DefaultMipMode =0x05, 10 | StackPlayBack =0x06, 11 | TrickProgrammingAndPlayback =0x07, 12 | RoamModePlayBack =0x08 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Pages/AnderssonScaleDemo.razor: -------------------------------------------------------------------------------- 1 | @page "/AnderssonScaleDemo" 2 | @page "/" 3 | @*<1. AddNamespace>*@ 4 | @using Blazm.Bluetooth 5 | @**@ 6 | @*<2. AddBluetoothNavigator>*@ 7 | @inject IBluetoothNavigator navigator 8 | @**@ 9 | 10 | 11 |

AnderssonScale

12 | @*<4. ShowWeight>*@ 13 | @weight 14 | @**@ 15 | 16 | 17 | @code { 18 | @*<3. AddWeightVariable>*@ 19 | int weight = 0; 20 | @**@ 21 | 22 | protected async Task Connect() 23 | { 24 | var serviceid = "0000ffb0-0000-1000-8000-00805f9b34fb"; 25 | var characteristics = "0000ffb2-0000-1000-8000-00805f9b34fb"; 26 | 27 | @*<5. AddFilter>*@ 28 | var q = new RequestDeviceQuery(); 29 | q.Filters.Add(new Filter() { Services = { serviceid } }); 30 | @**@ 31 | 32 | @*<6. RequestDevice>*@ 33 | var device = await navigator.RequestDeviceAsync(q); 34 | @**@ 35 | 36 | @*<7. Setup notification>*@ 37 | await device.SetupNotifyAsync(serviceid, characteristics); 38 | device.Notification += Value_Notification; 39 | @**@ 40 | } 41 | 42 | @*<8. HandleNotification>*@ 43 | private void Value_Notification(object sender, CharacteristicEventArgs e) 44 | { 45 | var data = e.Value; 46 | weight = (256 * data[4]) + data[5]; 47 | if (data[7] == 1) 48 | { 49 | weight *= -1; 50 | } 51 | 52 | StateHasChanged(); 53 | } 54 | @**@ 55 | } 56 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Pages/ChristmasDemo.razor: -------------------------------------------------------------------------------- 1 | @page "/christmas" 2 | 3 | @using Blazm.Bluetooth 4 | @inject IBluetoothNavigator navigator 5 | 6 | 7 |

Christmas lights

8 | 9 |
10 | @if(device!=null) 11 | { 12 |
13 |
14 |
15 | } 16 | 17 | @code { 18 | Device device = null; 19 | 20 | 21 | string serviceid = "00007777-0000-1000-8000-00805f9b34fb"; 22 | string serviceid2 = "00006666-0000-1000-8000-00805f9b34fb"; 23 | string characteristicsid = "00008877-0000-1000-8000-00805f9b34fb"; 24 | protected async Task Connect() 25 | { 26 | var q = new RequestDeviceQuery(); 27 | q.Filters.Add(new Filter() { Services = { serviceid2 } }); 28 | q.OptionalServices.Add(serviceid); 29 | device = await navigator.RequestDeviceAsync(q); 30 | 31 | StateHasChanged(); 32 | } 33 | 34 | protected async Task Green() 35 | { 36 | var signed=new sbyte[]{ -91, 90, 85, 0, 127, 0 }; 37 | var value = (byte[])(Array)signed; 38 | await device.WriteValueAsync(serviceid,characteristicsid,value); 39 | } 40 | 41 | protected async Task Red() 42 | { 43 | var signed = new sbyte[] { -91, 90, 85, 127, 0, 0 }; 44 | var value = (byte[])(Array)signed; 45 | await device.WriteValueAsync(serviceid,characteristicsid,value); 46 | } 47 | 48 | protected async Task Blue() 49 | { 50 | var signed = new sbyte[] { -91, 90, 85, 0, 0, 127 }; 51 | var value = (byte[])(Array)signed; 52 | await device.WriteValueAsync(serviceid,characteristicsid,value); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Shared/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row:not(.auth) { 41 | display: none; 42 | } 43 | 44 | .top-row.auth { 45 | justify-content: space-between; 46 | } 47 | 48 | .top-row ::deep a, .top-row ::deep .btn-link { 49 | margin-left: 0; 50 | } 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .page { 55 | flex-direction: row; 56 | } 57 | 58 | .sidebar { 59 | width: 250px; 60 | height: 100vh; 61 | position: sticky; 62 | top: 0; 63 | } 64 | 65 | .top-row { 66 | position: sticky; 67 | top: 0; 68 | z-index: 1; 69 | } 70 | 71 | .top-row.auth ::deep a:first-child { 72 | flex: 1; 73 | text-align: right; 74 | width: 0; 75 | } 76 | 77 | .top-row, article { 78 | padding-left: 2rem !important; 79 | padding-right: 1.5rem !important; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Blazm.Bluetooth.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31825.309 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazm.Bluetooth", "Blazm.Bluetooth\Blazm.Bluetooth.csproj", "{8F3C849C-B968-4707-9386-18C7C47821C6}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9186B306-C357-4957-B55C-2AA0E26D2F7D}" 9 | ProjectSection(SolutionItems) = preProject 10 | DeadpoolsHead.md = DeadpoolsHead.md 11 | Demo.md = Demo.md 12 | README.md = README.md 13 | EndProjectSection 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWebAssemblySample", "BlazorWebAssemblySample\BlazorWebAssemblySample.csproj", "{966E39EB-EB45-4EB6-9078-B2FB88E97961}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {8F3C849C-B968-4707-9386-18C7C47821C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {8F3C849C-B968-4707-9386-18C7C47821C6}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {8F3C849C-B968-4707-9386-18C7C47821C6}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {8F3C849C-B968-4707-9386-18C7C47821C6}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {966E39EB-EB45-4EB6-9078-B2FB88E97961}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {966E39EB-EB45-4EB6-9078-B2FB88E97961}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {966E39EB-EB45-4EB6-9078-B2FB88E97961}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {966E39EB-EB45-4EB6-9078-B2FB88E97961}.Release|Any CPU.Build.0 = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(SolutionProperties) = preSolution 33 | HideSolutionNode = FALSE 34 | EndGlobalSection 35 | GlobalSection(ExtensibilityGlobals) = postSolution 36 | SolutionGuid = {EF4A2350-F375-4EAD-8FBF-AF9EC65EBBEE} 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Robosapien.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWebAssemblySample.Devices.Wowwee; 2 | 3 | public class Robosapien 4 | { 5 | public static byte LeftShoulderBackward = 0x8c; 6 | public static byte RightShoulderForward = 0x81; 7 | public static byte RightShoulderBackward = 0x84; 8 | public static byte LeftHandForward = 0x8a; 9 | public static byte LeftHandBackward = 0x8d; 10 | public static byte RightHandForward = 0x82; 11 | public static byte RightHandBackward = 0x85; 12 | public static byte WaistLeft = 0x8b; 13 | public static byte WaistRight = 0x83; 14 | public static byte Up = 0x86; 15 | public static byte Down = 0x87; 16 | public static byte Right = 0x80; 17 | public static byte Left = 0x88; 18 | public static byte Stop = 0x8e; 19 | public static byte LeanForward = 0xad; 20 | public static byte LeanBackward = 0xa5; 21 | public static byte Demo1 = 0xd2; 22 | public static byte Demo2 = 0xd3; 23 | public static byte SFXWhistle = 0xca; 24 | public static byte SFXBurp = 0xc2; 25 | public static byte SFXRoar = 0xce; 26 | public static byte SFXOops = 0xc7; 27 | public static byte Sleep = 0xa3; 28 | public static byte Listen = 0xab; 29 | public static byte LeftArmPickUp = 0xac; 30 | public static byte LeftArmThump = 0xa9; 31 | public static byte LeftArmSweep = 0xc9; 32 | public static byte LeftArmThrow = 0xaa; 33 | public static byte RightArmPickUp = 0xa4; 34 | public static byte RightArmThump = 0xa1; 35 | public static byte RightArmSweep = 0xc1; 36 | public static byte RightArmThrow = 0xa2; 37 | public static byte LeftArmStrike1 = 0xcd; 38 | public static byte LeftArmStrike2 = 0xcb; 39 | public static byte TalkBack = 0xcc; 40 | public static byte LeftArmStrike3 = 0xc8; 41 | public static byte RightArmStrike1 = 0xc5; 42 | public static byte RightArmStrike2 = 0xc3; 43 | public static byte LeftShoulderForward = 0x89; 44 | public static byte RightArmHigh5 = 0xc4; 45 | public static byte RightArmStrike3 = 0xc0; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 | 43 |
44 | 45 | @code { 46 | private bool collapseNavMenu = true; 47 | 48 | private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; 49 | 50 | private void ToggleNavMenu() 51 | { 52 | collapseNavMenu = !collapseNavMenu; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Pages/MiPDemo.razor: -------------------------------------------------------------------------------- 1 | @page "/MipDemo" 2 | @using Blazm.Bluetooth 3 | @using BlazorWebAssemblySample.Components 4 | @using System.ComponentModel 5 | @inject IBluetoothNavigator navigator 6 | @inject MiPWinRTSDK.MiP.MiPRobot mip 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | @mip.BatteryLevel 15 | @mip.Status; 16 | 17 | 18 | 19 | @code{ 20 | VirtualJoystick vjoy; 21 | 22 | protected void RequestDevice() 23 | { 24 | mip.Connect(); 25 | mip.PropertyChanged += (object sender, PropertyChangedEventArgs e) => 26 | { 27 | InvokeAsync(() => 28 | { 29 | StateHasChanged(); 30 | }); 31 | }; 32 | vjoy.JoystickPositionChanged += async (object sender, VirtualJoystick.JoyPosition pos) => { 33 | 34 | byte drive = 0; 35 | byte spin = 0; 36 | if (pos.Y > 0) 37 | { 38 | drive = Normalize(pos.Y,0x01,0x20); 39 | } 40 | else 41 | { 42 | drive = Normalize(Math.Abs(pos.Y), 0x21, 0x40); 43 | } 44 | 45 | if (pos.X > 0) 46 | { 47 | spin = Normalize(pos.X, 0x41, 0x60); 48 | } 49 | else 50 | { 51 | spin = Normalize(Math.Abs(pos.X), 0x61, 0x80); 52 | } 53 | System.Diagnostics.Debug.WriteLine(drive); 54 | 55 | await mip.ContinuousDrive(drive, spin); 56 | }; 57 | } 58 | 59 | private byte Normalize(decimal value, byte min, byte max) 60 | { 61 | int interval=max-min; 62 | var result = min + Convert.ToInt32(Math.Round(interval * (value / 100))); 63 | if (result < min) return 0; 64 | if (result > max) return max; 65 | return (byte)(result & 0xFF); 66 | } 67 | 68 | protected async Task TurnRed() 69 | { 70 | await mip.SetChestLED(0xff, 0x00, 0x00); 71 | } 72 | 73 | 74 | protected async Task GetHardwareInfo() 75 | { 76 | await mip.GetHardwareInfo(); 77 | 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Pages/RobosapienDemo.razor: -------------------------------------------------------------------------------- 1 | @page "/RobosapienDemo" 2 | @using Blazm.Bluetooth 3 | @using Devices.Wowwee; 4 | @inject IBluetoothNavigator navigator 5 | 6 |

Robosapien Blue

7 | 8 |
9 |
10 | 11 | 12 |
13 | 14 | 15 |
16 | 17 |
18 |
19 | 20 | 21 | 22 |
23 | 24 |
25 | 26 |
27 | 28 | 29 |
30 | 31 | 32 |
33 |
34 | 35 | 36 | @code { 37 | 38 | Device robosapien; 39 | string serviceId; 40 | string characteristicId; 41 | 42 | protected async Task Connect() 43 | { 44 | serviceId = "0000ffe5-0000-1000-8000-00805f9b34fb"; 45 | characteristicId = "0000ffe9-0000-1000-8000-00805f9b34fb"; 46 | //<1. Add Robosapien> 47 | var q = new RequestDeviceQuery(); 48 | q.Filters.Add(new Filter() { Name = "Robosapien Blue" }); 49 | q.OptionalServices.Add(serviceId.ToString()); 50 | robosapien = await navigator.RequestDeviceAsync(q); 51 | // 52 | } 53 | 54 | public async Task SendCommand(byte command) 55 | { 56 | //<2. Send Command> 57 | await robosapien.WriteValueAsync(serviceId, characteristicId, new byte[] { command }); 58 | // 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Pages/DeadpoolsHeadDemo.razor: -------------------------------------------------------------------------------- 1 | @page "/Deadpool" 2 | @using Blazm.Bluetooth 3 | @inject IBluetoothNavigator navigator 4 | 5 |

Deadpools head

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

@command

17 | @code { 18 | System.Timers.Timer Timer = new System.Timers.Timer(5000); 19 | Device device; 20 | string serviceid = "7cd80aa0-b9ec-42cb-9ace-10389f7e2576"; 21 | string writecharacteristicid = "7cd801b0-b9ec-42cb-9ace-10389f7e2576"; 22 | string notificationcharacteristicid = "7cd802b0-b9ec-42cb-9ace-10389f7e2576"; 23 | byte command = 0; 24 | 25 | protected async Task ConnectAsync() 26 | { 27 | //===== Connect Bluetooth ===== 28 | Timer.Elapsed += async (object? sender, System.Timers.ElapsedEventArgs e) => { await AwakeAsync(); }; 29 | var q = new RequestDeviceQuery(); 30 | q.Filters.Add(new Filter() { Name = "WilsonBLE" }); 31 | q.OptionalServices.Add(serviceid); 32 | 33 | device = await navigator.RequestDeviceAsync(q); 34 | 35 | //Setting up notifications 36 | await device.SetupNotifyAsync(serviceid, notificationcharacteristicid); 37 | device.Notification += Value_Notification; 38 | 39 | //===== Connect App ===== 40 | await device.WriteValueAsync(serviceid, writecharacteristicid, new byte[] { 0x01, 0x00 }); 41 | await device.WriteValueAsync(serviceid, writecharacteristicid, new byte[] { 0x50, 0x8d }); //keep awake 42 | await device.WriteValueAsync(serviceid, writecharacteristicid, new byte[] { 0x04, 0x00, 0xf5, 0x8c, 0x54, 0xa4, 0xec, 0xb1 }); 43 | await device.WriteValueAsync(serviceid, writecharacteristicid, new byte[] { 0x50, 0x8d }); 44 | 45 | Timer.Start(); 46 | } 47 | 48 | protected async Task SendCommandAsync(byte command,byte section) 49 | { 50 | Timer.Stop(); 51 | var bytes = new byte[] { 0x17, 0x00,command, section }; 52 | Console.WriteLine("Sent:" + string.Join(',', bytes.Select(h => "0x" + h.ToString("X2")))); 53 | await device.WriteValueAsync(serviceid, writecharacteristicid, bytes); 54 | await Task.Delay(2000); 55 | await AwakeAsync(); 56 | StateHasChanged(); 57 | Timer.Start(); 58 | command++; 59 | } 60 | 61 | protected async Task AwakeAsync() 62 | { 63 | await device.WriteValueAsync(serviceid, writecharacteristicid, new byte[] { 0x50, 0x8d }); //keep awake 64 | } 65 | 66 | private void Value_Notification(object sender, CharacteristicEventArgs e) 67 | { 68 | if (e.Value.Length == 3 && e.Value[0] == 0x20 && e.Value[1] == 0x4E && e.Value[2] == 0x01) 69 | { 70 | Timer.Start(); 71 | } 72 | 73 | string values = ""; 74 | values = string.Join(',', e.Value.Select(h => "0x" + h.ToString("X2"))); 75 | Console.WriteLine(values); 76 | StateHasChanged(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); 2 | 3 | 4 | .buttonsPanel { 5 | float: left; 6 | width: 150px; 7 | height: 300px; 8 | } 9 | 10 | .connectButtons { 11 | margin: auto; 12 | width: 650px; 13 | } 14 | 15 | button { 16 | width: 200px; 17 | } 18 | 19 | 20 | 21 | 22 | html, body { 23 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 24 | } 25 | 26 | h1:focus { 27 | outline: none; 28 | } 29 | 30 | a, .btn-link { 31 | color: #0077cc; 32 | } 33 | 34 | .btn-primary { 35 | color: #fff; 36 | background-color: #1b6ec2; 37 | border-color: #1861ac; 38 | } 39 | 40 | .content { 41 | padding-top: 1.1rem; 42 | } 43 | 44 | .valid.modified:not([type=checkbox]) { 45 | outline: 1px solid #26b050; 46 | } 47 | 48 | .invalid { 49 | outline: 1px solid red; 50 | } 51 | 52 | .validation-message { 53 | color: red; 54 | } 55 | 56 | #blazor-error-ui { 57 | background: lightyellow; 58 | bottom: 0; 59 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 60 | display: none; 61 | left: 0; 62 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 63 | position: fixed; 64 | width: 100%; 65 | z-index: 1000; 66 | } 67 | 68 | #blazor-error-ui .dismiss { 69 | cursor: pointer; 70 | position: absolute; 71 | right: 0.75rem; 72 | top: 0.5rem; 73 | } 74 | 75 | .blazor-error-boundary { 76 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 77 | padding: 1rem 1rem 1rem 3.7rem; 78 | color: white; 79 | } 80 | 81 | .blazor-error-boundary::after { 82 | content: "An error has occurred." 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to use Blazm.Bluetooth 2 | 3 | Blazm.Bluetooth makes it easy to connect Blazor to your Bluetooth devices using Web Bluetooth. 4 | 5 | Works both Client-side and Server-side. 6 | 7 | ## Sponsors 8 | Thanks you to much to my sponsors! 9 | ![](https://raw.githubusercontent.com/EngstromJimmy/Blazm.Components/master/Display%20Ads%20Horizontal%20Leaderboard%20728x90%20TOP_RITM0148003.png) 10 | 11 | 12 | Telerik UI for Blazor – Increase productivity and cut cost in half! Use the Telerik truly native Blazor UI components and high-performing grid to cover any app scenario. [Give it a try for free.](https://www.telerik.com/blazor-ui?utm_source=jimmyengstrom&utm_medium=cpm&utm_campaign=blazor-trial-github-blazmcomp-sponsored-message ) 13 | 14 | 15 | ## Getting Started 16 | 17 | 1. Add Nuget package Blazm.Bluetooth 18 | 2. In Program.cs add ```builder.Services.AddBlazmBluetooth();``` 19 | 3. In the component you want to connect to a device add the Blazm.Bluetooth Namespace 20 | ```@using Blazm.Bluetooth``` 21 | 4. Inject the IBluetoothNavigator (the instance that will communicate with your device) 22 | ```@inject IBluetoothNavigator navigator``` 23 | 24 | Now you are all setup now it is time to connect to a device. 25 | 26 | ## Connecting to a device 27 | 28 | You need to create a query or a filter to filter devices, you could also specify ```AcceptAllDevices``` but that is only recommended while testing. 29 | To take a look at available devices you can use (for Microsoft Edge) edge://bluetooth-internals. 30 | You can query devices using a ServiceId or by name, you also have to specify all the services that you want to access in ```OptionalServices``` 31 | 32 | To connect to an Andersson (SenSun) scale you need to do the following (check Pages/AnderssonScaleDemo for more info) 33 | Specify the ServiceId and CharacteristicId you want to communicate with. 34 | 35 | ``` cs 36 | var serviceId = "0000ffb0-0000-1000-8000-00805f9b34fb"; 37 | var characteristicId = "0000ffb2-0000-1000-8000-00805f9b34fb"; 38 | ``` 39 | 40 | Create a filter 41 | 42 | ``` cs 43 | var q = new RequestDeviceQuery(); 44 | q.Filters.Add(new Filter() { Services = { serviceId } }); 45 | ``` 46 | 47 | Request a device 48 | 49 | ``` cs 50 | var device = await navigator.RequestDeviceAsync(q); 51 | ``` 52 | 53 | This will return a device and it contains an id that you can use to read, write, or set up notifications. 54 | Call the ```SetupNotifyAsync``` to get notifications when the value changes. 55 | 56 | ``` cs 57 | await device.SetupNotifyAsync(serviceid, characteristics); 58 | device.Notification += Value_Notification; 59 | ``` 60 | 61 | and add an event listener that parses the data from the notification. 62 | 63 | ``` cs 64 | private void Value_Notification(object sender, CharacteristicEventArgs e) 65 | { 66 | var data = e.Value.ToArray(); 67 | 68 | // Do something with the data 69 | 70 | StateHasChanged(); 71 | } 72 | ``` 73 | 74 | The same thing goes for read and write 75 | ``` cs 76 | //Write 77 | await device.WriteValueAsync(serviceid,characteristicsid,value); 78 | 79 | //Read 80 | var bytes = await device.ReadValueAsync(serviceid, characteristicId); 81 | ``` 82 | 83 | There is still many scenarios to implement but this should cover the basics. 84 | 85 | Please feel free to add ideas /problems/needs you might have or make a PR. 86 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/css/open-iconic/README.md: -------------------------------------------------------------------------------- 1 | [Open Iconic v1.1.1](http://useiconic.com/open) 2 | =========== 3 | 4 | ### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons) 5 | 6 | 7 | 8 | ## What's in Open Iconic? 9 | 10 | * 223 icons designed to be legible down to 8 pixels 11 | * Super-light SVG files - 61.8 for the entire set 12 | * SVG sprite—the modern replacement for icon fonts 13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats 14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats 15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px. 16 | 17 | 18 | ## Getting Started 19 | 20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections. 21 | 22 | ### General Usage 23 | 24 | #### Using Open Iconic's SVGs 25 | 26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute). 27 | 28 | ``` 29 | icon name 30 | ``` 31 | 32 | #### Using Open Iconic's SVG Sprite 33 | 34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack. 35 | 36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.* 37 | 38 | ``` 39 | 40 | 41 | 42 | ``` 43 | 44 | Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions. 45 | 46 | ``` 47 | .icon { 48 | width: 16px; 49 | height: 16px; 50 | } 51 | ``` 52 | 53 | Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag. 54 | 55 | ``` 56 | .icon-account-login { 57 | fill: #f00; 58 | } 59 | ``` 60 | 61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/). 62 | 63 | #### Using Open Iconic's Icon Font... 64 | 65 | 66 | ##### …with Bootstrap 67 | 68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}` 69 | 70 | 71 | ``` 72 | 73 | ``` 74 | 75 | 76 | ``` 77 | 78 | ``` 79 | 80 | ##### …with Foundation 81 | 82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}` 83 | 84 | ``` 85 | 86 | ``` 87 | 88 | 89 | ``` 90 | 91 | ``` 92 | 93 | ##### …on its own 94 | 95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}` 96 | 97 | ``` 98 | 99 | ``` 100 | 101 | ``` 102 | 103 | ``` 104 | 105 | 106 | ## License 107 | 108 | ### Icons 109 | 110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT). 111 | 112 | ### Fonts 113 | 114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web). 115 | -------------------------------------------------------------------------------- /Blazm.Bluetooth/wwwroot/Blazm.Bluetooth.js: -------------------------------------------------------------------------------- 1 | var PairedBluetoothDevices = []; 2 | export async function requestDevice(query) 3 | { 4 | var objquery = JSON.parse(query); 5 | console.log(query); 6 | 7 | var device = await navigator.bluetooth.requestDevice(objquery); 8 | await device.gatt.connect(); 9 | device.addEventListener('gattserverdisconnected', onDisconnected); 10 | PairedBluetoothDevices.push(device); 11 | return { "Name": device.name, "Id": device.id }; 12 | } 13 | 14 | 15 | export async function onDisconnected(arg) { 16 | console.log(arg.srcElement); 17 | console.log('> Bluetooth Device disconnected'); 18 | connect(arg.srcElement); 19 | } 20 | 21 | function connect(bluetoothDevice) { 22 | exponentialBackoff(3 /* max retries */, 1 /* seconds delay */, 23 | function toTry() { 24 | time('Connecting to Bluetooth Device... '); 25 | return bluetoothDevice.gatt.connect(); 26 | }, 27 | function success() { 28 | console.log('> Bluetooth Device connected. Try disconnect it now.'); 29 | }, 30 | function fail() { 31 | time('Failed to reconnect.'); 32 | }); 33 | } 34 | 35 | function exponentialBackoff(max, delay, toTry, success, fail) { 36 | toTry().then(result => success(result)) 37 | .catch(_ => { 38 | if (max === 0) { 39 | return fail(); 40 | } 41 | time('Retrying in ' + delay + 's... (' + max + ' tries left)'); 42 | setTimeout(function () { 43 | exponentialBackoff(--max, delay * 2, toTry, success, fail); 44 | }, delay * 1000); 45 | }); 46 | } 47 | 48 | function time(text) { 49 | console.log('[' + new Date().toJSON().substr(11, 8) + '] ' + text); 50 | } 51 | 52 | function getDevice(deviceId) { 53 | var device = PairedBluetoothDevices.filter(function (item) { 54 | return item.id == deviceId; 55 | }); 56 | return device[0]; 57 | } 58 | 59 | export async function writeValue(deviceId, serviceId, characteristicId, value) 60 | { 61 | var device = getDevice(deviceId); 62 | console.log("Found device" + device); 63 | if (device.gatt.connected) { 64 | var service = await device.gatt.getPrimaryService(serviceId); 65 | var characteristic = await service.getCharacteristic(characteristicId); 66 | var b = Uint8Array.from(value); 67 | await characteristic.writeValue(b); 68 | } 69 | else 70 | { 71 | await sleep(1000); 72 | await writeValue(deviceId, serviceId, characteristicId, value); 73 | } 74 | } 75 | 76 | function sleep(ms) { 77 | return new Promise(resolve => setTimeout(resolve, ms)); 78 | } 79 | 80 | export async function readValue(deviceId, serviceId, characteristicId) 81 | { 82 | var device = getDevice(deviceId); 83 | 84 | var service = await device.gatt.getPrimaryService(serviceId); 85 | var characteristic = await service.getCharacteristic(characteristicId); 86 | 87 | var value = await characteristic.readValue(); 88 | var uint8Array = new Uint8Array(value.buffer); 89 | var array = Array.from(uint8Array); 90 | return array; 91 | } 92 | 93 | var NotificationHandler = []; 94 | 95 | export async function setupNotify(deviceId, serviceId, characteristicId, notificationHandler) 96 | { 97 | console.log("Setting up"); 98 | var device = getDevice(deviceId); 99 | device.NotificationHandler = notificationHandler; 100 | var service = await device.gatt.getPrimaryService(serviceId); 101 | var characteristic = await service.getCharacteristic(characteristicId); 102 | await characteristic.startNotifications(); 103 | characteristic.addEventListener('characteristicvaluechanged', handleCharacteristicValueChanged); 104 | console.log("Characteristics listening"); 105 | } 106 | 107 | async function handleCharacteristicValueChanged(event) { 108 | var value = event.target.value; 109 | var deviceId = event.target.service.device.id; 110 | var uint8Array = new Uint8Array(value.buffer); 111 | var device = getDevice(deviceId); 112 | await device.NotificationHandler.invokeMethodAsync('HandleCharacteristicValueChanged', event.target.service.uuid, event.target.uuid, uint8Array); 113 | } -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/css/open-iconic/FONT-LICENSE: -------------------------------------------------------------------------------- 1 | SIL OPEN FONT LICENSE Version 1.1 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | PREAMBLE 6 | The goals of the Open Font License (OFL) are to stimulate worldwide 7 | development of collaborative font projects, to support the font creation 8 | efforts of academic and linguistic communities, and to provide a free and 9 | open framework in which fonts may be shared and improved in partnership 10 | with others. 11 | 12 | The OFL allows the licensed fonts to be used, studied, modified and 13 | redistributed freely as long as they are not sold by themselves. The 14 | fonts, including any derivative works, can be bundled, embedded, 15 | redistributed and/or sold with any software provided that any reserved 16 | names are not used by derivative works. The fonts and derivatives, 17 | however, cannot be released under any other type of license. The 18 | requirement for fonts to remain under this license does not apply 19 | to any document created using the fonts or their derivatives. 20 | 21 | DEFINITIONS 22 | "Font Software" refers to the set of files released by the Copyright 23 | Holder(s) under this license and clearly marked as such. This may 24 | include source files, build scripts and documentation. 25 | 26 | "Reserved Font Name" refers to any names specified as such after the 27 | copyright statement(s). 28 | 29 | "Original Version" refers to the collection of Font Software components as 30 | distributed by the Copyright Holder(s). 31 | 32 | "Modified Version" refers to any derivative made by adding to, deleting, 33 | or substituting -- in part or in whole -- any of the components of the 34 | Original Version, by changing formats or by porting the Font Software to a 35 | new environment. 36 | 37 | "Author" refers to any designer, engineer, programmer, technical 38 | writer or other person who contributed to the Font Software. 39 | 40 | PERMISSION & CONDITIONS 41 | Permission is hereby granted, free of charge, to any person obtaining 42 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 43 | redistribute, and sell modified and unmodified copies of the Font 44 | Software, subject to the following conditions: 45 | 46 | 1) Neither the Font Software nor any of its individual components, 47 | in Original or Modified Versions, may be sold by itself. 48 | 49 | 2) Original or Modified Versions of the Font Software may be bundled, 50 | redistributed and/or sold with any software, provided that each copy 51 | contains the above copyright notice and this license. These can be 52 | included either as stand-alone text files, human-readable headers or 53 | in the appropriate machine-readable metadata fields within text or 54 | binary files as long as those fields can be easily viewed by the user. 55 | 56 | 3) No Modified Version of the Font Software may use the Reserved Font 57 | Name(s) unless explicit written permission is granted by the corresponding 58 | Copyright Holder. This restriction only applies to the primary font name as 59 | presented to the users. 60 | 61 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 62 | Software shall not be used to promote, endorse or advertise any 63 | Modified Version, except to acknowledge the contribution(s) of the 64 | Copyright Holder(s) and the Author(s) or with their explicit written 65 | permission. 66 | 67 | 5) The Font Software, modified or unmodified, in part or in whole, 68 | must be distributed entirely under this license, and must not be 69 | distributed under any other license. The requirement for fonts to 70 | remain under this license does not apply to any document created 71 | using the Font Software. 72 | 73 | TERMINATION 74 | This license becomes null and void if any of the above conditions are 75 | not met. 76 | 77 | DISCLAIMER 78 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 81 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 82 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 83 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 84 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 85 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 86 | OTHER DEALINGS IN THE FONT SOFTWARE. 87 | -------------------------------------------------------------------------------- /Blazm.Bluetooth/BluetoothNavigator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace Blazm.Bluetooth; 9 | 10 | public class BluetoothNavigator : IBluetoothNavigator 11 | { 12 | private readonly Lazy> moduleTask; 13 | public BluetoothNavigator(IJSRuntime jsRuntime) 14 | { 15 | moduleTask = new(() => jsRuntime.InvokeAsync( 16 | "import", "./_content/Blazm.Bluetooth/Blazm.Bluetooth.js").AsTask()); 17 | } 18 | 19 | public async Task RequestDeviceAsync(RequestDeviceQuery query) 20 | { 21 | string json = JsonConvert.SerializeObject(query, 22 | Formatting.None, 23 | new JsonSerializerSettings 24 | { 25 | NullValueHandling = NullValueHandling.Ignore 26 | }); 27 | var module = await moduleTask.Value; 28 | var device = await module.InvokeAsync("requestDevice", json); 29 | device.InitDevice(this); 30 | return device; 31 | } 32 | 33 | #region WriteValueAsync 34 | public async Task WriteValueAsync(string deviceId, Guid serviceId, Guid characteristicId, byte[] value) 35 | { 36 | await WriteValueAsync(deviceId, serviceId.ToString(), characteristicId.ToString(), value); 37 | } 38 | 39 | public async Task WriteValueAsync(string deviceId, Guid serviceId, string characteristicId, byte[] value) 40 | { 41 | await WriteValueAsync(deviceId, serviceId.ToString(), characteristicId, value); 42 | } 43 | 44 | public async Task WriteValueAsync(string deviceId, string serviceId, Guid characteristicId, byte[] value) 45 | { 46 | await WriteValueAsync(deviceId, serviceId, characteristicId.ToString(), value); 47 | } 48 | 49 | public async Task WriteValueAsync(string deviceId, string serviceId, string characteristicId, byte[] value) 50 | { 51 | var bytes = value.Select(v => (uint)v).ToArray(); 52 | var module = await moduleTask.Value; 53 | await module.InvokeVoidAsync("writeValue", deviceId, serviceId.ToLowerInvariant(), characteristicId.ToLowerInvariant(), bytes); 54 | } 55 | #endregion 56 | 57 | #region ReadValueAsync 58 | public async Task ReadValueAsync(string deviceId, string serviceId, string characteristicId) 59 | { 60 | var module = await moduleTask.Value; 61 | var value = await module.InvokeAsync("readValue", deviceId, serviceId.ToLowerInvariant(), characteristicId.ToLowerInvariant()); 62 | return value.Select(v => (byte)(v & 0xFF)).ToArray(); 63 | } 64 | 65 | public async Task ReadValueAsync(string deviceId, Guid serviceId, string characteristicId) 66 | { 67 | return await ReadValueAsync(deviceId, serviceId.ToString(), characteristicId); 68 | } 69 | 70 | public async Task ReadValueAsync(string deviceId, string serviceId, Guid characteristicId) 71 | { 72 | return await ReadValueAsync(deviceId, serviceId, characteristicId.ToString()); 73 | } 74 | 75 | public async Task ReadValueAsync(string deviceId, Guid serviceId, Guid characteristicId) 76 | { 77 | return await ReadValueAsync(deviceId, serviceId.ToString(), characteristicId.ToString()); 78 | } 79 | #endregion 80 | 81 | #region SetupNotifyAsync 82 | private List> NotificationHandlers = new(); 83 | public async Task SetupNotifyAsync(Device device, string serviceId, string characteristicId) 84 | { 85 | var handler = DotNetObjectReference.Create(device); 86 | NotificationHandlers.Add(handler); 87 | var module = await moduleTask.Value; 88 | await module.InvokeVoidAsync("setupNotify", device.Id, serviceId.ToLowerInvariant(), characteristicId.ToLowerInvariant(), handler); 89 | } 90 | #endregion 91 | } 92 | 93 | public class NotificationHandler 94 | { 95 | IBluetoothNavigator bluetoothNavigator; 96 | public NotificationHandler(IBluetoothNavigator bluetoothNavigator) 97 | { 98 | this.bluetoothNavigator = bluetoothNavigator; 99 | } 100 | 101 | } 102 | 103 | public class CharacteristicEventArgs : EventArgs 104 | { 105 | public Guid ServiceId { get; set; } 106 | public Guid CharacteristicId { get; set; } 107 | public byte[] Value { get; set; } 108 | } 109 | -------------------------------------------------------------------------------- /Demo.md: -------------------------------------------------------------------------------- 1 | # Blazm.Bluetooth demo script 2 | ## Blazm 3 | One of the really cool things with Blazor is that it is possible to make JavaScript calls from .NET as well as making .NET calls from JavaScript. 4 | Blazm.Bluetooth make use of both of these. 5 | 1. Open **Blazm.Bluetooth/wwwroot/Blazm.Bluetooth.js** 6 | 2. Show the **requestDevice** call 7 | Here we make one of the .NET to JavaScript calls, we will call this method, the web browser UI will let us choose a device followed by returning the selected device. 8 | They are also stored in an array on the JavaScript side so that we can access the device later on. 9 | 3. Show connect 10 | When we connect to the device we do a couple of retries 11 | 4. Show handleCharacteristicValueChanged 12 | When we subscribe to an event the handleCharacteristicValueChanged will be called when the event occurs. 13 | The last line here **.....invokeMethodAsync** will call back to the .NET code. 14 | 5. Open BlueToothnavigator.cs 15 | This is where the magic happens. 16 | We get the JavaScript and load that when we need it (no need to manually add that to the HTML). 17 | Here we have all the methods that mkes JavaScript calls. 18 | 6. SetupNotify 19 | I won't go into details when it comes to calling .NET code from JavaScript but basically, we create a **DotNetObjectReference** and send that to the JavaScript and then we can use that object to call methods in .NET. 20 | 21 | 22 | ## Andersson scale 23 | 1. Add namespace 24 | ```csharp 25 | @using Blazm.Bluetooth 26 | ``` 27 | 2. AddBluetoothNavigator 28 | ```csharp 29 | @inject IBluetoothNavigator navigator 30 | ``` 31 | 3. AddWeightVariable 32 | ```csharp 33 | int weight = 0; 34 | ``` 35 | 4. ShowWeight 36 | ```csharp 37 | @weight 38 | ``` 39 | 5. Add filter 40 | Now it's time to connect to the device, I have already added the serviceid and characteristicsid. 41 | ```csharp 42 | var q = new RequestDeviceQuery(); 43 | q.Filters.Add(new Filter() { Services = { serviceid } }); 44 | ``` 45 | 6. Request a device 46 | I have kept the terminology from the JavaScript methods so that it's easier to understand and look up. 47 | ```csharp 48 | var q = new RequestDeviceQuery(); 49 | q.Filters.Add(new Filter() { Services = { serviceid } }); 50 | ``` 51 | In this case, we are using a filter asking for all devices that have the serviceid. It will also automatically grant us access to that service and all the characteristics underneath. 52 | 53 | 7. Set up notifications 54 | For us to be able to receive notifications we need to set them up. 55 | We can listen to multiple characteristics, but we will only have one notification event to handle them. 56 | ```csharp 57 | await device.SetupNotifyAsync(serviceid, characteristics); 58 | device.Notification += Value_Notification; 59 | ``` 60 | 8. Handle the notification 61 | The last step in all of this is to handle the notification. 62 | ```csharp 63 | private void Value_Notification(object sender, CharacteristicEventArgs e) 64 | { 65 | var data = e.Value.ToArray(); 66 | weight = (256 * data[4]) + data[5]; 67 | if (data[7] == 1) 68 | { 69 | weight *= -1; 70 | } 71 | 72 | StateHasChanged(); 73 | } 74 | ``` 75 | We get ServiceId, CharacteristicId and an array of bytes which is the data sent from the device. 76 | 77 | ## Robosapien 78 | 1. Add Robosapien 79 | ``` Csharp 80 | var q = new RequestDeviceQuery(); 81 | q.Filters.Add(new Filter() { Name = "Robosapien Blue" }); 82 | q.OptionalServices.Add(serviceId.ToString()); 83 | robosapien = await navigator.RequestDeviceAsync(q); 84 | ``` 85 | First, we get the Robosapien device, int this case we use the device name instead of the serviceId, but for this, to work we also need to supply the ServiceId we want to access otherwise we won't know what to give access to. 86 | 1. Show the **Devices/Robosapien.cs 87 | The Robosapien file is basically an enum with bytes for the different things the Robosapien Blue can do. 88 | 2. Send command 89 | Then it's time to send the command. 90 | ```csharp 91 | await robosapien.WriteValueAsync(serviceId, characteristicId, new byte[] { command }); 92 | ``` 93 | 3. Show the buttons 94 | 4. Open **devices/Robosapien.cs** 95 | This file is basically an enum but is represented as a class. 96 | 5. Run the app and connect to the Robosapien. 97 | 98 | ## MiP / MiPosaur 99 | The **MiP robot** and **MiPosaur** have almost the same robot, same serviceid, and characteristics but different names. 100 | In this example, I have created a base class for both robots, **MiPBase**. 101 | 1. Open **Devices/MipBase.cs** 102 | Here we have some helper methods, service ids, and stuff like that. 103 | 2. Open **Devices/MipRobot.cs** 104 | Here we have the implementation of the robot, making sure values change, and so on. As you might see from the namespace this is a file I have moved from my WinRT library that I wrote a while back. 105 | 106 | This is an example that shows that you can easily create classes that handle the connections and commands. 107 | 108 | 109 | ## Deadpool's head 110 | 1. Open **Deadpoolsheaddemo.razor** 111 | This device works just like the others but has a bit more requirements, it needs a keep awake signal every 5 seconds. 112 | This is the only way to keep Deadpool quiet, otherwise, he will just talk all the time. 113 | 2. Show the **timer** 114 | We simply set up a timer and every **5 seconds** we send a keep awake command... or keep quiet I guess command. 115 | 3. Show **connect** 116 | Here we simply set up the timer, get the device, set up notifications, and connect the app. 117 | These are commands I sniffed from my iPhone to figure out. 118 | 4. When we send a command we wait for 2 seconds before we update the UI and then start the timer again. 119 | 120 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | [Ss]tart/ 34 | [Ee]nd/ 35 | [Ss]nippets/ 36 | 37 | # Visual Studio 2015/2017 cache/options directory 38 | .vs/ 39 | # Uncomment if you have tasks that create the project's static files in wwwroot 40 | #wwwroot/ 41 | 42 | # Visual Studio 2017 auto generated files 43 | Generated\ Files/ 44 | 45 | # MSTest test Results 46 | [Tt]est[Rr]esult*/ 47 | [Bb]uild[Ll]og.* 48 | 49 | # NUnit 50 | *.VisualState.xml 51 | TestResult.xml 52 | nunit-*.xml 53 | 54 | # Build Results of an ATL Project 55 | [Dd]ebugPS/ 56 | [Rr]eleasePS/ 57 | dlldata.c 58 | 59 | # Benchmark Results 60 | BenchmarkDotNet.Artifacts/ 61 | 62 | # .NET Core 63 | project.lock.json 64 | project.fragment.lock.json 65 | artifacts/ 66 | 67 | # StyleCop 68 | StyleCopReport.xml 69 | 70 | # Files built by Visual Studio 71 | *_i.c 72 | *_p.c 73 | *_h.h 74 | *.ilk 75 | *.meta 76 | *.obj 77 | *.iobj 78 | *.pch 79 | *.pdb 80 | *.ipdb 81 | *.pgc 82 | *.pgd 83 | *.rsp 84 | *.sbr 85 | *.tlb 86 | *.tli 87 | *.tlh 88 | *.tmp 89 | *.tmp_proj 90 | *_wpftmp.csproj 91 | *.log 92 | *.vspscc 93 | *.vssscc 94 | .builds 95 | *.pidb 96 | *.svclog 97 | *.scc 98 | 99 | # Chutzpah Test files 100 | _Chutzpah* 101 | 102 | # Visual C++ cache files 103 | ipch/ 104 | *.aps 105 | *.ncb 106 | *.opendb 107 | *.opensdf 108 | *.sdf 109 | *.cachefile 110 | *.VC.db 111 | *.VC.VC.opendb 112 | 113 | # Visual Studio profiler 114 | *.psess 115 | *.vsp 116 | *.vspx 117 | *.sap 118 | 119 | # Visual Studio Trace Files 120 | *.e2e 121 | 122 | # TFS 2012 Local Workspace 123 | $tf/ 124 | 125 | # Guidance Automation Toolkit 126 | *.gpState 127 | 128 | # ReSharper is a .NET coding add-in 129 | _ReSharper*/ 130 | *.[Rr]e[Ss]harper 131 | *.DotSettings.user 132 | 133 | # TeamCity is a build add-in 134 | _TeamCity* 135 | 136 | # DotCover is a Code Coverage Tool 137 | *.dotCover 138 | 139 | # AxoCover is a Code Coverage Tool 140 | .axoCover/* 141 | !.axoCover/settings.json 142 | 143 | # Visual Studio code coverage results 144 | *.coverage 145 | *.coveragexml 146 | 147 | # NCrunch 148 | _NCrunch_* 149 | .*crunch*.local.xml 150 | nCrunchTemp_* 151 | 152 | # MightyMoose 153 | *.mm.* 154 | AutoTest.Net/ 155 | 156 | # Web workbench (sass) 157 | .sass-cache/ 158 | 159 | # Installshield output folder 160 | [Ee]xpress/ 161 | 162 | # DocProject is a documentation generator add-in 163 | DocProject/buildhelp/ 164 | DocProject/Help/*.HxT 165 | DocProject/Help/*.HxC 166 | DocProject/Help/*.hhc 167 | DocProject/Help/*.hhk 168 | DocProject/Help/*.hhp 169 | DocProject/Help/Html2 170 | DocProject/Help/html 171 | 172 | # Click-Once directory 173 | publish/ 174 | 175 | # Publish Web Output 176 | *.[Pp]ublish.xml 177 | *.azurePubxml 178 | # Note: Comment the next line if you want to checkin your web deploy settings, 179 | # but database connection strings (with potential passwords) will be unencrypted 180 | *.pubxml 181 | *.publishproj 182 | 183 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 184 | # checkin your Azure Web App publish settings, but sensitive information contained 185 | # in these scripts will be unencrypted 186 | PublishScripts/ 187 | 188 | # NuGet Packages 189 | *.nupkg 190 | # NuGet Symbol Packages 191 | *.snupkg 192 | # The packages folder can be ignored because of Package Restore 193 | **/[Pp]ackages/* 194 | # except build/, which is used as an MSBuild target. 195 | !**/[Pp]ackages/build/ 196 | # Uncomment if necessary however generally it will be regenerated when needed 197 | #!**/[Pp]ackages/repositories.config 198 | # NuGet v3's project.json files produces more ignorable files 199 | *.nuget.props 200 | *.nuget.targets 201 | 202 | # Microsoft Azure Build Output 203 | csx/ 204 | *.build.csdef 205 | 206 | # Microsoft Azure Emulator 207 | ecf/ 208 | rcf/ 209 | 210 | # Windows Store app package directories and files 211 | AppPackages/ 212 | BundleArtifacts/ 213 | Package.StoreAssociation.xml 214 | _pkginfo.txt 215 | *.appx 216 | *.appxbundle 217 | *.appxupload 218 | 219 | # Visual Studio cache files 220 | # files ending in .cache can be ignored 221 | *.[Cc]ache 222 | # but keep track of directories ending in .cache 223 | !?*.[Cc]ache/ 224 | 225 | # Others 226 | ClientBin/ 227 | ~$* 228 | *~ 229 | *.dbmdl 230 | *.dbproj.schemaview 231 | *.jfm 232 | *.pfx 233 | *.publishsettings 234 | orleans.codegen.cs 235 | 236 | # Including strong name files can present a security risk 237 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 238 | #*.snk 239 | 240 | # Since there are multiple workflows, uncomment next line to ignore bower_components 241 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 242 | #bower_components/ 243 | 244 | # RIA/Silverlight projects 245 | Generated_Code/ 246 | 247 | # Backup & report files from converting an old project file 248 | # to a newer Visual Studio version. Backup files are not needed, 249 | # because we have git ;-) 250 | _UpgradeReport_Files/ 251 | Backup*/ 252 | UpgradeLog*.XML 253 | UpgradeLog*.htm 254 | ServiceFabricBackup/ 255 | *.rptproj.bak 256 | 257 | # SQL Server files 258 | *.mdf 259 | *.ldf 260 | *.ndf 261 | 262 | # Business Intelligence projects 263 | *.rdl.data 264 | *.bim.layout 265 | *.bim_*.settings 266 | *.rptproj.rsuser 267 | *- [Bb]ackup.rdl 268 | *- [Bb]ackup ([0-9]).rdl 269 | *- [Bb]ackup ([0-9][0-9]).rdl 270 | 271 | # Microsoft Fakes 272 | FakesAssemblies/ 273 | 274 | # GhostDoc plugin setting file 275 | *.GhostDoc.xml 276 | 277 | # Node.js Tools for Visual Studio 278 | .ntvs_analysis.dat 279 | node_modules/ 280 | 281 | # Visual Studio 6 build log 282 | *.plg 283 | 284 | # Visual Studio 6 workspace options file 285 | *.opt 286 | 287 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 288 | *.vbw 289 | 290 | # Visual Studio LightSwitch build output 291 | **/*.HTMLClient/GeneratedArtifacts 292 | **/*.DesktopClient/GeneratedArtifacts 293 | **/*.DesktopClient/ModelManifest.xml 294 | **/*.Server/GeneratedArtifacts 295 | **/*.Server/ModelManifest.xml 296 | _Pvt_Extensions 297 | 298 | # Paket dependency manager 299 | .paket/paket.exe 300 | paket-files/ 301 | 302 | # FAKE - F# Make 303 | .fake/ 304 | 305 | # CodeRush personal settings 306 | .cr/personal 307 | 308 | # Python Tools for Visual Studio (PTVS) 309 | __pycache__/ 310 | *.pyc 311 | 312 | # Cake - Uncomment if you are using it 313 | # tools/** 314 | # !tools/packages.config 315 | 316 | # Tabs Studio 317 | *.tss 318 | 319 | # Telerik's JustMock configuration file 320 | *.jmconfig 321 | 322 | # BizTalk build output 323 | *.btp.cs 324 | *.btm.cs 325 | *.odx.cs 326 | *.xsd.cs 327 | 328 | # OpenCover UI analysis results 329 | OpenCover/ 330 | 331 | # Azure Stream Analytics local run output 332 | ASALocalRun/ 333 | 334 | # MSBuild Binary and Structured Log 335 | *.binlog 336 | 337 | # NVidia Nsight GPU debugger configuration file 338 | *.nvuser 339 | 340 | # MFractors (Xamarin productivity tool) working folder 341 | .mfractor/ 342 | 343 | # Local History for Visual Studio 344 | .localhistory/ 345 | 346 | # BeatPulse healthcheck temp database 347 | healthchecksdb 348 | 349 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 350 | MigrationBackup/ 351 | 352 | # Ionide (cross platform F# VS Code tools) working folder 353 | .ionide/ 354 | /Reset demos.ps1 355 | /Copy Snippets.ps1 356 | /Blazm.Blutetooth Demo.sln 357 | -------------------------------------------------------------------------------- /DeadpoolsHead.md: -------------------------------------------------------------------------------- 1 | I wanted to update my demos when speaking about Blazm.Bluetooth. 2 | I found Deadpools head from Hasbro and I got really excited. 3 | Sadly there are no public APIs and no documentation, this opened a whole new rabbit hole for me. 4 | I was able to get a Bluetooth connection to a device, but I couldn't get it to work. 5 | But after testing and debugging Bluetooth commands I figured a bunch of things out, In this document I will document my finds. 6 | 7 | ## The device 8 | The device is a model of Deadpools head, everything from the box it comes in to the device itself is really well thought out.Evertything is in a deadpool theme (quirky). 9 | Eyes and mouth moves as well as the whole head, super well done robotics. 10 | 11 | The name of the device is **WilsonBLE**. 12 | 13 | ## Services & Characteristics 14 | 15 | It has 2 services with a couple of characteristics each 16 | **7cd80aa0-b9ec-42cb-9ace-10389f7e2576** 17 | 7cd801b0-b9ec-42cb-9ace-10389f7e2576 Read,WriteWithoutResponse 18 | 7cd802b0-b9ec-42cb-9ace-10389f7e2576 Read,Notify 19 | **00001800-0000-1000-8000-00805f9b34fb** 20 | 00002a00-0000-1000-8000-00805f9b34fb Read 21 | 00002a01-0000-1000-8000-00805f9b34fb Read 22 | 00002a04-0000-1000-8000-00805f9b34fb Read 23 | 24 | When I saw this I assumed that the app is able to send commands to the head, and the head probably can send commands (notifications) back to the app. 25 | The two characteristics that we are interested in is **01b0** and **02b0** and since both of them ar located in the same Service we are only interested in that service. 26 | 27 | Next, I rigged my Mac and Iphone to capture Bluetooth (see blog post) and opened the log in Wireshark. 28 | The app seems to have an authentication step, when the user should press a button on the bottom of the head. 29 | Looking at the data coming back an forth from the head I didn't see any difference between the commands befor and after so I guessed that the authentication part just might be in the app only. 30 | 31 | There are 3 segments that I have found when it comes to phrases. 32 | These segments are controlled by the last byte. The phrases has different categories and looking in the app I have tried to categorize them as they are beeing used in the Hasbro app. 33 | All of the starts with byte 0x17 followed be 0x00, changing these doesn't make any difference (except nothing happens). 34 | 0x17,0x00, code, segment 35 | 36 | ### Segment 0x00 37 | Code | Category | Phrase 38 | --- | --- |--- | 39 | 40 | ### Segment 0x01 41 | Code | Category | Phrase 42 | --- | --- |--- | 43 | 44 | ### Segment 0x02 45 | Code | Category | Phrase 46 | --- | --- |--- | 47 | 00 | |zzZZ o Logan I love it when you call me bub 48 | 01 | |psst how about we make out way down to the danger room? 49 | 02 | | I'll be the captain of the football team and you'll be wolverine 50 | 03 | Food | Hey can I get a double paddy wolverine style 51 | 04 | Food | Is that gluten-free? Gluten makes me gassy 52 | 05 | Food | Cough garson if this isn't as hot as??????? 53 | 06 | Food | Hey bob can you read the specials in your best wolverine voice? 54 | 07 | Food | Cough I need it extra spicy 55 | ?08 | Food | 56 | 09 | Food | Hahaha do you sell hot-dog water? 57 | 10 | Food | Okey, would you please pass the, you know, everything I have no arms 58 | 11 | Food | hahaha quick I need some beefy chilly blasten straight into my face hole aaaaaah 59 | 12 | Food | ooh Got any ranch dressing? I need a whole lo of ranchdressing, give med some D, D is for ranch dressing. 60 | 13 | Food | ooh Got any ranch dressing? I need a whole lo of ranchdressing, give med some D, D is for diabeties 61 | 14 | |Oh I forgot I gotta take squirellpool out of the blender 62 | 15 | |I gotta go, I gotta take sabertooth for a walk 63 | 16 | |Peace out you mother father, yep this is how we swear on basic cable 64 | 17 | |Bye bye, see you after your 10year stay in the vault, tell juggernauth I said hi 65 | 18 | |I gotta go!, Baby seal in the owen 66 | 19 | |Bye bye, gotta go big comic book cross over and you were invited 67 | 20 | |Dos verdanja ????? (Russian?) 68 | 21 | |Mike drop deadpool out 69 | 22 | |bye bye, see you next tuesday 70 | 23 | |adious el banjo 71 | 24 | | 72 | 25 | |Hi i'm deadpools head, not to be confused with headpool, different guy 73 | 26 | |Name's pool, dead pool nice to meet you 74 | 27 | |Wow, Hi there my name is wade but you can cal me renaldo 75 | 28 | |Greating from you uncanny pool guy 76 | 29 | |Oh hello, I am the grously unpaid voice in deadpools head 77 | 30 | |Hiiii yiiiiii 78 | 31 | |hi am Wade but you can call me the nail in the coffin of you marrige bye bye 79 | 32 | |Cough Hi i'm deadpool but n my currentstate more like deadpuddle 80 | 33 | |Cough Hi Deadpool here, mecenery for hire, I charge by the limb hahah 81 | 34 | |Ooh Hi am deadpool or as corporate calls me of screen demice pool 82 | 35 | |Hi I'm marvel x-men's deadpool presented by Hasbro 83 | 36 | |ooh hey there handsome, have you seen my abs, no really where are they? 84 | 37 | |You are super attractive, you can't tell bu I am using air quotes ha ha ha 85 | 38 | |Eue You smell lik beatst lab after taco night 86 | ?39 || Egh you smell like a second batch og egg nog had a secondery mutation 87 | ?40 || You so ugly you 88 | 41 | |Wohoo wow that's an ugmug 89 | 42 | | 90 | 43 | |I know colosis since he was a weee anvill 91 | 44 | |Music I like my x-men with big guns an full pouches 92 | 45 | |psst, wolverine is actually just a pig, talking, wolverine 93 | 46 | |Truth is I lost my body trying to teleport with nightcrawler into a taxi 94 | 47 | | 95 | 48 | | 96 | 49 | |*music* I mean face it cable you need to update you name, how about captain streaming? 97 | 50 | |*music* Magneto is so old *How old id he* that he call his turkey neck sagneto 98 | 51 | |*music* Hey cable, more like basic cable. Hashtag dadpool joke 99 | 52 | |*music* Why did cable pay so much almoney? His ex forced him to, no he is really distrot I am francly worried about him 100 | 53 | |Who is the least popular x-men at a poolparty? Canonball! I hate myself 101 | 54 | |*music* What do you call a cevered head floating in a kids pool? waaade 102 | 55 | |*music* I just flow in from New York and boy are my stumps tired. I'll be here all night, try the veil. 103 | 56 | News | Breaking news man sixe humoculus plays with superhero toys 104 | 57 | News | After the break unicorns are real and boy are they hot 105 | 58 | News | This just in Twerking help lowe colesterole 106 | 59 | News | This just in I decided not to remove my trampstamp 107 | 60 | News | 108 | 61 | News | Bob gets a reservation for one at the mall food court 109 | 62 | News | Jean grey has been resurrected, oh wait I'm beeing told she is dead again 110 | 63 | |Bleep bleep you 111 | 64 | |bleep censored again 112 | 65 | |Chimmy changaaaaa 113 | 66 | |Chimmy changa 114 | 67 | |shape shifters? 115 | 68 | |K-pop is not for sissies 116 | 69 | |Put me in a professor x magneto sanwich 117 | 70 | |I feel like a british girl 118 | 71 | |Can you belive that a person was PAID to write this 119 | 72 | |Professor X I don't wanna go for a hover chair ride 120 | 73 | |Chimmy changaaaa, who the fuck gave away my chimy changa 121 | 74 | |what x-men universe is this? Continuity people! 122 | 75 | |I can't feel my legs! 123 | 76 | |I can't feel my stump 124 | 77 | |Cheeese 125 | 78 | |Selfie 126 | 79 | |Selfie2 127 | 80 | |Selfie3 128 | 81 | |You gotta pose me I don't have a booty 129 | 82 | |Let's get one for grandma 130 | 83 | |hahahah ok let's get one for granmama 131 | 84 | |Let's get one for mema 132 | 85 | |woh woh lets get on for peppa and gigi 133 | 86 | |Duck faces OBOY 134 | 87 | |How's my hair look? 135 | 88 | |Hiyeeeh, how my nub look? 136 | 89 | |Hey's how my mass ?? look 137 | 90 | |Makup! 138 | 91 | |Make sure you get my good angle, who am I kidding they ar all good 139 | 92 | |say chimiy chunga 140 | 93 | |Strike a pode girls 141 | 94 | |So help me bob, I will bleep in you litter box 142 | 95 | |HA, so you think just because I don't have a body I can't kick your ass? Let's dance 143 | 96 | |Listen ....?? teeth 144 | 97 | |Hey bub I'm the second best at whar wolvie does so don't mess with the head 145 | 98 | |Iam here to chew gum and to chew gum, do you have any gum? 146 | 99 | |You talking to me? You talking to me? Oh you ARE talking to me... hi! 147 | 148 | ## Sensors 149 | 0x21,0x0B Touch mouth 150 | 0x20,0x0E,0x01 Carry 151 | 0x20,0x0C,0x01 OnTable 152 | 0x20,0x4C,0x00 Button press 153 | 0x20,0x4C,0x00 Button releas 154 | 155 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/MiPBase.cs: -------------------------------------------------------------------------------- 1 | using Blazm.Bluetooth; 2 | using MiPWinRTSDK.MiP.Models; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace MiPWinRTSDK; 8 | 9 | class MiPBase : INotifyPropertyChanged 10 | { 11 | protected IBluetoothNavigator Navigator { get; set; } 12 | 13 | public Device Device { get; set; } 14 | public string WriteServiceGuid = "0000ffe5-0000-1000-8000-00805f9b34fb"; 15 | public string WriteCharacteristicGuid = "0000ffe9-0000-1000-8000-00805f9b34fb"; 16 | public string ReadServiceGuid = "0000ffe0-0000-1000-8000-00805f9b34fb"; 17 | public string ReadCharacteristicGuid = "0000ffe4-0000-1000-8000-00805f9b34fb"; 18 | 19 | #region Properties 20 | private byte chestLEDTimeOff; 21 | private byte chestLEDTimeOn; 22 | private Color chestLEDColor; 23 | public Color ChestLEDColor 24 | { 25 | get 26 | { 27 | return chestLEDColor; 28 | } 29 | set 30 | { 31 | chestLEDColor = value; 32 | RaisePropertyChanged(); 33 | } 34 | } 35 | public byte ChestLEDTimeOn 36 | { 37 | get 38 | { 39 | return chestLEDTimeOn; 40 | } 41 | set 42 | { 43 | chestLEDTimeOn = value; 44 | RaisePropertyChanged(); 45 | } 46 | } 47 | public byte ChestLEDTimeOff 48 | { 49 | get 50 | { 51 | return chestLEDTimeOff; 52 | } 53 | set 54 | { 55 | chestLEDTimeOff = value; 56 | RaisePropertyChanged(); 57 | } 58 | } 59 | #endregion 60 | 61 | public async Task DistanceDrive(DirectionEnum direction, byte distance, TurnEnum turn, int angle) 62 | { 63 | List bytes = new List(); 64 | 65 | bytes.Add((byte)direction); 66 | bytes.Add(distance); 67 | bytes.Add((byte)turn); 68 | byte highbyte = (byte)((angle >> 8) & 0xff); 69 | byte lowbyte = (byte)(angle & 0xff); 70 | bytes.Add(highbyte); 71 | bytes.Add(lowbyte); 72 | await SendCommand(0x70, bytes); 73 | } 74 | 75 | /// 76 | /// 77 | /// 78 | /// Speed (0~30) 79 | /// Time in 7ms intervals (0~255) 80 | /// 81 | public async Task DriveForwardWithTime(byte speed, byte timeInMiliseconds) 82 | { 83 | List bytes = new List(); 84 | byte time = (byte)(timeInMiliseconds); 85 | bytes.Add(speed); 86 | bytes.Add(time); 87 | await SendCommand(0x71, bytes); 88 | } 89 | 90 | public async Task DriveBackwardWithTime(byte speed, byte timeInMiliseconds) 91 | { 92 | List bytes = new List(); 93 | byte time = (byte)(timeInMiliseconds / 7); 94 | bytes.Add(speed); 95 | bytes.Add(time); 96 | await SendCommand(0x72, bytes); 97 | } 98 | 99 | public async Task TurnLeftByAngle(byte speed, int angle) 100 | { 101 | List bytes = new List(); 102 | bytes.Add((byte)(angle / 5)); 103 | bytes.Add(speed); 104 | await SendCommand(0x73, bytes); 105 | } 106 | public async Task TurnRightByAngle(byte speed, int angle) 107 | { 108 | List bytes = new List(); 109 | bytes.Add((byte)(angle / 5)); 110 | bytes.Add(speed); 111 | await SendCommand(0x74, bytes); 112 | } 113 | 114 | public async Task ContinuousDrive(byte direction, byte spin) 115 | { 116 | List bytes = new List(); 117 | bytes.Add(direction); 118 | bytes.Add(spin); 119 | await SendCommand(0x78, bytes); 120 | } 121 | 122 | public async Task DelayTimeBetweenTwoClaps(int delay) 123 | { 124 | List bytes = new List(); 125 | byte highbyte = (byte)((delay >> 8) & 0xff); 126 | byte lowbyte = (byte)(delay & 0xff); 127 | await SendCommand(0x20, bytes); 128 | } 129 | 130 | internal async Task SendCommand(byte command, List commandBytes) 131 | { 132 | 133 | //Add command to the beginning 134 | commandBytes.Insert(0, command); 135 | 136 | await Navigator.WriteValueAsync(Device.Id, WriteServiceGuid, WriteCharacteristicGuid, commandBytes.ToArray()); 137 | } 138 | 139 | public async Task RequestChestLED() 140 | { 141 | List bytes = new List(); 142 | await SendCommand(0x83, bytes); 143 | } 144 | 145 | public async Task SetChestLED(byte red, byte green, byte blue) 146 | { 147 | List bytes = new List(); 148 | bytes.Add((byte)red); 149 | bytes.Add((byte)green); 150 | bytes.Add((byte)blue); 151 | await SendCommand(0x84, bytes); 152 | } 153 | 154 | public async Task FlashChestLED(byte red, byte green, byte blue, byte timeOn, byte timeOff) 155 | { 156 | List bytes = new List(); 157 | bytes.Add((byte)red); 158 | bytes.Add((byte)green); 159 | bytes.Add((byte)blue); 160 | bytes.Add((byte)timeOn); 161 | bytes.Add((byte)timeOff); 162 | await SendCommand(0x89, bytes); 163 | } 164 | 165 | public async Task SetHeadLED(LightEnum light1, LightEnum light2, LightEnum light3, LightEnum light4) 166 | { 167 | List bytes = new List(); 168 | bytes.Add((byte)light1); 169 | bytes.Add((byte)light2); 170 | bytes.Add((byte)light3); 171 | bytes.Add((byte)light4); 172 | await SendCommand(0x8a, bytes); 173 | } 174 | 175 | public async Task RequestHeadLED() 176 | { 177 | List bytes = new List(); 178 | await SendCommand(0x8b, bytes); 179 | } 180 | 181 | public async Task SetUserData(byte address, byte data) 182 | { 183 | List bytes = new List(); 184 | bytes.Add(address); 185 | bytes.Add(data); 186 | await SendCommand(0x12, bytes); 187 | } 188 | 189 | public async Task GetUserOrOtherEepromData(byte address) 190 | { 191 | List bytes = new List(); 192 | bytes.Add((byte)address); 193 | await SendCommand(0x13, bytes); 194 | } 195 | 196 | public async Task GetHardwareInfo() 197 | { 198 | List bytes = new List(); 199 | await SendCommand(0x19, bytes); 200 | } 201 | 202 | public async Task GetVolume() 203 | { 204 | List bytes = new List(); 205 | await SendCommand(0x16, bytes); 206 | } 207 | 208 | public async Task PlaySound(MiPSound[] sounds, byte numberOfTimesToRepeat) 209 | { 210 | List bytes = new List(); 211 | foreach (var s in sounds) 212 | { 213 | if (s.Volume == null) 214 | bytes.Add(s.Sound.Value); 215 | else 216 | bytes.Add((byte)(s.Volume.Value + 0xf7)); 217 | } 218 | bytes.Add(numberOfTimesToRepeat); 219 | await SendCommand(0x06, bytes); 220 | } 221 | 222 | public async Task Sleep() 223 | { 224 | List bytes = new List(); 225 | await SendCommand(0xFA, bytes); 226 | } 227 | 228 | public async Task DisconnectApp() 229 | { 230 | List bytes = new List(); 231 | await SendCommand(0xFE, bytes); 232 | } 233 | public async Task Stop() 234 | { 235 | List bytes = new List(); 236 | await SendCommand(0x77, bytes); 237 | } 238 | 239 | public async Task RequestStatus() 240 | { 241 | List bytes = new List(); 242 | await SendCommand(0x79, bytes); 243 | } 244 | 245 | #region Recieve 246 | //GattCharacteristic CharacteristicsNotify { get; set; } 247 | 248 | public virtual void Connect() 249 | { 250 | 251 | } 252 | 253 | public byte[] StringToByteArray(string hex) 254 | { 255 | return Enumerable.Range(0, hex.Length) 256 | .Where(x => x % 2 == 0) 257 | .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) 258 | .ToArray(); 259 | } 260 | #endregion 261 | 262 | #region Events 263 | public event EventHandler ShakeDetectedRecievedEvent; 264 | protected void OnShakeDetectedRecieved() 265 | { 266 | ShakeDetectedRecievedEvent?.Invoke(this, null); 267 | } 268 | 269 | public event EventHandler ChestLEDRecievedEvent; 270 | public event PropertyChangedEventHandler PropertyChanged; 271 | 272 | protected void OnChestLEDRecieved(ChestLED chestLED) 273 | { 274 | ChestLEDRecievedEvent?.Invoke(this, chestLED); 275 | } 276 | #endregion 277 | 278 | internal void RaisePropertyChanged([CallerMemberName] string caller = "") 279 | { 280 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller)); 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'} -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Script/joy.js: -------------------------------------------------------------------------------- 1 | window.blazmjoystick = {}; 2 | 3 | 4 | var joystick = []; 5 | 6 | window.blazmjoystick.init = (containerid) => { 7 | joystick = new JoyStick('joyDiv'); 8 | 9 | }; 10 | 11 | window.blazmjoystick.getposition = () => { 12 | var pos = { X: Number(joystick.GetX()), Y: Number(joystick.GetY()) }; 13 | return pos; 14 | 15 | }; 16 | 17 | 18 | 19 | /* 20 | * Name : joy.js 21 | * @author : Roberto D'Amico (Bobboteck) 22 | * Last modified : 07.01.2020 23 | * Revision : 1.1.4 24 | * 25 | * Modification History: 26 | * Date Version Modified By Description 27 | * 2020-04-20 1.1.5 Roberto D'Amico Correct: Two sticks in a row, thanks to @liamw9534 for the suggestion 28 | * 2020-04-03 Roberto D'Amico Correct: InternalRadius when change the size of canvas, thanks to @vanslipon for the suggestion 29 | * 2020-01-07 1.1.4 Roberto D'Amico Close #6 by implementing a new parameter to set the functionality of auto-return to 0 position 30 | * 2019-11-18 1.1.3 Roberto D'Amico Close #5 correct indication of East direction 31 | * 2019-11-12 1.1.2 Roberto D'Amico Removed Fix #4 incorrectly introduced and restored operation with touch devices 32 | * 2019-11-12 1.1.1 Roberto D'Amico Fixed Issue #4 - Now JoyStick work in any position in the page, not only at 0,0 33 | * 34 | * The MIT License (MIT) 35 | * 36 | * This file is part of the JoyStick Project (https://github.com/bobboteck/JoyStick). 37 | * Copyright (c) 2015 Roberto D'Amico (Bobboteck). 38 | * 39 | * Permission is hereby granted, free of charge, to any person obtaining a copy 40 | * of this software and associated documentation files (the "Software"), to deal 41 | * in the Software without restriction, including without limitation the rights 42 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 43 | * copies of the Software, and to permit persons to whom the Software is 44 | * furnished to do so, subject to the following conditions: 45 | * 46 | * The above copyright notice and this permission notice shall be included in all 47 | * copies or substantial portions of the Software. 48 | * 49 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 50 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 51 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 52 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 53 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 54 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 55 | * SOFTWARE. 56 | */ 57 | 58 | /** 59 | * @desc Principal object that draw a joystick, you only need to initialize the object and suggest the HTML container 60 | * @costructor 61 | * @param container {String} - HTML object that contains the Joystick 62 | * @param parameters (optional) - object with following keys: 63 | * title {String} (optional) - The ID of canvas (Default value is 'joystick') 64 | * width {Int} (optional) - The width of canvas, if not specified is setted at width of container object (Default value is the width of container object) 65 | * height {Int} (optional) - The height of canvas, if not specified is setted at height of container object (Default value is the height of container object) 66 | * internalFillColor {String} (optional) - Internal color of Stick (Default value is '#00AA00') 67 | * internalLineWidth {Int} (optional) - Border width of Stick (Default value is 2) 68 | * internalStrokeColor {String}(optional) - Border color of Stick (Default value is '#003300') 69 | * externalLineWidth {Int} (optional) - External reference circonference width (Default value is 2) 70 | * externalStrokeColor {String} (optional) - External reference circonference color (Default value is '#008000') 71 | * autoReturnToCenter {Bool} (optional) - Sets the behavior of the stick, whether or not, it should return to zero position when released (Default value is True and return to zero) 72 | */ 73 | var JoyStick = (function (container, parameters) { 74 | parameters = parameters || {}; 75 | var title = (typeof parameters.title === "undefined" ? "joystick" : parameters.title), 76 | width = (typeof parameters.width === "undefined" ? 0 : parameters.width), 77 | height = (typeof parameters.height === "undefined" ? 0 : parameters.height), 78 | internalFillColor = (typeof parameters.internalFillColor === "undefined" ? "#00AA00" : parameters.internalFillColor), 79 | internalLineWidth = (typeof parameters.internalLineWidth === "undefined" ? 2 : parameters.internalLineWidth), 80 | internalStrokeColor = (typeof parameters.internalStrokeColor === "undefined" ? "#003300" : parameters.internalStrokeColor), 81 | externalLineWidth = (typeof parameters.externalLineWidth === "undefined" ? 2 : parameters.externalLineWidth), 82 | externalStrokeColor = (typeof parameters.externalStrokeColor === "undefined" ? "#008000" : parameters.externalStrokeColor), 83 | autoReturnToCenter = (typeof parameters.autoReturnToCenter === "undefined" ? true : parameters.autoReturnToCenter); 84 | 85 | // Create Canvas element and add it in the Container object 86 | var objContainer = document.getElementById(container); 87 | var canvas = document.createElement("canvas"); 88 | canvas.id = title; 89 | if (width === 0) { width = objContainer.clientWidth; } 90 | if (height === 0) { height = objContainer.clientHeight; } 91 | canvas.width = width; 92 | canvas.height = height; 93 | objContainer.appendChild(canvas); 94 | var context = canvas.getContext("2d"); 95 | 96 | var pressed = 0; // Bool - 1=Yes - 0=No 97 | var circumference = 2 * Math.PI; 98 | var internalRadius = (canvas.width - ((canvas.width / 2) + 10)) / 2; 99 | var maxMoveStick = internalRadius + 5; 100 | var externalRadius = internalRadius + 30; 101 | var centerX = canvas.width / 2; 102 | var centerY = canvas.height / 2; 103 | var directionHorizontalLimitPos = canvas.width / 10; 104 | var directionHorizontalLimitNeg = directionHorizontalLimitPos * -1; 105 | var directionVerticalLimitPos = canvas.height / 10; 106 | var directionVerticalLimitNeg = directionVerticalLimitPos * -1; 107 | // Used to save current position of stick 108 | var movedX = centerX; 109 | var movedY = centerY; 110 | 111 | //// Check if the device support the touch or not 112 | //if ("ontouchstart" in document.documentElement) { 113 | canvas.addEventListener("touchstart", onTouchStart, false); 114 | canvas.addEventListener("touchmove", onTouchMove, false); 115 | canvas.addEventListener("touchend", onTouchEnd, false); 116 | //} 117 | //else { 118 | canvas.addEventListener("mousedown", onMouseDown, false); 119 | canvas.addEventListener("mousemove", onMouseMove, false); 120 | canvas.addEventListener("mouseup", onMouseUp, false); 121 | //} 122 | // Draw the object 123 | drawExternal(); 124 | drawInternal(); 125 | /****************************************************** 126 | * Private methods 127 | *****************************************************/ 128 | /** 129 | * @desc Draw the external circle used as reference position 130 | */ 131 | function drawExternal() { 132 | context.beginPath(); 133 | context.arc(centerX, centerY, externalRadius, 0, circumference, false); 134 | context.lineWidth = externalLineWidth; 135 | context.strokeStyle = externalStrokeColor; 136 | context.stroke(); 137 | } 138 | /** 139 | * @desc Draw the internal stick in the current position the user have moved it 140 | */ 141 | function drawInternal() { 142 | context.beginPath(); 143 | if (movedX < internalRadius) { movedX = maxMoveStick; } 144 | if ((movedX + internalRadius) > canvas.width) { movedX = canvas.width - (maxMoveStick); } 145 | if (movedY < internalRadius) { movedY = maxMoveStick; } 146 | if ((movedY + internalRadius) > canvas.height) { movedY = canvas.height - (maxMoveStick); } 147 | context.arc(movedX, movedY, internalRadius, 0, circumference, false); 148 | // create radial gradient 149 | var grd = context.createRadialGradient(centerX, centerY, 5, centerX, centerY, 200); 150 | // Light color 151 | grd.addColorStop(0, internalFillColor); 152 | // Dark color 153 | grd.addColorStop(1, internalStrokeColor); 154 | context.fillStyle = grd; 155 | context.fill(); 156 | context.lineWidth = internalLineWidth; 157 | context.strokeStyle = internalStrokeColor; 158 | context.stroke(); 159 | } 160 | 161 | /** 162 | * @desc Events for manage touch 163 | */ 164 | function onTouchStart(event) { 165 | pressed = 1; 166 | } 167 | function onTouchMove(event) { 168 | // Prevent the browser from doing its default thing (scroll, zoom) 169 | event.preventDefault(); 170 | if (pressed === 1 && event.targetTouches[0].target === canvas) { 171 | movedX = event.targetTouches[0].pageX; 172 | movedY = event.targetTouches[0].pageY; 173 | // Manage offset 174 | movedX -= canvas.offsetLeft; 175 | movedY -= canvas.offsetTop; 176 | // Delete canvas 177 | context.clearRect(0, 0, canvas.width, canvas.height); 178 | // Redraw object 179 | drawExternal(); 180 | drawInternal(); 181 | } 182 | } 183 | function onTouchEnd(event) { 184 | pressed = 0; 185 | // If required reset position store variable 186 | if (autoReturnToCenter) { 187 | movedX = centerX; 188 | movedY = centerY; 189 | } 190 | // Delete canvas 191 | context.clearRect(0, 0, canvas.width, canvas.height); 192 | // Redraw object 193 | drawExternal(); 194 | drawInternal(); 195 | //canvas.unbind('touchmove'); 196 | } 197 | /** 198 | * @desc Events for manage mouse 199 | */ 200 | function onMouseDown(event) { 201 | pressed = 1; 202 | } 203 | function onMouseMove(event) { 204 | if (pressed === 1) { 205 | movedX = event.pageX; 206 | movedY = event.pageY; 207 | // Manage offset 208 | movedX -= canvas.offsetLeft; 209 | movedY -= canvas.offsetTop; 210 | // Delete canvas 211 | context.clearRect(0, 0, canvas.width, canvas.height); 212 | // Redraw object 213 | drawExternal(); 214 | drawInternal(); 215 | } 216 | } 217 | function onMouseUp(event) { 218 | pressed = 0; 219 | // If required reset position store variable 220 | if (autoReturnToCenter) { 221 | movedX = centerX; 222 | movedY = centerY; 223 | } 224 | // Delete canvas 225 | context.clearRect(0, 0, canvas.width, canvas.height); 226 | // Redraw object 227 | drawExternal(); 228 | drawInternal(); 229 | //canvas.unbind('mousemove'); 230 | } 231 | /****************************************************** 232 | * Public methods 233 | *****************************************************/ 234 | /** 235 | * @desc The width of canvas 236 | * @return Number of pixel width 237 | */ 238 | this.GetWidth = function () { 239 | return canvas.width; 240 | }; 241 | 242 | /** 243 | * @desc The height of canvas 244 | * @return Number of pixel height 245 | */ 246 | this.GetHeight = function () { 247 | return canvas.height; 248 | }; 249 | 250 | /** 251 | * @desc The X position of the cursor relative to the canvas that contains it and to its dimensions 252 | * @return Number that indicate relative position 253 | */ 254 | this.GetPosX = function () { 255 | return movedX; 256 | }; 257 | 258 | /** 259 | * @desc The Y position of the cursor relative to the canvas that contains it and to its dimensions 260 | * @return Number that indicate relative position 261 | */ 262 | this.GetPosY = function () { 263 | return movedY; 264 | }; 265 | 266 | /** 267 | * @desc Normalizzed value of X move of stick 268 | * @return Integer from -100 to +100 269 | */ 270 | this.GetX = function () { 271 | return (100 * ((movedX - centerX) / maxMoveStick)).toFixed(); 272 | }; 273 | 274 | /** 275 | * @desc Normalizzed value of Y move of stick 276 | * @return Integer from -100 to +100 277 | */ 278 | this.GetY = function () { 279 | return ((100 * ((movedY - centerY) / maxMoveStick)) * -1).toFixed(); 280 | }; 281 | 282 | /** 283 | * @desc Get the direction of the cursor as a string that indicates the cardinal points where this is oriented 284 | * @return String of cardinal point N, NE, E, SE, S, SW, W, NW and C when it is placed in the center 285 | */ 286 | this.GetDir = function () { 287 | var result = ""; 288 | var orizontal = movedX - centerX; 289 | var vertical = movedY - centerY; 290 | 291 | if (vertical >= directionVerticalLimitNeg && vertical <= directionVerticalLimitPos) { 292 | result = "C"; 293 | } 294 | if (vertical < directionVerticalLimitNeg) { 295 | result = "N"; 296 | } 297 | if (vertical > directionVerticalLimitPos) { 298 | result = "S"; 299 | } 300 | 301 | if (orizontal < directionHorizontalLimitNeg) { 302 | if (result === "C") { 303 | result = "W"; 304 | } 305 | else { 306 | result += "W"; 307 | } 308 | } 309 | if (orizontal > directionHorizontalLimitPos) { 310 | if (result === "C") { 311 | result = "E"; 312 | } 313 | else { 314 | result += "E"; 315 | } 316 | } 317 | 318 | return result; 319 | }; 320 | }); -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/Script/joy.js: -------------------------------------------------------------------------------- 1 | window.blazmjoystick = {}; 2 | 3 | 4 | var joystick = []; 5 | 6 | window.blazmjoystick.init = (containerid) => { 7 | joystick = new JoyStick('joyDiv'); 8 | 9 | }; 10 | 11 | window.blazmjoystick.getposition = () => { 12 | var pos = { X: Number(joystick.GetX()), Y: Number(joystick.GetY()) }; 13 | return pos; 14 | 15 | }; 16 | 17 | 18 | 19 | /* 20 | * Name : joy.js 21 | * @author : Roberto D'Amico (Bobboteck) 22 | * Last modified : 07.01.2020 23 | * Revision : 1.1.4 24 | * 25 | * Modification History: 26 | * Date Version Modified By Description 27 | * 2020-04-20 1.1.5 Roberto D'Amico Correct: Two sticks in a row, thanks to @liamw9534 for the suggestion 28 | * 2020-04-03 Roberto D'Amico Correct: InternalRadius when change the size of canvas, thanks to @vanslipon for the suggestion 29 | * 2020-01-07 1.1.4 Roberto D'Amico Close #6 by implementing a new parameter to set the functionality of auto-return to 0 position 30 | * 2019-11-18 1.1.3 Roberto D'Amico Close #5 correct indication of East direction 31 | * 2019-11-12 1.1.2 Roberto D'Amico Removed Fix #4 incorrectly introduced and restored operation with touch devices 32 | * 2019-11-12 1.1.1 Roberto D'Amico Fixed Issue #4 - Now JoyStick work in any position in the page, not only at 0,0 33 | * 34 | * The MIT License (MIT) 35 | * 36 | * This file is part of the JoyStick Project (https://github.com/bobboteck/JoyStick). 37 | * Copyright (c) 2015 Roberto D'Amico (Bobboteck). 38 | * 39 | * Permission is hereby granted, free of charge, to any person obtaining a copy 40 | * of this software and associated documentation files (the "Software"), to deal 41 | * in the Software without restriction, including without limitation the rights 42 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 43 | * copies of the Software, and to permit persons to whom the Software is 44 | * furnished to do so, subject to the following conditions: 45 | * 46 | * The above copyright notice and this permission notice shall be included in all 47 | * copies or substantial portions of the Software. 48 | * 49 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 50 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 51 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 52 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 53 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 54 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 55 | * SOFTWARE. 56 | */ 57 | 58 | /** 59 | * @desc Principal object that draw a joystick, you only need to initialize the object and suggest the HTML container 60 | * @costructor 61 | * @param container {String} - HTML object that contains the Joystick 62 | * @param parameters (optional) - object with following keys: 63 | * title {String} (optional) - The ID of canvas (Default value is 'joystick') 64 | * width {Int} (optional) - The width of canvas, if not specified is setted at width of container object (Default value is the width of container object) 65 | * height {Int} (optional) - The height of canvas, if not specified is setted at height of container object (Default value is the height of container object) 66 | * internalFillColor {String} (optional) - Internal color of Stick (Default value is '#00AA00') 67 | * internalLineWidth {Int} (optional) - Border width of Stick (Default value is 2) 68 | * internalStrokeColor {String}(optional) - Border color of Stick (Default value is '#003300') 69 | * externalLineWidth {Int} (optional) - External reference circonference width (Default value is 2) 70 | * externalStrokeColor {String} (optional) - External reference circonference color (Default value is '#008000') 71 | * autoReturnToCenter {Bool} (optional) - Sets the behavior of the stick, whether or not, it should return to zero position when released (Default value is True and return to zero) 72 | */ 73 | var JoyStick = (function (container, parameters) { 74 | parameters = parameters || {}; 75 | var title = (typeof parameters.title === "undefined" ? "joystick" : parameters.title), 76 | width = (typeof parameters.width === "undefined" ? 0 : parameters.width), 77 | height = (typeof parameters.height === "undefined" ? 0 : parameters.height), 78 | internalFillColor = (typeof parameters.internalFillColor === "undefined" ? "#00AA00" : parameters.internalFillColor), 79 | internalLineWidth = (typeof parameters.internalLineWidth === "undefined" ? 2 : parameters.internalLineWidth), 80 | internalStrokeColor = (typeof parameters.internalStrokeColor === "undefined" ? "#003300" : parameters.internalStrokeColor), 81 | externalLineWidth = (typeof parameters.externalLineWidth === "undefined" ? 2 : parameters.externalLineWidth), 82 | externalStrokeColor = (typeof parameters.externalStrokeColor === "undefined" ? "#008000" : parameters.externalStrokeColor), 83 | autoReturnToCenter = (typeof parameters.autoReturnToCenter === "undefined" ? true : parameters.autoReturnToCenter); 84 | 85 | // Create Canvas element and add it in the Container object 86 | var objContainer = document.getElementById(container); 87 | var canvas = document.createElement("canvas"); 88 | canvas.id = title; 89 | if (width === 0) { width = objContainer.clientWidth; } 90 | if (height === 0) { height = objContainer.clientHeight; } 91 | canvas.width = width; 92 | canvas.height = height; 93 | objContainer.appendChild(canvas); 94 | var context = canvas.getContext("2d"); 95 | 96 | var pressed = 0; // Bool - 1=Yes - 0=No 97 | var circumference = 2 * Math.PI; 98 | var internalRadius = (canvas.width - ((canvas.width / 2) + 10)) / 2; 99 | var maxMoveStick = internalRadius + 5; 100 | var externalRadius = internalRadius + 30; 101 | var centerX = canvas.width / 2; 102 | var centerY = canvas.height / 2; 103 | var directionHorizontalLimitPos = canvas.width / 10; 104 | var directionHorizontalLimitNeg = directionHorizontalLimitPos * -1; 105 | var directionVerticalLimitPos = canvas.height / 10; 106 | var directionVerticalLimitNeg = directionVerticalLimitPos * -1; 107 | // Used to save current position of stick 108 | var movedX = centerX; 109 | var movedY = centerY; 110 | 111 | //// Check if the device support the touch or not 112 | //if ("ontouchstart" in document.documentElement) { 113 | canvas.addEventListener("touchstart", onTouchStart, false); 114 | canvas.addEventListener("touchmove", onTouchMove, false); 115 | canvas.addEventListener("touchend", onTouchEnd, false); 116 | //} 117 | //else { 118 | canvas.addEventListener("mousedown", onMouseDown, false); 119 | canvas.addEventListener("mousemove", onMouseMove, false); 120 | canvas.addEventListener("mouseup", onMouseUp, false); 121 | //} 122 | // Draw the object 123 | drawExternal(); 124 | drawInternal(); 125 | /****************************************************** 126 | * Private methods 127 | *****************************************************/ 128 | /** 129 | * @desc Draw the external circle used as reference position 130 | */ 131 | function drawExternal() { 132 | context.beginPath(); 133 | context.arc(centerX, centerY, externalRadius, 0, circumference, false); 134 | context.lineWidth = externalLineWidth; 135 | context.strokeStyle = externalStrokeColor; 136 | context.stroke(); 137 | } 138 | /** 139 | * @desc Draw the internal stick in the current position the user have moved it 140 | */ 141 | function drawInternal() { 142 | context.beginPath(); 143 | if (movedX < internalRadius) { movedX = maxMoveStick; } 144 | if ((movedX + internalRadius) > canvas.width) { movedX = canvas.width - (maxMoveStick); } 145 | if (movedY < internalRadius) { movedY = maxMoveStick; } 146 | if ((movedY + internalRadius) > canvas.height) { movedY = canvas.height - (maxMoveStick); } 147 | context.arc(movedX, movedY, internalRadius, 0, circumference, false); 148 | // create radial gradient 149 | var grd = context.createRadialGradient(centerX, centerY, 5, centerX, centerY, 200); 150 | // Light color 151 | grd.addColorStop(0, internalFillColor); 152 | // Dark color 153 | grd.addColorStop(1, internalStrokeColor); 154 | context.fillStyle = grd; 155 | context.fill(); 156 | context.lineWidth = internalLineWidth; 157 | context.strokeStyle = internalStrokeColor; 158 | context.stroke(); 159 | } 160 | 161 | /** 162 | * @desc Events for manage touch 163 | */ 164 | function onTouchStart(event) { 165 | pressed = 1; 166 | } 167 | function onTouchMove(event) { 168 | // Prevent the browser from doing its default thing (scroll, zoom) 169 | event.preventDefault(); 170 | if (pressed === 1 && event.targetTouches[0].target === canvas) { 171 | movedX = event.targetTouches[0].pageX; 172 | movedY = event.targetTouches[0].pageY; 173 | // Manage offset 174 | movedX -= canvas.offsetLeft; 175 | movedY -= canvas.offsetTop; 176 | // Delete canvas 177 | context.clearRect(0, 0, canvas.width, canvas.height); 178 | // Redraw object 179 | drawExternal(); 180 | drawInternal(); 181 | } 182 | } 183 | function onTouchEnd(event) { 184 | pressed = 0; 185 | // If required reset position store variable 186 | if (autoReturnToCenter) { 187 | movedX = centerX; 188 | movedY = centerY; 189 | } 190 | // Delete canvas 191 | context.clearRect(0, 0, canvas.width, canvas.height); 192 | // Redraw object 193 | drawExternal(); 194 | drawInternal(); 195 | //canvas.unbind('touchmove'); 196 | } 197 | /** 198 | * @desc Events for manage mouse 199 | */ 200 | function onMouseDown(event) { 201 | pressed = 1; 202 | } 203 | function onMouseMove(event) { 204 | if (pressed === 1) { 205 | movedX = event.pageX; 206 | movedY = event.pageY; 207 | // Manage offset 208 | movedX -= canvas.offsetLeft; 209 | movedY -= canvas.offsetTop; 210 | // Delete canvas 211 | context.clearRect(0, 0, canvas.width, canvas.height); 212 | // Redraw object 213 | drawExternal(); 214 | drawInternal(); 215 | } 216 | } 217 | function onMouseUp(event) { 218 | pressed = 0; 219 | // If required reset position store variable 220 | if (autoReturnToCenter) { 221 | movedX = centerX; 222 | movedY = centerY; 223 | } 224 | // Delete canvas 225 | context.clearRect(0, 0, canvas.width, canvas.height); 226 | // Redraw object 227 | drawExternal(); 228 | drawInternal(); 229 | //canvas.unbind('mousemove'); 230 | } 231 | /****************************************************** 232 | * Public methods 233 | *****************************************************/ 234 | /** 235 | * @desc The width of canvas 236 | * @return Number of pixel width 237 | */ 238 | this.GetWidth = function () { 239 | return canvas.width; 240 | }; 241 | 242 | /** 243 | * @desc The height of canvas 244 | * @return Number of pixel height 245 | */ 246 | this.GetHeight = function () { 247 | return canvas.height; 248 | }; 249 | 250 | /** 251 | * @desc The X position of the cursor relative to the canvas that contains it and to its dimensions 252 | * @return Number that indicate relative position 253 | */ 254 | this.GetPosX = function () { 255 | return movedX; 256 | }; 257 | 258 | /** 259 | * @desc The Y position of the cursor relative to the canvas that contains it and to its dimensions 260 | * @return Number that indicate relative position 261 | */ 262 | this.GetPosY = function () { 263 | return movedY; 264 | }; 265 | 266 | /** 267 | * @desc Normalizzed value of X move of stick 268 | * @return Integer from -100 to +100 269 | */ 270 | this.GetX = function () { 271 | return (100 * ((movedX - centerX) / maxMoveStick)).toFixed(); 272 | }; 273 | 274 | /** 275 | * @desc Normalizzed value of Y move of stick 276 | * @return Integer from -100 to +100 277 | */ 278 | this.GetY = function () { 279 | return ((100 * ((movedY - centerY) / maxMoveStick)) * -1).toFixed(); 280 | }; 281 | 282 | /** 283 | * @desc Get the direction of the cursor as a string that indicates the cardinal points where this is oriented 284 | * @return String of cardinal point N, NE, E, SE, S, SW, W, NW and C when it is placed in the center 285 | */ 286 | this.GetDir = function () { 287 | var result = ""; 288 | var orizontal = movedX - centerX; 289 | var vertical = movedY - centerY; 290 | 291 | if (vertical >= directionVerticalLimitNeg && vertical <= directionVerticalLimitPos) { 292 | result = "C"; 293 | } 294 | if (vertical < directionVerticalLimitNeg) { 295 | result = "N"; 296 | } 297 | if (vertical > directionVerticalLimitPos) { 298 | result = "S"; 299 | } 300 | 301 | if (orizontal < directionHorizontalLimitNeg) { 302 | if (result === "C") { 303 | result = "W"; 304 | } 305 | else { 306 | result += "W"; 307 | } 308 | } 309 | if (orizontal > directionHorizontalLimitPos) { 310 | if (result === "C") { 311 | result = "E"; 312 | } 313 | else { 314 | result += "E"; 315 | } 316 | } 317 | 318 | return result; 319 | }; 320 | }); -------------------------------------------------------------------------------- /BlazorWebAssemblySample/Devices/Wowwee/Mip/MiPRobot.cs: -------------------------------------------------------------------------------- 1 | using Blazm.Bluetooth; 2 | using MiPWinRTSDK.MiP.Models; 3 | using MiPWinRTSDK.MiP.Models.MiP; 4 | using System.Drawing; 5 | using System.Text; 6 | 7 | namespace MiPWinRTSDK.MiP; 8 | 9 | class MiPRobot : MiPBase 10 | { 11 | public MiPRobot(IBluetoothNavigator navigator) 12 | { 13 | base.Navigator = navigator; 14 | } 15 | 16 | public override async void Connect() 17 | { 18 | //try 19 | //{ 20 | var q = new RequestDeviceQuery(); 21 | //q.Filters.Add(new Filter() { Name = "WowWee-MiP-27244" }); 22 | q.Filters.Add(new Filter() { Name = "Miposaur-17704" }); 23 | q.OptionalServices.Add("battery_service"); 24 | q.OptionalServices.Add(WriteServiceGuid); //Write service 25 | q.OptionalServices.Add(ReadServiceGuid); //Read service 26 | 27 | try 28 | { 29 | Device = await Navigator.RequestDeviceAsync(q); 30 | Console.WriteLine(Device); 31 | 32 | await Device.SetupNotifyAsync(ReadServiceGuid, ReadCharacteristicGuid); 33 | Device.Notification += Value_Notification; 34 | 35 | base.Connect(); 36 | } 37 | catch { } 38 | } 39 | 40 | private void Value_Notification(object sender, CharacteristicEventArgs e) 41 | { 42 | var data = e.Value.ToArray(); 43 | var hexstring = UTF8Encoding.UTF8.GetString(data, 0, data.Count()); 44 | var bytes = StringToByteArray(hexstring); 45 | 46 | switch (bytes[0]) 47 | { 48 | case 0x82: //GetCurrentMIPGameMode 49 | GameMode = (GameModeEmum)Enum.Parse(typeof(GameModeEmum), bytes[1].ToString()); 50 | OnGameModeRecieved(GameMode); 51 | break; 52 | case 0x03: //Receive IR Dongle code 53 | OnIRDongleCodeRecievedEvent(bytes.ToList()); 54 | break; 55 | case 0x79: //Request MIP status 56 | BatteryLevel = bytes[1]; 57 | Status = (StatusEnum)Enum.Parse(typeof(StatusEnum), bytes[2].ToString()); 58 | OnMiPStatusRecieved(new MiPStatus() { BatteryLevel = BatteryLevel, Status = Status }); 59 | break; 60 | case 0x81: //Request weight update 61 | Weight = bytes[1]; 62 | OnWeightUpdateRecieved(Weight); 63 | break; 64 | case 0x83: //RequestChestLED 65 | ChestLEDColor = Color.FromArgb(255, bytes[1], bytes[2], bytes[3]); 66 | if (bytes.Count() >= 5) 67 | { 68 | ChestLEDTimeOn = bytes[4]; 69 | } 70 | if (bytes.Count() == 6) 71 | { 72 | ChestLEDTimeOff = bytes[5]; 73 | } 74 | 75 | OnChestLEDRecieved(new ChestLED() { Color = ChestLEDColor, TimeOn = ChestLEDTimeOn, TimeOff = ChestLEDTimeOff }); 76 | break; 77 | case 0x8b:// RequestHeadLED 78 | Light1 = (LightEnum)Enum.Parse(typeof(LightEnum), bytes[1].ToString()); 79 | Light2 = (LightEnum)Enum.Parse(typeof(LightEnum), bytes[2].ToString()); 80 | Light3 = (LightEnum)Enum.Parse(typeof(LightEnum), bytes[3].ToString()); 81 | Light4 = (LightEnum)Enum.Parse(typeof(LightEnum), bytes[4].ToString()); 82 | OnHeadLEDRecieved(new HeadLED() { Light1 = Light1, Light2 = Light2, Light3 = Light3, Light4 = Light4 }); 83 | break; 84 | case 0x0A: //GestureDetect 85 | var detectedGesture = (GestureEnum)Enum.Parse(typeof(GestureEnum), bytes[1].ToString()); 86 | OnGestureRecieved(detectedGesture); 87 | break; 88 | case 0x0D: //RadarMode 89 | RadarMode = (GestureRadarEnum)Enum.Parse(typeof(GestureRadarEnum), bytes[1].ToString()); 90 | OnRadarModeRecieved(RadarMode); 91 | break; 92 | case 0x0C: //Radar Response 93 | byte response = bytes[1]; 94 | OnRadarRecieved(response); 95 | break; 96 | case 0x0F: //Mip Detection Status 97 | MiPDetectionMode = bytes[1]; 98 | MiPDetectionIRTxPower = bytes[2]; 99 | OnMiPDetectionStatusRecieved(new MiPDetectionStatus() { Mode = MiPDetectionMode, IRTxPower = MiPDetectionIRTxPower }); 100 | break; 101 | case 0x04: //Mip Detected 102 | var mipsettingNumber = bytes[1]; 103 | OnMipDetectedRecieved(mipsettingNumber); 104 | break; 105 | case 0x1A: //Shake Detected 106 | OnShakeDetectedRecieved(); 107 | break; 108 | case 0x11: //IR Control Status 109 | IRControlStatus = bytes[1]; 110 | OnIRControlStatusRecieved(IRControlStatus); 111 | break; 112 | case 0xFA: //Sleep 113 | OnSleepRecieved(); 114 | break; 115 | case 0x13: //MIP User Or Other Eeprom Data 116 | var address = bytes[1]; 117 | var userEepromdata = bytes[2]; 118 | OnEePromDataRecieved(new EepromData() { Address = address, Data = userEepromdata }); 119 | break; 120 | case 0x14: //Mip Software Version 121 | MiPVersion = string.Format("{0}.{1}.{2}.{3}", Convert.ToInt32(bytes[1]), Convert.ToInt32(bytes[2]), Convert.ToInt32(bytes[3]), Convert.ToInt32(bytes[4])); 122 | OnSoftwareVersionRecieved(MiPVersion); 123 | break; 124 | case 0x19: //Mip Hardware Info 125 | VoiceChipVersion = bytes[1]; 126 | HardwareVersion = bytes[2]; 127 | OnMiPHardwareVersionRecieved(new MiPHardwareVersion() { VoiceChipVersion = VoiceChipVersion, HardwareVersion = HardwareVersion }); 128 | break; 129 | case 0x16: //GetVolume 130 | Volume = bytes[1]; 131 | OnVolumeRecieved(Volume); 132 | break; 133 | case 0x1d: //Clap times 134 | var times = bytes[1]; 135 | OnClapTimesRecieved(times); 136 | break; 137 | case 0x1f: //Clap Status 138 | var isEnabled = bytes[1] == 0x00 ? false : true; 139 | int delayTime = (bytes[2] << 8) & bytes[3]; 140 | OnClapStatusRecieved(new ClapStatus() { Enabled = isEnabled, DelayTime = delayTime }); 141 | break; 142 | default: 143 | //Value_Notification(bytes); 144 | break; 145 | } 146 | } 147 | 148 | private byte volume = 7; 149 | private byte hardwareVersion; 150 | private byte voiceChipVersion; 151 | private string miPVersion; 152 | private byte iRControlStatus; 153 | private byte mipDetectionIRTxPower; 154 | private byte mipDetectionMode; 155 | private GestureRadarEnum radarMode; 156 | private LightEnum light4; 157 | private LightEnum light3; 158 | private LightEnum light2; 159 | private LightEnum light1; 160 | 161 | private byte weight; 162 | private byte batteryLevel; 163 | private StatusEnum status; 164 | private GameModeEmum gameMode; 165 | public GameModeEmum GameMode 166 | { 167 | get 168 | { 169 | return gameMode; 170 | } 171 | set 172 | { 173 | gameMode = value; 174 | RaisePropertyChanged(); 175 | } 176 | } 177 | public StatusEnum Status 178 | { 179 | get 180 | { 181 | return status; 182 | } 183 | private set 184 | { 185 | status = value; 186 | RaisePropertyChanged(); 187 | } 188 | } 189 | public byte BatteryLevel 190 | { 191 | get 192 | { 193 | return batteryLevel; 194 | } 195 | private set 196 | { 197 | batteryLevel = value; 198 | RaisePropertyChanged(); 199 | } 200 | } 201 | public byte Weight 202 | { 203 | get 204 | { 205 | return weight; 206 | } 207 | private set 208 | { 209 | weight = value; 210 | RaisePropertyChanged(); 211 | } 212 | } 213 | 214 | public LightEnum Light1 215 | { 216 | get 217 | { 218 | return light1; 219 | } 220 | set 221 | { 222 | light1 = value; 223 | RaisePropertyChanged(); 224 | } 225 | } 226 | public LightEnum Light2 227 | { 228 | get 229 | { 230 | return light2; 231 | } 232 | set 233 | { 234 | light2 = value; 235 | RaisePropertyChanged(); 236 | } 237 | } 238 | public LightEnum Light3 239 | { 240 | get 241 | { 242 | return light3; 243 | } 244 | set 245 | { 246 | light3 = value; 247 | RaisePropertyChanged(); 248 | } 249 | } 250 | public LightEnum Light4 251 | { 252 | get 253 | { 254 | return light4; 255 | } 256 | set 257 | { 258 | light4 = value; 259 | RaisePropertyChanged(); 260 | } 261 | } 262 | 263 | public GestureRadarEnum RadarMode 264 | { 265 | get 266 | { 267 | return radarMode; 268 | } 269 | private set 270 | { 271 | radarMode = value; 272 | RaisePropertyChanged(); 273 | } 274 | } 275 | 276 | public byte MiPDetectionMode 277 | { 278 | get 279 | { 280 | return mipDetectionMode; 281 | } 282 | private set 283 | { 284 | mipDetectionMode = value; 285 | RaisePropertyChanged(); 286 | } 287 | } 288 | public byte MiPDetectionIRTxPower 289 | { 290 | get 291 | { 292 | return mipDetectionIRTxPower; 293 | } 294 | private set 295 | { 296 | mipDetectionIRTxPower = value; 297 | RaisePropertyChanged(); 298 | } 299 | } 300 | 301 | public byte IRControlStatus 302 | { 303 | get 304 | { 305 | return iRControlStatus; 306 | } 307 | set 308 | { 309 | iRControlStatus = value; 310 | RaisePropertyChanged(); 311 | } 312 | } 313 | 314 | public string MiPVersion 315 | { 316 | get 317 | { 318 | return miPVersion; 319 | } 320 | private set 321 | { 322 | miPVersion = value; 323 | RaisePropertyChanged(); 324 | } 325 | } 326 | public byte VoiceChipVersion 327 | { 328 | get 329 | { 330 | return voiceChipVersion; 331 | } 332 | private set 333 | { 334 | voiceChipVersion = value; 335 | RaisePropertyChanged(); 336 | } 337 | } 338 | public byte HardwareVersion 339 | { 340 | get 341 | { 342 | return hardwareVersion; 343 | } 344 | private set 345 | { 346 | hardwareVersion = value; 347 | RaisePropertyChanged(); 348 | } 349 | } 350 | 351 | public byte Volume 352 | { 353 | get 354 | { 355 | return volume; 356 | } 357 | private set 358 | { 359 | volume = value; 360 | RaisePropertyChanged(); 361 | } 362 | } 363 | 364 | public async Task SetPosition(PositionEnum position) 365 | { 366 | List bytes = new List(); 367 | //Direction 368 | bytes.Add(((byte)position)); 369 | await SendCommand(0x08, bytes); 370 | } 371 | 372 | public async Task SetGameMode(GameModeEmum gamemode) 373 | { 374 | List bytes = new List(); 375 | bytes.Add((byte)gamemode); 376 | await SendCommand(0x76, bytes); 377 | } 378 | 379 | public async Task GetCurrentMIPGameMode() 380 | { 381 | List bytes = new List(); 382 | await SendCommand(0x82, bytes); 383 | } 384 | 385 | public async Task MIPGetUp(GetUpEnum getUp) 386 | { 387 | List bytes = new List(); 388 | bytes.Add((byte)getUp); 389 | await SendCommand(0x23, bytes); 390 | } 391 | 392 | public async Task RequestWeightUpdate() 393 | { 394 | List bytes = new List(); 395 | 396 | await SendCommand(0x81, bytes); 397 | } 398 | 399 | public async Task ReadOdometer() 400 | { 401 | List bytes = new List(); 402 | await SendCommand(0x85, bytes); 403 | } 404 | 405 | public async Task ResetOdometer() 406 | { 407 | List bytes = new List(); 408 | await SendCommand(0x86, bytes); 409 | } 410 | public async Task SetGestureOrRadarMode(GestureRadarEnum mode) 411 | { 412 | List bytes = new List(); 413 | bytes.Add((byte)mode); 414 | await SendCommand(0x0C, bytes); 415 | } 416 | 417 | public async Task GetRadarMode() 418 | { 419 | List bytes = new List(); 420 | await SendCommand(0x0D, bytes); 421 | } 422 | 423 | public async Task SetMiPDetectionMode(byte idNumber, byte irTxPower) 424 | { 425 | List bytes = new List(); 426 | bytes.Add((byte)idNumber); 427 | bytes.Add((byte)irTxPower); 428 | await SendCommand(0x0E, bytes); 429 | } 430 | 431 | public async Task RequestMiPDetectionMode() 432 | { 433 | List bytes = new List(); 434 | await SendCommand(0x0F, bytes); 435 | } 436 | 437 | public async Task IRRemoteControlEnabled(bool enabled) 438 | { 439 | List bytes = new List(); 440 | bytes.Add((byte)(enabled ? 0x01 : 0x00)); 441 | await SendCommand(0x10, bytes); 442 | } 443 | 444 | public async Task RequestIRControlEnabled() 445 | { 446 | List bytes = new List(); 447 | await SendCommand(0x11, bytes); 448 | } 449 | 450 | public async Task ForceBLEDisconnect() 451 | { 452 | List bytes = new List(); 453 | await SendCommand(0xFC, bytes); 454 | } 455 | 456 | public async Task GetSoftwareVersion() 457 | { 458 | List bytes = new List(); 459 | await SendCommand(0x14, bytes); 460 | } 461 | 462 | public async Task SetVolume(byte volume) 463 | { 464 | List bytes = new List(); 465 | bytes.Add((byte)volume); 466 | await SendCommand(0x15, bytes); 467 | } 468 | 469 | /// 470 | /// 471 | /// IR data bit23 ~bit16 472 | /// IR data bit15 ~bit8 473 | /// IR data bit7 ~bit0 474 | /// IR data bit7 ~bit0 475 | /// Data numbers(1~32):e.g.BYTE5=0x08 means BYTE4 is useful. 476 | /// IR Tx power(1~120)(About 1cm ~300cm) 477 | /// 478 | public async Task SendIRDongleCode(byte byte1, byte byte2, byte byte3, byte byte4, byte irdDataNumbers, byte irTxPower) 479 | { 480 | List bytes = new List(); 481 | bytes.Add((byte)byte1); 482 | bytes.Add((byte)byte2); 483 | bytes.Add((byte)byte3); 484 | bytes.Add((byte)byte4); 485 | bytes.Add((byte)irdDataNumbers); 486 | bytes.Add((byte)irTxPower); 487 | await SendCommand(0x8C, bytes); 488 | } 489 | 490 | public async Task ClapEnabled(bool enabled) 491 | { 492 | List bytes = new List(); 493 | bytes.Add((byte)(enabled ? 0x01 : 0x00)); 494 | await SendCommand(0x1e, bytes); 495 | } 496 | public async Task RequestClapEnabled() 497 | { 498 | List bytes = new List(); 499 | await SendCommand(0x1f, bytes); 500 | } 501 | 502 | public event EventHandler GameModeRecievedEvent; 503 | private void OnGameModeRecieved(GameModeEmum gameMode) 504 | { 505 | GameModeRecievedEvent?.Invoke(this, gameMode); 506 | } 507 | 508 | public event EventHandler> IRDongleCodeRecievedEvent; 509 | private void OnIRDongleCodeRecievedEvent(IList irDongleCode) 510 | { 511 | IRDongleCodeRecievedEvent?.Invoke(this, irDongleCode); 512 | } 513 | 514 | public event EventHandler MiPStatusRecievedEvent; 515 | private void OnMiPStatusRecieved(MiPStatus status) 516 | { 517 | MiPStatusRecievedEvent?.Invoke(this, status); 518 | } 519 | 520 | public event EventHandler WeightUpdateRecievedEvent; 521 | private void OnWeightUpdateRecieved(byte weight) 522 | { 523 | WeightUpdateRecievedEvent?.Invoke(this, weight); 524 | } 525 | 526 | public event EventHandler HeadLEDRecievedEvent; 527 | private void OnHeadLEDRecieved(HeadLED headLED) 528 | { 529 | HeadLEDRecievedEvent?.Invoke(this, headLED); 530 | } 531 | 532 | public event EventHandler GestureRecievedEvent; 533 | private void OnGestureRecieved(GestureEnum gesture) 534 | { 535 | GestureRecievedEvent?.Invoke(this, gesture); 536 | } 537 | 538 | public event EventHandler RadarModeRecievedEvent; 539 | private void OnRadarModeRecieved(GestureRadarEnum gestureRadarMode) 540 | { 541 | RadarModeRecievedEvent?.Invoke(this, gestureRadarMode); 542 | } 543 | 544 | public event EventHandler RadarRecievedEvent; 545 | private void OnRadarRecieved(byte radar) 546 | { 547 | RadarRecievedEvent?.Invoke(this, radar); 548 | } 549 | 550 | public event EventHandler MiPDetectionStatusRecievedEvent; 551 | private void OnMiPDetectionStatusRecieved(MiPDetectionStatus mipDetection) 552 | { 553 | MiPDetectionStatusRecievedEvent?.Invoke(this, mipDetection); 554 | } 555 | 556 | public event EventHandler MipDetectedRecievedEvent; 557 | private void OnMipDetectedRecieved(byte mipSettingsnumber) 558 | { 559 | MipDetectedRecievedEvent?.Invoke(this, mipSettingsnumber); 560 | } 561 | 562 | public event EventHandler IRControlStatusRecievedEvent; 563 | private void OnIRControlStatusRecieved(byte irControlStatus) 564 | { 565 | IRControlStatusRecievedEvent?.Invoke(this, IRControlStatus); 566 | } 567 | 568 | public event EventHandler SleepRecievedEvent; 569 | private void OnSleepRecieved() 570 | { 571 | SleepRecievedEvent?.Invoke(this, null); 572 | } 573 | 574 | public event EventHandler EePromDataRecievedEvent; 575 | private void OnEePromDataRecieved(EepromData eepromData) 576 | { 577 | EePromDataRecievedEvent?.Invoke(this, eepromData); 578 | } 579 | 580 | public event EventHandler SoftwareVersionRecievedEvent; 581 | private void OnSoftwareVersionRecieved(string version) 582 | { 583 | SoftwareVersionRecievedEvent?.Invoke(this, version); 584 | } 585 | 586 | public event EventHandler MiPHardwareVersionRecievedEvent; 587 | private void OnMiPHardwareVersionRecieved(MiPHardwareVersion hardwareVersion) 588 | { 589 | MiPHardwareVersionRecievedEvent?.Invoke(this, hardwareVersion); 590 | } 591 | 592 | public event EventHandler VolumeRecievedEvent; 593 | private void OnVolumeRecieved(byte volume) 594 | { 595 | VolumeRecievedEvent?.Invoke(this, volume); 596 | } 597 | 598 | public event EventHandler ClapTimesRecievedEvent; 599 | private void OnClapTimesRecieved(byte claps) 600 | { 601 | ClapTimesRecievedEvent?.Invoke(this, claps); 602 | } 603 | 604 | public event EventHandler ClapStatusRecievedEvent; 605 | private void OnClapStatusRecieved(ClapStatus clapStatus) 606 | { 607 | ClapStatusRecievedEvent?.Invoke(this, clapStatus); 608 | } 609 | //internal override void CharacteristicsNotify_ValueChanged(Windows.Devices.Bluetooth.GenericAttributeProfile.GattCharacteristic sender, Windows.Devices.Bluetooth.GenericAttributeProfile.GattValueChangedEventArgs args) 610 | //{ 611 | // var data = args.CharacteristicValue.ToArray(); 612 | // var hexstring = UTF8Encoding.UTF8.GetString(data, 0, data.Count()); 613 | // var bytes = StringToByteArray(hexstring); 614 | // switch (bytes[0]) 615 | // { 616 | // case 0x82: //GetCurrentMIPGameMode 617 | // GameMode = (GameModeEmum)Enum.Parse(typeof(GameModeEmum), bytes[1].ToString()); 618 | // OnGameModeRecieved(GameMode); 619 | // break; 620 | // case 0x03: //Receive IR Dongle code 621 | // OnIRDongleCodeRecievedEvent(bytes.ToList()); 622 | // break; 623 | // case 0x79: //Request MIP status 624 | // BatteryLevel = bytes[1]; 625 | // Status = (StatusEnum)Enum.Parse(typeof(StatusEnum), bytes[2].ToString()); 626 | // OnMiPStatusRecieved(new MiPStatus() { BatteryLevel = BatteryLevel, Status = Status }); 627 | // break; 628 | // case 0x81: //Request weight update 629 | // Weight = bytes[1]; 630 | // OnWeightUpdateRecieved(Weight); 631 | // break; 632 | // //case 0x83: //RequestChestLED 633 | // // ChestLEDColor = Color.FromArgb(255, bytes[1], bytes[2], bytes[3]); 634 | // // if (bytes.Count() >= 5) 635 | // // { 636 | // // ChestLEDTimeOn = bytes[4]; 637 | // // } 638 | // // if (bytes.Count() == 6) 639 | // // { 640 | // // ChestLEDTimeOff = bytes[5]; 641 | // // } 642 | 643 | // // OnChestLEDRecieved(new ChestLED() { Color = ChestLEDColor, TimeOn = ChestLEDTimeOn, TimeOff = ChestLEDTimeOff }); 644 | // // break; 645 | // case 0x8b:// RequestHeadLED 646 | // Light1 = (LightEnum)Enum.Parse(typeof(LightEnum), bytes[1].ToString()); 647 | // Light2 = (LightEnum)Enum.Parse(typeof(LightEnum), bytes[2].ToString()); 648 | // Light3 = (LightEnum)Enum.Parse(typeof(LightEnum), bytes[3].ToString()); 649 | // Light4 = (LightEnum)Enum.Parse(typeof(LightEnum), bytes[4].ToString()); 650 | // OnHeadLEDRecieved(new HeadLED() { Light1 = Light1, Light2 = Light2, Light3 = Light3, Light4 = Light4 }); 651 | // break; 652 | // case 0x0A: //GestureDetect 653 | // var detectedGesture = (GestureEnum)Enum.Parse(typeof(GestureEnum), bytes[1].ToString()); 654 | // OnGestureRecieved(detectedGesture); 655 | // break; 656 | // case 0x0D: //RadarMode 657 | // RadarMode = (GestureRadarEnum)Enum.Parse(typeof(GestureRadarEnum), bytes[1].ToString()); 658 | // OnRadarModeRecieved(RadarMode); 659 | // break; 660 | // case 0x0C: //Radar Response 661 | // byte response = bytes[1]; 662 | // OnRadarRecieved(response); 663 | // break; 664 | // case 0x0F: //Mip Detection Status 665 | // MiPDetectionMode = bytes[1]; 666 | // MiPDetectionIRTxPower = bytes[2]; 667 | // OnMiPDetectionStatusRecieved(new MiPDetectionStatus() { Mode = MiPDetectionMode, IRTxPower = MiPDetectionIRTxPower }); 668 | // break; 669 | // case 0x04: //Mip Detected 670 | // var mipsettingNumber = bytes[1]; 671 | // OnMipDetectedRecieved(mipsettingNumber); 672 | // break; 673 | // //case 0x1A: //Shake Detected 674 | // // OnShakeDetectedRecieved(); 675 | // // break; 676 | // case 0x11: //IR Control Status 677 | // IRControlStatus = bytes[1]; 678 | // OnIRControlStatusRecieved(IRControlStatus); 679 | // break; 680 | // case 0xFA: //Sleep 681 | // OnSleepRecieved(); 682 | // break; 683 | // case 0x13: //MIP User Or Other Eeprom Data 684 | // var address = bytes[1]; 685 | // var userEepromdata = bytes[2]; 686 | // OnEePromDataRecieved(new EepromData() { Address = address, Data = userEepromdata }); 687 | // break; 688 | // case 0x14: //Mip Software Version 689 | // MiPVersion = string.Format("{0}.{1}.{2}.{3}", Convert.ToInt32(bytes[1]), Convert.ToInt32(bytes[2]), Convert.ToInt32(bytes[3]), Convert.ToInt32(bytes[4])); 690 | // OnSoftwareVersionRecieved(MiPVersion); 691 | // break; 692 | // case 0x19: //Mip Hardware Info 693 | // VoiceChipVersion = bytes[1]; 694 | // HardwareVersion = bytes[2]; 695 | // OnMiPHardwareVersionRecieved(new MiPHardwareVersion() { VoiceChipVersion = VoiceChipVersion, HardwareVersion = HardwareVersion }); 696 | // break; 697 | // case 0x16: //GetVolume 698 | // Volume = bytes[1]; 699 | // OnVolumeRecieved(Volume); 700 | // break; 701 | // case 0x1d: //Clap times 702 | // var times = bytes[1]; 703 | // OnClapTimesRecieved(times); 704 | // break; 705 | // case 0x1f: //Clap Status 706 | // var isEnabled = bytes[1] == 0x00 ? false : true; 707 | // int delayTime = (bytes[2] << 8) & bytes[3]; 708 | // OnClapStatusRecieved(new ClapStatus() { Enabled = isEnabled, DelayTime = delayTime }); 709 | // break; 710 | // default: 711 | // base.CharacteristicsNotify_ValueChanged(sender,args); 712 | // break; 713 | // } 714 | 715 | //} 716 | 717 | } 718 | -------------------------------------------------------------------------------- /BlazorWebAssemblySample/wwwroot/css/open-iconic/font/fonts/open-iconic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014 9 | By P.J. Onori 10 | Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net) 11 | 12 | 13 | 14 | 27 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 45 | 47 | 49 | 51 | 53 | 55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 74 | 76 | 79 | 81 | 84 | 86 | 88 | 91 | 93 | 95 | 98 | 100 | 102 | 104 | 106 | 109 | 112 | 115 | 117 | 121 | 123 | 125 | 127 | 130 | 132 | 134 | 136 | 138 | 141 | 143 | 145 | 147 | 149 | 151 | 153 | 155 | 157 | 159 | 162 | 165 | 167 | 169 | 172 | 174 | 177 | 179 | 181 | 183 | 185 | 189 | 191 | 194 | 196 | 198 | 200 | 202 | 205 | 207 | 209 | 211 | 213 | 215 | 218 | 220 | 222 | 224 | 226 | 228 | 230 | 232 | 234 | 236 | 238 | 241 | 243 | 245 | 247 | 249 | 251 | 253 | 256 | 259 | 261 | 263 | 265 | 267 | 269 | 272 | 274 | 276 | 280 | 282 | 285 | 287 | 289 | 292 | 295 | 298 | 300 | 302 | 304 | 306 | 309 | 312 | 314 | 316 | 318 | 320 | 322 | 324 | 326 | 330 | 334 | 338 | 340 | 343 | 345 | 347 | 349 | 351 | 353 | 355 | 358 | 360 | 363 | 365 | 367 | 369 | 371 | 373 | 375 | 377 | 379 | 381 | 383 | 386 | 388 | 390 | 392 | 394 | 396 | 399 | 401 | 404 | 406 | 408 | 410 | 412 | 414 | 416 | 419 | 421 | 423 | 425 | 428 | 431 | 435 | 438 | 440 | 442 | 444 | 446 | 448 | 451 | 453 | 455 | 457 | 460 | 462 | 464 | 466 | 468 | 471 | 473 | 477 | 479 | 481 | 483 | 486 | 488 | 490 | 492 | 494 | 496 | 499 | 501 | 504 | 506 | 509 | 512 | 515 | 517 | 520 | 522 | 524 | 526 | 529 | 532 | 534 | 536 | 539 | 542 | 543 | 544 | --------------------------------------------------------------------------------