The most common thing I find myself doing with my current home automation setup is turning my fan and light on and off, but it's still not as easy as it could be. To turn off my fan, I open the site on my PC and toggle it there. For my light, I have a hardware dimmer switch near my bed for turning it off at night.
I currently use a Fossil Gen 5 smartwatch, which has Google's seemingly abandoned Wear OS installed. I use this watch and OS mainly because of Google Assistant support, which is pretty good and automatically integrates into your Google account, which is quite nice.
A lot of people don't recommend Wear OS over others (like Samsung's Galaxy Watch), but I've tried this same experiment on the Galaxy Watch Active2, and I can tell you, coding anything on that watch is an absolute nightmare. At least on Wear OS you can use familiar Android tools!
I think the main problem with Wear OS is it's lack of useful software. All of it's great features are very hidden, so when a user first gets their Wear OS watch they just end up looking at the app on their phone, with like 5 buttons and a handful of watch faces, and that's it. Meanwhile, you actually need to look through the Play Store to look for watch faces and apps, and can even get them directly on the watch through the Play Store installed there. After using both Samsung's Galaxy Watch and a Wear OS watch, they really do all the same things (although Samsung's integration with their own apps is much better, like dismissing an alarm ringing on your phone by snoozing it on your watch, etc)…
But that's not the point here. The point is, I want to be able to control my room light and my fan with my watch! So, let's make it happen!
Getting the address
openHAB has a convenient REST API, and when used with the myopenhab Cloud Connector, that same API is exposed over the internet behind a secure address, so that's all we need. We need two APIs for each device: one to query the on/off status of the fan or light, and the other to set it's status.
After looking through the also very convenient API documentation generator and a bit of googling, this is the full API for those two things:
GET or PUT https://my%40email.com:[email protected]/rest/items/ITEM_NAME/state
The GET call returns the text ON, OFF, or NULL for the fan, since it uses a Switch type item (null if it hasn't been set, so we can consider that as OFF). For the light it uses a Dimmer type item, so it returns a number from 0 to 100 instead.
To modify the items, we just use PUT instead of GET, and the body is the value we want it to be (ON/OFF for the fan, 0-100 for the light).
Create the watch app
The Fossil Gen 5 smartwatch has two user-programmable buttons. I have one of them mapped to Keep Notes, which is amazingly useful. The other one opens Spotify, but since I don't use that much I'm going to use that second button to launch my home control app. This means if I want to toggle my fan or light, I just have to press the bottom button on my watch and then tap the icon on the screen for watch or fan. Simple!
First, I need to create the watch app though. Easily done through Android Studio. I'll use the Blank Activity template. I'll leave "Pair with Empty Phone app" off, since I have no idea what that means.
Connect the watch
First I had to enable developer mode by tapping the Build number, enable ADB debugging over WiFi, and connect to the watch. This is amazingly similar to Android development.
After that, the watch appeared in Android Studio, I could hit the Run button, and the app appeared on the watch. That was easy.
Setup the UI layout
Ok, so now the next part is to create the UI layout. Android Studio has a visual drag-n-drop editor, so this part is simple. We just need two buttons, which are TOGGLE FAN and TOGGLE LIGHT.
Add the code
Now we just need to call our APIs that we discovered above in response to tapping on the buttons. To make things easier, I'm going to add one of my favorite Kotlin libraries for promises (Kovenant) as a dependency.
class MainActivity : WearableActivity() { // Username val username = "[email protected]" val password = "myopenhab_password" // Called on startup override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Enables Always-on setAmbientEnabled() } fun onLightPress(view : View) { toggleItem("Josh_Bedside_Lamp", "0", "100") } fun onFanPress(view : View) { toggleItem("Josh_Fan", "ON", "OFF") } // Do an API call to openHAB fun doCall(method : String, endpoint : String, body : String = "") = task { // Open connection Log.i("Network", "$method $endpoint") val conn = URL("https://home.myopenhab.org$endpoint").openConnection() as HttpURLConnection conn.requestMethod = method // Add auth header val authHeader = "Basic " + String(Base64.getEncoder().encode("$username:$password".toByteArray()), Charset.forName("UTF-8")) conn.setRequestProperty("Authorization", authHeader) // Add body if necessary if (body.isNotEmpty()) { conn.setRequestProperty("Content-Type", "text/plain") conn.doOutput = true conn.outputStream.write(body.toByteArray()) } // Check response code if (conn.responseCode == 401) throw Exception("Username or password is incorrect.") else if (conn.responseCode < 200 || conn.responseCode >= 300) throw Exception("Returned error code ${conn.responseCode}") // Read response val buffer = ByteArray(1024) val len = conn.inputStream.read(buffer) return@task String(buffer, 0, len) } // Toggle an item fun toggleItem(itemName : String, offState : String, onState : String) = task {} bind { // Get current state doCall("GET", "/rest/items/$itemName/state") } bind { // Toggle state val newState = if (it == onState) offState else onState Log.i("Home", "item=$itemName, oldState=$it, newState=$newState") doCall("PUT", "/rest/items/$itemName/state", newState) } success { // Done Log.i("Home", "Item toggled for $itemName") } fail { // Failed! Log.w("Home", "Failed to toggle state for $itemName: ${it.localizedMessage}") it.printStackTrace() } }
Done! And now I can just bind the app to the bottom button on my watch, and that's it!