Enode Developers

Scheduling

A Schedule policy targets a specific device (vehicle/charger/HVAC) at a particular Location. It describes whether the target should be charging at any given time, or in the case of HVACs, what the holdType, mode, coolSetpoint or heatSetpoint should be. These target states are specified as shouldCharge and targetState, respectively.

Hardware kindScheduled properties
VehicleshouldCharge
ChargershouldCharge
HVACholdType, mode, coolSetpoint, and heatSetpoint

Copy linkConstructing a Schedule

A vehicle schedule policy begins by choosing a default value for shouldCharge, typically false.

Sample

{
  "defaultShouldCharge": false
}

HVAC schedules first target either a holdType of SCHEDULED or PERMANENT. A thermostat in a PERMANENT hold type is configured to keep the current settings for mode, heatSetpoint, and coolSetpoint (referred to as "mode settings" from this point forward) until the next command is sent to the device. A thermostat in a SCHEDULED hold type follows a program set in the OEM's app. This program is not accessible through Enode, but the changes it makes to the mode settings are.

Sample

{
  "defaultTargetState": {
    "holdType": "SCHEDULED"
  }
}

The policy then specifies 0 or more rules that override the value of shouldCharge within certain timespans and situations:

Sample

{
  "defaultShouldCharge": false,
  "rules": [
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "01:00",
        "to": "04:00"
      },
      "weekdays": [0, 1, 2, 3, 4]
    },
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "01:00",
        "to": "09:00"
      },
      "weekdays": [5, 6]
    }
  ]
}

An HVAC policy may wish to override the thermostat's program during certain periods of the day. This could be achieved by sending the following schedule to Enode.

Sample

{
  "defaultTargetState": {
    "holdType": "SCHEDULED"
  },
  "rules": [
    {
      "from": "01:00",
      "to": "04:00",
      "targetState": {
        "holdType": "PERMANENT",
        "mode": "HEAT",
        "heatSetpoint": 20
      }
    }
  ]
}

This instructs the HVAC to follow the program on the device, except for hours 1 through 4. During this time, the thermostat will heat to at least 20 degrees.

Each rule specifies a value for shouldCharge and at least one filter. A filter restricts the times and situations to which the rule applies.

Sample

{
  "shouldCharge": true,
  "hourMinute": {
    "from": "01:00",
    "to": "09:00"
  },
  "weekdays": [5, 6]
}

Copy linkSupported Filters

KeyDescription
hourMinutePair of clock times {"from": "xx:xx", "to": "xx:xx"} within which this rule should apply each day. from is inclusive, to is exclusive. to always resolves to a timestamp after from, and thus may span across midnight and fall on the next day. If an EV is at a Location, the time will be interpreted in the time zone configured for that location. For HVACs and Chargers: if there are locations associated with the schedule or user, the time will be interpreted in each of the time zones for applicable locations; if there are no locations, the time will be interpreted in UTC.
fromTimestampUTC timestamp (inclusive) from which this rule should apply.
toTimestampUTC timestamp (exclusive) until which this rule should apply.
weekdaysAn array of weekdays to which this rule should apply. A weekday starts with 0 for Monday and ends with 6 for Sunday.

Copy linkExample Vehicle/Charger Policies

Copy linkAlways charge when possible

This is the typical default behavior of EVs and is equivalent to a null policy:

Sample

{
  "defaultShouldCharge": true,
  "rules": []
}

Copy linkPrevent any charging until a future time

Sample

{
  "defaultShouldCharge": true,
  "rules": [
    {
      "shouldCharge": false,
      "toTimestamp": "2020-01-07T16:21:76Z"
    }
  ]
}

Copy linkOnly charge between 01:00 and 06:00 each day

Sample

{
  "defaultShouldCharge": false,
  "rules": [
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "01:00",
        "to": "06:00"
      }
    }
  ]
}

Copy linkOnly charge between 01:00 and 06:00 each work day, and any time on weekends

Sample

{
  "defaultShouldCharge": false,
  "rules": [
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "01:00",
        "to": "06:00"
      }
    },
    {
      "shouldCharge": true,
      "weekdays": [5, 6]
    }
  ]
}

Copy linkSet a custom charging window for every day of the week

Sample

{
  "defaultShouldCharge": false,
  "rules": [
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "01:00",
        "to": "06:00"
      },
      "weekdays": [0]
    },
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "22:00",
        "to": "04:00"
      },
      "weekdays": [1]
    },
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "02:00",
        "to": "09:00"
      },
      "weekdays": [2]
    },
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "01:00",
        "to": "06:00"
      },
      "weekdays": [3]
    },
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "01:00",
        "to": "06:00"
      },
      "weekdays": [4]
    },

    // charge anytime on saturday
    {
      "shouldCharge": true,
      "weekdays": [5]
    },

    // two windows on Sunday
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "01:00",
        "to": "06:00"
      },
      "weekdays": [6]
    },
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "12:00",
        "to": "15:00"
      },
      "weekdays": [6]
    }
  ]
}

Copy linkExample HVAC Policies

Copy linkTarget 20° during the day, and 15° at night

Sample

{
  "defaultTargetState": {
    "mode": "AUTO",
    "heatSetpoint": 18,
    "coolSetpoint": 22,
    "holdType": "PERMANENT",
  },
  "rules": [{
    "targetState": {
      "mode": "AUTO",
      "heatSetpoint": 14,
      "coolSetpoint": 16,
      "holdType": "PERMANENT",
    },
    "hourMinute": {
      "from": "23:00",
      "to": "06:00"
    }
  }
}

Copy linkTarget 20° during the day, 15° at night, and 7° until January 7, 2020

Sample

{
  "defaultTargetState": {
    "mode": "COOL",
    "coolSetpoint": 20,
    "holdType": "PERMANENT",
  },
  "rules": [{
    "targetState": {
      "mode": "HEAT",
      "heatSetpoint": 15,
      "holdType": "PERMANENT",
    },
    "hourMinute": {
      "from": "23:00",
      "to": "06:00"
    },
    {
      "targetState": {
        "mode": "AUTO",
        "heatSetpoint": 6,
        "coolSetpoint": 8,
        "holdType": "PERMANENT",
      },
      "toTimestamp": "2020-01-07T00:00:00",
    }
  }
}

Copy linkTarget 15° between 23:00 and 06:00 each work day and until 08:00 on weekends

Sample

{
  "defaultTargetState": {
    "mode": "AUTO",
    "heatSetpoint": 18,
    "coolSetpoint": 22,
    "holdType": "PERMANENT"
  },
  "rules": [
    {
      "targetState": {
        "mode": "AUTO",
        "heatSetpoint": 14,
        "coolSetpoint": 16,
        "holdType": "PERMANENT"
      },
      "hourMinute": {
        "from": "23:00",
        "to": "06:00"
      }
    },
    {
      "targetState": {
        "mode": "AUTO",
        "heatSetpoint": 19,
        "coolSetpoint": 21,
        "holdType": "PERMANENT"
      },
      "hourMinute": {
        "from": "06:00",
        "to": "08:00"
      },
      "weekdays": [5, 6]
    }
  ]
}

Copy linkBehavior across day/rule boundaries

Copy linkClock times can cross midnight

Sample

{
  "defaultShouldCharge": false,
  "rules": [
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "22:00", // If this is 22:00 on a Monday...
        "to": "06:00" // ...then this is 06:00 on a Tuesday
      }
    }
  ]
}

Copy linkA rule is applied only during the intersection of all filters

With the following configuration:

Sample

{
  "defaultShouldCharge": false,
  "rules": [
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "22:00",
        "to": "06:00"
      },
      "weekdays": [0]
    }
  ]
}

This rule only applies from 22:00 - 05:59 on the clock. Additionally, it only applies on Mondays. Thus:

  • Monday 00:00-05:59 - shouldCharge: true (inside 22:00-06:00, inside Monday)
  • Monday 21:00 - shouldCharge: false (outside 22:00-06:00)
  • Monday 22:00 - shouldCharge: true (inside 22:00-06:00 and inside Monday)
  • Tuesday 01:00 - shouldCharge: false (inside 22:00-06:00, but outside Monday)

Copy linkWhen rules overlap, the latter one has precedence

Sample

{
  "defaultShouldCharge": false,
  "rules": [
    {
      "shouldCharge": true,
      "hourMinute": {
        "from": "02:00",
        "to": "10:00"
      }
    },
    {
      "shouldCharge": false,
      "hourMinute": {
        "from": "08:00",
        "to": "09:00"
      }
    }
  ]
}
  • 00:00 - shouldCharge: false
  • 02:00 - shouldCharge: true
  • 08:00 - shouldCharge: false (inside 02:00-10:00, but also inside 08:00-09:00, which was defined later in the array)
  • 09:00 - shouldCharge: true (inside 02:00-10:00 only)

Reading the current status of a Schedule

Every Schedule has a status object that describes the current status of its execution.

Copy linkReceiving the Schedule Status object

This object can be obtained at any time via the Get Schedule StatusAPI request.

It is also sent via webhook every time any of its values change, using the event user:schedule:execution-updated

Copy linkSchema of the Schedule Status object

Remember that Schedules are all about scheduling a shouldCharge value, which is just one part of whether a target is expected to be charged. For example, if a Vehicle is scheduled to be charging but is out driving on the road, it will not be charging. This is not a failure or problem from the scheduling point of view.

The scheduler combines the following properties to decide whether a target is expected to be charging:

  • needsCharge - Does the target want to charge?
  • isPluggedIn - Is the target connected to allow charging?
  • shouldCharge - Do the schedule rules dictate that the target should be charging?

If all are true, the target is expected to be charging. If any are false, the target is not expected to be charging. Therefore, the execution engine always strives to keep the target's isCharging value equal to the expected value. This state where this is achieved is called ALIGNED. If it cannot be achieved, that is the state MISALIGNED.

Sample

{
  "scheduleId": "8d90101b-3f2f-462a-bbb4-1ed320d33bbe",
  "changedAt": "2019-08-24T14:15:22Z",
  "state": "ALIGNED",
  "isCharging": false,
  "isChargingExpected": false,
  "isChargingExpectedParts": {
    "needsCharge": true,
    "isPluggedIn": true,
    "shouldCharge": false
  },
  "upcomingTransitions": [
    {
      "at": "2020-04-08T02:00:00Z",
      "shouldCharge": true
    },
    {
      "at": "2020-04-08T06:00:00Z",
      "shouldCharge": false
    }
  ]
}

The state field always has one of the following values:

KeyDescription
ALIGNEDThe target's scheduled property is in the expected state.
MISALIGNEDThe target's scheduled property is stuck in an unexpected state.
PENDINGThe target's scheduled property is currently transitioning to the expected state.
INACTIVE:OVERRIDDENSchedule is inactive because an External Start has been created. Read more at the vehicleAPI or chargerAPI External Start APIs.
INACTIVE:DISABLEDSchedule is inactive because its isEnabled property is false.
INACTIVE:AWAYSchedule is inactive because its target is away from the configured Location. Only applicable to schedules targeting vehicles because we don't want to apply an optimization schedule to a location where the user does not pay for electricity. Schedules targeting HVACs or Chargers won't enter this state.
INACTIVE:INCAPABLESchedule is inactive because its target is incapable of receiving charging actions.

The upcomingTransitions field returns up to 2 upcoming transitions of the shouldCharge field.

Was this article helpful?