# 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 kind | Scheduled properties | | --- | --- | | Vehicle | `shouldCharge` | | Charger | `shouldCharge` | | HVAC | `holdType`, `mode`, `coolSetpoint`, and `heatSetpoint` | Only one controller can be active at the same time: Smart Charging and Schedules cannot be active on the same vehicle simultaneously. If you define a Schedule on a car with Smart Charging activated, Smart Charging will be disabled. Similarly, if you activate Smart Charging on a vehicle with an active Schedule set, the Schedule will be disabled. ## Anatomy of a Schedule ### Vehicles and chargers A schedule policy for vehicles and chargers begins by choosing a default value for `shouldCharge`, typically `false`. ```json { "defaultShouldCharge": false } ``` The policy then specifies 0 or more **rules** that override the value of `shouldCharge`. Each rule should contain a **[filter](https://developers.enode.com/#supported-filters)** that specifies the situations when the rule should apply. ```json { "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] } ] } ``` ### HVACs 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. ```json { "defaultTargetState": { "holdType": "SCHEDULED" } } ``` Like with vehicles/chargers, you specify 0 or more **rules** that override the target state. Each rule should contain a **[filter](https://developers.enode.com/#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. ```json { "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. ### Supported 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. ```json { "shouldCharge": true, "hourMinute": { "from": "01:00", "to": "09:00" }, "weekdays": [5, 6] } ``` | Key | Description | | | | | | | | | | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 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. | **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. | | fromTimestamp | UTC timestamp (inclusive) from which this rule should apply. | | | | | | | | | | | toTimestamp | UTC timestamp (exclusive) until which this rule should apply. | | | | | | | | | | | weekdays | An array of weekdays to which this rule should apply. A weekday starts with 0 for Monday and ends with 6 for Sunday. | | | | | | | | | | ## Creating Schedules You should [create](https://developers.enode.com/api/reference#createSchedule) or [update](https://developers.enode.com/api/reference#updateSchedule) 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. ## Example Vehicle/Charger Policies ### Always charge when possible This is the typical default behavior of EVs and is equivalent to a null policy: ```json { "defaultShouldCharge": true, "rules": [] } ``` ### Prevent any charging until a future time ```json { "defaultShouldCharge": true, "rules": [ { "shouldCharge": false, "toTimestamp": "2020-01-07T16:21:76Z" } ] } ``` ### Only charge between 01:00 and 06:00 each day ```json { "defaultShouldCharge": false, "rules": [ { "shouldCharge": true, "hourMinute": { "from": "01:00", "to": "06:00" } } ] } ``` ### Only charge between 01:00 and 06:00 each work day, and any time on weekends ```json { "defaultShouldCharge": false, "rules": [ { "shouldCharge": true, "hourMinute": { "from": "01:00", "to": "06:00" } }, { "shouldCharge": true, "weekdays": [5, 6] } ] } ``` ### Set 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). ```json { "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] } ] } ``` ## Example HVAC Policies ### Target 20° during the day, and 15° at night ```json { "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" } } ] } ``` ### Target 20° during the day, 15° at night, and 7° until January 7, 2020 ```json { "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", } }] } ``` ### Target 15° between 23:00 and 06:00 each work day and until 08:00 on weekends ```json { "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] } ] } ``` ## Schedule behavior and edge cases ### External 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](https://developers.enode.com/#reading-the-current-status-of-a-schedule)'s state will be `MISALIGNED`. ### Clock times can cross midnight ```json { "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 } } ] } ``` ### A rule is applied only during the intersection of all filters With the following configuration: ```json { "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) ### When rules overlap, the latter one has precedence ```json { "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. ### Receiving the Schedule Status object This object can be obtained at any time via the [Get Schedule Status](https://developers.enode.com/api/reference#getScheduleStatus) request. It is also sent via webhook every time any of its values change, using the event [user:schedule:execution-updated](https://developers.enode.com/docs/webhooks) ### Anatomy of a Schedule Status EV example: This example walks through an EV Schedule Status to explain key concepts. The same principles would apply to HVAC Schedule Statuses, which have a different schema. 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`. ```json { "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 | | --- | --- | | `ALIGNED` | The target's scheduled property is in the expected state. | | `MISALIGNED` | The 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. | | `PENDING` | The target's scheduled property is currently transitioning to the expected state. | | `INACTIVE:OVERRIDDEN` | Schedule is inactive because a Smart Override has been created. Learn about creating overrides in our [API Reference](https://developers.enode.com/api/reference#vehicleCreateSmartOverride). | | `INACTIVE:DISABLED` | Schedule is inactive because its isEnabled property is `false`. | | `INACTIVE:AWAY` | Schedule 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:INCAPABLE` | Schedule is inactive because its target is incapable of receiving charging actions. | The `upcomingTransitions` field returns up to 2 upcoming transitions of the `shouldCharge` field.