Controlling Xiaomi vacuum cleaners with Node.js API

Introduction

If you have a Xiaomi vacuum cleaner and want to control it using Node.js API, then you've come to the right place.

We will start by setting up the environment and installing the required packages. Then, we will show you how to get the configuration information for your vacuum cleaner using Consul.

Consul is a service discovery and configuration tool that can be used to store and retrieve key-value pairs, making it an ideal tool for storing configuration information for your applications. In this case, we are going to use Consul to store the configuration information for our Xiaomi vacuum cleaner, such as the IP address and authentication token. However, this is an option. We will also explain how to achieve our goal without employing Consul.

This article is based on this project: https://github.com/TheWitchHouse/vacuum-controller

Setting Up the Environment

Before we can start writing our code to control a Xiaomi vacuum cleaner using Node.js, we need to set up our development environment. This involves installing Node.js and the necessary packages, as well as configuring Consul.

Install Node.js

Node.js is a JavaScript runtime that allows us to run JavaScript code outside of a web browser. To install Node.js, you can follow the instructions on the official Node.js website: https://nodejs.org/.

Once you have installed Node.js, you can verify that it's working by opening a terminal window and running the following command:

node --version

Install Required Packages

We also need to install the required packages for our Node.js project. These packages are listed in the package.json file, which is included in the project repository.

To install the packages, navigate to the project directory in a terminal window and run the following command:

npm install

This will install all of the packages listed in package.json.

Configure Consul (optional)

To install Consul, you can follow these steps:

  • Download the Consul binary from the official website (https://www.consul.io/)

  • Extract the binary from the downloaded archive to a directory of your choice.

  • Run the Consul binary with the agent command, specifying the dev flag to start a development instance of Consul - consul agent -dev

We will use this agent to store and retrieve our vacuum cleaner configuration. However, using Consul is an option. If you opt not to use it, please refer to the getVacuum(), where we describe a bypass of this option.

The Code

Now that we have our development environment set up, we can start writing the code.

Create a server

Our first step is to create an HTTP server that will handle incoming requests to start and stop the vacuum cleaner. We'll be using the express package to create our server. Express is a popular Node.js web application framework for building APIs and web applications. It provides a set of features and tools for building server-side applications, including routing, middleware, templates, and more. It allows creating scalable web apps quickly utilising a flexible and minimalist approach to web development.

Here's the code to create a basic HTTP server:

const express = require('express')

const listen_port = 8080

const server = express()
server.listen(listen_port, function () {
  console.log('Listening on port ' + listen_port)
})

This code creates an instance of the express module and sets up a basic HTTP server that listens on port 8080. We can run this code by executing the following command in the terminal:

node index.js

This should start the server and output a message that it's listening on port 8080.

Define Routes

Next, we need to define routes for our server that will handle incoming requests to start and stop the vacuum cleaner.

Here's the code to define our /start and /stop routes:

server.get('/start', async function (request, response) {
  console.log('Starting the vacuum...')
  // code to start the vacuum goes here
  response.send('OK')
})

server.get('/stop', async function (request, response) {
  console.log('Stopping the vacuum...')
  // code to stop the vacuum goes here
  response.send('OK')
})

These routes use the get method of our server instance to handle incoming GET requests to the /start and /stop endpoints. You can paste http://127.0.0.1:8080/start and http://127.0.0.1:8080/stop in your browser to check how they work.

withVacuum()

To control our Xiaomi vacuum cleaner, we'll be using the miio package, which provides a Node.js API for interacting with Xiaomi smart devices. We'll define a function called withVacuum that takes a function as an argument and provides a connected miio device instance to that function.

Here's the code for the withVacuum function:

const withVacuum = async function(job) {
  var vacuum = await getVacuum()
  try {
    await job(vacuum)
  }
  finally {
    console.log("Disconnecting...")
    vacuum.destroy()
    console.log("Disconnected...")
  }
}

This function uses the getVacuum (which we'll define in the next section) to connect to the Xiaomi vacuum cleaner and create a miio device instance. It then executes the function passed as an argument, providing the vacuum instance as a parameter. After the function completes, the withVacuum function disconnects from the vacuum cleaner.

getVacuum()

Before we can connect to the Xiaomi vacuum cleaner, we need to retrieve its configuration from Consul. We'll define a function called getVacuum that retrieves the IP address and authentication token for the vacuum cleaner from Consul and uses them to connect to the device.

Getting a device token is a quest by itself although with fairly good documentation - refer to instruction here: https://github.com/aholstenson/miio/blob/master/docs/management.md#getting-the-token-of-a-device

const consul_module = require('consul')
const consul_host = process.argv[2]
const consul = consul_module( { host: consul_host, promisify: true } )

const getVacuum = async function() {
  logger.info("Loading configuration...")
  const host = (await consul.kv.get('the-witch-house/config/vacuum/host')).Value
  const token = (await consul.kv.get('the-witch-house/config/vacuum/token')).Value

  logger.info("Connecting to vacuum " + host + "; token is " + token)
  const vacuum = await miio.device({ address: host, token: token })
  logger.info("Connected to ", vacuum)
  return vacuum
}

The consul library provides a way to interact with Consul's HTTP API using promises and callbacks, and allows us to perform various tasks such as service registration and discovery, key/value storage, and health checking.

Here's the link to the official consul library documentation: https://www.npmjs.com/package/consul

You could just hardcode the host and token here if you skipped setting up Consul stage:

const getVacuum = async function() {
  const host = "192.168.1.100" // Replace with the IP address of your vacuum cleaner
  const token = "abcdef0123456789" // Replace with your authentication token
  logger.info("Connecting to vacuum " + host + "; token is " + token)
  const vacuum = await miio.device({ address: host, token: token })
  logger.info("Connected to ", vacuum)
  return vacuum
}

Let's test

Now that we have written our code, it's time to test it out and make sure that it works as expected.

To test the code, we'll need to run the index.js file and send some HTTP requests to the server. Here's how you can do it:

  1. Open a terminal window and navigate to the directory where the index.js file is located.

  2. Run the following command to start the server:

node index.js CONSUL_HOST

Make sure to replace CONSUL_HOST with the appropriate value.

  1. Once the server is running, open a web browser and navigate to http://localhost:8080/start. This will send a request to the server to start the vacuum cleaner.

  2. Check the logs to make sure that the vacuum cleaner has been started successfully. You should see a message similar to the following:

info: Starting the vacuum...
info: Started the cleaning successfully…

Your vacuum cleaner should also start working.

  1. Next, navigate to http://localhost:8080/stop. This will send a request to the server to stop the vacuum cleaner.

  2. Check the logs again to make sure that the vacuum cleaner has been stopped successfully. You should see a message similar to the following

info: Stopping the vacuum...
info: Stopped the cleaning successfully…

Congratulations, you have successfully controlled your Xiaomi vacuum cleaner using Node.js API.

Conclusion

In this article, we have seen how to control Xiaomi vacuum cleaners using Node.js API. We started by setting up the environment and installing the required packages. Then, we wrote the code to start and stop the vacuum cleaner.

We used Consul to store the configuration information for the vacuum cleaner.

Finally, we tested the code by sending HTTP requests to the server and checking the logs. If the logs showed the expected messages, we knew that our code was working correctly.

Controlling Xiaomi vacuum cleaners using Node.js API is a great way to automate your cleaning tasks and make your life easier. By using this code as a starting point, you can easily customise it to suit your needs and add new features.