Compare commits

..

16 Commits

Author SHA1 Message Date
3c19797f72 Added Get Scenes Method 2024-02-26 05:32:07 +01:00
fdf74f5f52 Added Set Scenes Methods
Changed Control Endpoints
2024-02-26 05:23:59 +01:00
20a9b726a4 Added New Guids to Requests 2024-02-26 04:59:04 +01:00
da62e176cd Delete old Files 2024-02-26 04:49:33 +01:00
368de74820 Removed Example Console Project
Removed Old Api Classes
Added new Http Api Service and Classes
TODO Dynamic Effect Methods
2024-02-26 04:47:47 +01:00
8cb2a51cd3 Marking "old" Api as Obsoloete 2024-02-11 05:08:29 +01:00
d2cd0f0fe0 changed workflow added readme 2024-02-08 14:26:55 +01:00
1a69dad891 Set Api Key on Request 2024-02-03 01:24:00 +01:00
488211e307 Update GoveeCSharpConnector.csproj 2024-02-03 00:56:00 +01:00
d24b3592ac Moved Class and changed Namespace 2024-02-03 00:52:45 +01:00
1b099f2a5e Added ApiKey check 2024-02-03 00:51:28 +01:00
b8c838caa3 Added UdpListener check 2024-02-03 00:49:18 +01:00
ecfcd26b47 Added GoveeService that unites Api and Udp Service
Added ColorTemp Method to Udp Service
2024-02-03 00:48:12 +01:00
4b537633f6 Added Readme, Added Nuget Infos in Project 2024-02-02 19:05:25 +01:00
15a7332931 Merge branch 'main' into dev 2024-02-02 18:52:16 +01:00
2b0aefba60 Delete .gitignore 2024-02-02 18:47:02 +01:00
33 changed files with 636 additions and 534 deletions

View File

@ -1,27 +1,106 @@
name: Release to NuGet # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
name: publish
on: on:
workflow_dispatch: # Allow running the workflow manually from the GitHub UI
push: push:
branches: branches:
- master # Default release branch - 'main' # Run the workflow when pushing to the main branch
pull_request: pull_request:
branches: branches:
- master # Run the workflow for all pull requests on Branch Master - '*' # Run the workflow for all pull requests
release:
types:
- published # Run the workflow when a new GitHub release is published
env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
DOTNET_NOLOGO: true
NuGetDirectory: ${{ github.workspace}}/nuget
defaults:
run:
shell: pwsh
jobs: jobs:
build: create_nuget:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 5
steps: steps:
- name: Checkout - uses: actions/checkout@v3
uses: actions/checkout@v4.1.1 with:
- name: Setup .NET SDK fetch-depth: 0 # Get all history to allow automatic versioning using MinVer
uses: actions/setup-dotnet@v1.5
- name: Build # Install the .NET SDK indicated in the global.json file
run: dotnet build -c Release - name: Setup .NET
- name: Test uses: actions/setup-dotnet@v4
run: dotnet test -c Release --no-build
- name: Pack nugets # Create the NuGet package in the folder from the environment variable NuGetDirectory
run: dotnet pack -c Release --no-build --output . - run: dotnet pack --configuration Release --output ${{ env.NuGetDirectory }}
- name: Push to NuGet
run: dotnet nuget push "*.nupkg" --api-key ${{secrets.NUGET_KEY}} --source https://api.nuget.org/v3/index.json # Publish the NuGet package as an artifact, so they can be used in the following jobs
- uses: actions/upload-artifact@v3
with:
name: nuget
if-no-files-found: error
retention-days: 7
path: ${{ env.NuGetDirectory }}/*.nupkg
validate_nuget:
runs-on: ubuntu-latest
needs: [ create_nuget ]
steps:
# Install the .NET SDK indicated in the global.json file
- name: Setup .NET
uses: actions/setup-dotnet@v4
# Download the NuGet package created in the previous job
- uses: actions/download-artifact@v3
with:
name: nuget
path: ${{ env.NuGetDirectory }}
- name: Install nuget validator
run: dotnet tool update Meziantou.Framework.NuGetPackageValidation.Tool --global
# Validate metadata and content of the NuGet package
# https://www.nuget.org/packages/Meziantou.Framework.NuGetPackageValidation.Tool#readme-body-tab
# If some rules are not applicable, you can disable them
# using the --excluded-rules or --excluded-rule-ids option
- name: Validate package
run: meziantou.validate-nuget-package (Get-ChildItem "${{ env.NuGetDirectory }}/*.nupkg")
run_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v4
- name: Run tests
run: dotnet test --configuration Release
deploy:
# Publish only when creating a GitHub Release
# https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository
# You can update this logic if you want to manage releases differently
if: github.event_name == 'release'
runs-on: ubuntu-latest
needs: [ validate_nuget, run_test ]
steps:
# Download the NuGet package created in the previous job
- uses: actions/download-artifact@v3
with:
name: nuget
path: ${{ env.NuGetDirectory }}
# Install the .NET SDK indicated in the global.json file
- name: Setup .NET Core
uses: actions/setup-dotnet@v4
# Publish all NuGet packages to NuGet.org
# Use --skip-duplicate to prevent errors if a package with the same version already exists.
# If you retry a failed workflow, already published packages will be skipped without error.
- name: Publish NuGet package
run: |
foreach($file in (Get-ChildItem "${{ env.NuGetDirectory }}" -Recurse -Include *.nupkg)) {
dotnet nuget push $file --api-key "${{ secrets.NUGET_KEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate
}

View File

@ -2,8 +2,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoveeCSharpConnector", "GoveeCSharpConnector\GoveeCSharpConnector.csproj", "{EDF67B3A-9EBF-4C76-92E2-0AACD6B8081B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoveeCSharpConnector", "GoveeCSharpConnector\GoveeCSharpConnector.csproj", "{EDF67B3A-9EBF-4C76-92E2-0AACD6B8081B}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoveeCsharpConnector.Example", "GoveeCsharpConnector.Example\GoveeCsharpConnector.Example.csproj", "{E487B84B-F619-430A-A0D3-80D44FE9EE3F}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -14,9 +12,5 @@ Global
{EDF67B3A-9EBF-4C76-92E2-0AACD6B8081B}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{EDF67B3A-9EBF-4C76-92E2-0AACD6B8081B}.Release|Any CPU.Build.0 = 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 EndGlobalSection
EndGlobal EndGlobal

View File

@ -1,2 +1,3 @@
<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"> <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> <s:Boolean x:Key="/Default/UserDictionary/Words/=Govee/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=reauired/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -13,6 +13,7 @@
<RepositoryUrl>https://github.com/Locxion/GoveeCSharpConnector</RepositoryUrl> <RepositoryUrl>https://github.com/Locxion/GoveeCSharpConnector</RepositoryUrl>
<PackageLicenseUrl>https://github.com/Locxion/GoveeCSharpConnector/blob/main/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/Locxion/GoveeCSharpConnector/blob/main/LICENSE</PackageLicenseUrl>
<Version>1.1.2</Version> <Version>1.1.2</Version>
<PackageReadmeFile>../README.md</PackageReadmeFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" /> <PackageReference Include="System.Net.Http.Json" Version="8.0.0" />

View File

@ -1,8 +1,9 @@
using GoveeCSharpConnector.Objects; using GoveeCSharpConnector.Objects;
using GoveeCSharpConnector.Objects.Misc;
namespace GoveeCSharpConnector.Interfaces; namespace GoveeCSharpConnector.Interfaces;
public interface IGoveeApiService public interface IGoveeHttpService
{ {
/// <summary> /// <summary>
/// Sets the required Api Key for the Govee Api. /// Sets the required Api Key for the Govee Api.
@ -10,32 +11,27 @@ public interface IGoveeApiService
/// </summary> /// </summary>
/// <param name="apiKey">Api Key as String</param> /// <param name="apiKey">Api Key as String</param>
void SetApiKey(string apiKey); void SetApiKey(string apiKey);
/// <summary> /// <summary>
/// Returns current set Govee Api Key /// Returns current set Govee Api Key
/// </summary> /// </summary>
/// <returns>Govee Api Key as String</returns> /// <returns>Govee Api Key as String</returns>
string GetApiKey(); string GetApiKey();
/// <summary> /// <summary>
/// Removes the Set Api Key and resets the HTTP Header /// Removes the Set Api Key and resets the HTTP Header
/// </summary> /// </summary>
void RemoveApiKey(); void RemoveApiKey();
/// <summary> /// <summary>
/// Requests all Devices registered to Api Key Govee Account /// Requests all Devices registered to Api Key Govee Account
/// </summary> /// </summary>
/// <returns>List of GoveeApiDevices</returns> /// <returns>List of GoveeHttpDevices</returns>
Task<List<GoveeApiDevice>> GetDevices(); Task<ServiceResponse<List<GoveeHttpDevice>>> GetDevices();
/// <summary> /// <summary>
/// Requests the State of a single Govee Device /// Requests the State of a single Govee Device
/// </summary> /// </summary>
/// <param name="deviceId">Device Id Guid as string</param> /// <param name="deviceId">Device Id Guid as string</param>
/// <param name="deviceModel">Device Model Number as string</param> /// <param name="deviceModel">Device Model Number as string</param>
/// <returns>GoveeApiStat Object</returns> /// <returns>GoveeHttpState Object</returns>
Task<GoveeApiState> GetDeviceState(string deviceId, string deviceModel); Task<ServiceResponse<GoveeHttpState>> GetDeviceState(string deviceId, string deviceModel);
/// <summary> /// <summary>
/// Sets the On/Off state of a single Govee Device /// Sets the On/Off state of a single Govee Device
/// </summary> /// </summary>
@ -43,17 +39,7 @@ public interface IGoveeApiService
/// <param name="deviceModel">Device Model Number as string</param> /// <param name="deviceModel">Device Model Number as string</param>
/// <param name="on"></param> /// <param name="on"></param>
/// <returns></returns> /// <returns></returns>
Task ToggleState(string deviceId, string deviceModel, bool on); Task<ServiceResponse<bool>> SetOnOff(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>
Task SetBrightness(string deviceId, string deviceModel, int value);
/// <summary> /// <summary>
/// Sets a Rgb Color of a single Govee Device /// Sets a Rgb Color of a single Govee Device
/// </summary> /// </summary>
@ -61,8 +47,7 @@ public interface IGoveeApiService
/// <param name="deviceModel">Device Model Number as string</param> /// <param name="deviceModel">Device Model Number as string</param>
/// <param name="color">Rgb Color</param> /// <param name="color">Rgb Color</param>
/// <returns></returns> /// <returns></returns>
Task SetColor(string deviceId, string deviceModel, RgbColor color); Task<ServiceResponse<bool>> SetColor(string deviceId, string deviceModel, RgbColor color);
/// <summary> /// <summary>
/// Sets the Color Temperature of a single Govee Device /// Sets the Color Temperature of a single Govee Device
/// </summary> /// </summary>
@ -70,5 +55,37 @@ public interface IGoveeApiService
/// <param name="deviceModel">Device Model Number as string</param> /// <param name="deviceModel">Device Model Number as string</param>
/// <param name="value">Color Temp in Kelvin as Int</param> /// <param name="value">Color Temp in Kelvin as Int</param>
/// <returns></returns> /// <returns></returns>
Task SetColorTemp(string deviceId, string deviceModel, int value); Task<ServiceResponse<bool>> SetColorTemp(string deviceId, string deviceModel, int value);
/// <summary>
/// Sets the Brightness 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">Value 1-100</param>
/// <returns></returns>
Task<ServiceResponse<bool>> SetBrightness(string deviceId, string deviceModel, int value);
/// <summary>
/// Gets a List of all available Govee Scenes for the Device
/// </summary>
/// <param name="deviceId">Device Id Guid as string</param>
/// <param name="deviceModel">Device Model Number as string</param>
/// <returns></returns>
Task<ServiceResponse<List<GoveeScene>>> GetScenes(string deviceId, string deviceModel);
/// <summary>
/// Sets the LightScene 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="sceneValue">Number of the Scene</param>
/// <returns></returns>
Task<ServiceResponse<bool>> SetLightScene(string deviceId, string deviceModel, int sceneValue);
/// <summary>
/// Sets the DiyScene 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="sceneValue">Number of the Scene</param>
/// <returns></returns>
Task<ServiceResponse<bool>> SetDiyScene(string deviceId, string deviceModel, int sceneValue);
} }

View File

@ -12,7 +12,7 @@ public interface IGoveeUdpService
/// <summary> /// <summary>
/// Sends a Scan Command via Udp Multicast. /// Sends a Scan Command via Udp Multicast.
/// </summary> /// </summary>
/// <param name="timeout">Standard 250ms</param> /// <param name="timeout">Standard 200ms</param>
/// <returns>List of GoveeUdpDevices</returns> /// <returns>List of GoveeUdpDevices</returns>
Task<List<GoveeUdpDevice>> GetDevices(TimeSpan? timeout = null); Task<List<GoveeUdpDevice>> GetDevices(TimeSpan? timeout = null);
@ -21,7 +21,7 @@ public interface IGoveeUdpService
/// </summary> /// </summary>
/// <param name="deviceAddress">Ip Address of the Device</param> /// <param name="deviceAddress">Ip Address of the Device</param>
/// <param name="uniCastPort">Port of the Device. Standard 4003</param> /// <param name="uniCastPort">Port of the Device. Standard 4003</param>
/// <param name="timeout">Standard 250ms</param> /// <param name="timeout">Standard 200ms</param>
/// <returns></returns> /// <returns></returns>
Task<GoveeUdpState> GetState(string deviceAddress, int uniCastPort = 4003, TimeSpan? timeout = null); Task<GoveeUdpState> GetState(string deviceAddress, int uniCastPort = 4003, TimeSpan? timeout = null);

View File

@ -1,7 +0,0 @@
namespace GoveeCSharpConnector.Objects;
public class ApiResponse
{
public string? Message { get; set; }
public int Code { get; set; }
}

View File

@ -1,8 +0,0 @@
using System.Collections.Generic;
namespace GoveeCSharpConnector.Objects;
public class Data
{
public List<GoveeApiDevice> Devices { get; set; }
}

View File

@ -1,14 +0,0 @@
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; }
}

View File

@ -1,17 +0,0 @@
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; }
}

View File

@ -1,16 +0,0 @@
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; }
}

View File

@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
using GoveeCSharpConnector.Objects.Misc;
namespace GoveeCSharpConnector.Objects;
public class GoveeHttpDevice
{
[JsonPropertyName("sku")]
public string Model { get; set; }
[JsonPropertyName("device")]
public string Device { get; set; }
[JsonPropertyName("capabilities")]
public List<Capability> Capabilities { get; set; }
}

View File

@ -0,0 +1,16 @@
using System.Text.Json.Serialization;
using GoveeCSharpConnector.Objects.Misc;
namespace GoveeCSharpConnector.Objects;
public class GoveeHttpState
{
[JsonPropertyName("requestId")]
public string RequestId { get; set; }
[JsonPropertyName("msg")]
public string Msg { get; set; }
[JsonPropertyName("code")]
public long Code { get; set; }
[JsonPropertyName("payload")]
public Payload Payload { get; set; }
}

View File

@ -1,6 +0,0 @@
namespace GoveeCSharpConnector.Objects;
public class GoveeResponse : ApiResponse
{
public Data Data { get; set; }
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
using GoveeCSharpConnector.Objects.Misc;
namespace GoveeCSharpConnector.Objects;
public class GoveeScene
{
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("value")]
public SceneValue SceneValue { get; set; }
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace GoveeCSharpConnector.Objects.Misc;
public class ApiResponse
{
[JsonPropertyName("code")]
public int Code { get; set; }
[JsonPropertyName("message")]
public string Message { get; set; }
[JsonPropertyName("data")]
public List<object> Data { get; set; }
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace GoveeCSharpConnector.Objects.Misc;
public class Capability
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("instance")]
public string Instance { get; set; }
[JsonPropertyName("state")]
public State State { get; set; }
}

View File

@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace GoveeCSharpConnector.Objects.Misc;
public class Field
{
[JsonPropertyName("fieldName")]
public string FieldName { get; set; }
[JsonPropertyName("dataType")]
public string DataType { get; set; }
[JsonPropertyName("options")]
public List<Option> Options { get; set; }
[JsonPropertyName("required")]
public bool Required { get; set; }
[JsonPropertyName("range")]
public Range Range { get; set; }
[JsonPropertyName("unit")]
public string Unit { get; set; }
[JsonPropertyName("reauired")]
public bool? Reauired { get; set; }
}

View File

@ -0,0 +1,11 @@
using System.Text.Json.Serialization;
namespace GoveeCSharpConnector.Objects.Misc;
public class Option
{
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("value")]
public int Value { get; set; }
}

View File

@ -0,0 +1,17 @@
using System.Text.Json.Serialization;
namespace GoveeCSharpConnector.Objects.Misc;
public class Parameters
{
[JsonPropertyName("dataType")]
public string DataType { get; set; }
[JsonPropertyName("options")]
public List<Option> Options { get; set; }
[JsonPropertyName("unit")]
public string Unit { get; set; }
[JsonPropertyName("range")]
public Range Range { get; set; }
[JsonPropertyName("fields")]
public List<Field> Fields { get; set; }
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace GoveeCSharpConnector.Objects.Misc;
public class Payload
{
[JsonPropertyName("sku")]
public string Model { get; set; }
[JsonPropertyName("device")]
public string Device { get; set; }
[JsonPropertyName("capabilities")]
public List<Capability> Capabilities { get; set; }
}

View File

@ -1,12 +1,18 @@
using System.Text.Json.Serialization;
using GoveeCSharpConnector.Enums; using GoveeCSharpConnector.Enums;
namespace GoveeCSharpConnector.Objects; namespace GoveeCSharpConnector.Objects.Misc;
public class Properties public class Properties
{ {
[JsonPropertyName("online")]
public bool Online { get; set; } public bool Online { get; set; }
[JsonPropertyName("powerState")]
public PowerState PowerState { get; set; } public PowerState PowerState { get; set; }
[JsonPropertyName("brightness")]
public int Brightness { get; set; } public int Brightness { get; set; }
[JsonPropertyName("colorTemp")]
public int ColorTemp { get; set; } public int ColorTemp { get; set; }
[JsonPropertyName("color")]
public RgbColor Color { get; set; } public RgbColor Color { get; set; }
} }

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace GoveeCSharpConnector.Objects.Misc;
public class Range
{
[JsonPropertyName("min")]
public int Min { get; set; }
[JsonPropertyName("max")]
public int Max { get; set; }
[JsonPropertyName("precision")]
public int Precision { get; set; }
}

View File

@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace GoveeCSharpConnector.Objects.Misc;
public class SceneValue
{
[JsonPropertyName("paramId")]
public long ParamId { get; set; }
[JsonPropertyName("id")]
public long Id { get; set; }
}

View File

@ -0,0 +1,8 @@
namespace GoveeCSharpConnector.Objects.Misc;
public class ServiceResponse<T>
{
public T? Data { get; set; }
public bool Success { get; set; } = true;
public string Message { get; set; } = string.Empty;
}

View File

@ -0,0 +1,9 @@
using System.Text.Json.Serialization;
namespace GoveeCSharpConnector.Objects.Misc;
public class State
{
[JsonPropertyName("value")]
public object Value { get; set; }
}

View File

@ -1,85 +0,0 @@
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();
private readonly JsonSerializerOptions? _jsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
/// <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, _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}");
}
}

View File

@ -0,0 +1,306 @@
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using GoveeCSharpConnector.Interfaces;
using GoveeCSharpConnector.Objects;
using GoveeCSharpConnector.Objects.Misc;
namespace GoveeCSharpConnector.Services;
public class GoveeHttpService : IGoveeHttpService
{
private string _apiKey = string.Empty;
private const string GoveeApiAddress = "https://openapi.api.govee.com";
private const string GoveeDevicesEndpoint = "/router/api/v1/user/devices";
private const string GoveeControlEndpoint = "/router/api/v1/device/control";
private const string GoveeStateEndpoint = "/router/api/v1/device/state";
private const string GoveeScenesEndpoint = "/router/api/v1/device/scenes";
private readonly HttpClient _httpClient = new();
private readonly JsonSerializerOptions? _jsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
public GoveeHttpService()
{
_httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
}
/// <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<ServiceResponse<List<GoveeHttpDevice>>> GetDevices()
{
var serviceResponse = new ServiceResponse<List<GoveeHttpDevice>>();
var response = await _httpClient.GetFromJsonAsync<ApiResponse>($"{GoveeApiAddress}{GoveeDevicesEndpoint}");
if (response.Code != 200)
{
if (response.Code == 429)
{
serviceResponse.Success = false;
serviceResponse.Message = "Api Limit reached! 10000/Account/Day";
return serviceResponse;
}
serviceResponse.Success = false;
serviceResponse.Message = response.Message;
return serviceResponse;
}
var allDevices = response.Data.OfType<GoveeHttpDevice>().ToList();
var devices = (from device in allDevices where device.Capabilities.Exists(x => x.Type == "devices.capabilities.on_off")
where device.Capabilities.Exists(x => x.Type == "devices.capabilities.color_setting")
where device.Capabilities.Exists(x => x.Type == "devices.capabilities.range")
where device.Capabilities.Exists(x => x.Type == "devices.capabilities.dynamic_scene")
select device).ToList();
serviceResponse.Success = true;
serviceResponse.Data = devices;
serviceResponse.Message = "Request Successful";
return serviceResponse;
}
/// <inheritdoc/>
public async Task<ServiceResponse<GoveeHttpState>> GetDeviceState(string deviceId, string deviceModel)
{
var serviceResponse = new ServiceResponse<GoveeHttpState>();
var jsonPayload = $@"
{{
""requestId"": ""{Guid.NewGuid()}"",
""payload"": {{
""sku"": ""{deviceModel}"",
""device"": ""{deviceId}""
}}
}}";
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{GoveeApiAddress}{GoveeStateEndpoint}", content);
if (!response.IsSuccessStatusCode)
{
serviceResponse.Success = false;
serviceResponse.Message = response.ReasonPhrase;
return serviceResponse;
}
var state = await response.Content.ReadFromJsonAsync<GoveeHttpState>();
serviceResponse.Success = true;
serviceResponse.Message = "";
serviceResponse.Data = state;
return serviceResponse;
}
/// <inheritdoc/>
public async Task<ServiceResponse<bool>> SetOnOff(string deviceId, string deviceModel, bool on)
{
var serviceResponse = new ServiceResponse<bool>();
var value = "0";
if (on)
{
value = "1";
}
var jsonPayload = $@"
{{
""requestId"": ""{Guid.NewGuid()}"",
""payload"": {{
""sku"": ""{deviceModel}"",
""device"": ""{deviceId}"",
""capability"": {{
""type"": ""devices.capabilities.on_off"",
""instance"": ""powerSwitch"",
""value"": {value}
}}
}}
}}";
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{GoveeApiAddress}{GoveeControlEndpoint}", content);
if (!response.IsSuccessStatusCode)
{
serviceResponse.Success = false;
serviceResponse.Message = response.ReasonPhrase;
return serviceResponse;
}
serviceResponse.Success = true;
serviceResponse.Message = "";
return serviceResponse;
}
/// <inheritdoc/>
public async Task<ServiceResponse<bool>> SetColor(string deviceId, string deviceModel, RgbColor color)
{
var serviceResponse = new ServiceResponse<bool>();
var value = ((color.R & 0xFF) << 16) | ((color.G & 0xFF) << 8) | (color.B & 0xFF);
var jsonPayload = $@"
{{
""requestId"": ""{Guid.NewGuid()}"",
""payload"": {{
""sku"": ""{deviceModel}"",
""device"": ""{deviceId}"",
""capability"": {{
""type"": ""devices.capabilities.color_setting"",
""instance"": ""colorRgb"",
""value"": {value}
}}
}}
}}";
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{GoveeApiAddress}{GoveeControlEndpoint}", content);
if (!response.IsSuccessStatusCode)
{
serviceResponse.Success = false;
serviceResponse.Message = response.ReasonPhrase;
return serviceResponse;
}
serviceResponse.Success = true;
serviceResponse.Message = "";
return serviceResponse;
}
public Task<ServiceResponse<bool>> SetColorTemp(string deviceId, string deviceModel, int value)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public async Task<ServiceResponse<bool>> SetBrightness(string deviceId, string deviceModel, int value)
{
var serviceResponse = new ServiceResponse<bool>();
var jsonPayload = $@"
{{
""requestId"": ""{Guid.NewGuid()}"",
""payload"": {{
""sku"": ""{deviceModel}"",
""device"": ""{deviceId}"",
""capability"": {{
""type"": ""devices.capabilities.range"",
""instance"": ""brightness"",
""value"": {value}
}}
}}
}}";
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{GoveeApiAddress}{GoveeControlEndpoint}", content);
if (!response.IsSuccessStatusCode)
{
serviceResponse.Success = false;
serviceResponse.Message = response.ReasonPhrase;
return serviceResponse;
}
serviceResponse.Success = true;
serviceResponse.Message = "";
return serviceResponse;
}
/// <inheritdoc/>
public async Task<ServiceResponse<List<GoveeScene>>> GetScenes(string deviceId, string deviceModel)
{
var serviceResponse = new ServiceResponse<List<GoveeScene>>();
var jsonPayload = $@"
{{
""requestId"": ""{Guid.NewGuid()}"",
""payload"": {{
""sku"": ""{deviceModel}"",
""device"": ""{deviceId}""
}}
}}";
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{GoveeApiAddress}{GoveeControlEndpoint}", content);
if (!response.IsSuccessStatusCode)
{
serviceResponse.Success = false;
serviceResponse.Message = response.ReasonPhrase;
return serviceResponse;
}
// TODO Test response Content
serviceResponse.Success = true;
serviceResponse.Message = "";
return serviceResponse;
}
/// <inheritdoc/>
public async Task<ServiceResponse<bool>> SetLightScene(string deviceId, string deviceModel, int sceneValue)
{
var serviceResponse = new ServiceResponse<bool>();
var jsonPayload = $@"
{{
""requestId"": ""{Guid.NewGuid()}"",
""payload"": {{
""sku"": ""{deviceModel}"",
""device"": ""{deviceId}"",
""capability"": {{
""type"": ""devices.capabilities.dynamic_scene"",
""instance"": ""lightScene"",
""value"": {sceneValue}
}}
}}
}}";
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{GoveeApiAddress}{GoveeControlEndpoint}", content);
if (!response.IsSuccessStatusCode)
{
serviceResponse.Success = false;
serviceResponse.Message = response.ReasonPhrase;
return serviceResponse;
}
serviceResponse.Success = true;
serviceResponse.Message = "";
return serviceResponse;
}
/// <inheritdoc/>
public async Task<ServiceResponse<bool>> SetDiyScene(string deviceId, string deviceModel, int sceneValue)
{
var serviceResponse = new ServiceResponse<bool>();
var jsonPayload = $@"
{{
""requestId"": ""{Guid.NewGuid()}"",
""payload"": {{
""sku"": ""{deviceModel}"",
""device"": ""{deviceId}"",
""capability"": {{
""type"": ""devices.capabilities.dynamic_scene"",
""instance"": ""diyScene"",
""value"": {sceneValue}
}}
}}
}}";
var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{GoveeApiAddress}{GoveeControlEndpoint}", content);
if (!response.IsSuccessStatusCode)
{
serviceResponse.Success = false;
serviceResponse.Message = response.ReasonPhrase;
return serviceResponse;
}
serviceResponse.Success = true;
serviceResponse.Message = "";
return serviceResponse;
}
}

View File

@ -19,7 +19,6 @@ public class GoveeService : IGoveeService
{ {
if (string.IsNullOrWhiteSpace(GoveeApiKey)) throw new Exception("No Govee Api Key Set!"); if (string.IsNullOrWhiteSpace(GoveeApiKey)) throw new Exception("No Govee Api Key Set!");
_apiService.SetApiKey(GoveeApiKey); _apiService.SetApiKey(GoveeApiKey);
var apiDevices = await _apiService.GetDevices(); var apiDevices = await _apiService.GetDevices();
var devices = apiDevices.Select(apiDevice => new GoveeDevice() { DeviceId = apiDevice.DeviceId, DeviceName = apiDevice.DeviceName, Model = apiDevice.Model, Address = "onlyAvailableOnUdpRequest" }).ToList(); var devices = apiDevices.Select(apiDevice => new GoveeDevice() { DeviceId = apiDevice.DeviceId, DeviceName = apiDevice.DeviceName, Model = apiDevice.Model, Address = "onlyAvailableOnUdpRequest" }).ToList();
if (!onlyLan) if (!onlyLan)
@ -88,7 +87,6 @@ public class GoveeService : IGoveeService
return; return;
} }
if (string.IsNullOrWhiteSpace(GoveeApiKey)) throw new Exception("No Govee Api Key Set!"); if (string.IsNullOrWhiteSpace(GoveeApiKey)) throw new Exception("No Govee Api Key Set!");
_apiService.SetApiKey(GoveeApiKey); _apiService.SetApiKey(GoveeApiKey);
await _apiService.SetColor(goveeDevice.DeviceId, goveeDevice.Model, color); await _apiService.SetColor(goveeDevice.DeviceId, goveeDevice.Model, color);
} }

View File

@ -32,8 +32,6 @@ public class GoveeUdpService : IGoveeUdpService
/// <inheritdoc/> /// <inheritdoc/>
public async Task<List<GoveeUdpDevice>> GetDevices(TimeSpan? timeout = null) 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 // Block this Method until current call reaches end of Method
await _semaphore.WaitAsync(); await _semaphore.WaitAsync();
@ -50,7 +48,7 @@ public class GoveeUdpService : IGoveeUdpService
}; };
// Subscribe to ScanResultSubject // Subscribe to ScanResultSubject
var devicesTask = _scanResultSubject var devicesTask = _scanResultSubject
.TakeUntil(Observable.Timer(timeout ?? TimeSpan.FromMilliseconds(250))) .TakeUntil(Observable.Timer(timeout ?? TimeSpan.FromMilliseconds(300)))
.ToList() .ToList()
.ToTask(); .ToTask();
@ -75,8 +73,6 @@ public class GoveeUdpService : IGoveeUdpService
/// <inheritdoc/> /// <inheritdoc/>
public async Task<GoveeUdpState> GetState(string deviceAddress, int uniCastPort = 4003, TimeSpan? timeout = null) public async Task<GoveeUdpState> GetState(string deviceAddress, int uniCastPort = 4003, TimeSpan? timeout = null)
{ {
if (!_udpListenerActive)
throw new Exception("Udp Listener not started!");
try try
{ {
// Build Message // Build Message
@ -90,7 +86,7 @@ public class GoveeUdpService : IGoveeUdpService
}; };
// Subscribe to ScanResultSubject // Subscribe to ScanResultSubject
var devicesTask = _stateResultSubject var devicesTask = _stateResultSubject
.TakeUntil(Observable.Timer(timeout ?? TimeSpan.FromMilliseconds(250))) .TakeUntil(Observable.Timer(timeout ?? TimeSpan.FromMilliseconds(200)))
.ToTask(); .ToTask();
// Send Message // Send Message
@ -232,8 +228,6 @@ public class GoveeUdpService : IGoveeUdpService
public void StopUdpListener() public void StopUdpListener()
{ {
_udpListenerActive = false; _udpListenerActive = false;
_udpClient.DropMulticastGroup(IPAddress.Parse(GoveeMulticastAddress));
_udpClient.Close();
} }
private static void SendUdpMessage(string message, string receiverAddress, int receiverPort) private static void SendUdpMessage(string message, string receiverAddress, int receiverPort)
@ -255,13 +249,12 @@ public class GoveeUdpService : IGoveeUdpService
} }
} }
private async void SetupUdpClientListener() private void SetupUdpClientListener()
{ {
_udpClient.ExclusiveAddressUse = false; _udpClient.ExclusiveAddressUse = false;
var localEndPoint = new IPEndPoint(IPAddress.Any, GoveeMulticastPortListen); var localEndPoint = new IPEndPoint(IPAddress.Any, GoveeMulticastPortListen);
_udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_udpClient.Client.Bind(localEndPoint); _udpClient.Client.Bind(localEndPoint);
await StartListener();
} }
private async Task StartListener() private async Task StartListener()
@ -284,9 +277,10 @@ public class GoveeUdpService : IGoveeUdpService
} }
}); });
} }
catch(Exception ex) finally
{ {
throw ex; _udpClient.DropMulticastGroup(IPAddress.Parse(GoveeMulticastAddress));
_udpClient.Close();
} }
} }

View File

@ -1,14 +0,0 @@
<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>

View File

@ -1,299 +0,0 @@
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 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)
{
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;
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)
{
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");
}
}

View File

@ -1,8 +1,7 @@
# GoveeCSharpConnector # GoveeCSharpConnector
![netStandard2.0](https://img.shields.io/badge/.NET%20Standard-2.0-blueviolet) ![netStandard2.0](https://img.shields.io/badge/.NET%20Standard-2.0-blueviolet)
[![Nuget](https://img.shields.io/nuget/v/GoveeCSharpConnector?cacheSeconds=50)](https://www.nuget.org/packages/GoveeCSharpConnector/) [![Nuget](https://img.shields.io/nuget/v/GoveeApiClient?cacheSeconds=50)](https://www.nuget.org/packages/GoveeCSharpConnector/)
# About # About
Simple .net Library to interface with Govee Smart Lights via their Web Api or Lan Udp connection. Simple .net Library to interface with Govee Smart Lights via their Web Api or Lan Udp connection.