Samsung Smart Home Vacuum Cleaner Robot

I do not believe the API gives you access to the “cleaned areas.” I have only been able to find this in the SmartThings app. Samsung has a site that lists the Capabilities for all of their devices, including the POWERbot R7070, so you could double-check there in case I missed something.

You’ll need a SmartThings API Personal Token.

Basically, what I ended up doing is first creating a script that POSTs to the SmartThings API whenever a command changes. From what I can remember, the HTTP binding in openHAB do not let you set HTTP headers with a POST request (which I needed to pass the authorization token), and the HTTP util functions don’t allow it either, so I had to write this script for it:

import org.eclipse.xtext.xbase.lib.Functions
import java.net.URL
import java.nio.charset.StandardCharsets
import javax.net.ssl.HttpsURLConnection
import java.io.BufferedInputStream
import java.io.BufferedReader
import java.io.InputStreamReader

// Function called to send command to Samsung SmartThings API. This was required
// because the HTTP binding does not allow us to POST with headers.
// 
// Inspiration: https://community.openhab.org/t/icloud-device-data-integration-in-openhab/32329
val Functions$Function3<String, String, String, String> sendVacuumCommand= [ capability, command, argument |
    val String filename = "robotvacuum.rules"
    var String jsonResponse = null

    logInfo(filename, "Sending command to Samsung Robot Vacuum.")

    try {

	var commandUrl = new URL("https://api.smartthings.com/v1/devices/[device ID here]/commands");
	var HttpsURLConnection connection = commandUrl.openConnection() as HttpsURLConnection
	var request = '{"commands": [{"capability": "%1$s", "command": "%2$s", "arguments": ["%3$s"] }]}'.format(capability, command, argument)
	var byte[] postData = request.getBytes(StandardCharsets.UTF_8)

	connection.requestMethod = "POST"
    connection.setRequestProperty("Accept", "*/*")
    connection.setRequestProperty("Authorization", "Bearer [API key here]")
	connection.setRequestProperty("Content-Type", "application/json; charset=utf-8")

	connection.doOutput = true
	connection.setDoInput = true
	connection.outputStream.write(postData)

	var responseCode = connection.responseCode

	logInfo(filename, "Got HTTP response code:" + responseCode)
	logInfo(filename, "Got message: " + connection.responseMessage)

	var StringBuffer sb = new StringBuffer()
	var inputStream = new BufferedInputStream(connection.getInputStream())
	var BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))
	var String inputLine = ""

	while ((inputLine = br.readLine()) !== null) {
		sb.append(inputLine)
	}

	jsonResponse = sb.toString()

	inputStream.close()
	br.close()
	connection.disconnect()
	} catch (Exception e) {
		logError(filename, "Error sending command to Samsung Robot Vacuum: " + e.toString)
	}
	return jsonResponse
]

So far, I am using this script to send 3 commands:

sendVacuumCommand.apply("robotCleanerMovement", "setRobotCleanerMovement", [oneof: "cleaning", "homing", "charging"])
sendVacuumCommand.apply("robotCleanerCleaningMode", "setRobotCleanerCleaningMode", [oneof: "auto", "repeat", "stop"])
sendVacuumCommand.apply("robotCleanerTurboMode", "setRobotCleanerTurboMode", [oneof: "on", "off", "silence"])

To activate the vacuum, I use

// Order matters here, at least it did for my experimentation.
sendVacuumCommand.apply("robotCleanerCleaningMode", "setRobotCleanerCleaningMode", "auto")
sendVacuumCommand.apply("robotCleanerMovement", "setRobotCleanerMovement", "cleaning")

To deactivate it, I use,

sendVacuumCommand.apply("robotCleanerMovement", "setRobotCleanerMovement", "homing")

And finally, to retrieve the status of the vacuum, I can actually use the HTTP binding since GET requests allow headers. Define the item:

String RobotVacuumStatusJson " Robot Vacuum Status" { http="<[https://api.smartthings.com/v1/devices/[device ID here]/status{Authorization=Bearer [API key here]&Content-Type=application/json; charset=utf-8}:60000:REGEX((.*))]" }

This JSON needs to be parsed out to get the status of each individual capability. Whenever that JSON gets updated, I run the following in a rule:

RobotVacuumBatteryLevel.postUpdate(transform("JSONPATH", "components.main.battery[*].value", RobotVacuumStatusJson.state.toString))
RobotVacuumCleanerMovement.postUpdate(transform("JSONPATH", "components.main.robotCleanerMovement[*].value", RobotVacuumStatusJson.state.toString))
RobotVacuumCleaningMode.postUpdate(transform("JSONPATH", "components.main.robotCleanerCleaningMode[*].value", RobotVacuumStatusJson.state.toString))
RobotVacuumCleanerSuctionMode.postUpdate(transform("JSONPATH", "components.main.robotCleanerTurboMode[*].value", RobotVacuumStatusJson.state.toString))

Hope you can get this working :slight_smile: If you do manage to get a map or extend/improve this implementation, please let me know! I would be very interested.

1 Like