Skip to main content

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[] where service.type === "esp.service.schedules"
  • Parameter Type: esp.param.schedules (ESPRM_PARAM_SCHEDULES)

    • Identifies the schedules parameter within the schedules service
    • Found in service.params[] where param.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

PropertyTypeDescription
scheduleListSchedule[] (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:

  1. Extracts schedule configurations from each node's nodeConfig.services[schedules]
  2. Transforms schedules from node configurations into Schedule instances
  3. Combines schedules with the same ID across multiple nodes into a single schedule
  4. Detects and tracks out-of-sync configurations
  5. 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 nodes array contains all node IDs that have this schedule
    • The action object contains actions per node: { node1: {...}, node2: {...} }
    • The devicesCount is the total count across all nodes
  • 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:

  1. Updates Node Configurations: The schedule configuration is updated in each node's nodeConfig.services[schedules] through the updateNodeSchedule function
  2. 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 updateNodeSchedule function handles updating the node's schedule list in nodeConfig.services[schedules].params[schedules].value
  3. Synchronizes NodeStore: The NodeStore is automatically updated with the new node configurations using NodeStore.updateNode()
  4. Maintains Schedule Integrity: Multi-node schedules remain combined, with updates applied to the appropriate nodes
  5. Tracks Out-of-Sync State: If schedules differ across nodes, the outOfSyncMeta property 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 schedule
    • action: Object mapping node IDs to their schedule actions
    • devicesCount: Total device count across all nodes
    • outOfSyncMeta: 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