Skip to content

v9.2.0

Latest

Choose a tag to compare

@github-actions github-actions released this 06 Jul 17:39

Changed

  • CLI formatting stacks merged; Rich is the sole human renderer
    (#100 <https://github.com/eman/nwp500-python/issues/100>_): a new
    src/nwp500/cli/presentation.py owns all data-shaping for CLI output
    (field selection, labels, units, ordering, value formatting and energy
    aggregation) as presentation-neutral structures. Because the CLI
    hard-requires rich (there is no plain-text fallback), the redundant
    plain-text human renderer and the Rich-vs-plain fallback machinery were
    removed: rich_output.py no longer contains _should_use_rich,
    _rich_available, the NWP500_NO_RICH toggle, or any
    _print_*_plain methods, and now renders the neutral structures
    (including the energy TOTAL SUMMARY) exclusively with Rich, consuming
    the presentation dataclasses directly instead of dict adapters.
    output_formatters.py keeps only JSON/CSV rendering plus thin
    human-output dispatch. Net CLI code drops from ~2088 to ~1838 lines with a
    single data-shaping layer and a single human renderer. Non-energy output
    is byte-for-byte unchanged (verified by golden capture and the existing
    CLI tests); energy output now renders a summary table plus a breakdown
    table entirely in Rich rather than printing plain text and a Rich table.

  • mqtt/client.py slimmed toward a thin façade (#99 <https://github.com/eman/nwp500-python/issues/99>_): the ~40 device
    control command proxies and the typed subscribe_*/unsubscribe_*
    device-subscription proxies were moved out of NavienMqttClient into
    two focused mixins, DeviceControlCommandsMixin
    (mqtt/_control_commands.py) and DeviceSubscriptionsMixin
    (mqtt/_device_subscriptions.py). NavienMqttClient now inherits
    both, so its public API is unchanged, while mqtt/client.py shrinks
    from ~1572 to ~1196 lines and reads more clearly as connection
    orchestration plus a public façade. No behavior change.

  • Event system relationship clarified and de-duplicated (#102 <https://github.com/eman/nwp500-python/issues/102>_): events.py and
    mqtt_events.py are not two competing event mechanisms.
    EventEmitter (events.py) is the sole delivery mechanism, while
    mqtt_events.py provides the event-name registry
    (MqttClientEvents) and the typed dataclass payloads it carries.
    Internal emit call sites in mqtt/state_tracker.py,
    mqtt/client.py and mqtt/subscriptions.py now reference the
    MqttClientEvents constants instead of duplicating the raw event-name
    strings, so each event name is defined in exactly one place. Module
    docstrings and the event-system reference docs were updated to document
    the relationship, and typed-payload delivery is now covered by tests.

  • awscrt types wrapped out of public MQTT signatures (#101 <https://github.com/eman/nwp500-python/issues/101>_): the library no
    longer exposes awscrt SDK types on its own public/semi-public API
    surface. A library-owned nwp500.mqtt.QoS IntEnum (also exported
    as nwp500.QoS) now replaces awscrt.mqtt.QoS on the publish
    and subscribe methods of NavienMqttClient, MqttConnection
    and MqttSubscriptionManager, on MqttCommandQueue.enqueue and on
    the QueuedCommand dataclass. awscrt.mqtt.Connection handles are
    typed behind the MqttConnectionHandle alias, and translation to/from
    awscrt happens only at the connection-layer boundary
    (nwp500/mqtt/types.py). NavienMqttClient.publish now wraps
    otherwise-uncaught AwsCrtError in MqttPublishError so awscrt
    exceptions no longer leak out of the public boundary.

    .. code-block:: python

    OLD

    from awscrt import mqtt
    await client.publish(topic, payload, qos=mqtt.QoS.AT_LEAST_ONCE)

    NEW

    from nwp500 import QoS
    await client.publish(topic, payload, qos=QoS.AT_LEAST_ONCE)

Fixed

  • MQTT ack futures now consumed on abandonment (#97 <https://github.com/eman/nwp500-python/issues/97>_): _await_ack()
    in mqtt/connection.py and the inline subscribe/unsubscribe
    acknowledgement waits in mqtt/subscriptions.py shield the AWS CRT
    future so a timeout or cancellation doesn't propagate into the SDK
    future. Previously, if the shielded future later completed with an
    exception (e.g. AwsCrtError from clean-session cancellation during
    reconnect) after the awaiting task had already given up, nobody
    retrieved that exception, and asyncio logged a "Future exception was
    never retrieved" warning at garbage-collection time. A done callback
    is now attached whenever a wait is abandoned so the eventual
    result/exception is always retrieved and logged at debug level
    instead of leaking as an unhandled asyncio warning.

Documentation

  • Unit-system preference is a deliberate process-wide global (#103 <https://github.com/eman/nwp500-python/issues/103>_): clarified and
    locked in that the preference in nwp500.unit_system is an intentional
    process-wide module-level global rather than a
    contextvars.ContextVar. A context-local value silently reverted to
    auto-detect for real-time data because MQTT message handling runs in tasks
    scheduled from AWS CRT callback threads whose context never inherits from
    the application task. Added prominent docstring notes to the affected
    models (DeviceStatus, DeviceFeature, ReservationEntry,
    WeeklyReservationEntry, and TOUPeriod) explaining that unit-aware
    computed fields read the preference at access time and share it across all
    async tasks and threads. This is a non-breaking documentation and test
    change; no API changes.

Added

  • Tests for process-wide unit-system semantics (#103 <https://github.com/eman/nwp500-python/issues/103>_): new
    tests/test_unit_system_process_wide.py verifies that setting the global
    preference affects already-constructed model instances at access time and
    that the preference is visible across async tasks and threads.