nwp500-python 9.3.0__py3-none-any.whl → 9.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nwp500/__init__.py +4 -0
- nwp500/cli/__main__.py +74 -15
- nwp500/cli/handlers.py +9 -5
- nwp500/models/__init__.py +4 -0
- nwp500/models/device.py +49 -2
- nwp500/models/feature.py +6 -1
- nwp500/models/tou.py +6 -5
- nwp500/mqtt/_control_commands.py +0 -8
- nwp500/mqtt/_device_subscriptions.py +6 -5
- nwp500/mqtt/control.py +0 -32
- nwp500/mqtt/subscriptions.py +7 -6
- nwp500/reservations.py +3 -1
- {nwp500_python-9.3.0.dist-info → nwp500_python-9.3.2.dist-info}/METADATA +1 -1
- {nwp500_python-9.3.0.dist-info → nwp500_python-9.3.2.dist-info}/RECORD +18 -18
- {nwp500_python-9.3.0.dist-info → nwp500_python-9.3.2.dist-info}/WHEEL +1 -1
- {nwp500_python-9.3.0.dist-info → nwp500_python-9.3.2.dist-info}/entry_points.txt +0 -0
- {nwp500_python-9.3.0.dist-info → nwp500_python-9.3.2.dist-info}/licenses/LICENSE.txt +0 -0
- {nwp500_python-9.3.0.dist-info → nwp500_python-9.3.2.dist-info}/top_level.txt +0 -0
nwp500/__init__.py
CHANGED
|
@@ -75,7 +75,9 @@ from nwp500.factory import (
|
|
|
75
75
|
)
|
|
76
76
|
from nwp500.models import (
|
|
77
77
|
ConvertedTOUPlan,
|
|
78
|
+
DescalingInfo,
|
|
78
79
|
Device,
|
|
80
|
+
DeviceErrorSummary,
|
|
79
81
|
DeviceFeature,
|
|
80
82
|
DeviceInfo,
|
|
81
83
|
DeviceStatus,
|
|
@@ -147,6 +149,8 @@ __all__ = [
|
|
|
147
149
|
"DeviceInfo",
|
|
148
150
|
"Location",
|
|
149
151
|
"Device",
|
|
152
|
+
"DeviceErrorSummary",
|
|
153
|
+
"DescalingInfo",
|
|
150
154
|
"FirmwareInfo",
|
|
151
155
|
"ReservationEntry",
|
|
152
156
|
"ReservationSchedule",
|
nwp500/cli/__main__.py
CHANGED
|
@@ -143,6 +143,11 @@ def async_command(f: Any) -> Any:
|
|
|
143
143
|
await mqtt.disconnect()
|
|
144
144
|
return 0
|
|
145
145
|
|
|
146
|
+
except click.ClickException:
|
|
147
|
+
# A usage/config error a command raised deliberately. Let
|
|
148
|
+
# Click report it as itself; the catch-all below would
|
|
149
|
+
# otherwise log it as an "Unexpected Error" with a traceback.
|
|
150
|
+
raise
|
|
146
151
|
except (
|
|
147
152
|
InvalidCredentialsError,
|
|
148
153
|
AuthenticationError,
|
|
@@ -639,14 +644,69 @@ async def tou_apply(
|
|
|
639
644
|
_logger.error("API client not available")
|
|
640
645
|
|
|
641
646
|
|
|
642
|
-
|
|
647
|
+
def _parse_months(
|
|
648
|
+
ctx: click.Context, param: click.Parameter, value: str | None
|
|
649
|
+
) -> list[int] | None:
|
|
650
|
+
"""Parse ``--months`` at parse time, so bad input is a usage error."""
|
|
651
|
+
if value is None:
|
|
652
|
+
return None
|
|
653
|
+
months = []
|
|
654
|
+
for raw in value.split(","):
|
|
655
|
+
try:
|
|
656
|
+
month = int(raw.strip())
|
|
657
|
+
except ValueError:
|
|
658
|
+
raise click.BadParameter(
|
|
659
|
+
f"{raw.strip()!r} is not a month number; expected a "
|
|
660
|
+
"comma-separated list like 1,2,3"
|
|
661
|
+
) from None
|
|
662
|
+
if not 1 <= month <= 12:
|
|
663
|
+
raise click.BadParameter(f"{month} is not in the range 1-12")
|
|
664
|
+
months.append(month)
|
|
665
|
+
if not months:
|
|
666
|
+
raise click.BadParameter("at least one month is required")
|
|
667
|
+
return months
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
class _EnergySelection(click.Command):
|
|
671
|
+
"""Rejects an unusable --month/--months combination during parsing.
|
|
672
|
+
|
|
673
|
+
Cross-option checks have no natural home in a per-option callback, and
|
|
674
|
+
the command body is the wrong place: it runs inside ``async_command``,
|
|
675
|
+
after authentication and the MQTT connect, so a usage error there would
|
|
676
|
+
surface only to users whose credentials already work - and then as an
|
|
677
|
+
"Unexpected Error" traceback, because the wrapper's catch-all swallows
|
|
678
|
+
``ClickException``.
|
|
679
|
+
"""
|
|
680
|
+
|
|
681
|
+
def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
|
|
682
|
+
rest = super().parse_args(ctx, args)
|
|
683
|
+
month, months = ctx.params.get("month"), ctx.params.get("months")
|
|
684
|
+
if month is not None and months is not None:
|
|
685
|
+
raise click.UsageError(
|
|
686
|
+
"Use either --month (daily breakdown) or --months "
|
|
687
|
+
"(monthly summary), not both",
|
|
688
|
+
ctx=ctx,
|
|
689
|
+
)
|
|
690
|
+
if month is None and months is None:
|
|
691
|
+
raise click.UsageError(
|
|
692
|
+
"Either --months (for monthly summary) or --month "
|
|
693
|
+
"(for daily breakdown) is required",
|
|
694
|
+
ctx=ctx,
|
|
695
|
+
)
|
|
696
|
+
return rest
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
@cli.command(cls=_EnergySelection) # type: ignore[attr-defined]
|
|
643
700
|
@click.option("--year", type=int, required=True, help="Year to query")
|
|
644
701
|
@click.option(
|
|
645
|
-
"--months",
|
|
702
|
+
"--months",
|
|
703
|
+
required=False,
|
|
704
|
+
callback=_parse_months,
|
|
705
|
+
help="Comma-separated months for a monthly summary (e.g. 1,2,3)",
|
|
646
706
|
)
|
|
647
707
|
@click.option(
|
|
648
708
|
"--month",
|
|
649
|
-
type=
|
|
709
|
+
type=click.IntRange(1, 12),
|
|
650
710
|
required=False,
|
|
651
711
|
help="Show daily breakdown for a specific month (1-12)",
|
|
652
712
|
)
|
|
@@ -655,26 +715,25 @@ async def energy(
|
|
|
655
715
|
mqtt: NavienMqttClient,
|
|
656
716
|
device: Any,
|
|
657
717
|
year: int,
|
|
658
|
-
months:
|
|
718
|
+
months: list[int] | None,
|
|
659
719
|
month: int | None,
|
|
660
720
|
) -> None:
|
|
661
721
|
"""Query historical energy usage.
|
|
662
722
|
|
|
663
723
|
Use either --months for monthly summary or --month for daily breakdown.
|
|
724
|
+
Which option was passed decides the view, not how many months it names:
|
|
725
|
+
``--months 5`` is a one-month summary, ``--month 5`` is a daily
|
|
726
|
+
breakdown of that month.
|
|
664
727
|
"""
|
|
728
|
+
# _EnergySelection rejected every combination but these two during
|
|
729
|
+
# parsing, so exactly one of the options is set here.
|
|
665
730
|
if month is not None:
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
await handlers.handle_get_energy_request(mqtt, device, year, [month])
|
|
731
|
+
await handlers.handle_get_energy_request(
|
|
732
|
+
mqtt, device, year, [month], daily=True
|
|
733
|
+
)
|
|
670
734
|
elif months is not None:
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
await handlers.handle_get_energy_request(mqtt, device, year, month_list)
|
|
674
|
-
else:
|
|
675
|
-
raise click.ClickException(
|
|
676
|
-
"Either --months (for monthly summary) or --month "
|
|
677
|
-
"(for daily breakdown) required"
|
|
735
|
+
await handlers.handle_get_energy_request(
|
|
736
|
+
mqtt, device, year, months, daily=False
|
|
678
737
|
)
|
|
679
738
|
|
|
680
739
|
|
nwp500/cli/handlers.py
CHANGED
|
@@ -865,12 +865,16 @@ async def handle_tou_apply_request(
|
|
|
865
865
|
|
|
866
866
|
|
|
867
867
|
async def handle_get_energy_request(
|
|
868
|
-
mqtt: NavienMqttClient,
|
|
868
|
+
mqtt: NavienMqttClient,
|
|
869
|
+
device: Device,
|
|
870
|
+
year: int,
|
|
871
|
+
months: list[int],
|
|
872
|
+
daily: bool = False,
|
|
869
873
|
) -> None:
|
|
870
874
|
"""Request energy usage data.
|
|
871
875
|
|
|
872
|
-
If
|
|
873
|
-
|
|
876
|
+
If ``daily`` is set, shows the day-by-day breakdown for ``months[0]``.
|
|
877
|
+
Otherwise shows the monthly summary across the requested months.
|
|
874
878
|
"""
|
|
875
879
|
try:
|
|
876
880
|
res: Any = await _wait_for_response(
|
|
@@ -880,8 +884,8 @@ async def handle_get_energy_request(
|
|
|
880
884
|
action_name="energy usage",
|
|
881
885
|
timeout=15,
|
|
882
886
|
)
|
|
883
|
-
#
|
|
884
|
-
if
|
|
887
|
+
# Daily breakdown only when explicitly requested
|
|
888
|
+
if daily:
|
|
885
889
|
from .output_formatters import print_daily_energy_usage
|
|
886
890
|
|
|
887
891
|
print_daily_energy_usage(
|
nwp500/models/__init__.py
CHANGED
|
@@ -14,7 +14,9 @@ from ._converters import (
|
|
|
14
14
|
)
|
|
15
15
|
from .device import (
|
|
16
16
|
ConnectionStatusField,
|
|
17
|
+
DescalingInfo,
|
|
17
18
|
Device,
|
|
19
|
+
DeviceErrorSummary,
|
|
18
20
|
DeviceInfo,
|
|
19
21
|
FirmwareInfo,
|
|
20
22
|
Location,
|
|
@@ -69,6 +71,8 @@ __all__ = [
|
|
|
69
71
|
"DeviceInfo",
|
|
70
72
|
"Location",
|
|
71
73
|
"Device",
|
|
74
|
+
"DeviceErrorSummary",
|
|
75
|
+
"DescalingInfo",
|
|
72
76
|
"FirmwareInfo",
|
|
73
77
|
"TOUSchedule",
|
|
74
78
|
"ConvertedTOUPlan",
|
nwp500/models/device.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from typing import Annotated, Self
|
|
2
2
|
|
|
3
|
-
from pydantic import BeforeValidator
|
|
3
|
+
from pydantic import BeforeValidator, Field
|
|
4
4
|
|
|
5
5
|
from .._base import NavienBaseModel
|
|
6
6
|
from ..converters import enum_validator
|
|
7
|
-
from ..enums import ConnectionStatus, DeviceType
|
|
7
|
+
from ..enums import ConnectionStatus, DeviceType, ErrorCode
|
|
8
8
|
|
|
9
9
|
ConnectionStatusField = Annotated[
|
|
10
10
|
ConnectionStatus, BeforeValidator(enum_validator(ConnectionStatus))
|
|
@@ -21,6 +21,8 @@ class DeviceInfo(NavienBaseModel):
|
|
|
21
21
|
device_name: str = "Unknown"
|
|
22
22
|
connected: ConnectionStatusField = ConnectionStatus.DISCONNECTED
|
|
23
23
|
install_type: str | None = None
|
|
24
|
+
model_type_code: int | None = None
|
|
25
|
+
installer_id: str | None = None
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
class Location(NavienBaseModel):
|
|
@@ -34,11 +36,56 @@ class Location(NavienBaseModel):
|
|
|
34
36
|
altitude: float | None = None
|
|
35
37
|
|
|
36
38
|
|
|
39
|
+
class DeviceErrorSummary(NavienBaseModel):
|
|
40
|
+
"""Last device fault as reported by the REST API.
|
|
41
|
+
|
|
42
|
+
The cloud keeps this independently of the live MQTT status, so it is
|
|
43
|
+
readable without an MQTT connection - including while the device is
|
|
44
|
+
offline, where it reports the fault as of the last time the device
|
|
45
|
+
was heard from.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
#: ``NO_ERROR`` when the device has no recorded fault, and ``None`` when
|
|
49
|
+
#: the cloud reports no code at all - it sends ``"errorCode": null`` on
|
|
50
|
+
#: some devices, which is not the same claim as "no error". Typed to
|
|
51
|
+
#: accept a bare int as well, following ``device_type``, so neither a
|
|
52
|
+
#: null nor a code the enum does not know can make a whole
|
|
53
|
+
#: ``/device/list`` response unparseable.
|
|
54
|
+
#:
|
|
55
|
+
#: ``union_mode`` matters here: pydantic's default smart union matches an
|
|
56
|
+
#: incoming int against the ``int`` branch exactly and never reaches the
|
|
57
|
+
#: enum, so every code - known or not - would stay a plain int. Trying the
|
|
58
|
+
#: branches left to right instead means a known code becomes an
|
|
59
|
+
#: ``ErrorCode`` member and only an unknown one falls through to ``int``.
|
|
60
|
+
error_code: ErrorCode | int | None = Field(
|
|
61
|
+
default=None, union_mode="left_to_right"
|
|
62
|
+
)
|
|
63
|
+
#: Spelled "Occured" by the API; the Python name is spelled correctly.
|
|
64
|
+
error_occurred_time: str | None = Field(
|
|
65
|
+
default=None, alias="errorOccuredTime"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class DescalingInfo(NavienBaseModel):
|
|
70
|
+
"""Descaling window reported by the REST API.
|
|
71
|
+
|
|
72
|
+
Both timestamps are ``None`` on a device with no descaling scheduled
|
|
73
|
+
or recorded.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
descaling_start_time: str | None = None
|
|
77
|
+
descaling_end_time: str | None = None
|
|
78
|
+
|
|
79
|
+
|
|
37
80
|
class Device(NavienBaseModel):
|
|
38
81
|
"""Complete device information including location."""
|
|
39
82
|
|
|
40
83
|
device_info: DeviceInfo
|
|
41
84
|
location: Location
|
|
85
|
+
#: Present on ``/device/list``; absent from ``/device/info``.
|
|
86
|
+
error: DeviceErrorSummary | None = None
|
|
87
|
+
#: Present on both ``/device/list`` and ``/device/info``.
|
|
88
|
+
descaling: DescalingInfo | None = None
|
|
42
89
|
|
|
43
90
|
def with_info(self, info: DeviceInfo) -> Self:
|
|
44
91
|
"""Return a new Device instance with updated DeviceInfo."""
|
nwp500/models/feature.py
CHANGED
|
@@ -201,7 +201,12 @@ class DeviceFeature(NavienBaseModel):
|
|
|
201
201
|
),
|
|
202
202
|
)
|
|
203
203
|
mixing_valve_use: CapabilityFlag = Field(
|
|
204
|
-
|
|
204
|
+
# The device spells this "Value", not "Valve" - the misspelling is
|
|
205
|
+
# Navien's own (see KDResponseMgppDid$Feature in the vendor app and
|
|
206
|
+
# docs/reference/protocol/device_features.rst). Correcting it here
|
|
207
|
+
# makes the field silently fall back to its default, because the
|
|
208
|
+
# base model ignores unknown keys.
|
|
209
|
+
alias="mixingValueUse",
|
|
205
210
|
default=False,
|
|
206
211
|
description=("Thermostatic mixing valve support (2=supp, 1=not)"),
|
|
207
212
|
)
|
nwp500/models/tou.py
CHANGED
|
@@ -145,11 +145,12 @@ class TOUPeriod(NavienBaseModel):
|
|
|
145
145
|
class TOUReservationSchedule(NavienBaseModel):
|
|
146
146
|
"""TOU schedule as returned by the MQTT ``tou/rd`` response topic.
|
|
147
147
|
|
|
148
|
-
This model matches the raw MQTT payload
|
|
149
|
-
:meth:`~nwp500.NavienMqttClient.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
This model matches the raw MQTT payload the device sends to confirm a
|
|
149
|
+
:meth:`~nwp500.NavienMqttClient.configure_tou_schedule` write. The device
|
|
150
|
+
has no MQTT read for its TOU schedule - ``ctrl/tou/rd`` with
|
|
151
|
+
``CommandCode.TOU_RESERVATION`` is the write - so this payload only ever
|
|
152
|
+
arrives as a write confirmation. To read the current schedule, use the
|
|
153
|
+
REST :meth:`~nwp500.NavienAPIClient.get_tou_info`.
|
|
153
154
|
|
|
154
155
|
The payload structure is::
|
|
155
156
|
|
nwp500/mqtt/_control_commands.py
CHANGED
|
@@ -99,14 +99,6 @@ class DeviceControlCommandsMixin:
|
|
|
99
99
|
device, controller_serial_number, periods, enabled=enabled
|
|
100
100
|
)
|
|
101
101
|
|
|
102
|
-
async def request_tou_settings(
|
|
103
|
-
self, device: Device, controller_serial_number: str
|
|
104
|
-
) -> int:
|
|
105
|
-
"""Request the current TOU settings from the device."""
|
|
106
|
-
return await self._device_controller.request_tou_settings(
|
|
107
|
-
device, controller_serial_number
|
|
108
|
-
)
|
|
109
|
-
|
|
110
102
|
async def set_tou_enabled(self, device: Device, enabled: bool) -> int:
|
|
111
103
|
"""Enable or disable Time-of-Use optimization."""
|
|
112
104
|
return await self._device_controller.set_tou_enabled(device, enabled)
|
|
@@ -194,14 +194,15 @@ class DeviceSubscriptionsMixin:
|
|
|
194
194
|
device: Device,
|
|
195
195
|
callback: Callable[[TOUReservationSchedule], None],
|
|
196
196
|
) -> int:
|
|
197
|
-
"""Subscribe to Time-of-Use schedule
|
|
198
|
-
parsing.
|
|
197
|
+
"""Subscribe to Time-of-Use schedule write confirmations with
|
|
198
|
+
automatic parsing.
|
|
199
199
|
|
|
200
200
|
Subscribes to the ``tou/rd`` response topic for the given device.
|
|
201
201
|
The callback receives a fully-parsed
|
|
202
|
-
:class:`~nwp500.models.TOUReservationSchedule`
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
:class:`~nwp500.models.TOUReservationSchedule` when the device
|
|
203
|
+
confirms a TOU write (triggered by :meth:`configure_tou_schedule`).
|
|
204
|
+
There is no MQTT read to subscribe to; to read the current schedule,
|
|
205
|
+
use the REST :meth:`~nwp500.NavienAPIClient.get_tou_info`.
|
|
205
206
|
|
|
206
207
|
Args:
|
|
207
208
|
device: Device whose TOU responses to receive.
|
nwp500/mqtt/control.py
CHANGED
|
@@ -539,38 +539,6 @@ class MqttDeviceController:
|
|
|
539
539
|
reservation=reservation_payload,
|
|
540
540
|
)
|
|
541
541
|
|
|
542
|
-
async def request_tou_settings(
|
|
543
|
-
self,
|
|
544
|
-
device: Device,
|
|
545
|
-
controller_serial_number: str,
|
|
546
|
-
) -> int:
|
|
547
|
-
"""
|
|
548
|
-
Request current Time-of-Use schedule from the device.
|
|
549
|
-
|
|
550
|
-
Args:
|
|
551
|
-
device: Device object
|
|
552
|
-
controller_serial_number: Controller serial number
|
|
553
|
-
|
|
554
|
-
Returns:
|
|
555
|
-
Publish packet ID
|
|
556
|
-
|
|
557
|
-
Raises:
|
|
558
|
-
ValueError: If controller_serial_number is empty
|
|
559
|
-
"""
|
|
560
|
-
if not controller_serial_number:
|
|
561
|
-
raise ParameterValidationError(
|
|
562
|
-
"controller_serial_number is required",
|
|
563
|
-
parameter="controller_serial_number",
|
|
564
|
-
)
|
|
565
|
-
|
|
566
|
-
return await self._send_command(
|
|
567
|
-
device=device,
|
|
568
|
-
command_code=CommandCode.TOU_RESERVATION,
|
|
569
|
-
topic_suffix="ctrl/tou/rd",
|
|
570
|
-
response_topic_suffix="tou/rd",
|
|
571
|
-
controllerSerialNumber=controller_serial_number,
|
|
572
|
-
)
|
|
573
|
-
|
|
574
542
|
@requires_capability("program_reservation_use")
|
|
575
543
|
async def set_tou_enabled(self, device: Device, enabled: bool) -> int:
|
|
576
544
|
"""Toggle Time-of-Use functionality."""
|
nwp500/mqtt/subscriptions.py
CHANGED
|
@@ -811,15 +811,16 @@ class MqttSubscriptionManager:
|
|
|
811
811
|
device: Device,
|
|
812
812
|
callback: Callable[[TOUReservationSchedule], None],
|
|
813
813
|
) -> int:
|
|
814
|
-
"""Subscribe to Time-of-Use schedule
|
|
815
|
-
parsing.
|
|
814
|
+
"""Subscribe to Time-of-Use schedule write confirmations with
|
|
815
|
+
automatic parsing.
|
|
816
816
|
|
|
817
817
|
Subscribes to the ``tou/rd`` response topic for the given device.
|
|
818
818
|
The callback receives a fully-parsed
|
|
819
|
-
:class:`~nwp500.models.TOUReservationSchedule`
|
|
820
|
-
|
|
821
|
-
:meth:`~nwp500.NavienMqttClient.
|
|
822
|
-
|
|
819
|
+
:class:`~nwp500.models.TOUReservationSchedule` when the device
|
|
820
|
+
confirms a TOU write (triggered by
|
|
821
|
+
:meth:`~nwp500.NavienMqttClient.configure_tou_schedule`). There is no
|
|
822
|
+
MQTT read to subscribe to; to read the current schedule, use the REST
|
|
823
|
+
:meth:`~nwp500.NavienAPIClient.get_tou_info`.
|
|
823
824
|
|
|
824
825
|
Args:
|
|
825
826
|
device: Device whose TOU responses to receive.
|
nwp500/reservations.py
CHANGED
|
@@ -71,10 +71,12 @@ async def fetch_reservations(
|
|
|
71
71
|
try:
|
|
72
72
|
await mqtt.unsubscribe_reservation_response(device, on_schedule)
|
|
73
73
|
except Exception:
|
|
74
|
+
from .mqtt.utils import redact_mac
|
|
75
|
+
|
|
74
76
|
_logger.warning(
|
|
75
77
|
"Failed to unsubscribe reservations response handler for "
|
|
76
78
|
"device %s",
|
|
77
|
-
device.device_info.mac_address,
|
|
79
|
+
redact_mac(device.device_info.mac_address),
|
|
78
80
|
exc_info=True,
|
|
79
81
|
)
|
|
80
82
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
nwp500/__init__.py,sha256=
|
|
1
|
+
nwp500/__init__.py,sha256=CmryiTzxVWP8LEcXTzdWM4L99tmZC4B2zxX9r8-vZq0,5427
|
|
2
2
|
nwp500/_base.py,sha256=Ey-7qGockhwGnrr8RjI3HAxnowRr-2gj_Tt-61dS1YA,2866
|
|
3
3
|
nwp500/api_client.py,sha256=4O4hV4mNwxXpbDFu-cEN36-edNW2-fGl9akzF4a437E,18342
|
|
4
4
|
nwp500/auth.py,sha256=_XRmkCiFtxQ7H_xwF7tF1kJW8tce2qhJMVfKK9bgZo4,33783
|
|
@@ -16,46 +16,46 @@ nwp500/field_factory.py,sha256=DMQGOLZU5S-sAdT0Ue5nkmqyMAEMlGvCkEg2C8eCkB8,5200
|
|
|
16
16
|
nwp500/mqtt_events.py,sha256=2qWDkWEzoMurZw5G6WwzL9nEtlvJ-GUCs_HvMa0024E,10148
|
|
17
17
|
nwp500/openei.py,sha256=2gM3hEwrUvUEFykDgN-pKkamWomGuQNVT46XzFwOlxA,7071
|
|
18
18
|
nwp500/py.typed,sha256=vlGtiygNDDmA_Ji2HjDl1SBJEcvpIC6cANFx_GonPMk,69
|
|
19
|
-
nwp500/reservations.py,sha256=
|
|
19
|
+
nwp500/reservations.py,sha256=0i8QJ5SD8oaXX9VjLAGtx5TNbxXoTVh67lVmGneqpgU,12553
|
|
20
20
|
nwp500/temperature.py,sha256=O9pyjFaMstzfKxWtQTUDNP12YOJbS9rSwBH76s4p4MY,7982
|
|
21
21
|
nwp500/topic_builder.py,sha256=ZOQbH89K_oN6Wz0Fia4UDNGNUn6wMFNEVS3k39ZdNaM,2482
|
|
22
22
|
nwp500/tou_schedule.py,sha256=sPwceU3sXbEqJCrFMsNZsZRg4DE4TsROkgVN-qXzlk0,3675
|
|
23
23
|
nwp500/unit_system.py,sha256=_UYp7KR4qPNROx2upYovaoR-MU5Blllq5iknOigNTfQ,4456
|
|
24
24
|
nwp500/utils.py,sha256=rEKvGnUsvea_3owEhbgzA0-uy96WwOOIlxaLUEfz20I,2110
|
|
25
25
|
nwp500/cli/__init__.py,sha256=hWvrWPWcjpgHqPnPjEauX8KzXqXzf415wBHWLTpbFwI,1530
|
|
26
|
-
nwp500/cli/__main__.py,sha256=
|
|
27
|
-
nwp500/cli/handlers.py,sha256=
|
|
26
|
+
nwp500/cli/__main__.py,sha256=m6nv8Pks5-FmOV4rDWtuhC7TH9FjW0qcMf6SDy15Eis,25063
|
|
27
|
+
nwp500/cli/handlers.py,sha256=sXtmMeOMZ7tlVd7H3X14QanPtTohS639WqOm4UIip2o,31164
|
|
28
28
|
nwp500/cli/monitoring.py,sha256=MAMeV6KCHPTfBUQn_BY2PWK5kuvX3t1WN4lEs6tw46o,1490
|
|
29
29
|
nwp500/cli/output_formatters.py,sha256=V2Dgat4Augbkm7AOYUxZPTfeoTAIbkCM25gxNXb7ebs,5179
|
|
30
30
|
nwp500/cli/presentation.py,sha256=VmzB4h-wJUGke8sJIoyMw3VJKBo0SD9w4gpwKnELe0Q,27863
|
|
31
31
|
nwp500/cli/rich_output.py,sha256=Qy9doaDPnX4IP-Oahu2gfuYfsfpB0WYtdeLfVmMQmdc,22722
|
|
32
32
|
nwp500/cli/token_storage.py,sha256=zfFGah0l5owWYIk2uA2OTmgCu1nLdhYFYcVA5JEkwCE,2077
|
|
33
|
-
nwp500/models/__init__.py,sha256=
|
|
33
|
+
nwp500/models/__init__.py,sha256=VAPDE7u3f0lmhL2CnBxbLqf-1EbHSiqEdTkb-JSZwSM,2217
|
|
34
34
|
nwp500/models/_converters.py,sha256=lNQPigKfsVPE5RvX9-s0_UA3j35tF3hQidcpRNIWwyY,2427
|
|
35
|
-
nwp500/models/device.py,sha256=
|
|
35
|
+
nwp500/models/device.py,sha256=zkq1S54UTd3rbh6zd1BC7qjc0BMXAb7VDidXZmvw9-0,3588
|
|
36
36
|
nwp500/models/energy.py,sha256=y0eIADWhrC3YWRRBfbD3NNWsFvbLhY8GB5HuqGtlJ2s,2017
|
|
37
|
-
nwp500/models/feature.py,sha256=
|
|
37
|
+
nwp500/models/feature.py,sha256=7z1pDoW0LQGxv8__AlQn5M35lZRKgeybgwfnw2OCKIM,15433
|
|
38
38
|
nwp500/models/mqtt_models.py,sha256=1Etfn0lqiWvoUsW2niT6wXdZtNvN0sGjn7F1uPJQ9yU,774
|
|
39
39
|
nwp500/models/schedule.py,sha256=uI8k-Urwd9XZ9KNj_VVFGAHg3pXZRXgDGb9psrFHFSw,13056
|
|
40
40
|
nwp500/models/status.py,sha256=FBJpEiw9y7cg-JRW3x5GHO3biztnXLoteujka3WFM7I,36892
|
|
41
|
-
nwp500/models/tou.py,sha256=
|
|
41
|
+
nwp500/models/tou.py,sha256=wwxhYwCSY4JIWgVDa2SlwUzlCEDqqRBN994NrkqdVg8,7018
|
|
42
42
|
nwp500/mqtt/__init__.py,sha256=WYdBfpW-3tSLdFMObDWCX4pcLm_IqRBDluJqkPnQBN4,974
|
|
43
|
-
nwp500/mqtt/_control_commands.py,sha256=
|
|
44
|
-
nwp500/mqtt/_device_subscriptions.py,sha256=
|
|
43
|
+
nwp500/mqtt/_control_commands.py,sha256=fX49Ym2x4dTTj4cmaHqEnuaDNuVs7zXWPT_SOCZGvU0,8199
|
|
44
|
+
nwp500/mqtt/_device_subscriptions.py,sha256=WpJxaGyssDXdlXU2YTVy9LkVEJ3wg-vnQdhZqExX_YE,8197
|
|
45
45
|
nwp500/mqtt/client.py,sha256=IoTCbBKPpxmxLCJkRZvH6k55HmfBjoXW8W6QS452yXM,45770
|
|
46
46
|
nwp500/mqtt/command_queue.py,sha256=wiNxNzQe6psMqa0PQ3wzatf9N6IDrtDwpl4DPq1kDdk,6517
|
|
47
47
|
nwp500/mqtt/connection.py,sha256=fNrOtBJxsqk054YDUmipnwI3yfBHx1anXb_07e0X9XU,17712
|
|
48
|
-
nwp500/mqtt/control.py,sha256=
|
|
48
|
+
nwp500/mqtt/control.py,sha256=KWFlmDGn3GtpX4dimPnrXglsD6YTggXqzPigkBVMh_E,30102
|
|
49
49
|
nwp500/mqtt/diagnostics.py,sha256=NDkPExS4FObGCWJXAhENqNQUm8Bda5GDBR7_o3ylSAo,15723
|
|
50
50
|
nwp500/mqtt/periodic.py,sha256=WKyyO61yTZJfOm9JAtBp6Onyk-XNj0U544sYMCKvTZw,14729
|
|
51
51
|
nwp500/mqtt/reconnection.py,sha256=5SEj1vV5_QWXlApnKnUeFWAT-MgWnnu6M_vhiqTQh14,16129
|
|
52
52
|
nwp500/mqtt/state_tracker.py,sha256=uZlDFNYn7DOXmXPbk-8jXva_QKEvLES9T_BYbQSkhvM,6364
|
|
53
|
-
nwp500/mqtt/subscriptions.py,sha256=
|
|
53
|
+
nwp500/mqtt/subscriptions.py,sha256=5z5EWJKmgfGkT0gFcw8Wxqki9v-bTbshVc9jnmviwHY,31978
|
|
54
54
|
nwp500/mqtt/types.py,sha256=86R7gwsc1XabA_ABWeALeC5EImlTziFFn1DrP1x8Yy4,1605
|
|
55
55
|
nwp500/mqtt/utils.py,sha256=LAp678M8hzqhlgWBXt3Re59TbnMkX9l1fa57WtE2yug,13000
|
|
56
|
-
nwp500_python-9.3.
|
|
57
|
-
nwp500_python-9.3.
|
|
58
|
-
nwp500_python-9.3.
|
|
59
|
-
nwp500_python-9.3.
|
|
60
|
-
nwp500_python-9.3.
|
|
61
|
-
nwp500_python-9.3.
|
|
56
|
+
nwp500_python-9.3.2.dist-info/licenses/LICENSE.txt,sha256=EUOsypJ3iEqJ-9tdo_TSZHwB0Rd-7p0JzToEI5CMBJU,1085
|
|
57
|
+
nwp500_python-9.3.2.dist-info/METADATA,sha256=DJnyePJsNABe4Du8W8ngv9Q5IlUbeyA6RPNr5OaQ3iI,5091
|
|
58
|
+
nwp500_python-9.3.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
59
|
+
nwp500_python-9.3.2.dist-info/entry_points.txt,sha256=p8vGDEbUIOT_TzDG-XnJ3lw-9yaeHrDPNN7fAx4-78I,52
|
|
60
|
+
nwp500_python-9.3.2.dist-info/top_level.txt,sha256=DRPIenxKnssWeaetq428gYfwrKjLGN1z2NOtztIvxZE,7
|
|
61
|
+
nwp500_python-9.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|