MemoryStorage is a helper class for Cisco RoomOS devices that provides a simple, persistent key–value store for macros. It uses a dedicated “storage macro” on the device to store data across reboots and macro restarts.
This README focuses on:
- How to use
MemoryStoragefrom your own macros. - The public API (methods and behavior).
- How storage is structured.
- Error codes and how to handle them.
- Provides per‑macro persistent storage:
memory.read(key)/memory.write(key, value)- Data is stored under a unique UUID key tied to the macro’s name.
- Provides global persistent storage shared across macros:
memory.global.read(key)memory.global.write(key, value)
All data is stored in a dedicated “storage macro” on the device, so it survives:
- Device reboot
- Macro engine restart
- Macro edits/updates (as long as the macro name stays consistent)
MemoryStorage is a helper class for Cisco RoomOS devices that provides a simple, persistent key–value store for macros. It uses a dedicated “storage macro” on the device to store data across reboots and macro restarts.
This README focuses on:
- How to use
MemoryStoragefrom your own macros. - The public API (methods and behavior).
- How storage is structured.
- Error codes and how to handle them.
- Provides per‑macro persistent storage:
memory.read(key)/memory.write(key, value)- Data is stored under a unique UUID key tied to the macro’s name.
- Provides global persistent storage shared across macros:
memory.global.read(key)memory.global.write(key, value)
All data is stored in a dedicated “storage macro” on the device, so it survives:
- Device reboot
- Macro engine restart
- Macro edits/updates (as long as the macro name stays consistent)
- Cisco RoomOS device with macros enabled.
- Access to the Macro Editor or equivalent API (e.g., xAPI).
- A suitable
xapiobject available in your macro environment.
- Add the
MemoryStoragecode to a macro file, for example:Memory-Storage-Functions.js
- Ensure your macro that wants to use it:
- Either imports it (if your environment supports module syntax), or
- Has the class in the same macro file.
Note: The exact import/usage pattern may depend on your RoomOS macro environment. The class is exported as:
export { MemoryStorage };
In your macro:
import xapi from 'xapi';
import { MemoryStorage } from './Memory-Storage-Functions.js'; // adjust path as needed
const memory = new MemoryStorage(xapi, {
// Optional: customize the storage macro name prefix
// StorageMacroName: 'MySolution'
});
async function initMemory() {
const result = await memory.init();
console.log(`Memory initialized with storage key: ${result.Storage_KeyName}`);
}
initMemory();init()must be called (and resolved) before usingread,write, orglobalmethods.- If the storage macro does not exist yet, it will be created automatically.
When you construct MemoryStorage:
- If you pass
options.StorageMacroName, the storage macro name becomes:<StorageMacroName>-Storage- Example:
{ StorageMacroName: 'MySolution' }→MySolution-Storage
- Otherwise, the default storage macro name is:
Memory-Storage
The storage macro content is a JavaScript file that looks like:
const memory = {
"$_CRITICAL_WARNINGS": [ /* ... */ ],
"$_About_Memory_Storage": [ /* ... */ ],
"$_MACRO_UUID_MAP": {
"<MacroNameA>": "#_<uuidA>_<MacroNameA>",
"<MacroNameB>": "#_<uuidB>_<MacroNameB>"
},
"#_<uuidA>_<MacroNameA>": {
// per-macro data for MacroNameA
"someKey": "someValue"
},
"#_<uuidB>_<MacroNameB>": {
// per-macro data for MacroNameB
},
// Global keys (shared across macros) live at the top level:
"GlobalKey1": { /* ... */ },
"GlobalKey2": "some string"
};- Do not edit this storage macro by hand.
- All interaction should go through the
MemoryStorageAPI.
const memory = new MemoryStorage(XAPIObject, options);Parameters:
-
XAPIObject
Thexapiobject from the RoomOS macro environment. -
options(optional)StorageMacroName(string): A base name for the storage macro. The actual macro created will be:StorageMacroName + '-Storage'
Examples:
const memory = new MemoryStorage(xapi);
// -> Uses default storage macro name: 'Memory-Storage'
const memoryCustom = new MemoryStorage(xapi, { StorageMacroName: 'MySolution' });
// -> Uses storage macro name: 'MySolution-Storage'await memory.init();Initializes the storage system:
- Validates the
xapiobject. - Tries to read the storage macro:
- If present, parses its contents.
- If not present, creates a new storage macro with initial metadata and an entry for this macro.
- Ensures this macro has a unique UUID‑based key for its per‑macro data.
- Marks
MemoryStorageas initialized.
Returns:
A promise resolving to:
{
Status: 'OK',
Storage_KeyName: '#_<uuid>_<ActiveMacroName>'
}Must be called before:
memory.read(...)memory.write(...)memory.global.read(...)memory.global.write(...)
If you call those methods before init() has completed, you will get a “Memory not initialized” error.
These methods read/write data in the namespace unique to the current macro (ActiveMacroName).
const value = await memory.read('myKey');Behavior:
- Re-loads the current storage content from the storage macro.
- Locates this macro’s namespace using the internal UUID key (e.g.
#_<uuid>_<MacroName>). - Reads
myKeywithin that per‑macro object.
Throws if:
init()has not been successfully called.- The parent key for this macro is missing in storage.
keydoes not exist for this macro.
Example:
try {
const threshold = await memory.read('AlertThreshold');
console.log(`Threshold is: ${threshold}`);
} catch (e) {
console.error('Failed to read from memory:', e.message, e.code);
}await memory.write('myKey', myValue);Behavior:
- Re-loads the current storage content from the storage macro.
- Ensures this macro’s per‑macro object exists.
- Writes
valuetomyKeyinside this macro’s namespace. - Persists the updated storage back to the storage macro.
Throws if:
init()has not been successfully called.- There is a problem saving the storage macro.
Example:
try {
await memory.write('AlertThreshold', 5);
console.log('AlertThreshold saved.');
} catch (e) {
console.error('Failed to write to memory:', e.message, e.code);
}These methods work on the top-level of the storage object and are shared across all macros using the same storage macro.
const value = await memory.global.read('GlobalSetting');Behavior:
- Re-loads the storage object.
- Reads the top‑level key
GlobalSetting.
Throws if:
init()has not been successfully called.keyis not found at the top level of the storage object.
Example:
try {
const cfg = await memory.global.read('SharedConfig');
console.log('SharedConfig:', cfg);
} catch (e) {
console.error('Global read failed:', e.message, e.code);
}await memory.global.write('GlobalSetting', value);Behavior:
- Re-loads the storage object.
- Validates that the key name does not start with
$or$_(reserved for internal metadata). - Writes
valueto the top‑level keyGlobalSetting. - Persists the updated storage.
Throws if:
init()has not been successfully called.- The key starts with
$or$_. - Saving fails.
Example:
try {
await memory.global.write('SharedConfig', { enabled: true, mode: 'auto' });
console.log('SharedConfig updated.');
} catch (e) {
console.error('Global write failed:', e.message, e.code);
}Most errors thrown by MemoryStorage are instances of Error with an additional code property. The message also embeds the code in the form || MacroErrorCode: <code>.
Example error object:
{
message: "Unable to run .read() method. Memory not initialized || MacroErrorCode: msfv2.r.1",
code: "msfv2.r.1"
}- All codes start with
msfv2. - Private method errors typically include the method initials (
_te,_vxo,_wafc, etc.). - Public API errors have concise codes (
r,w,g-r,g-w). - Suffix
.1,.2,.3indicate different error cases within the same area.
You can use error.code to programmatically handle specific cases.
-
msfv2._te.1
Attempted to call_throwErrorwithout a valid internal token. Indicates internal misuse, not expected in normal macro usage. -
msfv2._vxo.1
_validateXapiObjectcalled without token. Internal misuse. -
msfv2._vxo.2
xapiobject is not defined or is not an object. -
msfv2._vxo.3
xapi.Configproperty is invalid or missing. -
msfv2._wafc.1
_wrapAndFormatContentcalled without token.
-
msfv2._ps.1
_parseStoragecalled without token. -
msfv2._ps.2
Not an error code; this is a log message when a trailing semicolon is removed. -
msfv2._ps.3
Failed to parse the storage macro content after trying all known patterns.- Usually indicates manual modification or corruption of the storage macro.
msfv2._gu.1
_generateUUIDcalled without token. Internal misuse.
-
msfv2._gmo.1
_getMemoryObjectcalled without token. -
msfv2._ssm.1
_saveStorageMacrocalled without token. -
msfv2._ssm.2
Failure while saving the storage macro viaxapi.Command.Macros.Macro.Save.- The error message will include the original
e.messageande.code.
- The error message will include the original
-
msfv2._fsm.1
_fetchStorageMacrocalled without token. -
msfv2._fsm.2
_fetchStorageMacrocalled without a target name. -
msfv2._fsm.3
Storage macro is missing (xAPI Get returned code1).- In
init(), this is handled by creating a new storage macro.
- In
-
msfv2._fsm.4
Malformed xCommand (e.g., invalid parameters) forMacros Macro Get. -
msfv2._fsm.5
Unknown error returned fromxapi.Command.Macros.Macro.Get.
-
msfv2._csm.1
_createStorageMacrocalled without token. -
msfv2._ps.3
May surface duringinit()if the storage macro is corrupted or manually edited. -
msfv2._fsm.3
Ininit(), indicates storage macro not found; triggers auto‑creation. -
Any underlying error code from fetch/parse/save may be re-wrapped with message:
Failed to Initialize memory.
-
msfv2.r.1
.read()called before initialization. -
msfv2.r.2
Parent key (this macro’s UUID‑based storage object) not found in storage.- This can indicate tampering with the storage macro or unexpected deletion of this macro’s data.
-
msfv2.r.3
Requested key not found within this macro’s storage. -
msfv2.w.1
.write()called before initialization.
-
msfv2._gr.1
_globalReadcalled without token (internal misuse). -
msfv2._gr.2
_globalReadinvoked while memory is not initialized. -
msfv2._gr.3
The requested global key does not exist in the storage macro. -
msfv2._gw.1
_globalWritecalled without token (internal misuse). -
msfv2._gw.2
_globalWriteinvoked while memory is not initialized. -
msfv2._gw.3
Attempted to use a global key starting with$or$_.- These prefixes are reserved for internal metadata keys (like
$_MACRO_UUID_MAP).
- These prefixes are reserved for internal metadata keys (like
-
msfv2._gw.4
Global write attempted with a#or#_prefixed key withoutoptions.Override = true; these keys are reserved for other solutions’ data.
Because all operations are async and can throw, always use try/catch around init, read, and write:
async function setup() {
try {
await memory.init();
} catch (e) {
console.error('Memory initialization failed:', e.message, e.code);
// Optionally, decide whether to continue or abort
}
}
async function useMemory() {
try {
await memory.write('ExampleKey', 'some value');
const v = await memory.read('ExampleKey');
console.log('ExampleKey:', v);
} catch (e) {
console.error('Memory operation failed:', e.message, e.code);
}
}-
Do not edit the storage macro manually.
Manual edits can break JSON parsing and causemsfv2._ps.3or other initialization errors. -
Macro name changes
The mapping is based on the macro’s active name (ActiveMacroName). Renaming the main macro may result in a new UUID mapping and a different per‑macro namespace. -
Unsupported by TAC
As captured in the created storage macro’s metadata, this is a custom solution and is not supported by Cisco TAC.
| Error Code | Function | Location | Description | Suggested Action |
|---|---|---|---|---|
msfv2.g-r.1 |
global.read |
Public API | Attempted .global.read() before memory was initialized. |
|
msfv2.g-w.1 |
global.write |
Public API | Attempted .global.write() before memory was initialized. |
|
msfv2.r.1 |
read |
Public API | Attempted .read() before memory was initialized. |
|
msfv2.r.2 |
read |
Public API | This macro’s parent key (_macroKeyName) was not found in the storage macro. |
|
msfv2.r.3 |
read |
Public API | Requested key not found in this macro’s storage object. | |
msfv2.w.1 |
write |
Public API | Attempted .write() before memory was initialized. |
|
msfv2._te.1 |
_throwError |
Internal | _throwError was called without a valid internal token (indicates internal misuse). |
|
msfv2._vxo.1 |
_validateXapiObject |
Internal | _validateXapiObject was called without a valid internal token. |
|
msfv2._vxo.2 |
_validateXapiObject |
Internal | XAPI object is not defined or not an object. |
|
msfv2._vxo.3 |
_validateXapiObject |
Internal | XAPI.Config is invalid or missing. |
|
msfv2._wafc.1 |
_wrapAndFormatContent |
Internal | _wrapAndFormatContent was called without a valid internal token. |
|
msfv2._ps.1 |
_parseStorage |
Internal | _parseStorage was called without a valid internal token. |
|
msfv2._ps.3 |
_parseStorage |
Internal | Failed to parse the storage macro content after trying all supported patterns. | |
msfv2._gu.1 |
_generateUUID |
Internal | _generateUUID was called without a valid internal token. |
|
msfv2._gmo.1 |
_getMemoryObject |
Internal | _getMemoryObject was called without a valid internal token. |
|
msfv2._ssm.1 |
_saveStorageMacro |
Internal | _saveStorageMacro was called without a valid internal token. |
|
msfv2._ssm.2 |
_saveStorageMacro |
Internal | Failed to save the storage macro via XAPI.Command.Macros.Macro.Save. |
|
msfv2._fsm.1 |
_fetchStorageMacro |
Internal | _fetchStorageMacro was called without a valid internal token. |
|
msfv2._fsm.2 |
_fetchStorageMacro |
Internal | _fetchStorageMacro was called without a target storage macro name. |
|
msfv2._fsm.3 |
_fetchStorageMacro |
Internal / init |
Target storage macro not found on the device. | |
msfv2._fsm.4 |
_fetchStorageMacro |
Internal | Malformed xCommand Macros Macro Get (invalid parameters or similar). |
|
msfv2._fsm.5 |
_fetchStorageMacro |
Internal | Unknown error occurred while performing xCommand Macros Macro Get. |
|
msfv2._csm.1 |
_createStorageMacro |
Internal | _createStorageMacro was called without a valid internal token. |
|
msfv2._gr.1 |
_globalRead |
Internal | _globalRead was called without a valid internal token (should use global.read()). |
|
msfv2._gr.2 |
_globalRead |
Internal | Attempted _globalRead() while memory was not initialized. |
|
msfv2._gr.3 |
_globalRead |
Internal | Requested global key not found in the storage macro. | |
msfv2._gw.1 |
_globalWrite |
Internal | _globalWrite was called without a valid internal token (should use global.write()). |
|
msfv2._gw.2 |
_globalWrite |
Internal | Attempted _globalWrite() while memory was not initialized. |
|
msfv2._gw.3 |
_globalWrite |
Internal | Global write attempted with a key starting with $ or $_, which is prohibited. |
|
msfv2._gw.4 |
_globalWrite |
Internal | Global write attempted with a # or #_ prefixed key without options.Override = true; these keys are reserved for other data. |