Skip to main content

Core weight routing primitives for multi-user scale integrations

Project description

multi-user-scale-core

Some smart scale apps can't tell who's standing on the scale. This library solves the routing problem: given an incoming weight measurement and a set of users with history, it returns a ranked list of likely owners. Pure Python, no runtime dependencies. Fully typed (PEP 561).

Features

  • WeightRouter: Route incoming weight measurements to users using adaptive tolerance.
  • Adaptive tolerance: Exponentially-weighted reference weight, variance-based tolerance, and recency scaling that automatically widens the window when a user hasn't weighed in recently.
  • Persistence: to_dict() / from_dict() for saving and restoring router state across restarts.
  • Models: WeightMeasurement, UserProfile, RouterConfig, MeasurementCandidate.

Buy Me A Coffee

Installation

Requires Python 3.10+. Install using pip:

pip install multi-user-scale-core

Quick Start

from multi_user_scale_core import RouterConfig, UserProfile, WeightMeasurement, WeightRouter
from datetime import datetime, timezone

router = WeightRouter(config=RouterConfig())
router.set_users([
    UserProfile(user_id="alice", display_name="Alice"),
    UserProfile(user_id="bob", display_name="Bob"),
])

# Evaluate an incoming measurement (e.g. from a scale sensor)
measurement = WeightMeasurement(
    weight_kg=75.2,
    timestamp=datetime.now(tz=timezone.utc),
    source_id="sensor.scale",
)
candidates = router.evaluate_measurement(measurement)
# candidates: list[MeasurementCandidate], ordered by match quality.
# Matched users come first, sorted by proximity to their reference weight.
# Users with no history yet are appended at the end.
#
# Matched candidates include:
#   .reference_weight_kg  — the weighted-average reference used for comparison
#   .tolerance_kg         — the tolerance band that accepted this reading
# No-history candidates have both fields as None.

# Once confirmed (e.g. by the user), record the measurement
router.record_measurement_for_user("alice", measurement)

Usage

Reassigning and removing measurements

# Move the latest measurement from alice to bob (e.g. after user correction)
router.reassign_measurement("alice", "bob")

# Move a specific measurement by ID
router.reassign_measurement("alice", "bob", measurement_id="abc123")

# Remove the latest measurement for a user
router.remove_measurement("alice")

# Remove a specific measurement by ID
router.remove_measurement("alice", measurement_id="abc123")

Managing users

router.set_users([
    UserProfile(user_id="alice", display_name="Alice"),
    UserProfile(user_id="bob", display_name="Bob"),
])

Note: set_users() replaces the entire user list. History for any user not present in the new list is permanently discarded. Call to_dict() first if you need to preserve that history.

Persistence

# Serialise state (e.g. to Home Assistant config entry data)
payload = router.to_dict()

# Restore state
router = WeightRouter.from_dict(payload)

# Inject a custom clock (useful in tests or when the stored "now" matters
# for pruning stale history on first mutation after restore)
router = WeightRouter.from_dict(payload, now_provider=lambda: my_fixed_time)

to_dict() includes a "now" snapshot timestamp for human inspection. It is not used during from_dict() restoration.

Configuration

config = RouterConfig(
    history_retention_days=90,       # drop measurements older than this
    max_history_size=100,            # cap per-user history length
    tolerance_percentage=0.04,       # base tolerance as fraction of body weight
    min_tolerance_kg=1.5,            # floor on tolerance regardless of body weight
    variance_window_days=30,         # window for variance-based adaptive tolerance
    reference_window_days=7,         # window for computing the reference weight
    min_measurements_for_adaptive=5, # minimum history needed for variance adaptation
)

Default tolerance constants are exported for convenience:

from multi_user_scale_core import (
    DEFAULT_TOLERANCE_PERCENTAGE,  # 0.04
    MIN_TOLERANCE_KG,              # 1.5
    MIN_MEASUREMENTS_FOR_ADAPTIVE, # 5
    REFERENCE_WINDOW_DAYS,         # 7
    VARIANCE_WINDOW_DAYS,          # 30
)

Error handling

All errors inherit from RouterError:

from multi_user_scale_core import (
    DuplicateMeasurementError,    # measurement_id already exists in history
    MeasurementNotFoundError,     # referenced measurement does not exist
    MeasurementValidationError,   # weight is NaN, infinite, or not a number
    RouterError,                  # base class
    UserNotFoundError,            # user_id not registered with set_users()
)

Compatibility

  • Python 3.10+
  • No runtime dependencies

Support the Project

If you find this project helpful, consider buying me a coffee! Your support helps maintain and improve this library.

Buy Me A Coffee

License

This project is licensed under the MIT License - see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

multi_user_scale_core-0.1.0.tar.gz (16.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

multi_user_scale_core-0.1.0-py3-none-any.whl (11.7 kB view details)

Uploaded Python 3

File details

Details for the file multi_user_scale_core-0.1.0.tar.gz.

File metadata

  • Download URL: multi_user_scale_core-0.1.0.tar.gz
  • Upload date:
  • Size: 16.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for multi_user_scale_core-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a1fa8d0b56c0427d9646803f4068256dd9d977733c9163f7c9e5fbe448181300
MD5 27929c3652626dbb6dfe86827737c7d4
BLAKE2b-256 bc6a8006e4a6e19ecdec3f645e20b4f01955b23cacfa703c122bdbc41b767f57

See more details on using hashes here.

File details

Details for the file multi_user_scale_core-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for multi_user_scale_core-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2f915068d1212d73b804ec17bffe550fce33fdec53ddf5bc5897836279aa8686
MD5 4a69d1dd14ed299b4699faa7f356fa08
BLAKE2b-256 13547f25dc14d8523152c2faaa76d005830c9c7b72847008341a6c434646fab1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page