test?
This commit is contained in:
parent
af503c04f8
commit
fc1a8e0367
9 changed files with 135 additions and 60 deletions
|
|
@ -1,6 +1,34 @@
|
||||||
<component name="InspectionProjectProfileManager">
|
<component name="InspectionProjectProfileManager">
|
||||||
<profile version="1.0">
|
<profile version="1.0">
|
||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
<option name="previewFile" value="true" />
|
<option name="previewFile" value="true" />
|
||||||
|
|
|
||||||
24
.vscode/launch.json
vendored
24
.vscode/launch.json
vendored
|
|
@ -1,24 +0,0 @@
|
||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "android",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Android launch",
|
|
||||||
"appSrcRoot": "${workspaceRoot}/app/src/main",
|
|
||||||
"apkFile": "${workspaceRoot}/app/build/outputs/apk/debug/app-debug.apk",
|
|
||||||
"adbPort": 5037
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "android",
|
|
||||||
"request": "attach",
|
|
||||||
"name": "Android attach",
|
|
||||||
"appSrcRoot": "${workspaceRoot}/app/src/main",
|
|
||||||
"adbPort": 5037,
|
|
||||||
"processId": "${command:PickAndroidProcess}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -76,6 +76,7 @@ dependencies {
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
implementation(libs.androidx.datastore.preferences)
|
implementation(libs.androidx.datastore.preferences)
|
||||||
implementation(libs.ktor.client.okhttp)
|
implementation(libs.ktor.client.okhttp)
|
||||||
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@ class MainActivity : ComponentActivity() {
|
||||||
val cf = obtainClient(ctx)
|
val cf = obtainClient(ctx)
|
||||||
runBlocking {
|
runBlocking {
|
||||||
homeClient = cf.first()
|
homeClient = cf.first()
|
||||||
|
homeClient!!.wsClient.connect()
|
||||||
}
|
}
|
||||||
homeClient!!.connect(rememberCoroutineScope())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val onLoginSubmit: suspend (String, String) -> Unit = { url: String, token: String ->
|
val onLoginSubmit: suspend (String, String) -> Unit = { url: String, token: String ->
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,12 @@ import io.ktor.client.call.body
|
||||||
import io.ktor.client.engine.okhttp.OkHttp
|
import io.ktor.client.engine.okhttp.OkHttp
|
||||||
import io.ktor.client.plugins.DefaultRequest
|
import io.ktor.client.plugins.DefaultRequest
|
||||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||||
|
import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
|
||||||
import io.ktor.client.plugins.websocket.WebSockets
|
import io.ktor.client.plugins.websocket.WebSockets
|
||||||
import io.ktor.client.plugins.websocket.receiveDeserialized
|
import io.ktor.client.plugins.websocket.receiveDeserialized
|
||||||
import io.ktor.client.plugins.websocket.sendSerialized
|
import io.ktor.client.plugins.websocket.sendSerialized
|
||||||
import io.ktor.client.plugins.websocket.webSocket
|
import io.ktor.client.plugins.websocket.webSocket
|
||||||
|
import io.ktor.client.plugins.websocket.webSocketSession
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
import io.ktor.client.request.header
|
import io.ktor.client.request.header
|
||||||
import io.ktor.client.request.post
|
import io.ktor.client.request.post
|
||||||
|
|
@ -19,23 +21,33 @@ import io.ktor.http.HttpHeaders
|
||||||
import io.ktor.http.contentType
|
import io.ktor.http.contentType
|
||||||
import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter
|
import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter
|
||||||
import io.ktor.serialization.kotlinx.json.json
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
|
import io.ktor.utils.io.InternalAPI
|
||||||
|
import io.ktor.websocket.DefaultWebSocketSession
|
||||||
|
import io.ktor.websocket.FrameType
|
||||||
|
import io.ktor.websocket.send
|
||||||
|
import io.ktor.websocket.serialization.receiveDeserializedBase
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.util.EventListener
|
||||||
|
|
||||||
class HomeClient(private val baseUrl: String, private val accessToken: String) {
|
class HomeClient(private val baseUrl: String, private val accessToken: String) {
|
||||||
|
val jsonConfig = Json { // isLenient = true
|
||||||
|
// useArrayPolymorphism = true
|
||||||
|
allowStructuredMapKeys = true
|
||||||
|
ignoreUnknownKeys = true // explicitNulls = false
|
||||||
|
// namingStrategy = JsonNamingStrategy.SnakeCase
|
||||||
|
encodeDefaults = true // IMPORTANT
|
||||||
|
}
|
||||||
|
|
||||||
private val client: HttpClient = HttpClient(OkHttp) {
|
private val client: HttpClient = HttpClient(OkHttp) {
|
||||||
install(WebSockets) {
|
install(WebSockets) {
|
||||||
contentConverter = KotlinxWebsocketSerializationConverter(Json)
|
contentConverter = KotlinxWebsocketSerializationConverter(Json)
|
||||||
}
|
}
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
json(Json { // isLenient = true
|
json(jsonConfig)
|
||||||
// useArrayPolymorphism = true
|
|
||||||
allowStructuredMapKeys = true
|
|
||||||
ignoreUnknownKeys = true // explicitNulls = false
|
|
||||||
// namingStrategy = JsonNamingStrategy.SnakeCase
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
install(DefaultRequest) {
|
install(DefaultRequest) {
|
||||||
|
|
@ -43,6 +55,8 @@ class HomeClient(private val baseUrl: String, private val accessToken: String) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val wsClient = WSClient(GlobalScope, baseUrl, accessToken, client)
|
||||||
|
|
||||||
suspend fun apiStatus(): HttpResponse {
|
suspend fun apiStatus(): HttpResponse {
|
||||||
val res = client.get("$baseUrl/api/")
|
val res = client.get("$baseUrl/api/")
|
||||||
return res
|
return res
|
||||||
|
|
@ -76,27 +90,6 @@ class HomeClient(private val baseUrl: String, private val accessToken: String) {
|
||||||
}
|
}
|
||||||
val res = updateService(entityId, "switch", service)
|
val res = updateService(entityId, "switch", service)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun connect(coroutineScope: CoroutineScope) {
|
|
||||||
GlobalScope.launch {
|
|
||||||
client.webSocket(
|
|
||||||
"${baseUrl.replace("http", "ws")}/api/websocket"
|
|
||||||
) {
|
|
||||||
var iteratingNumber = 0
|
|
||||||
var isReady = false
|
|
||||||
while (true) {
|
|
||||||
val data = receiveDeserialized<WebSocketIncoming>()
|
|
||||||
if (data.type == "auth_required") sendSerialized(WSAuth("auth", accessToken))
|
|
||||||
if (data.type == "auth_ok") {
|
|
||||||
isReady = true
|
|
||||||
sendSerialized(WSSubscribe(iteratingNumber, "state_changed"))
|
|
||||||
}
|
|
||||||
println(data)
|
|
||||||
iteratingNumber++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val switchServices = listOf("turn_off", "turn_on", "toggle")
|
val switchServices = listOf("turn_off", "turn_on", "toggle")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.coffeeco.homenative.data
|
package dev.coffeeco.homenative.data
|
||||||
|
|
||||||
|
import kotlinx.serialization.EncodeDefault
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
|
@ -21,7 +22,8 @@ data class WSAuth(
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
abstract class WebSocketMessage {
|
sealed class WebSocketMessage {
|
||||||
|
@EncodeDefault
|
||||||
@SerialName("type")
|
@SerialName("type")
|
||||||
abstract val type: String
|
abstract val type: String
|
||||||
|
|
||||||
|
|
@ -30,11 +32,16 @@ abstract class WebSocketMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class WSSubscribe (
|
data class WSSubscribe(
|
||||||
|
@SerialName("id")
|
||||||
override val id: Int,
|
override val id: Int,
|
||||||
|
// @EncodeDefault
|
||||||
|
// @SerialName("type")
|
||||||
|
// val type: String = "subscribe_events",
|
||||||
@SerialName("event_type")
|
@SerialName("event_type")
|
||||||
val eventType: String?
|
val eventType: String? = null,
|
||||||
): WebSocketMessage() {
|
) : WebSocketMessage() {
|
||||||
override val type: String
|
@EncodeDefault
|
||||||
get() = "subscribe_events"
|
@SerialName("type")
|
||||||
|
override val type: String = "subscribe_events"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
64
app/src/main/java/dev/coffeeco/homenative/data/WSClient.kt
Normal file
64
app/src/main/java/dev/coffeeco/homenative/data/WSClient.kt
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
package dev.coffeeco.homenative.data
|
||||||
|
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.plugins.websocket.receiveDeserialized
|
||||||
|
import io.ktor.client.plugins.websocket.sendSerialized
|
||||||
|
import io.ktor.client.plugins.websocket.webSocket
|
||||||
|
import io.ktor.websocket.DefaultWebSocketSession
|
||||||
|
import io.ktor.websocket.send
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
class WSClient(
|
||||||
|
private val coroutineScope: CoroutineScope,
|
||||||
|
private val baseUrl: String,
|
||||||
|
private val accessToken: String,
|
||||||
|
private val httpClient: HttpClient,
|
||||||
|
) {
|
||||||
|
private val wsUrl = "${baseUrl.replace("http", "ws")}/api/websocket"
|
||||||
|
private var session: DefaultWebSocketSession? = null
|
||||||
|
private var iteratingNumber = 0
|
||||||
|
|
||||||
|
suspend fun connect() {
|
||||||
|
GlobalScope.launch {
|
||||||
|
httpClient.webSocket(wsUrl) {
|
||||||
|
session = this
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
val data = receiveDeserialized<WebSocketIncoming>()
|
||||||
|
when (data.type) {
|
||||||
|
"auth_required" -> {
|
||||||
|
println("WebSocket: Authentication required")
|
||||||
|
sendSerialized(WSAuth("auth", accessToken))
|
||||||
|
}
|
||||||
|
|
||||||
|
"auth_ok" -> {
|
||||||
|
println("WebSocket: Authentication OK")
|
||||||
|
val data1 = WSSubscribe(iteratingNumber)
|
||||||
|
val serialized = Json.encodeToString(data1)
|
||||||
|
// send(serialized)
|
||||||
|
sendSerialized(data1)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
println("WebSocket: Misc data received: $data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iteratingNumber++
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("WS Exception: $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun send_dunnschiss() {
|
||||||
|
while (session == null) {
|
||||||
|
}
|
||||||
|
session!!.send("DÜNNSCHISS")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,9 +31,10 @@ fun ControllingCard(homeClient: HomeClient, state: StatesResItem) {
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(150.dp)
|
.height(150.dp)
|
||||||
.clickable {
|
.clickable {
|
||||||
println("hi")
|
println("Button clicked!")
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
homeClient.updateSwitch(state.entityId, "toggle")
|
homeClient.updateSwitch(state.entityId, "toggle")
|
||||||
|
// homeClient.wsClient.send_dunnschiss()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
colors = CardDefaults.cardColors(
|
colors = CardDefaults.cardColors(
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ navigationCompose = "2.7.7"
|
||||||
ktorClient = "3.0.0-beta-2"
|
ktorClient = "3.0.0-beta-2"
|
||||||
kotlinxSerializationJson = "1.7.2"
|
kotlinxSerializationJson = "1.7.2"
|
||||||
datastorePreferences = "1.1.1"
|
datastorePreferences = "1.1.1"
|
||||||
|
hilt = "1.2.0"
|
||||||
|
dagger = "2.51.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
|
|
@ -32,15 +34,18 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3"
|
||||||
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" }
|
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" }
|
||||||
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
|
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
|
||||||
ktor-client-websockets = { group = "io.ktor", name = "ktor-client-websockets", version.ref = "ktorClient" }
|
ktor-client-websockets = { group = "io.ktor", name = "ktor-client-websockets", version.ref = "ktorClient" }
|
||||||
ktor-client-android = { group = "io.ktor", name = "ktor-client-android", version.ref = "ktorClient" }
|
|
||||||
ktor-client-serialization = { group = "io.ktor", name = "ktor-client-serialization", version.ref = "ktorClient" }
|
ktor-client-serialization = { group = "io.ktor", name = "ktor-client-serialization", version.ref = "ktorClient" }
|
||||||
ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktorClient" }
|
ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktorClient" }
|
||||||
ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktorClient" }
|
ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktorClient" }
|
||||||
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
||||||
androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastorePreferences" }
|
androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastorePreferences" }
|
||||||
ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktorClient" }
|
ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktorClient" }
|
||||||
|
hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hilt" }
|
||||||
|
hilt-navcompose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hilt" }
|
||||||
|
hilt-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "hilt" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
dagger = { id = "com.google.dagger.hilt.android", version.ref = "dagger" }
|
||||||
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue