mirror of
https://github.com/basnijholt/adaptive-lighting.git
synced 2026-09-11 22:34:04 +02:00
Clean up context ID generation.
Using a sha256 hash is major overkill for the purposes of finding a short hash of a string. This change switches _short_hash to use Python's built-in hash() function (used for hashing in dicts and sets). which works just fine for this, and is much faster. In addition, since context IDs are length-limited, use base-85 encoding to pack as many bits of the hash and index into the context as possible.
This commit is contained in:
parent
8c39662318
commit
c785dcd202
1 changed files with 23 additions and 10 deletions
|
|
@ -2,6 +2,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import bisect
|
||||
from collections import defaultdict
|
||||
from copy import deepcopy
|
||||
|
|
@ -9,7 +10,6 @@ from dataclasses import dataclass
|
|||
import datetime
|
||||
from datetime import timedelta
|
||||
import functools
|
||||
import hashlib
|
||||
import logging
|
||||
import math
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
|
@ -163,10 +163,17 @@ BRIGHTNESS_ATTRS = {
|
|||
# Keep a short domain version for the context instances (which can only be 36 chars)
|
||||
_DOMAIN_SHORT = "adapt_lgt"
|
||||
|
||||
def _int_to_bytes(i: int, signed: bool = False) -> bytes:
|
||||
bits = i.bit_length()
|
||||
if signed:
|
||||
# Make room for the sign bit.
|
||||
bits += 1
|
||||
return i.to_bytes((bits + 7) // 8, 'little', signed=signed)
|
||||
|
||||
def _short_hash(string: str, length: int = 4) -> str:
|
||||
"""Create a hash of 'string' with length 'length'."""
|
||||
return hashlib.sha1(string.encode("UTF-8")).hexdigest()[:length]
|
||||
str_hash_bytes = _int_to_bytes(hash(string), signed=True)
|
||||
return base64.b85encode(str_hash_bytes)[:length]
|
||||
|
||||
|
||||
def create_context(name: str, which: str, index: int) -> Context:
|
||||
|
|
@ -174,7 +181,11 @@ def create_context(name: str, which: str, index: int) -> Context:
|
|||
# Use a hash for the name because otherwise the context might become
|
||||
# too long (max len == 36) to fit in the database.
|
||||
name_hash = _short_hash(name)
|
||||
return Context(id=f"{_DOMAIN_SHORT}_{name_hash}_{which}_{index}")
|
||||
# Pack index with base85 to maximize the number of contexts we can create
|
||||
# before we exceed the 36-character limit and are forced to wrap.
|
||||
index_packed = base64.b85encode(_int_to_bytes(index, signed=False))
|
||||
context_id = f"{_DOMAIN_SHORT}:{name_hash}:{which}:{index_packed}"[:36]
|
||||
return Context(id=context_id)
|
||||
|
||||
|
||||
def is_our_context(context: Optional[Context]) -> bool:
|
||||
|
|
@ -635,13 +646,15 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
def create_context(self, which: str = "default") -> Context:
|
||||
"""Create a context that identifies this Adaptive Lighting instance."""
|
||||
# Right now the highest number of each context_id it can create is
|
||||
# 'adapt_lgt_XXXX_turn_on_9999999999999'
|
||||
# 'adapt_lgt_XXXX_interval_999999999999'
|
||||
# 'adapt_lgt_XXXX_adapt_lights_99999999'
|
||||
# 'adapt_lgt_XXXX_sleep_999999999999999'
|
||||
# 'adapt_lgt_XXXX_light_event_999999999'
|
||||
# 'adapt_lgt_XXXX_service_9999999999999'
|
||||
# So 100 million calls before we run into the 36 chars limit.
|
||||
# 'adapt_lgt:XXXX:turn_on:*************'
|
||||
# 'adapt_lgt:XXXX:interval:************'
|
||||
# 'adapt_lgt:XXXX:adapt_lights:********'
|
||||
# 'adapt_lgt:XXXX:sleep:***************'
|
||||
# 'adapt_lgt:XXXX:light_event:*********'
|
||||
# 'adapt_lgt:XXXX:service:*************'
|
||||
# The smallest space we have is for adapt_lights, which has
|
||||
# 8 characters. In base85 encoding, that's enough space to hold values
|
||||
# up to 2**48 - 1, which should give us plenty of calls before we wrap.
|
||||
context = create_context(self._name, which, self._context_cnt)
|
||||
self._context_cnt += 1
|
||||
return context
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue