Programmable Locks
Programmable Lock lets you connect visual blocks into custom buttons and automations for a lock. A program can react to events, check conditions, keep counters, and perform actions such as assigning tasks or changing lock time. You do not need to create a developer extension or host a server.
Block reference by category
Use this guide for setup, testing, permissions, and sharing. The pages below follow the editor's toolbox categories and explain each block's inputs, behavior, and common uses. PLE means Programmable Lock Extension. Examples written as indented text describe how to connect blocks; they are not code to paste into the editor.
| Toolbox category | What you can build |
|---|---|
| Events | Buttons, coin flips, wheels, schedules, and reactions to lock activity. |
| Custom Events | Named events that run later and can be replaced or canceled. |
| Calendar | Calendar entries and scheduled tasks. |
| Functions | Reusable action sequences with typed local parameters. |
| Shock / Vibrate | Commands for the selected device and secondary accessories. |
| Lock Actions | Lock time, freeze, pillory, tasks, hygiene, and visibility. |
| Quiz | Generated quizzes and result handling. |
| Parental Control Lock Actions | Screen time, app rules, self-control locks, and Content Shield. |
| Logic | Conditions, branches, and weighted outcomes. |
| Lock Data | Current lock state, participant details, and event values. |
| Lists | Dice arrays, list statistics, and iteration. |
| Loops | Repeated steps and iteration over numbers. |
| Logs | Lock log entries and participant notifications. |
| UI | Printed results, dialogs, input prompts, and menus. |
| Values | Numbers and calculations. |
| Variables | Typed values, counters, and memory between sessions. |
| Group variables | Owner-managed shared values, wearer connections, and change events. |
| Text | Messages, joining text, and text operations. |
| Time & Date | Durations, timestamps, timezones, and readable dates. |
| Color | Fixed and random colors for supported inputs. |
Start with Make a small first program, then try the worked recipes. For dice lists, this guide also includes an importable workspace.
For the experimental text editor, use the PLE code manual for AI helpers. It covers the supported syntax, every block API, each event's variables, and complete code examples. Give it to your AI helper together with your current program when asking for changes.
In Code (experimental), hover over calls, option names, variables, and event fields for inline help. For example, hover over addTime in addTime("1m") to read the block's instructions. Variable help shows the declaration rather than live values. Click or tap the ? beside a line for persistent block instructions.
Open the editor
During lock setup, open Extensions, select Programmable Lock, and open its configuration. Enable it when you want the program included in the lock. If the app shows an access requirement, follow the availability shown there.
The editor can open in a modal or a larger page. On mobile, Blocks opens the block menu, and Workspace actions contains imports, exports, and other tools. In the larger setup page, Save Configuration returns your configuration to the surrounding lock setup; finish saving or creating the lock there too.
For an existing session, open its Programmable Lock extension. View code and Edit code appear according to your permissions. The Buttons area can remain usable even when the wearer cannot see or edit the program.
The runtime editor automatically saves workspace changes after a short delay. Tests associated with an active session can also save and execute the program immediately. Use pre-session draft testing while developing uncertain logic, and read the testing mode before pressing a button.
Secondary-keyholder editors
For invitations, consent, photo privacy and leaving or removing access, start with the Secondary Keyholders user guide.
Approved secondary keyholders use Programmable Lock on the normal lock page. Edit PLE exposes the existing editor, configuration and keyholder controls for the lock's programs. It uses the same toolbox, compiler, variables, functions, dialogs and schedules as primary-keyholder editing. Edit extensions exposes other extension configuration editors. Controller delegation and extension backend tokens remain excluded.
Use Compile to check the workspace and Save Configuration to save it. Draft tests use synthetic data. Testing an existing active extension saves and executes the selected event on the real lock. For example, editing a program to remove one minute requires Edit PLE; running that reward yourself also requires Remove time and available allowance. See the permission rules and example.
Configuration changes remain in place after the editing grant ends. Normal future program triggers keep the extension's configured behavior. Direct secondary runs recheck the current grant for each effect and share the manual controls' timing and device allowances. Existing grant-owned schedules keep their original grant until converted by an ordinary configuration save. A secondary editor cannot configure direct unlock or parental phone control, reveal combinations or password secrets, transfer ownership, or delegate control. Private memory, templates and global groups retain their ownership checks.
New and existing secondary keyholders use the trusted primary’s limits for enabled actions while that trust remains. This includes indefinite freezes and supported continuous device modes when no duration cap is set. Custom caps and accepted deadlines still apply; existing access does not need a new invitation. See shared timing limits.
A timer reaching zero does not end secondary access while an enabled extension still has an unfinished completion requirement. Once the session completes, access ends. Adding time or another requirement afterward does not restore the old permission; a new invitation and approval are required.
Understand the blocks
| Part | What it does | Example |
|---|---|---|
| Event / trigger | Decides when a stack runs. | A button press, lock start, task completion, or a schedule. |
| Condition | Chooses which branch runs. | Perform an action only when a counter reaches a value. |
| Value | Supplies text, numbers, durations, dates, or current information. | A duration connected to an add-time action. |
| Action | Changes state or produces an output. | Assign a task, update a variable, or print text. |
| Function | Groups reusable steps that other blocks can call. | Reuse the same reward sequence from several triggers. |
| Variable | Holds a value for later steps or executions. | A numeric progress counter. |
Connect action blocks inside the trigger's action area, in the order they should run. Separate stacks are separate entry points; leaving an action loose does not make it run automatically. A function runs when called.
Tap or click a block's ? icon to open its help. The popup stays open while you read; tap or click the help window to close it, or press Escape. This works in the Android app and web browsers. For example, open the help on an add time block to read its instructions before connecting its inputs.
An on GUI button trigger creates a control with a title, permitted role, visibility, active state, and cooldown. Visibility decides whether the control appears; active state decides whether it can be used. Role and cooldown checks also apply when the button runs.
Use Program notes to explain the program's controls and consequences to someone else. Notes allow up to 10,000 text characters; formatting does not count toward this allowance. A separate HTML size limit applies. Notes are documentation and are not executed.
Make a small first program
Start in setup before the lock has a runtime session:
- Inspect the starter workspace. Remove other starter triggers and actions so your first test contains only the behavior below.
- Add an on GUI button event from Events and give it a recognizable title, such as “Check program”. Choose who may press it.
- Connect print text in UI to its action area and supply “The button works” as the text.
- Select Compile (server), or Compile on mobile. Read any warnings and check the highlighted blocks.
- Under Results, confirm that the editor says testing runs in draft mock mode, then press your new control. Check the printed text and result.
- Save the configuration and finish the surrounding setup when it is ready.
Next, add one condition or variable at a time. Test both branches of a condition before adding actions with real consequences.
Choose when the program runs
The event menu includes lock changes, tasks and verification results, hygiene events, games, calendar events, device-related events, and other supported sources. An event trigger needs its corresponding feature and event to occur; adding the block does not enable that feature.
- Lock started runs in response to the lock-start event. Saving an edited program later is not another lock start.
- Buttons, coin flips, and wheels run when the corresponding control is used, subject to its role, active state, and cooldown.
- Interval schedules have a minimum interval of 10 minutes. Choose whether the first run is at lock start or after the delay.
- Weekly schedules use the selected weekdays, times, and timezone. Check whether you chose wearer-local time or UTC.
- Custom events let the program schedule or cancel a named event. Use the scheduled-events display to inspect its state.
These automations are processed by the server. A device action still depends on the relevant device feature and delivery path; a saved schedule does not prove that a phone or accessory received a command.
For related behavior, see Tasks & Task Queue, Calendar Events, Hygienic Unlock, and Shock Delivery & Queues.
Date calendar entries to the task submission
Event timestamp (Unix seconds) can connect directly to a calendar entry's start input. For queued lock events, it uses the event's source time when provided, or its original queue time, so delayed processing does not move the entry to a later day. Current Unix timestamp uses the time the program runs. If an existing program divides Event timestamp by 1,000 to work around the old millisecond value, remove that conversion.
Photo approval and task completion can happen after the wearer submitted their work. In when task completed or when task photo verification approved, use task submission timestamp (Unix seconds) as the calendar entry's start input to record that original submission date. Event timestamp still represents the approval/completion event, not the submission. Calendar dates use the wearer's timezone.
Task submission timestamp is 0 when no verification submission is available. If the same program handles tasks without verification, use Event timestamp when the submission timestamp is 0. This does not change when approval, rewards, or task completion take effect, and it does not move existing calendar entries.
Compile, test, and execute
| Control or context | What it checks or runs | Real session effects? |
|---|---|---|
| Compile (server) / Validate (server) | Converts the workspace into a program and reports errors or warnings. | The validation operation does not execute actions. Runtime editing may already have autosaved the workspace. |
| Setup with No runtime session yet | Runs a draft mock using supplied test inputs. | No real lock changes. |
| Run this block in the draft mock | Exercises a selected entry point where the draft editor offers this control. | No real lock changes; this control is not offered in active-lock editing. |
| Setup testing attached to an active session | Saves the current program, then runs it on that session. | Yes. |
| Runtime buttons or Execute event | Runs the live program against the active session. | Yes. |
| Wearer controls preview in the keyholder view | Shows the wearer's controls with execution disabled. | No. This is a separate preview, not the active setup tester. |
Draft test inputs
Open Draft test inputs to supply the values needed by the part of the program you are testing. Try conditions that should pass and fail, including empty or unexpected values where relevant.
Completed draft runs retain mock variable values and mock scheduled events for the next test. Use Reset test state to start those tests fresh. This does not reset live session values or remembered values.
Mock tests help inspect logic. They do not establish that a real device is connected, a required module is enabled, an external service is available, or a live action is permitted. Some interactive steps require runtime session testing; follow the result shown in the editor.
Live tests
Execute event lets you choose an event supported by the saved program and optionally provide Event values. Those values simulate the event input; the lock state, clock, saved variables, and resulting actions remain real.
Before a live test, read the entire path it can execute, including called functions and scheduled events. Save deliberately, check the action summary, and inspect the actual session afterward. A successful draft test is not a reason to repeat a live test without checking its previous result.
Execution is not an undoable transaction. If a later action fails, earlier actions or variable changes may already have taken effect. Inspect the result and current session before retrying, especially for time changes, tasks, device commands, or repeated rewards.
Place confirmation or input steps before consequential actions, and put the actions inside the appropriate response branch. Compiler warnings call out interactive steps that can cause earlier steps to run again when a response is submitted.
Example: player and bot dice arrays
Download the player and bot dice workspace.
Use Add blocks from file to add this example to your existing workspace, or Import workspace to replace it. Export your current workspace first if you choose replacement. The example requires the Lists blocks and Number list variable type from the dice-array update.
The When dice rolled stack reads both saved server results into playerDice and botDice. It combines these with ordinary blocks:
- For each plus if and change variable count sixes separately for the player and bot.
- Sum, maximum, length, and item 1 read statistics from each array.
- If/else compares the totals and prints the winner or a tie.
- Subtract and create text with print the score difference alongside the summaries.
The example sets its counters to zero on every roll and prints results in the UI preview. It contains no lock-time or device actions. The older text event variables and single-die blocks remain available alongside arrays.
To try it in the draft editor, select When dice rolled and open Draft test inputs. Enable the player and bot dice-value inputs, set their type to Text, and enter:
| Event input | Value |
|---|---|
| Player dice values | 6, 2, 6 |
| Bot dice values | 1, 3, 4 |
The event inputs use comma-separated text; the rolled dice blocks convert them into numeric arrays. Run the draft to see:
Player: total=14, highest=6, first=6, dice=3, sixes=2
Bot: total=8, highest=4, first=1, dice=3, sixes=0
Player wins!
Difference (player - bot)=6
Swap the inputs to test Bot wins!, or use the same values for both to test Tie!. Once the program is saved on a lock, actual dice-roll events supply the results automatically. The winner in this example is based on the sum of each array.
Variables and starting values
Create variables in Variables, then open Manage variables. Choose a type and starting value: Boolean, Number, Integer, Text, Number list, Date and time, or Color. Any (legacy) keeps dynamic behavior for older workspaces.
Use types that match the blocks: numeric changes need a numeric variable, and text append needs text. The compiler and runtime can reject incompatible assignments. Deleting a variable can also delete blocks that use it; read the confirmation first.
Ordinary variables belong to the current extension session and carry their values between executions. Saving a program does not generally reset existing compatible values to their starting values. Changing the starting value is therefore not a reliable way to reset a live counter; use an intentional variable-setting action when appropriate.
For values that should survive into another session, configure saved memory explicitly.
Remember between sessions
In Manage variables, find Remember between sessions:
- Create or choose a saved set.
- For each typed variable, choose This session only, Remember between sessions, or Use session history (read-only).
- To share a remembered value with another workspace, choose the same saved set and the same saved variable in both workspaces. Matching the display name alone is not enough.
A remembered variable uses its last saved value; its starting value is used when no saved value exists. Values are separated by wearer and controlling account. In a self-lock, the wearer is the controlling account; in a shared lock, it is the current controlling keyholder. Only that account can change memory links or reset saved values.
The read-only history sources provide the number of sessions using the saved set, when the previous session ended, and how it ended. They are not the wearer's complete account history. Programs cannot assign values to these history variables.
Changing the saved set disconnects the previous memory links. Review and reconnect the variables you want to remember.
Reset saved value removes that value for this wearer across all workspaces sharing the link. The next execution uses the variable's starting value. It does not reset the whole lock or undo actions. If the program is running, the reset may be rejected; wait for it to finish and try again.
Who can see, edit, and run the program
Configure self-lock programs as the wearer. In an active shared lock, configuration by the keyholder requires the wearer to trust the keyholder.
The visibility and editing settings below control wearer access. A trusted keyholder can still view the code and execution history and edit the program when these wearer permissions are turned off.
| Setting or permission | Effect |
|---|---|
| Show read-only code to wearer | Lets the wearer inspect the program. It does not by itself allow edits; wearer editing is controlled separately. |
| Allow wearer to edit code after lock starts | Gives the wearer runtime editing access, including viewing the code. Turning this off does not prevent a trusted keyholder from editing. |
| Show execution history to wearer | Controls whether the wearer can see Programmable Lock's execution log. The keyholder can still see it. |
| Button role | Limits the control to the wearer, keyholder, or either participant. Running an allowed button does not require code-editing access. |
| Allow wearer to edit AI access after lock starts | Lets a wearer with runtime code-editing access change the program's AI variable and function access settings. |
Choose these settings before starting. A wearer cannot grant themselves these runtime permissions after the session begins. Wearer runtime saves are also rejected when the program contains an end-lock action or a block changing execution-history visibility, even if general editing is enabled.
Password-unlock blocks and their secrets must be configured before runtime: the active editor does not let you introduce a new password-unlock block or change its saved passcode.
Under AI access settings, choose which variables and functions a coach may use. This is separate from permission to edit the workspace. See AI Coach for the coach's own action permissions. For handing someone limited control, see Control Passes; review those permissions separately from this program's buttons and editing settings.
Save, restore, and share work
Saves and temporary drafts
During new-lock setup, editor changes update the configuration being prepared. Finish Save Configuration and the surrounding lock setup to retain that configuration as intended.
In the runtime editor, workspace changes autosave after a short delay. Save changes gives you an explicit save result. If a save fails or another execution changes the state, check the error and current saved program before continuing.
The editor also keeps a temporary browser draft when storage is available. Restore last workspace recovers it; Discard autosave dismisses that draft. These drafts are tied to the account and editing context on that browser, expire after seven days, and can disappear when browser storage is cleared. They are not a backup across devices.
Temporary drafts exclude password-unlock passcodes. Check those blocks after recovery. Reset draft replaces the setup workspace with the starter program and clears its draft options, notes, and mock state; export work you want to keep first.
Imports and exports
- Export workspace or Export as text copies the editable block workspace with variable IDs, types, and starting values. It is not a complete lock or runtime-state backup. Older exports without variable declarations still import, but their types and starting values need to be configured again.
- Export selected parts includes selected stacks, their referenced functions and variables, and relevant variable declarations. Add blocks imports parts into the current workspace; resolve function-name or variable-type conflicts before retrying.
- Import workspace or Import from text replaces the current workspace. Export the existing version first. Replacement clears the selected saved-memory set; imported memory links need to be configured again.
Review task selections, device settings, location references, permissions, and memory after any import. A workspace from another lock cannot supply those prerequisites automatically. Keep exported JSON private when it contains personal text or secrets; ordinary raw workspace exports are not scrubbed in the same way as temporary drafts and library snapshots.
Snapshot library
Save snapshot can save a whole workspace or selected parts to your account. Creating snapshots requires the subscription access shown in the app. The Snapshot library also lets signed-in users browse and reuse public snapshots.
Snapshots contain reusable blocks and variable declarations. They exclude password-unlock passcodes and saved-memory links, and do not preserve the live lock's variable values or execution state. Use private visibility for your own reusable programs.
Public snapshots expose your username, avatar, description, and code to other users. Review block text and starting values before sharing. Changing a snapshot back to private cannot remove copies someone already imported.
Read the execution log
Open Execution log, expand it, and use Refresh log when checking a recent run. Entries show the timestamp, trigger or button, result, and available action messages or errors. The server retains recent execution history rather than a permanent audit of every run.
Use the log together with current variables, scheduled events, and the session itself. “Success” can include a step that was deliberately skipped or a device command accepted for delivery; it does not prove every intended real-world outcome. A hidden log reflects the configured visibility and does not mean the program has stopped.
Fix common problems
| Symptom | What to check |
|---|---|
| Compilation succeeds with warnings | Open each warning and inspect its block. Unsupported blocks or incomplete actions can be ignored; other values can be clamped. A warning-free compile still does not test live prerequisites. |
| A button is missing or disabled | Check its role, visible/active inputs, cooldown, and whether you are in the disabled wearer-controls preview. |
| A test changes the real lock | You were testing an active session. Check the applied actions before repeating it; use pre-session draft mode for further development. |
| An action reports a disabled feature | Enable/configure the underlying feature when permitted. Task, hygiene, pillory, card, and device actions have their own requirements. |
| A save or run is forbidden | Check the participant, trusted-keyholder status, runtime permissions, and restricted action blocks. Compiler success does not grant authorization. |
| Variables behave differently after import | Check saved-memory links and live runtime values. Older exports may also need variable types and starting values configured again. |
| A counter does not reset when you save | Existing compatible runtime values are retained. Distinguish live variables, mock test state, and remembered values. |
| A run hits an execution limit | Reduce repeated steps or recursive function calls; split long work into deliberate scheduled events. Do not create an unbounded event loop. |
| A run is busy or fails partway through | Wait for ongoing work, inspect the log and session, and confirm what already happened before retrying. |
When asking for support, include the trigger/button, approximate time, exact error, and whether it happened in draft or runtime mode. Share only the relevant blocks after removing personal information and secrets.