Generating Persistent UUIDs
Version 5 Universally Unique Identifiers (UUIDs)
The UUIDs used for <id> identifiers MUST be PERMANENT, REPEATABLE, UNIQUE identifiers for Atom feed or entry data streams, and all ESPI RESOURCE entries. The <id> values do not change when a new OAuth Access token is created — except for ESPI Authorization and <IntervalBlock> Resources (since the UUID of an Authorization MUST change when the Authorization expires and the UUID of an <IntervalBlock> should be based on a repeatable timestamp value).
This “PERMANENT, REPEATABLE, UNIQUE” requirement is necessary so that a user or Third Party can recognize updates and extensions to the data sets from one Green Button file to the next.
Each <id> identifier (with the above-exception for ESPI Authorization and <IntervalBlock> RESOURCE entries) is established once and does not change. See our page on Atom Elements for more detail about the <id> identifier.
Why Repeatable?
This is best understood with an example:
An interval between 4pm and 5pm on January 1st is provided with some value and also provided with a Quality-of-Reading flag stating “estimated using reference day” or “estimated using linear interpolation” (because the actual reading could not be obtained when the Green Button data were provided).
Because the interval reading was received by the Third Party with an “estimated …” quality flag, the Third Party may want to re-request the interval later (a few days later or at the end of the month).
Because the Data Custodian generated the identifier for the interval using a repeatable UUID, the re-requested interval reading will have the same UUID as it did originally.
The Third Party can then know for certain that the interval reading being received later — with that same UUID — is a replacement for the interval reading received initially. The Data Custodian should have updated the quality flag (e.g., “revenue-quality” or “verified” or a similar flag) when the new data were obtained — or at least before sending the data to the Third Party again.
For more information about the fourteen different QualityOfReading flags, please consult the NAESB REQ.21 ESPI documentation (.pdf), the espi.xsd file, or our Developer Resources on the topic of “Accuracy and Reading Quality.”
RFC4122 vs. RFC9562
Developers must generate the UUIDs in accordance with:
IETF RFC4122 (https://www.ietf.org/rfc/rfc4122.txt); notwithstanding the fact that…
IETF RFC9562 (https://www.ietf.org/rfc/rfc9562.txt) has made RFC4122 obsolete.
The most-significant difference for Green Button implementations between RFC4122 and RFC9562 is that the newer (2024) RFC9562 allows UUIDs to be output using upper, lower, or mixed case for the hexadecimal values.
Because RFC4122 and some standards based on RFC4122 require the UUIDs to use lowercase hex values…
Green Button implementations should continue to output lowercase values in the XML that is provided by Data Custodians; and Third Party apps and services should continue to accept UUIDs in any case (upper, lower, or mixed).
Use of SHA-1 Hashing
To make reproducible UUIDs that do not need to be stored, the developer may use a one-way hash based on SHA-1, which is a secure, non-reversible hash function1. The construction of the UUID in this way is called version 5 (note the method discussed below allows you to compute them each time; the algorithm for constructing UUIDs is in section 4.3 of RFC4122 or in 6.5 of RFC9562).
The requirements for Version-5 UUIDs are as follows:
-
- The UUIDs generated at different times from the same name in the same namespace MUST be equal.
- The UUIDs generated from two different names in the same namespace should be different (with very high probability).
- The UUIDs generated from the same name in two different namespaces should be different (with very high probability).
- If two UUIDs that were generated from names are equal, then they were generated from the same name in the same namespace (with very high probability).
Method to Generate Version-5 UUIDs
|
This method of generating a UUID combines a scheme, a namespace, and a name to create a universally unique string that can be formed into a UUID. It is called “version 5” in IETF RFC4122 & RFC9562 and it allows the creation of a UUID based on three data elements:
|
For UUIDs, there are four predefined “potentially interesting” namespace IDs (namespaceUUIDType) defined in Appendix C of IETF RFC4122 and in 6.6 of RFC9562:
-
-
Fully-Qualified Domain Name:
NameSpace_DNS =6ba7b810-9dad-11d1-80b4-00c04fd430c8 -
Uniform Resource Locator:
NameSpace_URL =6ba7b811-9dad-11d1-80b4-00c04fd430c8 -
ISO/IEC 9834-3 (ITU-T X.662) Object Identifier:
NameSpace_OID =6ba7b812-9dad-11d1-80b4-00c04fd430c8 -
ITU-T X.500 Distinguished Name:
NameSpace_X500 =6ba7b814-9dad-11d1-80b4-00c04fd430c8
-
For example: NameSpace_URL allows you to use a URL (uniform resource locator or weblink) for the namespace part (for this type of namespace, the NameSpace_URL value is 6ba7b811-9dad-11d1-80b4-00c04fd430c8)
The Format
The format of the UUID to use for Green Button implementations is:
urn:uuid:xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
… whereMandNare defined in the standard andxare hexadecimal digits, with each digit being a nibble of a byte (and a nibble is half of a byte: 4 bits).
-
- The value of
Mis5for version 5. - The value of
Nis the most-significant two bits of that character set to be 0b10 (binary 1 0).
- The value of
That is, values of 8, 9, a, or b are valid values of hex nibble N.
An example of how to use this scheme:
-
- The UsagePointId (identifier of the
<UsagePoint>) is used to identify a device that records either energy consumption or generation. It must remain the same identifier value even if the existing device is replaced. Therefore, it cannot be based on the device’s asset tag or serial number, since the asset tag or serial number will change if the device (electric meter, electric vehicle, etc.) requires replacement or upgrading. Because the resulting UsagePointId cannot be reversed, the street_address of the building can be used as the UsagePointId “namespaceUUIDType”. If the location contains more than one device, an increasing serial value can be appended to the “namespaceUUIDType”. Any value can be used as the “namespaceUUIDType” if it is repeatable and cannot be changed. Just keep in mind: the UsagePointId must be locally unique to the DataCustodian. Encoding the UUID obfuscates it, so no account or other personal data can be deduced from the value of the UsagePointId.
- Then, create the “names” for the persistent UUIDs for use in your Green Button installation:
- usagePointName = street_address
- meterReadingName = street_address + “mrWh” ⇐ This will be a constant for
<MeterReading>(“mr”) values of this<UsagePoint>for the “Wh” readings. - readingTypeName = readingTypeWh ⇐ This will be a constant for all Green Button data you make of that
<ReadingType>. - localTimeParametersName = localTimeParametersPT ⇐ This will be a constant for all Green Button data you make that are in the Pacific Time zone.
- Then generate the corresponding UUIDs by applying SHA-1 to the desired concatenated string:
- Generate the bytes of the
UUID = SHA1(namespaceUUIDType + namespace + name)where each term is an ordered sequence of bytes concatenated (note: leave out formatting and separating characters such as the “-” in the namespaceUUIDType). - Then, set the values of
MandNby binary-AND’ing:Mis the 13th nibble (from the left)Nis the upper 2 bits of the 17th nibble (from the left)
- Format the string with the appropriate hyphens (“
-”) as shown in the format, above.
- Generate the bytes of the
- The UsagePointId (identifier of the
Example Code for Generating Repeatable UUIDs
We are providing this Repeatable UUID JavaScript code as an example of how a Data Custodian (typically a Utility company offering Green Button outputs) can ensure consistency for UUIDs and ensure that they are always able to be re-generated when needed. This RepeatableUuid JavaScript class provides methods for generating UUIDs based on a namespace and a name using the UUID version 5 specification. This ensures that the same input produces the same output each time it is run, enabling repeatability in UUID generation.
JavaScript Example2
Please see the Footnote regarding any and all use of the provided code examples. The user assumes all responsibility if the examples are used in deployments. Code is provided here strictly for illustrative purposes.
class RepeatableUuid {
// The static UUID representing the DNS namespace as defined in the IETF RFCs for UUIDs.
static NAMESPACE_DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
// The static UUID representing the URL namespace as defined in the IETF RFCs for UUIDs.
static NAMESPACE_URL = "6ba7b811-9dad-11d1-80b4-00c04fd430c8";
// Generates a version-5 UUID, which is based on a namespace-UUID and a name.
static async uuidV5(namespaceUuid, name) {
const nameBytes = new TextEncoder().encode(name);
return await this.nameBasedUuid(namespaceUuid, nameBytes, 5, 'SHA-1');
}
// This is the core method for generating the UUID, based on the supplied namespace and name.
// It validates the UUID version and computes the hash.
static async nameBasedUuid(namespaceUuid, nameBytes, uuidVersion, cryp) {
if (uuidVersion !== 5) throw new Error("version must be 5");
const ns = this.parseUuidToBytes(namespaceUuid);
const digest = await this.computeHash(ns, nameBytes, cryp);
const out = new Uint8Array(16);
out.set(digest.subarray(0, 16));
// Set version and variant (IETF RFCs)
out[6] = (out[6] & 0x0F) | (uuidVersion << 4);
out[8] = (out[8] & 0x3F) | 0x80;
return this.formatUuid(out);
}
// This parses the UUID string into a Uint8Array of bytes.
static parseUuidToBytes(uuid) {
const s = uuid.toLowerCase();
if (s.length !== 36 || s[8] !== '-' || s[13] !== '-' || s[18] !== '-' || s[23] !== '-') {
throw new Error("Invalid UUID string: " + uuid);
}
const hex = s.replace(/-/g, "");
if (hex.length !== 32) throw new Error("Invalid UUID string: " + uuid);
const bytes = new Uint8Array(16);
for (let i = 0; i < 16; i++) {
const hi = this.hexVal(hex.charAt(i * 2));
const lo = this.hexVal(hex.charAt(i * 2 + 1));
bytes[i] = (hi << 4) | lo;
}
return bytes;
}
// This converts a hexadecimal character to its numeric value.
static hexVal(c) {
if (c >= '0' && c <= '9') return c.charCodeAt(0) - '0'.charCodeAt(0);
if (c >= 'a' && c <= 'f') return 10 + (c.charCodeAt(0) - 'a'.charCodeAt(0));
throw new Error("Invalid hex: " + c);
}
// This formats the Uint8Array of bytes into the standard UUID string representation.
static formatUuid(rawBytes) {
let formattedString = '';
for (let i = 0; i < rawBytes.length; i++) {
if (i === 4 || i === 6 || i === 8 || i === 10) formattedString += '-';
const interim = rawBytes[i] & 0xFF;
formattedString += ((interim >>> 4) & 0xF).toString(16);
formattedString += (interim & 0xF).toString(16);
}
return formattedString;
}
// This computes the cryptographic hash of the namespace and name bytes using the specified
// hash function, which will be SHA-1 because we want a version-5 UUID.
static async computeHash(ns, nameBytes, cryp) {
const hashBuffer = await crypto.subtle.digest(cryp, new Uint8Array([...ns, ...nameBytes]));
return new Uint8Array(hashBuffer);
}
}
Invocation of the code example



(async () => {
// The NAMESPACE_IPAL is the namespace that will represent every UUID generated by
// the “Imaginary Power & Light (IPAL)” utility company.
const NAMESPACE_IPAL = await RepeatableUuid.uuidV5(RepeatableUuid.NAMESPACE_DNS,
"imaginary-power-and-light.com");
console.log("UUID for Imaginary Power & Light (IPAL): " + NAMESPACE_IPAL);
// The NAMESPACE_IPAL is used to generate the UUID for “Electric Meter 0001” at
// this “1234 SAMPLETON RD APT A…” apartment service location.
const meterElectric0001 = await RepeatableUuid.uuidV5(NAMESPACE_IPAL,
"1234 SAMPLETON RD APT A, ATLANTIS DC 20000 -- Electric Meter 0001");
console.log("UUID for Electric Meter 0001: " + meterElectric0001);
// The NAMESPACE_IPAL is used to generate the UUID for “Natural Gas Meter 0001” at
// this “1234 SAMPLETON RD APT A…” apartment service location.
const meterGas0001 = await RepeatableUuid.uuidV5(NAMESPACE_IPAL,
"1234 SAMPLETON RD APT A, ATLANTIS DC 20000 -- Natural Gas Meter 0001");
console.log("UUID for Natural Gas Meter 0001: " + meterGas0001);
// The NAMESPACE_IPAL is used to generate the UUID for Customer Account “87654-3210-3001”
// (it is not specific to one service location).
const customer = await RepeatableUuid.uuidV5(NAMESPACE_IPAL, "87654-3210-3001");
console.log("UUID for Customer “87654-3210-3001”: " + customer);
})();
Output of the code example
UUID for Imaginary Light & Power (ILAP): f30ad8b1-8eb6-56d9-a95e-aba961ce074f
UUID for Electric Meter 0001: bba37b63-e44f-5643-9679-a288b2d6c4bf
UUID for Natural Gas Meter 0001: f27552bb-a155-5ff9-995a-edb4b330ab4f
UUID for Customer “87654-3210-3001”: d56bcb52-449b-5b60-be38-eb0e9a275b9a
- ^ While SHA-1 is technically not secure anymore, for the purpose of creating UUIDs that insecurity is irrelevant because the resultant is truncated (part of the value is deleted) and thus not reversible.
- ^ Any and all coding examples provided by the Green Button Alliance are for educational purposes only. They are provided strictly as a way to clarify and illuminate one or more ways to accomplish the text and intent of the article/page on which they are shown. They are not to be used in actual deployments as-is, without modifications, for any purpose; and the Green Button Alliance disclaims any responsibility for code examples used in any deployments internally and/or exposed to the public. It is the responsibility of the users of any code to ensure it meets the purpose, safety, and merchantability for which it is being considered by the user. The Green Button Alliance further notes that code may or may not be free of errors for the intent of the examples or for inadvertent results unrelated to the examples. Users of any and all code provided as examples assume all responsibility for their use or development that includes the example code.