Skip to content

Commit

Permalink
Added option to send MQTT messages
Browse files Browse the repository at this point in the history
  • Loading branch information
Waboodoo committed Oct 5, 2022
1 parent c090f46 commit d25578e
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 2 deletions.
2 changes: 2 additions & 0 deletions HTTPShortcuts/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ dependencies {
implementation("com.github.franmontiel:PersistentCookieJar:v1.0.1")
implementation("org.conscrypt:conscrypt-android:2.5.2")

implementation("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0")

/* Scheduling */
implementation("androidx.work:work-runtime-ktx:2.7.1")
implementation("androidx.work:work-rxjava2:2.7.1")
Expand Down
6 changes: 5 additions & 1 deletion HTTPShortcuts/app/src/main/assets/docs/scripting.html
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,11 @@
</code></pre><p><a name="wol"></a></p><h3>Wake-on-LAN</h3><p>You can use the <code>wakeOnLan</code> function to send a magic packet to turn on another device on your network. The first parameter has to be the MAC-address of the device. As the optional second parameter, you can pass the network/broadcast address to be used, and as the third parameter you can define the port.</p><pre><code class="language-js">wakeOnLan('01-23-45-67-89-ab');

wakeOnLan('01-23-45-67-89-ab', '255.255.255.255', 9);
</code></pre><p><a name="send-tcp-packet"></a></p><h3>Send TCP Packet</h3><p>You can use the <code>sendTCPPacket</code> function to send a TCP packet to another device on your network. Pass the packet data as the first parameter (either as a string, <code>Uint8Array</code> or array of numbers denoting bytes), the target host's name or IP address as the second parameter and its TCP port as the third parameter.</p><pre><code class="language-js">sendTCPPacket('hello', '192.168.1.42', 1337);
</code></pre><p><a name="send-mqtt-message"></a></p><h3>Send MQTT message</h3><p>The <code>sendMqttMessages</code> function allows you to connect to an MQTT broker, send (i.e. publish) one or more messages to it, and then disconnect again. The first parameter is the URI of the server/broker, the second (optional) parameter provides options for the connection (e.g. username and password) and the third parameter is a list of all the messages that should be sent.</p><pre><code class="language-js">sendMQTTMessages(&quot;tcp://192.168.0.42:1234&quot;, {&quot;username&quot;: &quot;admin&quot;, &quot;password&quot;: &quot;1234&quot;}, [
{&quot;topic&quot;: &quot;hallway-lamp/set&quot;, &quot;payload&quot;: &quot;{\&quot;state\&quot;:\&quot;ON\&quot;}&quot;},
{&quot;topic&quot;: &quot;desk-lamp/set&quot;, &quot;payload&quot;: &quot;{\&quot;state\&quot;:\&quot;ON\&quot;, \&quot;brightness\&quot;: 255}&quot;},
]);
</code></pre><p>Please note that this does not provide any particular quality of service guarantees, and that it is not possible to subscribe to topics this way.</p><p><a name="send-tcp-packet"></a></p><h3>Send TCP Packet</h3><p>You can use the <code>sendTCPPacket</code> function to send a TCP packet to another device on your network. Pass the packet data as the first parameter (either as a string, <code>Uint8Array</code> or array of numbers denoting bytes), the target host's name or IP address as the second parameter and its TCP port as the third parameter.</p><pre><code class="language-js">sendTCPPacket('hello', '192.168.1.42', 1337);

sendTCPPacket([0x68, 0x65, 0x6C, 0x6C, 0x6F], 'example.com', 4242);
</code></pre><p><a name="send-udp-packet"></a></p><h3>Send UDP Packet</h3><p>You can use the <code>sendUDPPacket</code> function to send a UDP packet to another device on your network. Pass the packet data as the first parameter (either as a string, <code>Uint8Array</code> or array of numbers denoting bytes), the target host's name or IP address as the second parameter and its UDP port as the third parameter.</p><pre><code class="language-js">sendUDPPacket('hello', '192.168.1.42', 1337);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,17 @@ constructor(
) {
insertText("wakeOnLan(\"", "\");\n")
}
item(
R.string.action_type_send_mqtt_message,
docRef = "send-mqtt-message",
keywords = setOf("network", "client", "publish"),
) {
insertText(
"sendMQTTMessages(\"tcp://broker:port\", {\"username\": \"\", \"password\": \"\"}, [\n " +
"{\"topic\": \"\", \"payload\": \"\"},\n]};\n",
"",
)
}
item(
R.string.action_type_send_tcp_packet,
docRef = "send-tcp-packet",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ class ActionDTO(
val data: List<JSValue?> = emptyList(),
) {

val argCount: Int
get() = data.size

fun getString(index: Int): String? {
val value = data.getOrNull(index)
return when {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import ch.rmy.android.http_shortcuts.scripting.actions.types.RenameShortcutActio
import ch.rmy.android.http_shortcuts.scripting.actions.types.ScanBarcodeActionType
import ch.rmy.android.http_shortcuts.scripting.actions.types.SelectionActionType
import ch.rmy.android.http_shortcuts.scripting.actions.types.SendIntentActionType
import ch.rmy.android.http_shortcuts.scripting.actions.types.SendMQTTMessagesActionType
import ch.rmy.android.http_shortcuts.scripting.actions.types.SendTCPPacketActionType
import ch.rmy.android.http_shortcuts.scripting.actions.types.SendUDPPacketActionType
import ch.rmy.android.http_shortcuts.scripting.actions.types.SetVariableActionType
Expand Down Expand Up @@ -80,6 +81,7 @@ class ActionFactory {
ScanBarcodeActionType(),
SelectionActionType(),
SendIntentActionType(),
SendMQTTMessagesActionType(),
SendTCPPacketActionType(),
SendUDPPacketActionType(),
SetVariableActionType(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package ch.rmy.android.http_shortcuts.scripting.actions.types

import ch.rmy.android.framework.extensions.applyIfNotNull
import ch.rmy.android.framework.extensions.logException
import ch.rmy.android.http_shortcuts.exceptions.ActionException
import ch.rmy.android.http_shortcuts.scripting.ExecutionContext
import io.reactivex.Completable
import io.reactivex.schedulers.Schedulers
import org.eclipse.paho.client.mqttv3.MqttClient
import org.eclipse.paho.client.mqttv3.MqttConnectOptions
import org.eclipse.paho.client.mqttv3.MqttException
import org.eclipse.paho.client.mqttv3.MqttMessage

class SendMQTTMessagesAction(
private val serverUri: String,
private val username: String?,
private val password: String?,
private val messages: List<Message>,
) : BaseAction() {

override fun execute(executionContext: ExecutionContext): Completable =
Completable.fromAction {
try {
val client = MqttClient(serverUri, MqttClient.generateClientId(), null)
val options = MqttConnectOptions()
.apply {
isCleanSession = true
}
.applyIfNotNull(username) {
userName = it
}
.applyIfNotNull(password) {
password = it.toCharArray()
}
client.connect(options)
messages.forEach { message ->
client.publish(message.topic, MqttMessage(message.payload))
}
client.disconnect()
client.close()
} catch (e: MqttException) {
logException(e)
throw ActionException {
"Failed to send MQTT message: $e"
}
}
}
.subscribeOn(Schedulers.io())

data class Message(val topic: String, val payload: ByteArray)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package ch.rmy.android.http_shortcuts.scripting.actions.types

import ch.rmy.android.http_shortcuts.scripting.ActionAlias
import ch.rmy.android.http_shortcuts.scripting.actions.ActionDTO
import org.json.JSONObject
import org.liquidplayer.javascript.JSValue

class SendMQTTMessagesActionType : BaseActionType() {

override val type = TYPE

override fun fromDTO(actionDTO: ActionDTO): SendMQTTMessagesAction {
val options = if (actionDTO.argCount >= 3) {
actionDTO.getObject(1)
} else {
null
}
val messages = if (actionDTO.argCount >= 3) {
actionDTO.getList(2)
} else {
actionDTO.getList(1)
}
.orEmpty()
.mapNotNull {
when (it) {
is JSValue -> {
val obj = JSONObject(it.toJSON())
SendMQTTMessagesAction.Message(
topic = obj.getString("topic"),
payload = obj.getString("payload").toByteArray(),
)
}
else -> null
}
}

return SendMQTTMessagesAction(
serverUri = actionDTO.getString(0) ?: "",
username = options?.get("username") as? String,
password = options?.get("password") as? String,
messages = messages,
)
}

override fun getAlias() = ActionAlias(
functionName = FUNCTION_NAME,
functionNameAliases = setOf("sendMQTTMessage", "sendMqttMessages", "sendMqttMessage"),
parameters = 3,
)

companion object {
private const val TYPE = "send_mqtt_messages"
private const val FUNCTION_NAME = "sendMQTTMessages"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import io.reactivex.schedulers.Schedulers
import java.net.InetAddress
import java.net.Socket


class SendTCPPacketAction(
private val data: ByteArray,
private val ipAddress: String,
Expand Down
2 changes: 2 additions & 0 deletions HTTPShortcuts/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,8 @@
<string name="action_type_send_udp_packet">Send UDP Packet</string>
<!-- Shortcut Action Title: Send a TCP packet to another device on the network -->
<string name="action_type_send_tcp_packet">Send TCP Packet</string>
<!-- Shortcut Action Title: Send an MQTT message to a broker -->
<string name="action_type_send_mqtt_message">Send MQTT Message</string>
<!-- Shortcut Action Title: Abort the execution of the shortcut -->
<string name="action_type_abort_execution">Abort Execution</string>
<!-- Shortcut Action Title: Wait for a number of milliseconds before continuing with the script execution -->
Expand Down

0 comments on commit d25578e

Please sign in to comment.