Used as a replacement server for sonoff devices.
It exports a REST server to control and manage devices, as well as a WebSocket server to communicate with the device.
It supports an optional user authentication, using Google Sign-In so only authenticated users will be able to
control their devices.
Based on this blog post and inspired by simple-sonoff-server.
This project is written in TypeScript and uses Express framework. The database server is MongoDB.
Currently this only works on old sonoff firmwares (below 1.6), with no SSL certifications validation. See mirko/SonOTA#58.
Configuration is done using a dotenv file.
To start, copy the .env.example
file to .env
and change the values to match your server configuration.
For multi user support, set MULTI_USER
to true. It would mean that a valid Google SSO Bearer token must be sent with each request.
You will also need to create a Google app client id, and set it to GOOGLE_CLIENT_ID
.
Next, as the device requires an SSL connection, you will need to generate an SSL certificate.
You can generate a self-signed one easily using openssl. For example, this will generate a key for 1 year, with no passphrase protection:
openssl req -x509 -nodes -newkey rsa:4096 -keyout server.key -out server.cert -days 365
Add the file paths to the .env
configuration file.
Alternatively, if you don't need node to run on HTTPS (e.g: you are using a proxy or a load balancer for HTTPS instead), you can change the scheme to HTTP by modifying the SERVER_SCHEME
value.
Requirements:
# using npm:
npm install
npm run build
npm run serve
# using yarn:
yarn
yarn build
yarn serve
This will compile the project and start the API and the WebSocket server on the configured ports.
See Available Commands for more running options.
This step must happen after the config and install steps.
For automatic pairing, you need to install the "wifi-control" package:
npm install wifi-control
(it is intentionally not included by package.json, as it's not always needed)
- Set device in pairing mode by long-pressing the button in your sonoff device.
- Run the pairing script:
node pair-device.cli --ssid YOUR_SSID --password --YOUR_WIFI_PASSWORD
- Your device will now connect to your WiFi network and your server (it will automatically retry if server is down)
If you are having trouble, try the manual pairing method explained in this blog post.
Note that in Multi User mode, you will have to add add the device before it will be able to connect to your server. You can do that by a POST request to /devices, with the device id you found in the pairing process. See Register new device.
If MULTI_USER
is set to true in your '.env' file, a Google Sign-in Bearer token must be sent along with any request to the devices
endpoint.
This will ensure that all the devices you add will be related to your user (by google id), and only you could view and control them.
See Google Sign-In to learn how to generate a token and a Google app client id.
Once you have generated a token, send the id_token
to the server as an Authorization header with each request.
The header should look like: Authorization: Bearer ID_TOKEN_HERE
.
Note that you might have to occasionally generate a new token, as it has a limited expiry time.
To verify you are using a valid token, you can make a GET request to /users/me
.
The server should reply with your Google ID and email.
GET /devices
- List all the devices
POST /devices
- Create new device
GET /devices/ID
- Get device by id
DELETE /devices/ID
- Delete device by id
PATCH /devices/ID
- Modify device attributes
This is the structure you should expect when doing a GET/POST.
Note that when doing a PATCH, you can only update the name or the state of the device.
interface IDeviceParams {
id: string; // Device id
model: string; // Device model
version: string; // ROM version
name: string; // Device nickname
state: {
switch: 'on' | 'off';
startup: 'on' | 'off' | 'keep';
rssi: string; // WiFi signal
timers: {
enabled?: boolean;
type: 'once' | 'repeat';
at: string; // Timestamp for 'once' or cron format for 'repeat'
do: { switch: 'on' | 'off' };
}[]
};
user: { // Used only if login is required. Inserted automatically according to the Bearer token
email: string,
googleId: string,
};
}
POST /devices
{
id: '10001f56ff',
model: 'ITA-GZ1-GL',
version: '1.5.5',
name: 'Living room light'
}
PATCH /devices/10001f56ff
{
name: 'Kitchen light'
}
PATCH /devices/10001f56ff
{
state: { switch: 'on' }
}
These routes are just helper routes. you can also use the 'devices' api directly, passing an array to the 'timers' property. However, this way is cleaner and easier to add/delete/modify timers.
GET /devices/ID/timers
- List all the device's timers
POST /devices/ID
- Create new timer for the device
GET /devices/ID/timer/id
- Get device by id
DELETE /devices/ID
- Delete device by id
PATCH /devices/ID
- Modify device attributes
This is the structure you should expect when doing a GET/POST/PATCH.
interface ITimerParams {
enabled?: boolean;
type: 'once' | 'repeat';
at: string; // ISO8601 time for 'once' or cron format for 'repeat'
do: { switch: 'on' | 'off' };
}
POST /devices/10001f56ff/timers
{
enabled: true,
type: 'once',
at: '2018-10-15T19:11:00.000Z',
do: { switch: 'on' }
}
The cron format is composed of 5 fields, which look like this:
<Day_of_the_Month> <Month_of_the_Year> <Day_of_the_Week>
Notes:
- It seems the "Day of month" and "Month of year" fields must be "*".
- "Day of the Week" is zero-based and can be comma separated to mention several days (2,3 for Tuesday and Wedensday)
- Time zone is UTC
POST /devices/10001f56ff/timers
{
enabled: true,
type: 'repeat',
at: '18 0 * * 3', // every wedensday at 6:00 pm
do: { switch: 'on' }
}
PATCH /devices/10001f56ff/timers/1
{
at: '2018-10-16T02:11:00.000Z'
}
DELETE /devices/10001f56ff/timers/1
Used solely by the Sonoff device.
POST /dispatch/device
- Returns the Websocket server details (ip and port) in the format expected by the device. Used by the device when it firsts connects to the server.
Any of the following commands can run by npm run
or yarn
.
build
- Compile typescript source to JS and output to "dist" directory
build-watch
- Compile source and rebuild on changes
lint
- Lint the code using TSLint
serve
- Start server
serve-watch
- Start server and watch for any changes in 'dist' directory
start
- Builds the project, then serves it (build+serve scripts)
test
- Run tests using jest
watch
- Compiles project and start server, in watch mode
An Alexa Skill is available to control this server.