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 linkAnatomy of a Schedule

Copy linkVehicles and chargers

A schedule policy for vehicles and chargers begins by choosing a default value for shouldCharge, typically false.

{
  "defaultShouldCharge": false
}

The policy then specifies 0 or more rules that override the value of shouldCharge. Each rule should contain a (filter)[#supported-filters] that specifies the situations when the rule should apply.

{
  "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]
    }
  ]
}

Copy linkHVACs

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.

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

Like with vehicles/chargers, you specify 0 or more rules that override the target state. Each rule should contain a (filter)[#supported-filters] that specifies the situations when the rule should apply.

For example, 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.

{
  "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.

Copy linkSupported Filters

Enode Schedules support a common set of rule filters across all asset types. A filter restricts the times and situations to which the rule applies.

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

hourMinute

Pair of clock times {"from": "xx:xx", "to": "xx:xx"} defining the daily time window within which this rule should apply. from is inclusive, to is exclusive. The to time always resolves to a timestamp after from, and thus may span midnight and fall on the next day.

Time Zone Interpretation for Different Assets:

  • Vehicles:

    • If the vehicle is at a Location (as determined by its current GPS coordinates matching a known Location), the hourMinute times are interpreted in the time zone configured for that Location.
    • If the vehicle is not at any known Location, the schedule becomes inactive (INACTIVE:AWAY), and the rules are not evaluated. This prevents actions from being sent when the vehicle is away from home (e.g., at public chargers).
    • Setting locationId to a specific Location ID will make the schedule active only when the vehicle is at that particular Location.
    • Setting locationId to null will make the schedule active whenever the vehicle is at any known Location. If the vehicle is away from all known Locations, the schedule becomes inactive.
  • Chargers and HVACs:

    • Since these assets are stationary, their schedules are always active at their associated Locations.
    • If there are Locations associated with the schedule or user, the hourMinute times are interpreted in the time zone(s) of those Locations.
    • If there are no Locations associated, the hourMinute times are 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 linkCreating Schedules

You should createAPI or updateAPI a Schedule in our API as soon as you know what the schedule will be.

Edge cases:

  • If you create or update a Schedule a short time before a rule is set to apply, Enode will still do our best to get the target into the desired state for the period specified in the rule, though there may be a delay.
    • Consider a Schedule that was created at 9:58:00 that contained a rule to start charging a vehicle at 10:00. The vehicle may actually start charging at 10:05.
  • If you create or update a Schedule after the start period of a rule, Enode will still do our best to get the target into the desired state for the period specified in the rule, though there may be a delay.
    • Consider a Schedule that was created at 10:30:00 that contained a rule to start charging a vehicle at 10:00. The vehicle may actually start charging at 10:35.

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:

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

Copy linkPrevent any charging until a future time

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

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

{
  "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

{
  "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

This example shows one charging window for every day of the week, except for Saturday (which can charge at any time) and Sunday (which has two charging windows).

{
  "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]
    },
    {
      "shouldCharge": true,
      "weekdays": [5]
    },
    {
      "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

{
  "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

{
  "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

{
  "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 linkSchedule behavior and edge cases

Copy linkExternal interference

Your users may interfere with a Schedule: for instance, if your Schedule stopped a vehicle from charging, your user may open their EV app and restart charging manually. Whenever this happens, Enode will try to get the target back into its expected state. If we are not able to, the Schedule Status's state will be MISALIGNED.

Copy linkClock times can cross midnight

{
  "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:

{
  "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

{
  "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)

Copy linkReading 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 linkAnatomy of a Schedule Status

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.

The Schedule's state is MISALIGNED whenever the expected state cannot be achieved. This can happen if the vendor APIs are down or have some other error. When a Schedule is MISALIGNED, Enode will not attempt to re-align the schedule until the next expected state transition, as specified in upcomingTransitions.

{
  "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:

Key Description
ALIGNEDThe target's scheduled property is in the expected state.
MISALIGNEDThe target's scheduled property is stuck in an unexpected state. Enode will not make any further attempts to get the target into the expected state. Look at the Schedule Status's upcomingTransitions list to understand the next opportunity for the schedule to become aligned.
PENDINGThe target's scheduled property is currently transitioning to the expected state.
INACTIVE:OVERRIDDENSchedule is inactive because a Smart Override has been created. Learn about creating overrides in our API ReferenceAPI.
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?