Compare commits
10 Commits
efe6863b14
...
76ccf20c30
Author | SHA1 | Date | |
---|---|---|---|
76ccf20c30 | |||
a069133a68 | |||
bb01b3342d | |||
602926c43e | |||
2eef997c6e | |||
bb9364c491 | |||
be6b9cc537 | |||
10983bea7f | |||
e60298b8a3 | |||
2161900a09 |
27
.github/workflows/nuget.yml
vendored
Normal file
27
.github/workflows/nuget.yml
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
name: Release to NuGet
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master # Default release branch
|
||||
pull_request:
|
||||
branches:
|
||||
- master # Run the workflow for all pull requests on Branch Master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4.1.1
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v1.5
|
||||
- name: Build
|
||||
run: dotnet build -c Release
|
||||
- name: Test
|
||||
run: dotnet test -c Release --no-build
|
||||
- name: Pack nugets
|
||||
run: dotnet pack -c Release --no-build --output .
|
||||
- name: Push to NuGet
|
||||
run: dotnet nuget push "*.nupkg" --api-key ${{secrets.NUGET_KEY}} --source https://api.nuget.org/v3/index.json
|
32
.gitignore
vendored
32
.gitignore
vendored
@ -1,5 +1,33 @@
|
||||
bin/
|
||||
obj/
|
||||
# Common IntelliJ Platform excludes
|
||||
|
||||
# User specific
|
||||
**/.idea/**/workspace.xml
|
||||
**/.idea/**/tasks.xml
|
||||
**/.idea/shelf/*
|
||||
**/.idea/dictionaries
|
||||
**/.idea/httpRequests/
|
||||
|
||||
# Sensitive or high-churn files
|
||||
**/.idea/**/dataSources/
|
||||
**/.idea/**/dataSources.ids
|
||||
**/.idea/**/dataSources.xml
|
||||
**/.idea/**/dataSources.local.xml
|
||||
**/.idea/**/sqlDataSources.xml
|
||||
**/.idea/**/dynamic.xml
|
||||
|
||||
# Rider
|
||||
# Rider auto-generates .iml files, and contentModel.xml
|
||||
**/.idea/**/*.iml
|
||||
**/.idea/**/contentModel.xml
|
||||
**/.idea/**/modules.xml
|
||||
|
||||
*.suo
|
||||
*.user
|
||||
.vs/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
_UpgradeReport_Files/
|
||||
[Pp]ackages/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
@ -5,16 +5,21 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Title>CSharp Library to control Govee Lights via Govee Web Api and Lan Udp!</Title>
|
||||
<Authors>Locxion</Authors>
|
||||
<Description>CSharp Library to control Govee Lights via Govee Web Api and Lan Udp!</Description>
|
||||
<PackageProjectUrl>https://github.com/Locxion/GoveeCSharpConnector</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/Locxion/GoveeCSharpConnector</RepositoryUrl>
|
||||
<PackageLicenseUrl>https://github.com/Locxion/GoveeCSharpConnector/blob/main/LICENSE</PackageLicenseUrl>
|
||||
<Version>1.1.2</Version>
|
||||
</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>
|
@ -10,27 +10,32 @@ public interface IGoveeApiService
|
||||
/// </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);
|
||||
Task<GoveeApiState> GetDeviceState(string deviceId, string deviceModel);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the On/Off state of a single Govee Device
|
||||
/// </summary>
|
||||
@ -38,7 +43,8 @@ public interface IGoveeApiService
|
||||
/// <param name="deviceModel">Device Model Number as string</param>
|
||||
/// <param name="on"></param>
|
||||
/// <returns></returns>
|
||||
public Task ToggleState(string deviceId, string deviceModel, bool on);
|
||||
Task ToggleState(string deviceId, string deviceModel, bool on);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Brightness in Percent of a single Govee Device
|
||||
/// </summary>
|
||||
@ -46,7 +52,8 @@ public interface IGoveeApiService
|
||||
/// <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);
|
||||
Task SetBrightness(string deviceId, string deviceModel, int value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a Rgb Color of a single Govee Device
|
||||
/// </summary>
|
||||
@ -54,7 +61,8 @@ public interface IGoveeApiService
|
||||
/// <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);
|
||||
Task SetColor(string deviceId, string deviceModel, RgbColor color);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Color Temperature of a single Govee Device
|
||||
/// </summary>
|
||||
@ -62,6 +70,5 @@ public interface IGoveeApiService
|
||||
/// <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);
|
||||
|
||||
Task SetColorTemp(string deviceId, string deviceModel, int value);
|
||||
}
|
62
GoveeCSharpConnector/Interfaces/IGoveeService.cs
Normal file
62
GoveeCSharpConnector/Interfaces/IGoveeService.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using GoveeCSharpConnector.Objects;
|
||||
|
||||
namespace GoveeCSharpConnector.Interfaces;
|
||||
|
||||
public interface IGoveeService
|
||||
{
|
||||
/// <summary>
|
||||
/// Govee Api Key
|
||||
/// </summary>
|
||||
string GoveeApiKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a List of Govee Devices
|
||||
/// </summary>
|
||||
/// <param name="onlyLan">If true returns that are available on Api and Lan</param>
|
||||
/// <returns>List of Govee Devices</returns>
|
||||
Task<List<GoveeDevice>> GetDevices(bool onlyLan = true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the State of a GoveeDevice
|
||||
/// </summary>
|
||||
/// <param name="goveeDevice">GoveeDevice</param>
|
||||
/// <param name="useUdp">Use Udp Connection instead of the Api</param>
|
||||
/// <returns></returns>
|
||||
Task<GoveeState> GetDeviceState(GoveeDevice goveeDevice, bool useUdp = true);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the On/Off State of the GoveeDevice
|
||||
/// </summary>
|
||||
/// <param name="goveeDevice">GoveeDevice</param>
|
||||
/// <param name="on"></param>
|
||||
/// <param name="useUdp">Use Udp Connection instead of the Api</param>
|
||||
/// <returns></returns>
|
||||
Task ToggleState(GoveeDevice goveeDevice, bool on, bool useUdp = true);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Brightness of the GoveeDevice
|
||||
/// </summary>
|
||||
/// <param name="goveeDevice">GoveeDevice</param>
|
||||
/// <param name="value">Brightness in Percent</param>
|
||||
/// <param name="useUdp">Use Udp Connection instead of the Api</param>
|
||||
/// <returns></returns>
|
||||
Task SetBrightness(GoveeDevice goveeDevice, int value, bool useUdp = true);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Color of the GoveeDevice
|
||||
/// </summary>
|
||||
/// <param name="goveeDevice">GoveeDevice</param>
|
||||
/// <param name="color">RgBColor</param>
|
||||
/// <param name="useUdp">Use Udp Connection instead of the Api</param>
|
||||
/// <returns></returns>
|
||||
Task SetColor(GoveeDevice goveeDevice, RgbColor color, bool useUdp = true);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Color Temperature in Kelvin for the GoveeDevice
|
||||
/// </summary>
|
||||
/// <param name="goveeDevice">GoveeDevice</param>
|
||||
/// <param name="value">Color Temp in Kelvin</param>
|
||||
/// <param name="useUdp">Use Udp Connection instead of the Api</param>
|
||||
/// <returns></returns>
|
||||
Task SetColorTemp(GoveeDevice goveeDevice, int value, bool useUdp = true);
|
||||
}
|
@ -1,4 +1,8 @@
|
||||
using System.Data;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using GoveeCSharpConnector.Objects;
|
||||
|
||||
namespace GoveeCSharpConnector.Interfaces;
|
||||
@ -8,7 +12,7 @@ public interface IGoveeUdpService
|
||||
/// <summary>
|
||||
/// Sends a Scan Command via Udp Multicast.
|
||||
/// </summary>
|
||||
/// <param name="timeout">Standard 200ms</param>
|
||||
/// <param name="timeout">Standard 250ms</param>
|
||||
/// <returns>List of GoveeUdpDevices</returns>
|
||||
Task<List<GoveeUdpDevice>> GetDevices(TimeSpan? timeout = null);
|
||||
|
||||
@ -17,7 +21,7 @@ public interface IGoveeUdpService
|
||||
/// </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>
|
||||
/// <param name="timeout">Standard 250ms</param>
|
||||
/// <returns></returns>
|
||||
Task<GoveeUdpState> GetState(string deviceAddress, int uniCastPort = 4003, TimeSpan? timeout = null);
|
||||
|
||||
@ -36,7 +40,7 @@ public interface IGoveeUdpService
|
||||
/// <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);
|
||||
Task SetBrightness(string deviceAddress, int brightness, int uniCastPort = 4003);
|
||||
/// <summary>
|
||||
/// Sets the Color of the Device
|
||||
/// </summary>
|
||||
@ -45,6 +49,15 @@ public interface IGoveeUdpService
|
||||
/// <param name="uniCastPort">Port of the Device. Standard 4003</param>
|
||||
/// <returns></returns>
|
||||
Task SetColor(string deviceAddress, RgbColor color, int uniCastPort = 4003);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the ColorTemp of the Device
|
||||
/// </summary>
|
||||
/// <param name="deviceAddress">Ip Address of the Device</param>
|
||||
/// <param name="colorTempInKelvin"></param>
|
||||
/// <param name="uniCastPort">Port of the Device. Standard 4003</param>
|
||||
/// <returns></returns>
|
||||
Task SetColorTemp(string deviceAddress, int colorTempInKelvin, int uniCastPort = 4003);
|
||||
/// <summary>
|
||||
/// Starts the Udp Listener
|
||||
/// </summary>
|
||||
@ -60,4 +73,4 @@ public interface IGoveeUdpService
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
void StopUdpListener();
|
||||
}
|
||||
}
|
||||
|
9
GoveeCSharpConnector/Objects/GoveeDevice.cs
Normal file
9
GoveeCSharpConnector/Objects/GoveeDevice.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class GoveeDevice
|
||||
{
|
||||
public string DeviceId { get; set; }
|
||||
public string Model { get; set; }
|
||||
public string DeviceName { get; set; }
|
||||
public string Address { get; set; }
|
||||
}
|
11
GoveeCSharpConnector/Objects/GoveeState.cs
Normal file
11
GoveeCSharpConnector/Objects/GoveeState.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using GoveeCSharpConnector.Enums;
|
||||
|
||||
namespace GoveeCSharpConnector.Objects;
|
||||
|
||||
public class GoveeState
|
||||
{
|
||||
public PowerState State { get; set; }
|
||||
public int Brightness { get; set; }
|
||||
public RgbColor Color { get; set; }
|
||||
public int ColorTempInKelvin { get; set; }
|
||||
}
|
@ -7,6 +7,6 @@ public class Properties
|
||||
public bool Online { get; set; }
|
||||
public PowerState PowerState { get; set; }
|
||||
public int Brightness { get; set; }
|
||||
public int? ColorTemp { get; set; }
|
||||
public int ColorTemp { get; set; }
|
||||
public RgbColor Color { get; set; }
|
||||
}
|
@ -11,7 +11,10 @@ public class GoveeApiService : IGoveeApiService
|
||||
private string _apiKey = string.Empty;
|
||||
private const string GoveeApiAddress = "https://developer-api.govee.com/v1";
|
||||
private readonly HttpClient _httpClient = new();
|
||||
|
||||
private readonly JsonSerializerOptions? _jsonOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
};
|
||||
/// <inheritdoc/>
|
||||
public void SetApiKey(string apiKey)
|
||||
{
|
||||
@ -74,7 +77,7 @@ public class GoveeApiService : IGoveeApiService
|
||||
Value = commandObject
|
||||
}
|
||||
};
|
||||
var httpContent = new StringContent(JsonSerializer.Serialize(commandRequest), Encoding.UTF8, "application/json");
|
||||
var httpContent = new StringContent(JsonSerializer.Serialize(commandRequest, _jsonOptions), 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}");
|
||||
|
108
GoveeCSharpConnector/Services/GoveeService.cs
Normal file
108
GoveeCSharpConnector/Services/GoveeService.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using GoveeCSharpConnector.Interfaces;
|
||||
using GoveeCSharpConnector.Objects;
|
||||
|
||||
namespace GoveeCSharpConnector.Services;
|
||||
|
||||
public class GoveeService : IGoveeService
|
||||
{
|
||||
public string GoveeApiKey { get; set; }
|
||||
|
||||
private readonly IGoveeApiService _apiService;
|
||||
private readonly IGoveeUdpService _udpService;
|
||||
|
||||
public GoveeService(IGoveeApiService apiService,IGoveeUdpService udpService)
|
||||
{
|
||||
_apiService = apiService ?? throw new ArgumentNullException(nameof(apiService));
|
||||
_udpService = udpService ?? throw new ArgumentNullException(nameof(udpService));
|
||||
}
|
||||
public async Task<List<GoveeDevice>> GetDevices(bool onlyLan = true)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(GoveeApiKey)) throw new Exception("No Govee Api Key Set!");
|
||||
_apiService.SetApiKey(GoveeApiKey);
|
||||
|
||||
var apiDevices = await _apiService.GetDevices();
|
||||
var devices = apiDevices.Select(apiDevice => new GoveeDevice() { DeviceId = apiDevice.DeviceId, DeviceName = apiDevice.DeviceName, Model = apiDevice.Model, Address = "onlyAvailableOnUdpRequest" }).ToList();
|
||||
if (!onlyLan)
|
||||
return devices;
|
||||
|
||||
if (!_udpService.IsListening())
|
||||
_udpService.StartUdpListener();
|
||||
|
||||
var udpDevices = await _udpService.GetDevices();
|
||||
|
||||
var combinedDevices = (from goveeDevice in devices let matchingDevice = udpDevices.FirstOrDefault(x => x.device == goveeDevice.DeviceId)
|
||||
where matchingDevice is not null select
|
||||
new GoveeDevice { DeviceId = goveeDevice.DeviceId, DeviceName = goveeDevice.DeviceName, Model = goveeDevice.Model, Address = matchingDevice.ip }).ToList();
|
||||
|
||||
return combinedDevices;
|
||||
}
|
||||
|
||||
public async Task<GoveeState> GetDeviceState(GoveeDevice goveeDevice, bool useUdp = true)
|
||||
{
|
||||
if (useUdp)
|
||||
{
|
||||
if (!_udpService.IsListening())
|
||||
_udpService.StartUdpListener();
|
||||
if (string.IsNullOrWhiteSpace(goveeDevice.Address)) throw new Exception("Device not available via Udp/Lan");
|
||||
var udpState = await _udpService.GetState(goveeDevice.Address);
|
||||
return new GoveeState() { State = udpState.onOff, Brightness = udpState.brightness, Color = udpState.color, ColorTempInKelvin = udpState.colorTempInKelvin };
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(GoveeApiKey)) throw new Exception("No Govee Api Key Set!");
|
||||
_apiService.SetApiKey(GoveeApiKey);
|
||||
var apiState = await _apiService.GetDeviceState(goveeDevice.DeviceId, goveeDevice.Model);
|
||||
return new GoveeState{State = apiState.Properties.PowerState, Brightness = apiState.Properties.Brightness, Color = apiState.Properties.Color, ColorTempInKelvin = apiState.Properties.ColorTemp};
|
||||
}
|
||||
|
||||
public async Task ToggleState(GoveeDevice goveeDevice, bool on, bool useUdp = true)
|
||||
{
|
||||
if (useUdp)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(goveeDevice.Address)) throw new Exception("Device not available via Udp/Lan");
|
||||
await _udpService.ToggleDevice(goveeDevice.Address, on);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(GoveeApiKey)) throw new Exception("No Govee Api Key Set!");
|
||||
_apiService.SetApiKey(GoveeApiKey);
|
||||
await _apiService.ToggleState(goveeDevice.DeviceId, goveeDevice.Model, on);
|
||||
}
|
||||
|
||||
public async Task SetBrightness(GoveeDevice goveeDevice, int value, bool useUdp = true)
|
||||
{
|
||||
if (useUdp)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(goveeDevice.Address)) throw new Exception("Device not available via Udp/Lan");
|
||||
await _udpService.SetBrightness(goveeDevice.Address, value);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(GoveeApiKey)) throw new Exception("No Govee Api Key Set!");
|
||||
_apiService.SetApiKey(GoveeApiKey);
|
||||
await _apiService.SetBrightness(goveeDevice.DeviceId, goveeDevice.Model, value);
|
||||
}
|
||||
|
||||
public async Task SetColor(GoveeDevice goveeDevice, RgbColor color, bool useUdp = true)
|
||||
{
|
||||
if (useUdp)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(goveeDevice.Address)) throw new Exception("Device not available via Udp/Lan");
|
||||
await _udpService.SetColor(goveeDevice.Address, color);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(GoveeApiKey)) throw new Exception("No Govee Api Key Set!");
|
||||
|
||||
_apiService.SetApiKey(GoveeApiKey);
|
||||
await _apiService.SetColor(goveeDevice.DeviceId, goveeDevice.Model, color);
|
||||
}
|
||||
|
||||
public async Task SetColorTemp(GoveeDevice goveeDevice, int value, bool useUdp = true)
|
||||
{
|
||||
if (useUdp)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(goveeDevice.Address)) throw new Exception("Device not available via Udp/Lan");
|
||||
await _udpService.SetColorTemp(goveeDevice.Address, value);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(GoveeApiKey)) throw new Exception("No Govee Api Key Set!");
|
||||
_apiService.SetApiKey(GoveeApiKey);
|
||||
await _apiService.SetColorTemp(goveeDevice.DeviceId, goveeDevice.Model, value);
|
||||
}
|
||||
}
|
@ -32,15 +32,17 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
/// <inheritdoc/>
|
||||
public async Task<List<GoveeUdpDevice>> GetDevices(TimeSpan? timeout = null)
|
||||
{
|
||||
if (!_udpListenerActive)
|
||||
throw new Exception("Udp Listener not started!");
|
||||
// Block this Method until current call reaches end of Method
|
||||
await _semaphore.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
// Build Message
|
||||
var message = new GoveeUdpMessage()
|
||||
var message = new GoveeUdpMessage
|
||||
{
|
||||
msg = new msg()
|
||||
msg = new msg
|
||||
{
|
||||
cmd = "scan",
|
||||
data = new { account_topic = "reserve" }
|
||||
@ -48,7 +50,7 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
};
|
||||
// Subscribe to ScanResultSubject
|
||||
var devicesTask = _scanResultSubject
|
||||
.TakeUntil(Observable.Timer(timeout ?? TimeSpan.FromMilliseconds(200)))
|
||||
.TakeUntil(Observable.Timer(timeout ?? TimeSpan.FromMilliseconds(250)))
|
||||
.ToList()
|
||||
.ToTask();
|
||||
|
||||
@ -73,12 +75,14 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
/// <inheritdoc/>
|
||||
public async Task<GoveeUdpState> GetState(string deviceAddress, int uniCastPort = 4003, TimeSpan? timeout = null)
|
||||
{
|
||||
if (!_udpListenerActive)
|
||||
throw new Exception("Udp Listener not started!");
|
||||
try
|
||||
{
|
||||
// Build Message
|
||||
var message = new GoveeUdpMessage()
|
||||
var message = new GoveeUdpMessage
|
||||
{
|
||||
msg = new msg()
|
||||
msg = new msg
|
||||
{
|
||||
cmd = "devStatus",
|
||||
data = new { }
|
||||
@ -86,7 +90,7 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
};
|
||||
// Subscribe to ScanResultSubject
|
||||
var devicesTask = _stateResultSubject
|
||||
.TakeUntil(Observable.Timer(timeout ?? TimeSpan.FromMilliseconds(200)))
|
||||
.TakeUntil(Observable.Timer(timeout ?? TimeSpan.FromMilliseconds(250)))
|
||||
.ToTask();
|
||||
|
||||
// Send Message
|
||||
@ -107,9 +111,9 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
try
|
||||
{
|
||||
// Build Message
|
||||
var message = new GoveeUdpMessage()
|
||||
var message = new GoveeUdpMessage
|
||||
{
|
||||
msg = new msg()
|
||||
msg = new msg
|
||||
{
|
||||
cmd = "turn",
|
||||
data = new { value = on ? 1 : 0 }
|
||||
@ -126,14 +130,14 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task SetBrightness(string deviceAddress, short brightness, int uniCastPort = 4003)
|
||||
public async Task SetBrightness(string deviceAddress, int brightness, int uniCastPort = 4003)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Build Message
|
||||
var message = new GoveeUdpMessage()
|
||||
var message = new GoveeUdpMessage
|
||||
{
|
||||
msg = new msg()
|
||||
msg = new msg
|
||||
{
|
||||
cmd = "brightness",
|
||||
data = new { value = brightness }
|
||||
@ -155,9 +159,9 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
try
|
||||
{
|
||||
// Build Message
|
||||
var message = new GoveeUdpMessage()
|
||||
var message = new GoveeUdpMessage
|
||||
{
|
||||
msg = new msg()
|
||||
msg = new msg
|
||||
{
|
||||
cmd = "colorwc",
|
||||
data = new
|
||||
@ -173,7 +177,6 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
};
|
||||
// Send Message
|
||||
SendUdpMessage(JsonSerializer.Serialize(message), deviceAddress, uniCastPort);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -181,6 +184,39 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SetColorTemp(string deviceAddress, int colorTempInKelvin, int uniCastPort = 4003)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Build Message
|
||||
var message = new GoveeUdpMessage
|
||||
{
|
||||
msg = new msg
|
||||
{
|
||||
cmd = "colorwc",
|
||||
data = new
|
||||
{
|
||||
color = new
|
||||
{
|
||||
r = 0,
|
||||
g = 0,
|
||||
b = 0
|
||||
},
|
||||
colorTempInKelvin = colorTempInKelvin
|
||||
}
|
||||
}
|
||||
};
|
||||
// Send Message
|
||||
SendUdpMessage(JsonSerializer.Serialize(message), deviceAddress, uniCastPort);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async void StartUdpListener()
|
||||
{
|
||||
@ -196,6 +232,8 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
public void StopUdpListener()
|
||||
{
|
||||
_udpListenerActive = false;
|
||||
_udpClient.DropMulticastGroup(IPAddress.Parse(GoveeMulticastAddress));
|
||||
_udpClient.Close();
|
||||
}
|
||||
|
||||
private static void SendUdpMessage(string message, string receiverAddress, int receiverPort)
|
||||
@ -217,12 +255,13 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupUdpClientListener()
|
||||
private async void SetupUdpClientListener()
|
||||
{
|
||||
_udpClient.ExclusiveAddressUse = false;
|
||||
var localEndPoint = new IPEndPoint(IPAddress.Any, GoveeMulticastPortListen);
|
||||
_udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
_udpClient.Client.Bind(localEndPoint);
|
||||
await StartListener();
|
||||
}
|
||||
|
||||
private async Task StartListener()
|
||||
@ -245,10 +284,9 @@ public class GoveeUdpService : IGoveeUdpService
|
||||
}
|
||||
});
|
||||
}
|
||||
finally
|
||||
catch(Exception ex)
|
||||
{
|
||||
_udpClient.DropMulticastGroup(IPAddress.Parse(GoveeMulticastAddress));
|
||||
_udpClient.Close();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,8 +8,10 @@ namespace GoveeCsharpConnector.Example;
|
||||
|
||||
public class Program
|
||||
{
|
||||
private static GoveeApiService _goveeApiService = new GoveeApiService();
|
||||
public static List<GoveeApiDevice> _apiDevices = new List<GoveeApiDevice>();
|
||||
private static readonly GoveeApiService GoveeApiService = new ();
|
||||
private static readonly GoveeUdpService GoveeUdpService = new ();
|
||||
private static List<GoveeApiDevice> _apiDevices = new ();
|
||||
private static List<GoveeUdpDevice> _udpDevices = new();
|
||||
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
@ -31,7 +33,7 @@ public class Program
|
||||
break;
|
||||
case "2":
|
||||
Console.WriteLine("Requesting Devices ...");
|
||||
_apiDevices = await _goveeApiService.GetDevices();
|
||||
_apiDevices = await GoveeApiService.GetDevices();
|
||||
Console.WriteLine("Devices:");
|
||||
foreach (var device in _apiDevices)
|
||||
{
|
||||
@ -39,7 +41,6 @@ public class Program
|
||||
}
|
||||
Console.WriteLine($"Total: {_apiDevices.Count} Devices.");
|
||||
EndSegment();
|
||||
|
||||
break;
|
||||
case "3":
|
||||
if (_apiDevices.Count == 0)
|
||||
@ -68,11 +69,11 @@ public class Program
|
||||
|
||||
if (input == "on")
|
||||
{
|
||||
await _goveeApiService.ToggleState(_apiDevices.First(x => x.DeviceName.ToLower() == nameInput).DeviceId, _apiDevices.First(x => x.DeviceName.ToLower() == nameInput).Model, true);
|
||||
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);
|
||||
await GoveeApiService.ToggleState(_apiDevices.First(x => x.DeviceName.ToLower() == nameInput).DeviceId, _apiDevices.First(x => x.DeviceName.ToLower() == nameInput).Model, false);
|
||||
}
|
||||
EndSegment();
|
||||
break;
|
||||
@ -102,7 +103,7 @@ public class Program
|
||||
return;
|
||||
}
|
||||
|
||||
await _goveeApiService.SetBrightness(_apiDevices.First(x => x.DeviceName.ToLower() == nameInput2).DeviceId, _apiDevices.First(x => x.DeviceName.ToLower() == nameInput2).Model, value);
|
||||
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;
|
||||
@ -123,7 +124,7 @@ public class Program
|
||||
}
|
||||
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")
|
||||
if (string.IsNullOrWhiteSpace(colorInput) || (colorInput != "blue" && colorInput != "green" && colorInput != "red"))
|
||||
{
|
||||
Console.WriteLine("Invalid Input!");
|
||||
EndSegment();
|
||||
@ -134,21 +135,117 @@ public class Program
|
||||
switch (colorInput)
|
||||
{
|
||||
case "blue":
|
||||
await _goveeApiService.SetColor(_apiDevices.First(x => x.DeviceName.ToLower() == nameInput3).DeviceId, model, new RgbColor(0, 0 ,254));
|
||||
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));
|
||||
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));
|
||||
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;
|
||||
case "6":
|
||||
Console.WriteLine("Requesting Devices ...");
|
||||
_udpDevices = await GoveeUdpService.GetDevices();
|
||||
Console.WriteLine("Devices:");
|
||||
foreach (var device in _udpDevices)
|
||||
{
|
||||
Console.WriteLine($"IpAddress: {device.ip}, Device Id: {device.device}, Model: {device.sku}");
|
||||
}
|
||||
Console.WriteLine($"Total: {_udpDevices.Count} Devices.");
|
||||
EndSegment();
|
||||
break;
|
||||
case "7":
|
||||
var selectedDevice = GetUdpDeviceSelection();
|
||||
|
||||
Console.WriteLine($"Do you want to turn the Device {selectedDevice.ip} on or off?");
|
||||
var onOffInput2 = Console.ReadLine()?.ToLower();
|
||||
if (string.IsNullOrWhiteSpace(onOffInput2) || (onOffInput2 != "on" && onOffInput2 != "off"))
|
||||
{
|
||||
Console.WriteLine("Invalid Input!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
|
||||
if (input == "on")
|
||||
{
|
||||
await GoveeUdpService.ToggleDevice(selectedDevice.ip, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await GoveeUdpService.ToggleDevice(selectedDevice.ip, false);
|
||||
}
|
||||
EndSegment();
|
||||
break;
|
||||
case "8":
|
||||
var selectedDevice2 = GetUdpDeviceSelection();
|
||||
|
||||
Console.WriteLine($"Please enter a Brightness Value for Device {selectedDevice2.ip}. 0-100");
|
||||
var brightnessInput2 = Console.ReadLine();
|
||||
int value2 = Convert.ToInt16(brightnessInput2);
|
||||
if (string.IsNullOrWhiteSpace(brightnessInput2) || value2 < 0 || value2 > 100)
|
||||
{
|
||||
Console.WriteLine("Invalid Input!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
|
||||
await GoveeUdpService.SetBrightness(selectedDevice2.ip, value2);
|
||||
Console.WriteLine($"Set Brightness of Device {selectedDevice2.ip} to {value2}%!");
|
||||
EndSegment();
|
||||
break;
|
||||
case "9":
|
||||
var selectedDevice3 = GetUdpDeviceSelection();
|
||||
Console.WriteLine($"Please choose a Color to set {selectedDevice3.ip} to ... (blue, red, green)");
|
||||
var colorInput2 = Console.ReadLine()?.ToLower();
|
||||
if (string.IsNullOrWhiteSpace(colorInput2) || (colorInput2 != "blue" && colorInput2 != "green" && colorInput2 != "red"))
|
||||
{
|
||||
Console.WriteLine("Invalid Input!");
|
||||
EndSegment();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (colorInput2)
|
||||
{
|
||||
case "blue":
|
||||
GoveeUdpService.SetColor(selectedDevice3.ip, new RgbColor(0, 0, 254));
|
||||
break;
|
||||
case "green":
|
||||
GoveeUdpService.SetColor(selectedDevice3.ip, new RgbColor(0, 254, 0));
|
||||
break;
|
||||
case "red":
|
||||
GoveeUdpService.SetColor(selectedDevice3.ip, new RgbColor(254, 0, 0));
|
||||
break;
|
||||
}
|
||||
Console.WriteLine($"Set Color of Device {selectedDevice3.ip} to {colorInput2}!");
|
||||
EndSegment();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static GoveeUdpDevice GetUdpDeviceSelection()
|
||||
{
|
||||
var count = 1;
|
||||
Console.WriteLine("Please Choose a Device from the List:");
|
||||
foreach (var device in _udpDevices)
|
||||
{
|
||||
Console.WriteLine($"{count} - IpAdress: {device.ip}, Device Id {device.device}, Model {device.sku}");
|
||||
count++;
|
||||
}
|
||||
|
||||
var input = Console.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(input) || Int16.TryParse(input, out var result) is false)
|
||||
{
|
||||
Console.WriteLine("Invalid Input!");
|
||||
return GetUdpDeviceSelection();
|
||||
}
|
||||
|
||||
return _udpDevices[result-1];
|
||||
}
|
||||
|
||||
private static void HandleApiInput()
|
||||
{
|
||||
while (true)
|
||||
@ -161,7 +258,7 @@ public class Program
|
||||
Console.WriteLine("Wrong Api Key Format!");
|
||||
continue;
|
||||
}
|
||||
_goveeApiService.SetApiKey(input);
|
||||
GoveeApiService.SetApiKey(input);
|
||||
break;
|
||||
}
|
||||
Console.WriteLine("Api Key saved!");
|
||||
@ -180,7 +277,7 @@ public class Program
|
||||
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()))
|
||||
if (string.IsNullOrEmpty(GoveeApiService.GetApiKey()))
|
||||
{
|
||||
Console.WriteLine("1 - Enter GoveeApi Key - START HERE (Required for Api Service Options!)");
|
||||
}
|
||||
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Locxion
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
8
README.md
Normal file
8
README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# GoveeCSharpConnector
|
||||
|
||||

|
||||
[](https://www.nuget.org/packages/GoveeCSharpConnector/)
|
||||
|
||||
# About
|
||||
|
||||
Simple .net Library to interface with Govee Smart Lights via their Web Api or Lan Udp connection.
|
Reference in New Issue
Block a user