Skip to content

Commit

Permalink
use Open CUE Service as backend
Browse files Browse the repository at this point in the history
  • Loading branch information
Legion2 committed Apr 19, 2020
1 parent 9bd7195 commit a23dbc2
Show file tree
Hide file tree
Showing 35 changed files with 234 additions and 395 deletions.
26 changes: 14 additions & 12 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Open CUE CLI [![Build](https://github.com/Legion2/open-cue-cli/workflows/Build/badge.svg)](https://github.com/Legion2/open-cue-cli/actions?query=workflow%3ABuild)
Command Line Interface (CLI) to change iCUE profiles from the command line and play multiple profiles at once.
This CLI uses the iCUE Game SDK via the [iCUE Unofficial Game Integration](https://github.com/Zac-McDonald/iCUE-Custom-Game-Integration) server.
This CLI uses the iCUE Game SDK via the [Open CUE Service](https://github.com/Legion2/open-cue-service).

## Getting Started
Download and start the [iCUE Unofficial Game Integration](https://github.com/Zac-McDonald/iCUE-Custom-Game-Integration) server.
Download and start the [Open CUE Service](https://github.com/Legion2/open-cue-service).
Download the [latest release](https://github.com/Legion2/open-cue-cli/releases/latest) of Open CUE CLI and extract the archive.
You must at least have Java 8 installed.
Call the cli tool `open-cue-cli.bat` or `open-cue-cli` from a command prompt with the option `--help`.
Expand All @@ -25,25 +25,25 @@ Commands:
```

### Examples
Activates the profile `SDKL_Fire` from the game `Common` and also set the game active
Activates the profile `Fire`
```
open-cue-cli state set -s -g Common SDKL_Fire
open-cue-cli profile activate Fire
```

Lists all profiles of the current game
Lists all profiles
```
open-cue-cli profile list
```

Plays the profile `SDKL_Explosion` of the current game as event
Plays the profile `Wave` as event for a short time
```
open-cue-cli event set SDKL_Explosion
open-cue-cli profile trigger Wave
```

### Profiles
All profiles that you want to use with the Open CUE CLI must be in the iCUE `GameSdkEffects` directory.
On Windows this is in the installation directory of iCUE `C:\Program Files (x86)\Corsair\CORSAIR iCUE Software\GameSdkEffects`.
These profiles are just the exported files from iCUE profiles.
These profiles are just the exported files when you export a profile in iCUE.
When export profiles from iCUE only select "Lighting Effects" in the export settings.

Profiles are grouped into games, this is because the SDK was designed for iCUE Game integration.
Expand All @@ -58,14 +58,16 @@ Create a new text file named `priorities.cfg` in the `profiles` directory.
The file must contain all profile names one per line with a unique priority value.
```properties
MyProfile1=3
OtherProfile=8
MyProfile=3
Other_Profile=8
Test=245
```
Profile names **SHOULD NOT** contain special characters like `-`, `*`, `.` and spaces.
Underscores (`_`) are allowed in profile names.
Profile names **MUST** only contain normal characters "a-z", "A-Z" and "_".
Also don't use language specific characters like ä and é.
The priorities comes into play when you activate two profiles, then the profile with the higher priority is shown on top of the other.

> `default` is not allowed as profile name.
## Packaging of this application
This package is provide as zip containing executables and dependencies as jars.
A Java Runtime Environment (JRE) must be installed to run the application.
Expand Down
6 changes: 2 additions & 4 deletions src/main/kotlin/io/github/legion2/open_cue_cli/CliContext.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package io.github.legion2.open_cue_cli

import io.github.legion2.open_cue_cli.client.CueSdkHttpServer
import io.github.legion2.open_cue_cli.client.LocalFileClient

class CliContext(
val sdkClient: CueSdkHttpServer,
val localFileClient: LocalFileClient) {
val sdkClient: CueSdkHttpServer) {
companion object {
fun createCliContext(host: String, port: Int): CliContext {
return CliContext(CueSdkHttpServer(host, port), LocalFileClient())
return CliContext(CueSdkHttpServer(host, port))
}
}
}
36 changes: 12 additions & 24 deletions src/main/kotlin/io/github/legion2/open_cue_cli/Main.kt
Original file line number Diff line number Diff line change
@@ -1,36 +1,24 @@
package io.github.legion2.open_cue_cli

import com.github.ajalt.clikt.core.subcommands
import io.github.legion2.open_cue_cli.event.ClearAllEvent
import io.github.legion2.open_cue_cli.event.EventCommand
import io.github.legion2.open_cue_cli.event.SetEvent
import io.github.legion2.open_cue_cli.game.*
import io.github.legion2.open_cue_cli.profile.ListProfiles
import io.github.legion2.open_cue_cli.profile.ProfileCommand
import io.github.legion2.open_cue_cli.state.ClearAllState
import io.github.legion2.open_cue_cli.state.ClearState
import io.github.legion2.open_cue_cli.state.SetState
import io.github.legion2.open_cue_cli.state.StateCommand
import io.github.legion2.open_cue_cli.profile.*
import io.github.legion2.open_cue_cli.sdk.*

fun createCLI(): OpenCueCli {
return OpenCueCli().subcommands(
GameCommand().subcommands(
SdkCommand().subcommands(
SdkInfo(),
CurrentGame(),
ListGames(),
SetGame(),
ResetGame()
),
StateCommand().subcommands(
SetState(),
ClearState(),
ClearAllState()
),
EventCommand().subcommands(
SetEvent(),
ClearAllEvent()
SdkControl(),
StopAllEvents(),
DeactivateAllProfiles()
),
ProfileCommand().subcommands(
ListProfiles()
ListProfiles(),
ProfileInfo(),
ActivateProfile(),
DeactivateProfile(),
TriggerProfile()
))
}

Expand Down
11 changes: 5 additions & 6 deletions src/main/kotlin/io/github/legion2/open_cue_cli/OpenCueCli.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.github.legion2.open_cue_cli

import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.ParameterHolder
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.versionOption
Expand All @@ -10,13 +10,13 @@ import com.github.ajalt.clikt.parameters.types.restrictTo
import io.github.legion2.open_cue_cli.CliContext.Companion.createCliContext

class OpenCueCli : CliktCommand(name = "open-cue-cli") {
private val port: Int by option("-p", "--port", help = "Port of the iCUE SDK HTTP Server", metavar = "<port>")
private val port: Int by option("-p", "--port", help = "Port of the Open CUE Service", metavar = "<port>")
.int().restrictTo(min = 0).default(25555)
private val host: String by option("-H", "--host", help = "Hostname or ip address of the iCUE SDK HTTP Server",
private val host: String by option("-H", "--host", help = "Hostname or ip address of the Open CUE Service",
metavar = "<host or ip>").default("localhost")

init {
versionOption("0.2.0")
versionOption("0.3.0")
}

override fun run() {
Expand All @@ -28,5 +28,4 @@ val <T> Iterable<T>.echoString: String get() = joinToString("\n")

fun <T> Iterable<T>.echoString(transform: (T) -> CharSequence): String = joinToString("\n", transform = transform)

fun ParameterHolder.optionForGame(help: String? = null) = option("-g", "--game", help = help
?: "Provide a game as option instead of using the current active game", metavar = "<game>")
fun CliktCommand.profileArgument(help: String? = null) = argument("profile", help = help ?: "The name of the profile")
Original file line number Diff line number Diff line change
@@ -1,81 +1,69 @@
package io.github.legion2.open_cue_cli.client

import io.github.legion2.open_cue_cli.model.Game
import com.google.gson.Gson
import io.github.legion2.open_cue_cli.model.Profile
import io.github.legion2.open_cue_cli.model.SdkDetails
import io.ktor.client.HttpClient
import io.ktor.client.call.typeInfo
import io.ktor.client.features.ClientRequestException
import io.ktor.client.features.HttpResponseValidator
import io.ktor.client.features.ResponseException
import io.ktor.client.features.addDefaultResponseValidation
import io.ktor.client.features.json.GsonSerializer
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.defaultSerializer
import io.ktor.client.request.get
import io.ktor.client.request.post
import io.ktor.client.statement.readText
import io.ktor.http.ParametersBuilder
import io.ktor.http.URLBuilder
import io.ktor.http.Url
import io.ktor.utils.io.core.Input
import java.net.ConnectException

class CueSdkHttpServer(private val host: String = "localhost", private val port: Int = 25555) {
val client = HttpClient {
private val client = HttpClient {
install(JsonFeature) {
serializer = GsonSerializer()
}
addDefaultResponseValidation()
HttpResponseValidator {
handleResponseException { exception ->

when (exception) {
is ClientRequestException -> throw GameSdkError(Gson().fromJson(exception.response.readText(), String::class.java))
is ResponseException -> throw SdkHttpServerException("Server can not process request: ${exception.response.call.request.url}", exception)
is ConnectException -> throw SdkHttpServerException("Can not send request to Open CUE Service\n"
+ "Make sure the Open CUE Service is running.", exception)
}
}
}
}

fun createURL(function: String, parameters: Map<String, String> = emptyMap()): Url {
private fun url(path: String, parameters: Map<String, String> = emptyMap()): Url {
val httpParameters = parameters.entries.fold(ParametersBuilder()) { builder, (key, value) ->
builder.append(key, value)
builder
}
httpParameters.append("func", function)
return URLBuilder(host = host, port = port, parameters = httpParameters).path("icue").build()
return URLBuilder(host = host, port = port, parameters = httpParameters).path(path).build()
}

suspend fun requestString(function: String, parameters: Map<String, String> = emptyMap()): String {
val url = createURL(function, parameters)
val response = try {
client.get<String>(url)
} catch (e: Throwable) {
throw SdkHttpServerException("Can not send request to SDK HTTP Server: $url\n"
+ "Make sure the SDK HTTP Server is running and this url is correct.", e)
}
return translateResponse(response)
}
suspend fun currentGame(): String = client.get(url("api/sdk/game"))

suspend inline fun <reified T> requestJson(function: String, parameters: Map<String, String> = emptyMap()): T {
val url = createURL(function, parameters)
val input = try {
client.get<Input>(url)
} catch (e: Throwable) {
throw SdkHttpServerException("Can not send request to SDK HTTP Server: $url\n"
+ "Make sure the SDK HTTP Server is running and this url is correct.", e)
}
return defaultSerializer().read(typeInfo<T>(), input) as T
}
suspend fun sdkInfo(): SdkDetails = client.get(url("api/sdk/details"))

suspend fun getControl(): Boolean = client.get(url("api/sdk/control"))

private fun translateResponse(response: String): String {
return when (response) {
"CE_Success" -> "Success"
"CE_MissingPrioritiesFile" -> throw GameSdkError(
"game or profile not found, check the GameSdkEffects directory if the game and profiles exists.\n" +
"Also make sure the priorities.cfg exist and contains all the profiles.")
else -> throw GameSdkError("Unknown Error, check your input and the logs of the SDK HTTP Server")
}
}
}

suspend fun CueSdkHttpServer.currentGame(): Game = requestJson("getgame")
suspend fun setControl(value: Boolean): Boolean = client.post(url("api/sdk/control/${value}"))

suspend fun CueSdkHttpServer.listGames(): Map<String, Game> = requestJson("getallgames")
suspend fun clearAllEvents(): Unit = client.post(url("api/sdk/stop-events"))

suspend fun CueSdkHttpServer.setGame(game: String): String = requestString("setgame", mapOf("game" to game))
suspend fun deactivateAll(): Unit = client.post(url("api/sdk/deactivate-profiles"))

suspend fun CueSdkHttpServer.resetGame(game: String): String = requestString("reset", mapOf("game" to game))
suspend fun listProfiles(): List<Profile> = client.get(url("api/profiles"))

suspend fun CueSdkHttpServer.clearAllEvents(game: String): String = requestString("clearallevents", mapOf("game" to game))
suspend fun getProfile(profile: String): Profile = client.get(url("api/profiles/${profile}"))

suspend fun CueSdkHttpServer.setEvent(game: String, event: String): String = requestString("setevent", mapOf("game" to game, "event" to event))
suspend fun trigger(profile: String): Profile = client.post(url("api/profiles/${profile}/trigger"))

suspend fun CueSdkHttpServer.clearAllStates(game: String): String = requestString("clearallstates", mapOf("game" to game))
suspend fun activate(profile: String): Profile = client.post(url("api/profiles/${profile}/state/true"))

suspend fun CueSdkHttpServer.setState(game: String, state: String): String = requestString("setstate", mapOf("game" to game, "state" to state))

suspend fun CueSdkHttpServer.clearState(game: String, state: String): String = requestString("clearstate", mapOf("game" to game, "state" to state))
suspend fun deactivate(profile: String): Profile = client.post(url("api/profiles/${profile}/state/false"))
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

21 changes: 0 additions & 21 deletions src/main/kotlin/io/github/legion2/open_cue_cli/event/SetEvent.kt

This file was deleted.

Loading

0 comments on commit a23dbc2

Please sign in to comment.