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">
|
||||
<profile version="1.0">
|
||||
<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">
|
||||
<option name="composableFile" 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.androidx.datastore.preferences)
|
||||
implementation(libs.ktor.client.okhttp)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ class MainActivity : ComponentActivity() {
|
|||
val cf = obtainClient(ctx)
|
||||
runBlocking {
|
||||
homeClient = cf.first()
|
||||
homeClient!!.wsClient.connect()
|
||||
}
|
||||
homeClient!!.connect(rememberCoroutineScope())
|
||||
}
|
||||
|
||||
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.plugins.DefaultRequest
|
||||
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.receiveDeserialized
|
||||
import io.ktor.client.plugins.websocket.sendSerialized
|
||||
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.header
|
||||
import io.ktor.client.request.post
|
||||
|
|
@ -19,23 +21,33 @@ import io.ktor.http.HttpHeaders
|
|||
import io.ktor.http.contentType
|
||||
import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter
|
||||
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.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.util.EventListener
|
||||
|
||||
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) {
|
||||
install(WebSockets) {
|
||||
contentConverter = KotlinxWebsocketSerializationConverter(Json)
|
||||
}
|
||||
install(ContentNegotiation) {
|
||||
json(Json { // isLenient = true
|
||||
// useArrayPolymorphism = true
|
||||
allowStructuredMapKeys = true
|
||||
ignoreUnknownKeys = true // explicitNulls = false
|
||||
// namingStrategy = JsonNamingStrategy.SnakeCase
|
||||
})
|
||||
json(jsonConfig)
|
||||
}
|
||||
|
||||
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 {
|
||||
val res = client.get("$baseUrl/api/")
|
||||
return res
|
||||
|
|
@ -76,27 +90,6 @@ class HomeClient(private val baseUrl: String, private val accessToken: String) {
|
|||
}
|
||||
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")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package dev.coffeeco.homenative.data
|
||||
|
||||
import kotlinx.serialization.EncodeDefault
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
|
@ -21,7 +22,8 @@ data class WSAuth(
|
|||
)
|
||||
|
||||
@Serializable
|
||||
abstract class WebSocketMessage {
|
||||
sealed class WebSocketMessage {
|
||||
@EncodeDefault
|
||||
@SerialName("type")
|
||||
abstract val type: String
|
||||
|
||||
|
|
@ -30,11 +32,16 @@ abstract class WebSocketMessage {
|
|||
}
|
||||
|
||||
@Serializable
|
||||
data class WSSubscribe (
|
||||
data class WSSubscribe(
|
||||
@SerialName("id")
|
||||
override val id: Int,
|
||||
// @EncodeDefault
|
||||
// @SerialName("type")
|
||||
// val type: String = "subscribe_events",
|
||||
@SerialName("event_type")
|
||||
val eventType: String?
|
||||
): WebSocketMessage() {
|
||||
override val type: String
|
||||
get() = "subscribe_events"
|
||||
val eventType: String? = null,
|
||||
) : WebSocketMessage() {
|
||||
@EncodeDefault
|
||||
@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
|
||||
.height(150.dp)
|
||||
.clickable {
|
||||
println("hi")
|
||||
println("Button clicked!")
|
||||
coroutineScope.launch {
|
||||
homeClient.updateSwitch(state.entityId, "toggle")
|
||||
// homeClient.wsClient.send_dunnschiss()
|
||||
}
|
||||
},
|
||||
colors = CardDefaults.cardColors(
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ navigationCompose = "2.7.7"
|
|||
ktorClient = "3.0.0-beta-2"
|
||||
kotlinxSerializationJson = "1.7.2"
|
||||
datastorePreferences = "1.1.1"
|
||||
hilt = "1.2.0"
|
||||
dagger = "2.51.1"
|
||||
|
||||
[libraries]
|
||||
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-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-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-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" }
|
||||
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" }
|
||||
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]
|
||||
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" }
|
||||
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
|
|
|
|||
Loading…
Reference in a new issue