Scheduling
Internet of Things (IoT) primarily relates to devices connected to Internet and naturally, exchanging data (like telemetry and commands) over the Internet. However, some automation, wherein the device itself acts independently is desired for many applications. Eg. Turning on lights at say 6pm and turning them off at say 11pm. To support such automations, we have now added the Scheduling functionality to ESP RainMaker.
Note: You also need to set appropriate timezone for scheduling to work correctly. If the specified timezone uses Day Light Saving (DST), that too is handled by the node. Check this section for more information.
In line with the rest of the ESP RainMaker features, scheduling has been implemented completely on the node side in the form of a "service". The cloud backend just acts as a conduit between the node and the clients (like Phone apps).
The entire complexity of the scheduling feature is hidden beneath a simple C API and also abstracted out by the phone apps. Even then, let's first understand the specifications.
Specifications
The scheduling service consist of a single parameter which is an array of objects. Enabling the feature adds the following service in the node config:
{
"name": "Schedule",
"params": [{
"bounds": {
"max": 5
},
"data_type": "array",
"name": "Schedules",
"properties": [
"read",
"write"
],
"type": "esp.param.schedules"
}],
"type": "esp.service.schedule"
}
The schedules parameter is an empty array by default. The "max" bound indicates the maximum number of schedules a node can have.
Adding a new schedule
A new schedule can be added by passing a value like this in set-param
{
"Schedule": {
"Schedules": [{
"name": "Evening",
"id": "8D36",
"operation": "add",
"triggers": [{
"d": 31,
"m": 1110
}],
"action": {
"Light": {
"power": true
}
}
}]
}
}
- name: A user friendly name for the schedule
- id: A unique ID for the schedule. (This needs to be unique only for the given user not universally unique). This should be generated by the client while adding a schedule, and then used for any further operations. A shorter id is desirable.
- operation: The operation to be performed. Possible values are add, remove, edit, enable and disable.
- triggers: The day and time at which the schedule should trigger.
- d: Bitmap for days. LSB is Monday. [ N/A | Sunday | Saturday | Friday | Tuesday | Wednesday | Tuesday | Monday ]. Eg. 0b00011111 (31) means all weekdays. A value of zero means trigger just once.
- m: Minutes since midnight. Eg. 6:30pm = 18 * 60 + 30 = 1110.
- action: The actual action to be performed at the given time. The value of this object will be same as what you pass while setting the params. Eg. For turning on Light, it will be
"Light": {"power": true}
. - info (Optional): Additional info or description as per the clients' requirements.
- flags (Optional): General purpose flags which can be used as per the clients' requirements.
Once the schedule is added successfully, a get on params will the report a similar value like below:
{
"Schedule": {
"Schedules": [{
"name": "Evening",
"id": "8D36",
"enabled": true,
"triggers": [{
"d": 31,
"m": 1110
}],
"action": {
"Light": {
"power": true
}
}
}]
}
}
As you can see, the response includes an "enabled" key, which indicates that the schedule is now enabled.
Note: While adding or updating schedules, a single entry is sent in the schedules array. However, when queried, the node will return information of all the schedules in the array.
Other operations
Edit
Value passed for editing the schedule will be similar to that passed for adding. The ID should match an existing schedule and the "operation" value will be "edit". You can either send the entire object or only the elements that have changed (name, trigger or action). However the objects in these keys cannot be partial. Eg. If current action is "action":{"Light": {"power": true, "brightness":90}}
, and you need to change brightness to 100, you should pass "action":{"Light": {"power": true, "brightness":100}}
and not just "action":{"Light": {"brightness":100}}
Remove
For removing an existing schedule you just need to pass its ID and the operation.
Eg.
{
"Schedule": {
"Schedules": [{
"id": "8D36",
"operation": "remove"
}]
}
}
Enable/Disable
The payload is very similar to the remove operation. This is useful when you have to temporarily disable a schedule instead of removing it. Eg.
{
"Schedule": {
"Schedules": [{
"id": "8D36",
"operation": "disable"
}]
}
}
Passing operation as "enable" will re-enable the schedule.
Note: A one time schedule automatically gets disabled after it has executed.
Usage
Firmware
The ESP RainMaker exposes a single API to enable scheduling
esp_err_t esp_rmaker_schedule_enable(void);
This API should be called after
esp_rmaker_node_init()
but beforeesp_rmaker_start()
.
The maximum number of schedules supported can be configured by using the CONFIG_ESP_RMAKER_SCHEDULING_MAX_SCHEDULES
config option (idf.py menuconfig -> ESP RainMaker Config -> ESP RainMaker Scheduling -> Maximum Schedules
). However, you may have to also increase CONFIG_ESP_RMAKER_MAX_PARAM_DATA_SIZE
to support larger number of schedules, as the schedules payload size will increase.
Before using scheduling feature, as mentioned earlier, it is recommended to set the timezone first. Please check here for information regarding timezones. The simplest way is to just set the CONFIG_ESP_RMAKER_DEF_TIMEZONE
config option.
Eg. CONFIG_ESP_RMAKER_DEF_TIMEZONE="Asia/Shanghai"
You can find this at idf.py menuconfig -> ESP RainMaker Config -> Default Timezone
Scheduling has now been enabled by default in switch, led_light, fan and multi_device examples.
Phone apps
The phone applications provide a very simple user interface for various scheduling operations. Please update your phone apps, enable scheduling in the firmware, and get started.
Schedules across nodes
As we have seen, scheduling information is maintained by the nodes, and not by the cloud backend. However, it is a common requirement to have multiple nodes as part of a single schedule. For such schedules, the client uses the same schedule ID across multiple nodes. Even while querying, it looks for common schedule IDs across all the nodes and shows them together.
Managing Day Light Savings (DST)
Normally, when Day Light Saving starts, the clocks are moved forward by 1 hour and when it ends, they are moved back by 1 hour. Let's consider America/Los_Angeles timezone for example. As per Wikipedia:
In the U.S., daylight saving time starts on the second Sunday in March and ends on the first Sunday in November, with the time changes taking place at 2:00 a.m. local time. With a mnemonic word play referring to seasons, clocks "spring forward, fall back"—that is, in springtime the clocks are moved forward from 2:00 a.m. to 3:00 a.m. and in fall they are moved back from 2:00 a.m. to 1:00 a.m.
On all the days apart from the ones at which the DST switch takes place, the schedule will trigger at the correct scheduled time, since the node automatically adjusts its clock as per the DST info for the specified timezone. However, what happens during the DST switch is something to be considered. Let's now see what happens to schedules during the switch:
Scheduled Time | DST Begins (spring forward from 2:00 to 3:00 | DST ends (move back from 2:00 to 1:00) |
---|---|---|
12:59 am and earlier | No change | No change |
1:00 am to 1:59am | No change | Triggered only once before the switch. Not triggered again the second time after the switch |
2:00 am to 2:59am | Delayed by 1hr. Triggered from 3:00 to 3:59am instead, on the day of switch | No change |
3:00 am and later | No change | No change |