Init
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
22
GoveeCSharpConnector.sln
Normal file
22
GoveeCSharpConnector.sln
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoveeCSharpConnector", "GoveeCSharpConnector\GoveeCSharpConnector.csproj", "{EDF67B3A-9EBF-4C76-92E2-0AACD6B8081B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoveeCsharpConnector.Example", "GoveeCsharpConnector.Example\GoveeCsharpConnector.Example.csproj", "{E487B84B-F619-430A-A0D3-80D44FE9EE3F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{EDF67B3A-9EBF-4C76-92E2-0AACD6B8081B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EDF67B3A-9EBF-4C76-92E2-0AACD6B8081B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EDF67B3A-9EBF-4C76-92E2-0AACD6B8081B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EDF67B3A-9EBF-4C76-92E2-0AACD6B8081B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E487B84B-F619-430A-A0D3-80D44FE9EE3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E487B84B-F619-430A-A0D3-80D44FE9EE3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E487B84B-F619-430A-A0D3-80D44FE9EE3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E487B84B-F619-430A-A0D3-80D44FE9EE3F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
2
GoveeCSharpConnector.sln.DotSettings
Normal file
2
GoveeCSharpConnector.sln.DotSettings
Normal file
@ -0,0 +1,2 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Govee/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
7
GoveeCSharpConnector/Enums/PowerState.cs
Normal file
7
GoveeCSharpConnector/Enums/PowerState.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace GoveeCSharpConnector.Enums;
|
||||
|
||||
public enum PowerState
|
||||
{
|
||||
Off = 0,
|
||||
On = 1
|
||||
}
|
20
GoveeCSharpConnector/GoveeCSharpConnector.csproj
Normal file
20
GoveeCSharpConnector/GoveeCSharpConnector.csproj
Normal file
@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||
<PackageReference Include="System.Reactive" Version="6.0.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
67
GoveeCSharpConnector/Interfaces/IGoveeApiService.cs
Normal file
67
GoveeCSharpConnector/Interfaces/IGoveeApiService.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using GoveeCSharpConnector.Objects;
|
||||
|
||||
namespace GoveeCSharpConnector.Interfaces;
|
||||
|
||||
public interface IGoveeApiService
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the required Api Key for the Govee Api.
|
||||
/// Request Api Key in the Mobile Phone App.
|
||||
/// </summary>
|
||||
/// <param name="apiKey">Api Key as String</param>
|
||||
void SetApiKey(string apiKey);
|
||||
/// <summary>
|
||||
/// Returns current set Govee Api Key
|
||||
/// </summary>
|
||||
/// <returns>Govee Api Key as String</returns>
|
||||
string GetApiKey();
|
||||
/// <summary>
|
||||
/// Removes the Set Api Key and resets the HTTP Header
|
||||
/// </summary>
|
||||
void RemoveApiKey();
|
||||
/// <summary>
|
||||
/// Requests all Devices registered to Api Key Govee Account
|
||||
/// </summary>
|
||||
/// <returns>List of GoveeApiDevices</returns>
|
||||
Task<List<GoveeApiDevice>> GetDevices();
|
||||
/// <summary>
|
||||
/// Requests the State of a single Govee Device
|
||||
/// </summary>
|
||||
/// <param name="deviceId">Device Id Guid as string</param>
|
||||
/// <param name="deviceModel">Device Model Number as string</param>
|
||||
/// <returns>GoveeApiStat Object</returns>
|
||||
public Task<GoveeApiState> GetDeviceState(string deviceId, string deviceModel);
|
||||
/// <summary>
|
||||
/// Sets the On/Off state of a single Govee Device
|
||||
/// </summary>
|
||||
/// <param name="deviceId">Device Id Guid as string</param>
|
||||
/// <param name="deviceModel">Device Model Number as string</param>
|
||||
/// <param name="on"></param>
|
||||
/// <returns></returns>
|
||||
public Task ToggleState(string deviceId, string deviceModel, bool on);
|
||||
/// <summary>
|
||||
/// Sets the Brightness in Percent of a single Govee Device
|
||||
/// </summary>
|
||||
/// <param name="deviceId">Device Id Guid as string</param>
|
||||
/// <param name="deviceModel">Device Model Number as string</param>
|
||||
/// <param name="value">Brightness in Percent as Int</param>
|
||||
/// <returns></returns>
|
||||
public Task SetBrightness(string deviceId, string deviceModel, int value);
|
||||
/// <summary>
|
||||
/// Sets a Rgb Color of a single Govee Device
|
||||
/// </summary>
|
||||
/// <param name="deviceId">Device Id Guid as string</param>
|
||||
/// <param name="deviceModel">Device Model Number as string</param>
|
||||
/// <param name="color">Rgb Color</param>
|
||||
/// <returns></returns>
|
||||
public Task SetColor(string deviceId, string deviceModel, RgbColor color);
|
||||
/// <summary>
|
||||
/// Sets the Color Temperature of a single Govee Device
|
||||
/// </summary>
|
||||
/// <param name="deviceId">Device Id Guid as string</param>
|
||||
/// <param name="deviceModel">Device Model Number as string</param>
|
||||
/// <param name="value">Color Temp in Kelvin as Int</param>
|
||||
/// <returns></returns>
|
||||
public Task SetColorTemp(string deviceId, string deviceModel, int value);
|
||||
|
||||
}
|
63
GoveeCSharpConnector/Interfaces/IGoveeUdpService.cs
Normal file
63
GoveeCSharpConnector/Interfaces/IGoveeUdpService.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System.Data;
|
||||
using GoveeCSharpConnector.Objects;
|
||||
|
||||
namespace GoveeCSharpConnector.Interfaces;
|
||||
|
||||
public interface IGoveeUdpService
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a Scan Command via Udp Multicast.
|
||||
/// </summary>
|
||||
/// <param name="timeout">Standard 200ms</param>
|
||||
/// <returns>List of GoveeUdpDevices</returns>
|
||||
Task<List<GoveeUdpDevice>> GetDevices(TimeSpan? timeout = null);
|
||||
|
||||
/// <summary>
|
||||
/// Request the State of the Device
|
||||
/// </summary>
|
||||
/// <param name="deviceAddress">Ip Address of the Device</param>
|
||||
/// <param name="uniCastPort">Port of the Device. Standard 4003</param>
|
||||
/// <param name="timeout">Standard 200ms</param>
|
||||
/// <returns></returns>
|
||||
Task<GoveeUdpState> GetState(string deviceAddress, int uniCastPort = 4003, TimeSpan? timeout = null);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the On/Off State of the Device
|
||||
/// </summary>
|
||||
/// <param name="deviceAddress">Ip Address of the Device</param>
|
||||
/// <param name="on"></param>
|
||||
/// <param name="uniCastPort">Port of the Device. Standard 4003</param>
|
||||
/// <returns></returns>
|
||||
Task ToggleDevice(string deviceAddress, bool on, int uniCastPort = 4003);
|
||||
/// <summary>
|
||||
/// Sets the Brightness of the Device
|
||||
/// </summary>
|
||||
/// <param name="deviceAddress">Ip Address of the Device</param>
|
||||
/// <param name="brightness">In Percent 1-100</param>
|
||||
/// <param name="uniCastPort">Port of the Device. Standard 4003</param>
|
||||
/// <returns></returns>
|
||||
Task SetBrightness(string deviceAddress, short brightness, int uniCastPort = 4003);
|
||||
/// <summary>
|
||||
/// Sets the Color of the Device
|
||||
/// </summary>
|
||||
/// <param name="deviceAddress">Ip Address of the Device</param>
|
||||
/// <param name="color"></param>
|
||||
/// <param name="uniCastPort">Port of the Device. Standard 4003</param>
|
||||
/// <returns></returns>
|
||||
Task SetColor(string deviceAddress, RgbColor color, int uniCastPort = 4003);
|
||||
/// <summary>
|
||||
/// Starts the Udp Listener
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
void StartUdpListener();
|
||||
/// <summary>
|
||||
/// Returns the State of the Udp Listener
|
||||
/// </summary>
|
||||
/// <returns>True if Active</returns>
|
||||
bool IsListening();
|
||||
/// <summary>
|
||||
/// Stops the Udp Listener
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
void StopUdpListener();
|
||||
}
|
7
GoveeCSharpConnector/Objects/ApiResponse.cs
Normal file
7
GoveeCSharpConnector/Objects/ApiResponse.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class ApiResponse
|
||||
{
|
||||
public string? Message { get; set; }
|
||||
public int Code { get; set; }
|
||||
}
|
8
GoveeCSharpConnector/Objects/Data.cs
Normal file
8
GoveeCSharpConnector/Objects/Data.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class Data
|
||||
{
|
||||
public List<GoveeApiDevice> Devices { get; set; }
|
||||
}
|
14
GoveeCSharpConnector/Objects/GoveeApiCommand.cs
Normal file
14
GoveeCSharpConnector/Objects/GoveeApiCommand.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class GoveeApiCommand
|
||||
{
|
||||
public string Device { get; set; }
|
||||
public string Model { get; set; }
|
||||
public Command Cmd { get; set; }
|
||||
}
|
||||
|
||||
public class Command
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public object Value { get; set; }
|
||||
}
|
17
GoveeCSharpConnector/Objects/GoveeApiDevice.cs
Normal file
17
GoveeCSharpConnector/Objects/GoveeApiDevice.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class GoveeApiDevice
|
||||
{
|
||||
[JsonPropertyName("device")]
|
||||
public string DeviceId { get; set; }
|
||||
public string Model { get; set; }
|
||||
public string DeviceName { get; set; }
|
||||
public bool Controllable { get; set; }
|
||||
public bool Retrievable { get; set; }
|
||||
[JsonPropertyName("supportCmds")]
|
||||
public List<string> SupportedCommands { get; set; }
|
||||
public Properties Properties { get; set; }
|
||||
}
|
16
GoveeCSharpConnector/Objects/GoveeApiState.cs
Normal file
16
GoveeCSharpConnector/Objects/GoveeApiState.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class GoveeApiState
|
||||
{
|
||||
[JsonPropertyName("device")]
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
public string Model { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Properties Properties { get; set; }
|
||||
}
|
6
GoveeCSharpConnector/Objects/GoveeResponse.cs
Normal file
6
GoveeCSharpConnector/Objects/GoveeResponse.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class GoveeResponse : ApiResponse
|
||||
{
|
||||
public Data Data { get; set; }
|
||||
}
|
13
GoveeCSharpConnector/Objects/GoveeUdpDevice.cs
Normal file
13
GoveeCSharpConnector/Objects/GoveeUdpDevice.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class GoveeUdpDevice
|
||||
{
|
||||
public string ip { get; set; }
|
||||
public string device { get; set; }
|
||||
public string sku { get; set; }
|
||||
public string bleVersionHard { get; set; }
|
||||
public string bleVersionSoft { get; set; }
|
||||
public string wifiVersionHard { get; set; }
|
||||
public string wifiVersionSoft { get; set; }
|
||||
}
|
12
GoveeCSharpConnector/Objects/GoveeUdpMessage.cs
Normal file
12
GoveeCSharpConnector/Objects/GoveeUdpMessage.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class GoveeUdpMessage
|
||||
{
|
||||
public msg msg { get; set; }
|
||||
}
|
||||
public class msg
|
||||
{
|
||||
public string cmd { get; set; }
|
||||
public object data { get; set; }
|
||||
}
|
11
GoveeCSharpConnector/Objects/GoveeUdpState.cs
Normal file
11
GoveeCSharpConnector/Objects/GoveeUdpState.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using GoveeCSharpConnector.Enums;
|
||||
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class GoveeUdpState
|
||||
{
|
||||
public PowerState onOff { get; set; }
|
||||
public short brightness { get; set; }
|
||||
public RgbColor color { get; set; }
|
||||
public int colorTempInKelvin { get; set; }
|
||||
}
|
12
GoveeCSharpConnector/Objects/Properties.cs
Normal file
12
GoveeCSharpConnector/Objects/Properties.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using GoveeCSharpConnector.Enums;
|
||||
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class Properties
|
||||
{
|
||||
public bool Online { get; set; }
|
||||
public PowerState PowerState { get; set; }
|
||||
public int Brightness { get; set; }
|
||||
public int? ColorTemp { get; set; }
|
||||
public RgbColor Color { get; set; }
|
||||
}
|
15
GoveeCSharpConnector/Objects/RgbColor.cs
Normal file
15
GoveeCSharpConnector/Objects/RgbColor.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class RgbColor
|
||||
{
|
||||
public short R { get; set; }
|
||||
public short G { get; set; }
|
||||
public short B { get; set; }
|
||||
|
||||
public RgbColor(int r, int g, int b)
|
||||
{
|
||||
R = Convert.ToInt16(r);
|
||||
G = Convert.ToInt16(g);
|
||||
B = Convert.ToInt16(b);
|
||||
}
|
||||
}
|
82
GoveeCSharpConnector/Services/GoveeApiService.cs
Normal file
82
GoveeCSharpConnector/Services/GoveeApiService.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using GoveeCSharpConnector.Interfaces;
|
||||
using GoveeCSharpConnector.Objects;
|
||||
|
||||
namespace GoveeCSharpConnector.Services;
|
||||
|
||||
public class GoveeApiService : IGoveeApiService
|
||||
{
|
||||
private string _apiKey = string.Empty;
|
||||
private const string GoveeApiAddress = "https://developer-api.govee.com/v1";
|
||||
private readonly HttpClient _httpClient = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SetApiKey(string apiKey)
|
||||
{
|
||||
_apiKey = apiKey;
|
||||
_httpClient.DefaultRequestHeaders.Add("Govee-API-Key", _apiKey);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public string GetApiKey()
|
||||
{
|
||||
return _apiKey;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void RemoveApiKey()
|
||||
{
|
||||
_apiKey = string.Empty;
|
||||
_httpClient.DefaultRequestHeaders.Remove("Govee-Api-Key");
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<GoveeApiDevice>> GetDevices()
|
||||
{
|
||||
var response = await _httpClient.GetFromJsonAsync<GoveeResponse>($"{GoveeApiAddress}/devices");
|
||||
|
||||
return response.Data.Devices;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task<GoveeApiState> GetDeviceState(string deviceId, string deviceModel)
|
||||
{
|
||||
return await _httpClient.GetFromJsonAsync<GoveeApiState>($"{GoveeApiAddress}/devices/state?device={deviceId}&model={deviceModel}");
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task ToggleState(string deviceId, string deviceModel, bool on)
|
||||
{
|
||||
await SendCommand(deviceId, deviceModel, "turn", on ? "on" : "off");
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task SetBrightness(string deviceId, string deviceModel, int value)
|
||||
{
|
||||
await SendCommand(deviceId, deviceModel, "brightness", value);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task SetColor(string deviceId, string deviceModel, RgbColor color)
|
||||
{
|
||||
await SendCommand(deviceId, deviceModel, "color", color);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task SetColorTemp(string deviceId, string deviceModel, int value)
|
||||
{
|
||||
await SendCommand(deviceId, deviceModel, "colorTem", value);
|
||||
}
|
||||
|
||||
private async Task SendCommand(string deviceId, string deviceModel, string command, object commandObject)
|
||||
{
|
||||
var commandRequest = new GoveeApiCommand()
|
||||
{
|
||||
Device = deviceId,
|
||||
Model = deviceModel,
|
||||
Cmd = new Command()
|
||||
{
|
||||
Name = command,
|
||||
Value = commandObject
|
||||
}
|
||||
};
|
||||
var httpContent = new StringContent(JsonSerializer.Serialize(commandRequest), Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.PutAsync($"{GoveeApiAddress}/devices/control", httpContent);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new Exception($"Govee Api Request failed. Status code: {response.StatusCode}, Message: {response.Content}");
|
||||
}
|
||||
}
|
270
GoveeCSharpConnector/Services/GoveeUdpService.cs
Normal file
270
GoveeCSharpConnector/Services/GoveeUdpService.cs
Normal file
@ -0,0 +1,270 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Reactive.Threading.Tasks;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using GoveeCSharpConnector.Interfaces;
|
||||
using GoveeCSharpConnector.Objects;
|
||||
namespace GoveeCSharpConnector.Services;
|
||||
|
||||
public class GoveeUdpService : IGoveeUdpService
|
||||
{
|
||||
private const string GoveeMulticastAddress = "239.255.255.250";
|
||||
private const int GoveeMulticastPortListen = 4002;
|
||||
private const int GoveeMulticastPortSend = 4001;
|
||||
private readonly UdpClient _udpClient = new();
|
||||
private bool _udpListenerActive = true;
|
||||
|
||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
||||
private readonly Subject<string> _messageSubject = new();
|
||||
private readonly Subject<GoveeUdpDevice> _scanResultSubject = new();
|
||||
private readonly Subject<GoveeUdpState> _stateResultSubject = new();
|
||||
|
||||
public IObservable<string> Messages => _messageSubject;
|
||||
|
||||
public GoveeUdpService()
|
||||
{
|
||||
SetupUdpClientListener();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<GoveeUdpDevice>> GetDevices(TimeSpan? timeout = null)
|
||||
{
|
||||
// Block this Method until current call reaches end of Method
|
||||
await _semaphore.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
// Build Message
|
||||
var message = new GoveeUdpMessage()
|
||||
{
|
||||
msg = new msg()
|
||||
{
|
||||
cmd = "scan",
|
||||
data = new { account_topic = "reserve" }
|
||||
}
|
||||
};
|
||||
// Subscribe to ScanResultSubject
|
||||
var devicesTask = _scanResultSubject
|
||||
.TakeUntil(Observable.Timer(timeout ?? TimeSpan.FromMilliseconds(200)))
|
||||
.ToList()
|
||||
.ToTask();
|
||||
|
||||
// Send Message
|
||||
SendUdpMessage(JsonSerializer.Serialize(message), GoveeMulticastAddress, GoveeMulticastPortSend);
|
||||
|
||||
// Return List
|
||||
return (await devicesTask).ToList();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Release Method Block
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<GoveeUdpState> GetState(string deviceAddress, int uniCastPort = 4003, TimeSpan? timeout = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Build Message
|
||||
var message = new GoveeUdpMessage()
|
||||
{
|
||||
msg = new msg()
|
||||
{
|
||||
cmd = "devStatus",
|
||||
data = new { }
|
||||
}
|
||||
};
|
||||
// Subscribe to ScanResultSubject
|
||||
var devicesTask = _stateResultSubject
|
||||
.TakeUntil(Observable.Timer(timeout ?? TimeSpan.FromMilliseconds(200)))
|
||||
.ToTask();
|
||||
|
||||
// Send Message
|
||||
SendUdpMessage(JsonSerializer.Serialize(message), deviceAddress, uniCastPort);
|
||||
|
||||
// Return state
|
||||
return await devicesTask;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task ToggleDevice(string deviceAddress, bool on, int uniCastPort = 4003)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Build Message
|
||||
var message = new GoveeUdpMessage()
|
||||
{
|
||||
msg = new msg()
|
||||
{
|
||||
cmd = "turn",
|
||||
data = new { value = on ? 1 : 0 }
|
||||
}
|
||||
};
|
||||
// Send Message
|
||||
SendUdpMessage(JsonSerializer.Serialize(message), deviceAddress, uniCastPort);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task SetBrightness(string deviceAddress, short brightness, int uniCastPort = 4003)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Build Message
|
||||
var message = new GoveeUdpMessage()
|
||||
{
|
||||
msg = new msg()
|
||||
{
|
||||
cmd = "brightness",
|
||||
data = new { value = brightness }
|
||||
}
|
||||
};
|
||||
// Send Message
|
||||
SendUdpMessage(JsonSerializer.Serialize(message), deviceAddress, uniCastPort);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task SetColor(string deviceAddress, RgbColor color, int uniCastPort = 4003)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Build Message
|
||||
var message = new GoveeUdpMessage()
|
||||
{
|
||||
msg = new msg()
|
||||
{
|
||||
cmd = "colorwc",
|
||||
data = new
|
||||
{ color = new
|
||||
{
|
||||
r = color.R,
|
||||
g = color.G,
|
||||
b = color.B
|
||||
},
|
||||
colorTempInKelvin = 0
|
||||
}
|
||||
}
|
||||
};
|
||||
// Send Message
|
||||
SendUdpMessage(JsonSerializer.Serialize(message), deviceAddress, uniCastPort);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async void StartUdpListener()
|
||||
{
|
||||
_udpListenerActive = true;
|
||||
await StartListener();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool IsListening()
|
||||
{
|
||||
return _udpListenerActive;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void StopUdpListener()
|
||||
{
|
||||
_udpListenerActive = false;
|
||||
}
|
||||
|
||||
private static void SendUdpMessage(string message, string receiverAddress, int receiverPort)
|
||||
{
|
||||
var client = new UdpClient();
|
||||
try
|
||||
{
|
||||
byte[] data = Encoding.UTF8.GetBytes(message);
|
||||
client.Send(data, data.Length, receiverAddress, receiverPort);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupUdpClientListener()
|
||||
{
|
||||
_udpClient.ExclusiveAddressUse = false;
|
||||
var localEndPoint = new IPEndPoint(IPAddress.Any, GoveeMulticastPortListen);
|
||||
_udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
_udpClient.Client.Bind(localEndPoint);
|
||||
}
|
||||
|
||||
private async Task StartListener()
|
||||
{
|
||||
try
|
||||
{
|
||||
_udpClient.JoinMulticastGroup(IPAddress.Parse(GoveeMulticastAddress));
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (_udpListenerActive)
|
||||
{
|
||||
var remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
var data = _udpClient.Receive(ref remoteEndPoint);
|
||||
|
||||
var message = Encoding.UTF8.GetString(data);
|
||||
|
||||
UdPMessageReceived(message);
|
||||
_messageSubject.OnNext(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
_udpClient.DropMulticastGroup(IPAddress.Parse(GoveeMulticastAddress));
|
||||
_udpClient.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void UdPMessageReceived(string message)
|
||||
{
|
||||
var response = JsonSerializer.Deserialize<GoveeUdpMessage>(message);
|
||||
switch (response.msg.cmd)
|
||||
{
|
||||
case "scan":
|
||||
var device = JsonSerializer.Deserialize<GoveeUdpDevice>(response.msg.data.ToString());
|
||||
_scanResultSubject.OnNext(device);
|
||||
break;
|
||||
case "devStatus":
|
||||
var state = JsonSerializer.Deserialize<GoveeUdpState>(response.msg.data.ToString());
|
||||
_stateResultSubject.OnNext(state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GoveeCSharpConnector\GoveeCSharpConnector.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
202
GoveeCsharpConnector.Example/Program.cs
Normal file
202
GoveeCsharpConnector.Example/Program.cs
Normal file
@ -0,0 +1,202 @@
|
||||
using System.Net.Mime;
|
||||
using System.Reflection;
|
||||
using System.Xml.Linq;
|
||||
using GoveeCSharpConnector.Objects;
|
||||
using GoveeCSharpConnector.Services;
|
||||
|
||||
namespace GoveeCsharpConnector.Example;
|
||||
|
||||
public class Program
|
||||
{
|
||||
private static GoveeApiService _goveeApiService = new GoveeApiService();
|
||||
public static List<GoveeApiDevice> _apiDevices = new List<GoveeApiDevice>();
|
||||
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
PrintWelcomeMessage();
|
||||
var input = Console.ReadLine();
|
||||
HandleKeyInput(input);
|
||||
}
|
||||
}
|
||||
|
||||
private static async void HandleKeyInput(string input)
|
||||
{
|
||||
switch (input)
|
||||
{
|
||||
case "1":
|
||||
HandleApiInput();
|
||||
EndSegment();
|
||||
break;
|
||||
case "2":
|
||||
Console.WriteLine("Requesting Devices ...");
|
||||
_apiDevices = await _goveeApiService.GetDevices();
|
||||
Console.WriteLine("Devices:");
|
||||
foreach (var device in _apiDevices)
|
||||
{
|
||||
Console.WriteLine($"Name: {device.DeviceName}, Device Id: {device.DeviceId}, Model: {device.Model}, Controllable {device.Controllable}");
|
||||
}
|
||||
Console.WriteLine($"Total: {_apiDevices.Count} Devices.");
|
||||
EndSegment();
|
||||
|
||||
break;
|
||||
case "3":
|
||||
if (_apiDevices.Count == 0)
|
||||
{
|
||||
Console.WriteLine("No Devices discovered! Please use Option 2 first!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
Console.WriteLine("Please enter the Name of the Device:");
|
||||
var nameInput = Console.ReadLine()?.ToLower();
|
||||
if (string.IsNullOrWhiteSpace(nameInput) || _apiDevices.FirstOrDefault(x => x.DeviceName.ToLower() == nameInput) is null)
|
||||
{
|
||||
Console.WriteLine("Device Name Invalid!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Do you want to turn the Device {nameInput} on or off?");
|
||||
var onOffInput = Console.ReadLine()?.ToLower();
|
||||
if (string.IsNullOrWhiteSpace(onOffInput) || (onOffInput != "on" && onOffInput != "off"))
|
||||
{
|
||||
Console.WriteLine("Invalid Input!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
|
||||
if (input == "on")
|
||||
{
|
||||
await _goveeApiService.ToggleState(_apiDevices.First(x => x.DeviceName.ToLower() == nameInput).DeviceId, _apiDevices.First(x => x.DeviceName.ToLower() == nameInput).Model, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _goveeApiService.ToggleState(_apiDevices.First(x => x.DeviceName.ToLower() == nameInput).DeviceId, _apiDevices.First(x => x.DeviceName.ToLower() == nameInput).Model, false);
|
||||
}
|
||||
EndSegment();
|
||||
break;
|
||||
case "4":
|
||||
if (_apiDevices.Count == 0)
|
||||
{
|
||||
Console.WriteLine("No Devices discovered! Please use Option 2 first!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
Console.WriteLine("Please enter the Name of the Device:");
|
||||
var nameInput2 = Console.ReadLine()?.ToLower();
|
||||
if (string.IsNullOrWhiteSpace(nameInput2) || _apiDevices.FirstOrDefault(x => x.DeviceName.ToLower() == nameInput2) is null)
|
||||
{
|
||||
Console.WriteLine("Device Name Invalid!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Please enter a Brightness Value for Device {nameInput2}. 0-100");
|
||||
var brightnessInput = Console.ReadLine();
|
||||
int value = Convert.ToInt16(brightnessInput);
|
||||
if (string.IsNullOrWhiteSpace(brightnessInput) || value < 0 || value > 100)
|
||||
{
|
||||
Console.WriteLine("Invalid Input!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
|
||||
await _goveeApiService.SetBrightness(_apiDevices.First(x => x.DeviceName.ToLower() == nameInput2).DeviceId, _apiDevices.First(x => x.DeviceName.ToLower() == nameInput2).Model, value);
|
||||
Console.WriteLine($"Set Brightness of Device {nameInput2} to {value}%!");
|
||||
EndSegment();
|
||||
break;
|
||||
case "5":
|
||||
if (_apiDevices.Count == 0)
|
||||
{
|
||||
Console.WriteLine("No Devices discovered! Please use Option 2 first!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
Console.WriteLine("Please enter the Name of the Device:");
|
||||
var nameInput3 = Console.ReadLine()?.ToLower();
|
||||
if (string.IsNullOrWhiteSpace(nameInput3) || _apiDevices.FirstOrDefault(x => x.DeviceName.ToLower() == nameInput3) is null)
|
||||
{
|
||||
Console.WriteLine("Device Name Invalid!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
Console.WriteLine($"Please choose a Color to set {nameInput3} to ... (blue, red, green)");
|
||||
var colorInput = Console.ReadLine()?.ToLower();
|
||||
if (string.IsNullOrWhiteSpace(colorInput) || colorInput != "blue" || colorInput != "green" || colorInput != "red")
|
||||
{
|
||||
Console.WriteLine("Invalid Input!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
|
||||
var model = _apiDevices.FirstOrDefault(x => x.DeviceName.ToLower()== nameInput3)?.Model;
|
||||
switch (colorInput)
|
||||
{
|
||||
case "blue":
|
||||
await _goveeApiService.SetColor(_apiDevices.First(x => x.DeviceName.ToLower() == nameInput3).DeviceId, model, new RgbColor(0, 0 ,254));
|
||||
break;
|
||||
case "green":
|
||||
await _goveeApiService.SetColor(_apiDevices.First(x => x.DeviceName.ToLower() == nameInput3).DeviceId, model, new RgbColor(0, 254 ,0));
|
||||
break;
|
||||
case "red":
|
||||
await _goveeApiService.SetColor(_apiDevices.First(x => x.DeviceName.ToLower() == nameInput3).DeviceId, model, new RgbColor(254, 0 ,0));
|
||||
break;
|
||||
}
|
||||
Console.WriteLine($"Set Color of Device {nameInput3} to {colorInput}!");
|
||||
EndSegment();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleApiInput()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Console.WriteLine("Please enter/paste your Govee Api Key ...");
|
||||
Console.WriteLine("Your Api Key should look something like this: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
|
||||
var input = Console.ReadLine();
|
||||
if (input is null || input.Length != 36)
|
||||
{
|
||||
Console.WriteLine("Wrong Api Key Format!");
|
||||
continue;
|
||||
}
|
||||
_goveeApiService.SetApiKey(input);
|
||||
break;
|
||||
}
|
||||
Console.WriteLine("Api Key saved!");
|
||||
}
|
||||
|
||||
private static void EndSegment()
|
||||
{
|
||||
Console.WriteLine("---------------------------Press any Key to continue---------------------------");
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
private static void PrintWelcomeMessage()
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Welcome to the GoveeCSharpConnector Example!");
|
||||
Console.WriteLine($"Version: {Assembly.GetEntryAssembly()?.GetName().Version}");
|
||||
Console.WriteLine($"To test/explore the GoveeCSharpConnector Version: {Assembly.Load("GoveeCSharpConnector").GetName().Version}");
|
||||
Console.WriteLine("----------------------------------------------------------");
|
||||
if (string.IsNullOrEmpty(_goveeApiService.GetApiKey()))
|
||||
{
|
||||
Console.WriteLine("1 - Enter GoveeApi Key - START HERE (Required for Api Service Options!)");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("1 - Enter GoveeApi Key - Already Set!");
|
||||
Console.WriteLine("Api Service:");
|
||||
Console.WriteLine("2 - Get a List of all Devices connected to the Api Key Account");
|
||||
Console.WriteLine("3 - Turn Device On or Off");
|
||||
Console.WriteLine("4 - Set Brightness for Device");
|
||||
Console.WriteLine("5 - Set Color of Device");
|
||||
}
|
||||
Console.WriteLine("Udp Service - No Api Key needed!");
|
||||
Console.WriteLine("6 - Get a List of all Devices available in the Network");
|
||||
Console.WriteLine("7 - Turn Device On or Off");
|
||||
Console.WriteLine("8 - Set Brightness for Device");
|
||||
Console.WriteLine("9 - Set Color of Device");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user