Skip to main content

Core weight routing primitives for multi-user scale integrations

Project description

multi-user-scale-core

PyPI version

Not every smart scale app or integration surfaces who's standing on the scale. This library solves that: given a weight reading 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
    MAX_TOLERANCE_KG,              # 5.0
    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.1.tar.gz (16.7 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.1-py3-none-any.whl (11.7 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for multi_user_scale_core-0.1.1.tar.gz
Algorithm Hash digest
SHA256 9a1d268e274a6fe4a6d1ff2fc25bbd6b47f09e5f4278ee932a90e2a43f10fc37
MD5 44eea456d23c060de64ff44e29f8836c
BLAKE2b-256 68ea065d3e68e201c345a986327f5950ef1b4f502c40e46b17ec02ec082edb79

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for multi_user_scale_core-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 69c4a4e4239fe51ec774c4e21d1a230c0d2aa8cdc4527efa5fd113849364ec6c
MD5 d48b02b1ec532b1ff71d14c5a938d673
BLAKE2b-256 4ac6ecb861e05ec3ea137456cd7d1e1010b01429292016484023ece08b03ddca

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