Schedule Store
The ScheduleStore manages scheduling operations and schedule management within your application. Schedules allow you to automate device actions based on time-based triggers, enabling devices to perform actions at specific times or intervals.
Overview
The ScheduleStore provides:
- Schedule list management and synchronization
- Schedule creation and updates
- Schedule enable/disable functionality
- Automatic transformation from node configurations
- Multi-node schedule combination
- Out-of-sync detection and handling
- Automatic synchronization with NodeStore
- Reactive state management
How Schedules Work in CDF
Schedule Transformation from Node Configurations
Schedules in CDF are automatically transformed from node configurations. Each node's nodeConfig.services contains a schedules service, which includes a list of schedules. The ScheduleStore automatically extracts and combines these schedules.
No manual transformation needed - When using CDF, schedules are automatically transformed from:
node.nodeConfig.services[schedules].params[schedules].value
Identifying Schedule Service and Parameters
The ScheduleStore identifies schedules using specific service and parameter types:
-
Service Type:
esp.service.schedules(ESPRM_SERVICE_SCHEDULES)- Identifies the schedules service within a node's services array
- Found in
node.nodeConfig.services[]whereservice.type === "esp.service.schedules"
-
Parameter Type:
esp.param.schedules(ESPRM_PARAM_SCHEDULES)- Identifies the schedules parameter within the schedules service
- Found in
service.params[]whereparam.type === "esp.param.schedules" - Contains the actual schedule list in
param.value[]
Example Node Configuration Structure:
node.nodeConfig.services = [
{
type: "esp.service.schedules", // Service type identifier
params: [
{
type: "esp.param.schedules", // Parameter type identifier
value: [
// Array of schedules
{
id: "schedule1",
name: "Morning Routine",
info: "Wake up schedule",
action: { light: { power: true, brightness: 80 } },
triggers: [{ m: 420, d: 127 }], // 7:00 AM daily
enabled: true,
},
{
id: "schedule2",
name: "Evening Routine",
action: { fan: { power: false } },
triggers: [{ m: 1200, d: 127 }], // 8:00 PM daily
enabled: false,
},
],
},
],
},
];
Multi-Node Schedule Combination
Schedules with the same ID across multiple nodes are automatically combined into a single schedule. This allows you to manage schedules that span multiple devices.
Example:
- Node 1 has schedule with ID
schedule1→{ id: 'schedule1', nodes: ['node1'], action: { node1: {...} } } - Node 2 has schedule with ID
schedule1→ Combined with Node 1's schedule - Node 3 has schedule with ID
schedule1→ Combined with Node 1 and Node 2's schedule - Result:
{ id: 'schedule1', nodes: ['node1', 'node2', 'node3'], action: { node1: {...}, node2: {...}, node3: {...} } }
When multiple nodes share the same schedule ID, they are merged into one schedule object. You can check the nodes array property of a schedule to see which nodes it spans:
const schedule = scheduleStore.getSchedule("schedule1");
console.log("Schedule spans nodes:", schedule.nodes); // ['node1', 'node2', 'node3']
console.log("Actions per node:", schedule.action);
// {
// node1: { light: { power: true } },
// node2: { fan: { power: false } },
// node3: { switch: { power: true } }
// }
Out-of-Sync Detection
The ScheduleStore automatically detects when schedules are out of sync across nodes. If a schedule with the same ID exists on multiple nodes but has different properties (name, enabled, triggers, validity, flags, info), the store tracks this using outOfSyncMeta.
const schedule = scheduleStore.getSchedule("schedule1");
if (schedule.outOfSyncMeta && Object.keys(schedule.outOfSyncMeta).length > 0) {
console.log("Schedule is out of sync on nodes:", schedule.outOfSyncMeta);
// Example: { node2: { name: 'Different Name', enabled: false } }
}
Schedule Updates and Node Targeting
When you update a schedule, the changes are applied based on the operation:
- Update all nodes: If you update a schedule that spans multiple nodes, the update is applied to all nodes that contain that schedule
- Update specific node: If you update a schedule for a specific device/node, only that node's schedule configuration is updated
The ScheduleStore automatically handles node updates through the updateNodeSchedule function, which updates the node's schedule configuration in the NodeStore.
Accessing the Store
Access the ScheduleStore through the CDF instance:
const scheduleStore = espCDF.scheduleStore;
Properties
| Property | Type | Description |
|---|---|---|
| scheduleList | Schedule[] (computed) | A computed array of all schedules in the store. This is automatically updated when schedules are added or removed. |
| schedulesByID | { [key: string]: Schedule } | A dictionary of schedules indexed by their schedule ID for quick lookup. |
Example Usage
// Get all schedules
const schedules = scheduleStore.scheduleList;
console.log("Total schedules:", schedules.length);
schedules.forEach((schedule) => {
console.log("Schedule:", schedule.name, schedule.id);
});
// Get schedule by ID
const schedule = scheduleStore.schedulesByID["schedule123"];
if (schedule) {
console.log("Schedule found:", schedule.name);
}
ScheduleStore Methods
Sync Schedules From Nodes
To synchronize schedules from specified nodes, use the syncSchedulesFromNodes() method. This method:
- Extracts schedule configurations from each node's
nodeConfig.services[schedules] - Transforms schedules from node configurations into Schedule instances
- Combines schedules with the same ID across multiple nodes into a single schedule
- Detects and tracks out-of-sync configurations
- Updates the ScheduleStore with the synchronized schedules
How it works:
- Schedules are identified by finding the service with
type === "esp.service.schedules"(ESPRM_SERVICE_SCHEDULES) - Within that service, the parameter with
type === "esp.param.schedules"(ESPRM_PARAM_SCHEDULES) is located - Schedules are read from
param.value[]array - If multiple nodes have a schedule with the same ID, they are merged:
- The
nodesarray contains all node IDs that have this schedule - The
actionobject contains actions per node:{ node1: {...}, node2: {...} } - The
devicesCountis the total count across all nodes
- The
- Out-of-sync detection compares properties (name, enabled, triggers, validity, flags, info) across nodes
/*
- nodeIds: Array of node IDs to sync schedules from
*/
try {
// Sync schedules from specific nodes
await scheduleStore.syncSchedulesFromNodes(["node1", "node2", "node3"]);
// If node1, node2, and node3 all have schedule with ID "schedule1",
// it will be combined into one schedule:
const schedule = scheduleStore.getSchedule("schedule1");
console.log("Schedule spans nodes:", schedule.nodes); // ['node1', 'node2', 'node3']
console.log("Actions per node:", schedule.action);
// Check for out-of-sync status
if (schedule.outOfSyncMeta) {
console.log("Out of sync on nodes:", schedule.outOfSyncMeta);
}
} catch (error) {
console.error("Error syncing schedules:", error);
}
Create Schedule
To create a new schedule in the store, use the createSchedule() method. The schedule is automatically made observable and interceptors are set up.
/*
- scheduleData: Schedule data object containing:
- id (optional): Schedule ID. If not provided, a timestamp-based ID is generated
- name: Schedule name
- info (optional): Schedule description/info
- nodes: Array of node IDs included in the schedule
- action: Device actions per node
- triggers: Array of trigger objects defining when the schedule should execute
- validity (optional): Validity period with start and end timestamps
- flags (optional): Schedule configuration flags
- enabled (optional): Whether the schedule is enabled (default: false)
*/
try {
const schedule = await scheduleStore.createSchedule({
name: "Morning Routine",
info: "Wake up schedule",
nodes: ["bedroom_light"],
action: {
bedroom_light: {
light: {
power: true,
brightness: 100,
},
},
},
triggers: [
{
m: 420, // 7:00 AM in minutes since midnight
d: 127, // Daily (binary: 01111111 = all days)
},
],
enabled: true,
});
console.log("Schedule created:", schedule);
} catch (error) {
console.error("Error creating schedule:", error);
}
Get Schedule
To retrieve a schedule by its ID, use the getSchedule() method.
/*
- scheduleId: The ID of the schedule to retrieve
*/
const schedule = scheduleStore.getSchedule("schedule123");
if (schedule) {
console.log("Schedule details:", schedule);
} else {
console.log("Schedule not found");
}
Set Schedule List
To set the entire schedule list, replacing all existing schedules, use the setScheduleList() method. Each schedule is made observable and interceptors are set up.
/*
- schedules: Array of schedules to set
*/
scheduleStore.setScheduleList(schedules);
Add Schedule
To add a single schedule to the store and make it observable, use the addSchedule() method.
/*
- schedule: The schedule to add
*/
const schedule = new Schedule(scheduleData, espCDF);
const observableSchedule = scheduleStore.addSchedule(schedule);
Update Schedule by ID
To update a schedule by ID without making it observable, use the updateScheduleByID() method.
/*
- id: The schedule ID
- schedule: The schedule object to set
*/
scheduleStore.updateScheduleByID("schedule123", updatedSchedule);
Delete Schedules
To remove multiple schedules from the store by their IDs, use the deleteSchedules() method.
/*
- ids: Array of schedule IDs to delete
*/
scheduleStore.deleteSchedules(["schedule1", "schedule2"]);
Enable Schedule
To enable a schedule, allowing it to execute according to its triggers, use the enableSchedule() method.
/*
- scheduleId: The ID of the schedule to enable
*/
try {
await scheduleStore.enableSchedule("schedule123");
console.log("Schedule enabled");
} catch (error) {
console.error("Error enabling schedule:", error);
}
Disable Schedule
To disable a schedule, preventing it from executing, use the disableSchedule() method.
/*
- scheduleId: The ID of the schedule to disable
*/
try {
await scheduleStore.disableSchedule("schedule123");
console.log("Schedule disabled");
} catch (error) {
console.error("Error disabling schedule:", error);
}
Clear Store
To clear all schedules from the store and reset hooks, use the clear() method.
scheduleStore.clear();
Add Custom Property
To dynamically add an observable property to the store with getter and setter methods, use the addProperty() method.
/*
- propertyName: Name of the property to add
- initialValue: Initial value for the property
*/
scheduleStore.addProperty("customData", {});
// Creates: customData, getCustomData(), setCustomData(value)
Accessing Schedule Properties
Once you have a schedule from the store, you can access its properties:
const schedule = scheduleStore.schedulesByID["schedule123"];
// Schedule properties
console.log("Schedule ID:", schedule.id);
console.log("Schedule Name:", schedule.name);
console.log("Schedule Info:", schedule.info);
console.log("Schedule Nodes:", schedule.nodes);
console.log("Schedule Actions:", schedule.action);
console.log("Schedule Triggers:", schedule.triggers);
console.log("Schedule Enabled:", schedule.enabled);
console.log("Schedule Validity:", schedule.validity);
console.log("Schedule Flags:", schedule.flags);
console.log("Devices Count:", schedule.devicesCount);
console.log("Out of Sync Meta:", schedule.outOfSyncMeta);
Automatic Store Synchronization
The ScheduleStore automatically synchronizes with the NodeStore when schedules are created, updated, enabled, disabled, or deleted. Schedule operations update the node configurations in the NodeStore to keep everything in sync.
How Schedule Updates Work
When you perform schedule operations (create, edit, remove, enable, disable), the ScheduleStore:
- Updates Node Configurations: The schedule configuration is updated in each node's
nodeConfig.services[schedules]through theupdateNodeSchedulefunction - Targets Specific Nodes: Based on the operation:
- If updating a schedule that spans multiple nodes, you can target specific nodes
- If updating for a specific device/node, only that node's schedule configuration is updated
- The
updateNodeSchedulefunction handles updating the node's schedule list innodeConfig.services[schedules].params[schedules].value
- Synchronizes NodeStore: The NodeStore is automatically updated with the new node configurations using
NodeStore.updateNode() - Maintains Schedule Integrity: Multi-node schedules remain combined, with updates applied to the appropriate nodes
- Tracks Out-of-Sync State: If schedules differ across nodes, the
outOfSyncMetaproperty tracks which nodes have different configurations
Example:
// Schedule spans node1, node2, node3
const schedule = scheduleStore.getSchedule("schedule1");
console.log("Schedule nodes:", schedule.nodes); // ['node1', 'node2', 'node3']
// Update schedule - affects all nodes
await schedule.edit({
name: "Updated Name",
action: {
node1: { light: { power: true } },
node2: { fan: { power: false } },
node3: { switch: { power: true } },
},
triggers: [{ m: 420, d: 127 }],
});
// All three nodes' schedule configurations are updated in their nodeConfig
// Or update for specific node only
await schedule.edit({
action: {
node1: { light: { brightness: 50 } },
},
});
// Only node1's schedule configuration is updated in its nodeConfig
Schedule Transformation Process
The #transformNodeListToSchedules function automatically:
- Finds the schedules service by matching
service.type === "esp.service.schedules"(ESPRM_SERVICE_SCHEDULES) - Locates the schedules parameter by matching
param.type === "esp.param.schedules"(ESPRM_PARAM_SCHEDULES) - Extracts schedules from
param.value[]array - Combines schedules with the same ID from multiple nodes
- Detects out-of-sync configurations by comparing properties (name, enabled, triggers, validity, flags, info) across nodes
- Creates a unified schedule representation with:
nodes: Array of all node IDs that have this scheduleaction: Object mapping node IDs to their schedule actionsdevicesCount: Total device count across all nodesoutOfSyncMeta: Object tracking which nodes have different configurations
Example transformation:
// Input: 3 nodes with schedule ID "schedule1"
// Node 1: { id: 'schedule1', name: 'Morning', enabled: true, action: { light: { power: true } } }
// Node 2: { id: 'schedule1', name: 'Morning', enabled: true, action: { fan: { power: false } } }
// Node 3: { id: 'schedule1', name: 'Evening', enabled: false, action: { switch: { power: true } } }
// Output: Single combined schedule with out-of-sync detection
// {
// id: 'schedule1',
// nodes: ['node1', 'node2', 'node3'],
// action: {
// node1: { light: { power: true } },
// node2: { fan: { power: false } },
// node3: { switch: { power: true } }
// },
// devicesCount: 3,
// name: 'Morning', // From node1/node2 (majority)
// enabled: true, // From node1/node2 (majority)
// outOfSyncMeta: {
// node3: { name: 'Evening', enabled: false } // Node3 differs
// }
// }
Additional Resources
- Scheduling Documentation - Overview of scheduling feature
- Scheduling Usage Guide (Firmware) - Firmware implementation
- Scheduling Usage Guide (App) - App implementation