Measured on a real installation with two bikes: the two "Last Seen" sensors were the number one and number two writers in the entire Home Assistant database, ahead of a three-phase energy meter. The integration accounted for 26.5% of all recorder writes on that system. Last Seen moved on every BLE notification, about once a second, or 2834 rows an hour per bike. The entity exists to say how fresh the values are, which needs nothing near that resolution, so it now advances at LAST_SEEN_RESOLUTION granularity. `self._last_seen` stays exact - that is what gets persisted and restored - only the entity is throttled. Measured after: 120 rows an hour, the theoretical maximum for a 30s window. The distance sensors carried four decimals of false precision, because the ebikemotion frames divide by 10000: a range that is really "14.6" was reported as 14.6065, then 14.6529, then 14.6297, each one another state write and another database row. They are rounded to 0.1 km now. Home Assistant offers integrations no way to exclude an entity from the recorder - `entity_filter` comes from user configuration only, and `_unrecorded_attributes` covers attributes rather than states - so reducing how often the state changes is the only fix that works without asking every user to edit configuration.yaml. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FyabWZzd7HoLpyBwEa5Zzh
67 lines
2.5 KiB
Python
67 lines
2.5 KiB
Python
"""Constants for the MySmartBike BLE integration."""
|
|
from typing import Final
|
|
|
|
DOMAIN: Final = "mysmartbike_ble"
|
|
|
|
# BLE UUIDs
|
|
WRITE_UUID: Final = "0000FFE2-0000-1000-8000-00805F9B34FB"
|
|
NOTIFY_UUID: Final = "0000FFD1-0000-1000-8000-00805F9B34FB"
|
|
|
|
# BLE Messages
|
|
VIN_REQUEST_MESSAGE: Final = bytearray([0x24, 0x53, 0x24, 0x56, 0x23, 0x40]) # $S$V#@
|
|
PROTOCOL_REQUEST_MESSAGE: Final = bytearray([0x24, 0x53, 0x24, 0x50, 0x23, 0x40]) # $S$P#@
|
|
CLOSE_MESSAGE: Final = bytearray([0x24, 0x44, 0x24, 0x49, 0x23, 0x40]) # $D$I#@
|
|
|
|
# Legacy alias
|
|
WAKEUP_MESSAGE: Final = VIN_REQUEST_MESSAGE
|
|
|
|
# Message lengths
|
|
BATTERY_MESSAGE_LENGTH: Final = 17
|
|
MOTOR_MESSAGE_LENGTH: Final = 18
|
|
EBM_MESSAGE_LENGTH: Final = 17
|
|
|
|
# Connection settings
|
|
MAX_CONNECT_ATTEMPTS: Final = 3
|
|
BLACKLIST_DURATION: Final = 300 # 5 minutes in seconds
|
|
CONNECTION_TIMEOUT: Final = 120 # seconds
|
|
SCAN_INTERVAL: Final = 30 # seconds
|
|
|
|
# Device info
|
|
MANUFACTURER: Final = "Mahle"
|
|
MODEL: Final = "iWoc BLE"
|
|
|
|
# Config entry keys
|
|
CONF_DEVICE_NAME: Final = "device_name"
|
|
CONF_DEVICE_ADDRESS: Final = "device_address"
|
|
|
|
# Options
|
|
CONF_LOG_BLE_MESSAGES: Final = "log_ble_messages"
|
|
|
|
# Persistence
|
|
STORAGE_VERSION: Final = 1
|
|
STORAGE_SAVE_DELAY: Final = 60 # seconds; BLE notifications arrive far too often to save eagerly
|
|
|
|
# Top-level parser state keys that survive a restart. "motor" and "assist" are
|
|
# deliberately absent: a restored speed or power reading would look like live
|
|
# data from a bike that is actually parked.
|
|
RESTORE_STATE_KEYS: Final = ("battery_primary", "battery_secondary", "ebm")
|
|
|
|
# Fields inside the restored dicts that describe an instantaneous condition and
|
|
# are therefore dropped (set to None) when reading the state back.
|
|
VOLATILE_FIELDS: Final[dict[str, tuple[str, ...]]] = {
|
|
"battery_primary": ("current", "is_charging"),
|
|
"battery_secondary": ("current", "is_charging"),
|
|
"ebm": ("status", "accel_y", "accel_z"),
|
|
}
|
|
|
|
# A link that survived at least this long is worth reconnecting immediately when
|
|
# it drops; anything shorter is left to the regular poll so a bike that cannot
|
|
# hold a connection does not spin in a reconnect loop.
|
|
MIN_LINK_SECONDS_FOR_FAST_RECONNECT: Final = 5.0
|
|
|
|
# Granularity of the "Last Seen" entity. Notifications arrive about once a
|
|
# second; publishing each one made this timestamp the single biggest recorder
|
|
# writer in a real installation. Its job - telling you how fresh the values are
|
|
# - needs nothing near that resolution. The precise value is still persisted.
|
|
LAST_SEEN_RESOLUTION: Final = 30 # seconds
|