Added basic MQTT functionality
This commit is contained in:
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
|
||||
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
||||
@@ -20,10 +20,26 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
Properties mqtt_props = new Properties()
|
||||
mqtt_props.load(new File('local.properties').newInputStream())
|
||||
|
||||
buildTypes {
|
||||
|
||||
debug {
|
||||
buildConfigField("String", "MQTT_URL", mqtt_props["MQTT_URL"])
|
||||
buildConfigField("Integer", "MQTT_PORT", mqtt_props["MQTT_PORT"])
|
||||
buildConfigField("String", "MQTT_USER", mqtt_props["MQTT_USER"])
|
||||
buildConfigField("String", "MQTT_PASSWORD", mqtt_props["MQTT_PASSWORD"])
|
||||
}
|
||||
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
|
||||
buildConfigField("String", "MQTT_URL", mqtt_props["MQTT_URL"])
|
||||
buildConfigField("Int", "MQTT_PORT", mqtt_props["MQTT_PORT"])
|
||||
buildConfigField("String", "MQTT_USER", mqtt_props["MQTT_USER"])
|
||||
buildConfigField("String", "MQTT_PASSWORD", mqtt_props["MQTT_PASSWORD"])
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
@@ -35,6 +51,7 @@ android {
|
||||
}
|
||||
buildFeatures {
|
||||
compose true
|
||||
buildConfig true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion '1.3.2'
|
||||
@@ -48,15 +65,21 @@ android {
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.8.0'
|
||||
implementation 'androidx.core:core-ktx:1.10.1'
|
||||
implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0')
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
|
||||
implementation 'androidx.activity:activity-compose:1.5.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
||||
implementation 'androidx.activity:activity-compose:1.7.2'
|
||||
implementation platform('androidx.compose:compose-bom:2022.10.00')
|
||||
implementation 'androidx.compose.ui:ui'
|
||||
implementation 'androidx.compose.ui:ui-graphics'
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
|
||||
implementation 'io.insert-koin:koin-android:3.4.2'
|
||||
implementation 'io.insert-koin:koin-androidx-compose:3.4.5'
|
||||
|
||||
implementation 'org.eclipse.paho:org.eclipse.paho.mqttv5.client:1.2.5'
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application
|
||||
android:name=".LEDMatrixControllerApplication"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
@@ -12,10 +15,10 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.LEDMatrixController"
|
||||
tools:targetApi="31">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.LEDMatrixController">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.cameronc.dev.ledmatrixcontroller
|
||||
|
||||
import android.app.Application
|
||||
import android.net.TrafficStats
|
||||
import android.os.StrictMode
|
||||
import org.eclipse.paho.mqttv5.client.MqttClient
|
||||
import org.eclipse.paho.mqttv5.client.MqttConnectionOptionsBuilder
|
||||
import org.eclipse.paho.mqttv5.client.persist.MemoryPersistence
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.module.dsl.factoryOf
|
||||
import org.koin.dsl.module
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class LEDMatrixControllerApplication : Application() {
|
||||
private val matrixController: MatrixController by inject()
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
// StrictMode.enableDefaults()
|
||||
|
||||
startKoin {
|
||||
androidContext(this@LEDMatrixControllerApplication)
|
||||
modules(AppModule)
|
||||
}
|
||||
|
||||
// Init to avoid slow reaction on first use
|
||||
thread {
|
||||
matrixController
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val AppModule = module {
|
||||
factoryOf(::MatrixController)
|
||||
|
||||
single {
|
||||
MqttClient(BuildConfig.MQTT_URL, "LED-Matrix-Controller-Android", MemoryPersistence()).apply {
|
||||
connect(get())
|
||||
}
|
||||
}
|
||||
|
||||
single {
|
||||
MqttConnectionOptionsBuilder()
|
||||
.username(BuildConfig.MQTT_USER)
|
||||
.password(BuildConfig.MQTT_PASSWORD.toByteArray())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -3,41 +3,138 @@ package com.cameronc.dev.ledmatrixcontroller
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.SoftwareKeyboardController
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.cameronc.dev.ledmatrixcontroller.ui.theme.LEDMatrixControllerTheme
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
private val weatherMatrixController: MatrixController by inject()
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
LEDMatrixControllerTheme {
|
||||
// A surface container using the 'background' color from the theme
|
||||
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
||||
Greeting("Android")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val keyboardController: SoftwareKeyboardController = LocalSoftwareKeyboardController.current!!
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
@Composable
|
||||
fun Greeting(name: String, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = "Hello $name!",
|
||||
modifier = modifier
|
||||
Column(
|
||||
Modifier
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
listOf(
|
||||
Color.Black,
|
||||
MaterialTheme.colorScheme.primary,
|
||||
Color.Black
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
.wrapContentSize()
|
||||
.padding(16.dp),
|
||||
Arrangement.SpaceBetween,
|
||||
Alignment.CenterHorizontally,
|
||||
) {
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun GreetingPreview() {
|
||||
LEDMatrixControllerTheme {
|
||||
Greeting("Android")
|
||||
MatrixModeButton(Modifier.padding(vertical = 8.dp), text = "Weather", onClick = {
|
||||
weatherMatrixController.weatherMode()
|
||||
})
|
||||
|
||||
MatrixModeButton(Modifier.padding(vertical = 8.dp), text = "Time", onClick = {
|
||||
weatherMatrixController.timeMode()
|
||||
})
|
||||
|
||||
MatrixModeButton(
|
||||
Modifier.padding(vertical = 8.dp),
|
||||
text = "Rainbow",
|
||||
onClick = { weatherMatrixController.rainbow() })
|
||||
|
||||
MatrixModeButton(Modifier.padding(vertical = 8.dp), text = "Off", onClick = { weatherMatrixController.off() })
|
||||
|
||||
Row(modifier = Modifier.padding(vertical = 8.dp), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
var brightness by remember { mutableStateOf(weatherMatrixController.brightness.value ?: "10") }
|
||||
TextField(
|
||||
modifier = Modifier.defaultMinSize(minWidth = 128.dp),
|
||||
value = brightness,
|
||||
label = { Text("Brightness") },
|
||||
onValueChange = { brightness = it },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Send),
|
||||
keyboardActions = KeyboardActions(onSend = {
|
||||
weatherMatrixController.updateBrightness(brightness)
|
||||
focusManager.clearFocus()
|
||||
keyboardController.hide()
|
||||
})
|
||||
)
|
||||
|
||||
Button(
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
onClick = {
|
||||
weatherMatrixController.updateBrightness(brightness)
|
||||
focusManager.clearFocus()
|
||||
keyboardController.hide()
|
||||
}) {
|
||||
Text("Set")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MatrixModeButton(modifier: Modifier = Modifier, text: String, onClick: () -> Unit = {}) {
|
||||
|
||||
ElevatedCard(
|
||||
modifier = modifier
|
||||
.defaultMinSize(minWidth = 128.dp, minHeight = 64.dp)
|
||||
.height(IntrinsicSize.Min)
|
||||
.width(IntrinsicSize.Max),
|
||||
onClick = onClick,
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(text = text, fontSize = 18.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.cameronc.dev.ledmatrixcontroller
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.eclipse.paho.mqttv5.client.MqttClient
|
||||
import org.eclipse.paho.mqttv5.client.MqttConnectionOptions
|
||||
import org.eclipse.paho.mqttv5.common.MqttMessage
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class MatrixController(
|
||||
private val mqttClient: MqttClient,
|
||||
private val mqttConnectionOptions: MqttConnectionOptions
|
||||
) {
|
||||
companion object {
|
||||
private const val MODE_TOPIC = "/weather/mode"
|
||||
private val OFF = "0".toMqttMessage()
|
||||
private val WEATHER = "2".toMqttMessage()
|
||||
private val TIME = "3".toMqttMessage()
|
||||
private val RAINBOW = "4".toMqttMessage()
|
||||
|
||||
private const val BRIGHTNESS = "/weather/brightness"
|
||||
}
|
||||
|
||||
val brightness: LiveData<String> get() = brightnessLiveData
|
||||
private val brightnessLiveData: MutableLiveData<String> = MutableLiveData()
|
||||
|
||||
private val threadPool: ExecutorService = Executors.newFixedThreadPool(1)
|
||||
|
||||
init {
|
||||
/*
|
||||
mqttClient.subscribe(
|
||||
arrayOf("/weather/brightness"),
|
||||
IntArray(1) { 1 },
|
||||
arrayOf<IMqttMessageListener>(object : IMqttMessageListener {
|
||||
override fun messageArrived(topic: String?, message: MqttMessage?) {
|
||||
brightnessLiveData.value = message.toString()
|
||||
}
|
||||
})
|
||||
)
|
||||
*/
|
||||
}
|
||||
|
||||
fun weatherMode() {
|
||||
queueMessage(MODE_TOPIC, WEATHER)
|
||||
}
|
||||
|
||||
private fun queueMessage(topic: String, message: MqttMessage) {
|
||||
threadPool.execute {
|
||||
if (mqttClient.isConnected) {
|
||||
mqttClient.publish(topic, message)
|
||||
} else {
|
||||
mqttClient.connect(mqttConnectionOptions)
|
||||
mqttClient.publish(topic, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun timeMode() {
|
||||
queueMessage(MODE_TOPIC, TIME)
|
||||
}
|
||||
|
||||
fun off() {
|
||||
queueMessage(MODE_TOPIC, OFF)
|
||||
}
|
||||
|
||||
fun rainbow() {
|
||||
queueMessage(MODE_TOPIC, RAINBOW)
|
||||
}
|
||||
|
||||
fun updateBrightness(brightness: String) {
|
||||
queueMessage(BRIGHTNESS, brightness.toMqttMessage())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun String.toMqttMessage(): MqttMessage {
|
||||
return MqttMessage(this.toByteArray())
|
||||
}
|
||||
Reference in New Issue
Block a user