diff --git a/homeassistant/core.py b/homeassistant/core.py index 7003b87ce67..b2525e2f096 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -871,7 +871,7 @@ class Event: self.origin = origin self.time_fired = time_fired or dt_util.utcnow() self.context: Context = context or Context( - id=ulid_util.ulid(dt_util.utc_to_timestamp(self.time_fired)) + id=ulid_util.ulid_at_time(dt_util.utc_to_timestamp(self.time_fired)) ) def as_dict(self) -> dict[str, Any]: @@ -1533,7 +1533,7 @@ class StateMachine: now = dt_util.utcnow() if context is None: - context = Context(id=ulid_util.ulid(dt_util.utc_to_timestamp(now))) + context = Context(id=ulid_util.ulid_at_time(dt_util.utc_to_timestamp(now))) state = State( entity_id, new_state, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 4a00b05d21b..5d05bec39c4 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -44,6 +44,7 @@ requests==2.28.1 scapy==2.5.0 sqlalchemy==2.0.4 typing-extensions>=4.5.0,<5.0 +ulid-transform==0.3.1 voluptuous-serialize==2.6.0 voluptuous==0.13.1 yarl==1.8.1 diff --git a/homeassistant/util/ulid.py b/homeassistant/util/ulid.py index d40b0f48e16..304a42ec610 100644 --- a/homeassistant/util/ulid.py +++ b/homeassistant/util/ulid.py @@ -1,21 +1,11 @@ """Helpers to generate ulids.""" from __future__ import annotations -from random import getrandbits import time +from ulid_transform import ulid_at_time, ulid_hex -def ulid_hex() -> str: - """Generate a ULID in lowercase hex that will work for a UUID. - - This ulid should not be used for cryptographically secure - operations. - - This string can be converted with https://github.com/ahawker/ulid - - ulid.from_uuid(uuid.UUID(ulid_hex)) - """ - return f"{int(time.time()*1000):012x}{getrandbits(80):020x}" +__all__ = ["ulid", "ulid_hex", "ulid_at_time"] def ulid(timestamp: float | None = None) -> str: @@ -35,41 +25,4 @@ def ulid(timestamp: float | None = None) -> str: import ulid ulid.parse(ulid_util.ulid()) """ - ulid_bytes = int((timestamp or time.time()) * 1000).to_bytes( - 6, byteorder="big" - ) + int(getrandbits(80)).to_bytes(10, byteorder="big") - - # This is base32 crockford encoding with the loop unrolled for performance - # - # This code is adapted from: - # https://github.com/ahawker/ulid/blob/06289583e9de4286b4d80b4ad000d137816502ca/ulid/base32.py#L102 - # - enc = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" - return ( - enc[(ulid_bytes[0] & 224) >> 5] - + enc[ulid_bytes[0] & 31] - + enc[(ulid_bytes[1] & 248) >> 3] - + enc[((ulid_bytes[1] & 7) << 2) | ((ulid_bytes[2] & 192) >> 6)] - + enc[((ulid_bytes[2] & 62) >> 1)] - + enc[((ulid_bytes[2] & 1) << 4) | ((ulid_bytes[3] & 240) >> 4)] - + enc[((ulid_bytes[3] & 15) << 1) | ((ulid_bytes[4] & 128) >> 7)] - + enc[(ulid_bytes[4] & 124) >> 2] - + enc[((ulid_bytes[4] & 3) << 3) | ((ulid_bytes[5] & 224) >> 5)] - + enc[ulid_bytes[5] & 31] - + enc[(ulid_bytes[6] & 248) >> 3] - + enc[((ulid_bytes[6] & 7) << 2) | ((ulid_bytes[7] & 192) >> 6)] - + enc[(ulid_bytes[7] & 62) >> 1] - + enc[((ulid_bytes[7] & 1) << 4) | ((ulid_bytes[8] & 240) >> 4)] - + enc[((ulid_bytes[8] & 15) << 1) | ((ulid_bytes[9] & 128) >> 7)] - + enc[(ulid_bytes[9] & 124) >> 2] - + enc[((ulid_bytes[9] & 3) << 3) | ((ulid_bytes[10] & 224) >> 5)] - + enc[ulid_bytes[10] & 31] - + enc[(ulid_bytes[11] & 248) >> 3] - + enc[((ulid_bytes[11] & 7) << 2) | ((ulid_bytes[12] & 192) >> 6)] - + enc[(ulid_bytes[12] & 62) >> 1] - + enc[((ulid_bytes[12] & 1) << 4) | ((ulid_bytes[13] & 240) >> 4)] - + enc[((ulid_bytes[13] & 15) << 1) | ((ulid_bytes[14] & 128) >> 7)] - + enc[(ulid_bytes[14] & 124) >> 2] - + enc[((ulid_bytes[14] & 3) << 3) | ((ulid_bytes[15] & 224) >> 5)] - + enc[ulid_bytes[15] & 31] - ) + return ulid_at_time(timestamp or time.time()) diff --git a/pyproject.toml b/pyproject.toml index a3d6d2f2446..7a0aff9ce9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ dependencies = [ "pyyaml==6.0", "requests==2.28.1", "typing-extensions>=4.5.0,<5.0", + "ulid-transform==0.3.1", "voluptuous==0.13.1", "voluptuous-serialize==2.6.0", "yarl==1.8.1", diff --git a/requirements.txt b/requirements.txt index aa6e85d1520..31831a93e2a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,6 +24,7 @@ python-slugify==4.0.1 pyyaml==6.0 requests==2.28.1 typing-extensions>=4.5.0,<5.0 +ulid-transform==0.3.1 voluptuous==0.13.1 voluptuous-serialize==2.6.0 yarl==1.8.1