nwp500-python 9.4.1__py3-none-any.whl → 9.4.3__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/models/status.py +20 -10
- nwp500/mqtt/control.py +155 -1
- nwp500/mqtt/subscriptions.py +38 -1
- nwp500/reservations.py +16 -0
- {nwp500_python-9.4.1.dist-info → nwp500_python-9.4.3.dist-info}/METADATA +1 -1
- {nwp500_python-9.4.1.dist-info → nwp500_python-9.4.3.dist-info}/RECORD +10 -10
- {nwp500_python-9.4.1.dist-info → nwp500_python-9.4.3.dist-info}/WHEEL +0 -0
- {nwp500_python-9.4.1.dist-info → nwp500_python-9.4.3.dist-info}/entry_points.txt +0 -0
- {nwp500_python-9.4.1.dist-info → nwp500_python-9.4.3.dist-info}/licenses/LICENSE.txt +0 -0
- {nwp500_python-9.4.1.dist-info → nwp500_python-9.4.3.dist-info}/top_level.txt +0 -0
nwp500/models/status.py
CHANGED
|
@@ -240,7 +240,9 @@ class DeviceStatus(NavienBaseModel):
|
|
|
240
240
|
"the current setpoint, in Watt-hours. This is NOT a fixed tank "
|
|
241
241
|
"size: it tracks the setpoint, rising about 140 Wh per 0.5 degC "
|
|
242
242
|
"of setpoint increase. Use it as the cost of a full recovery, "
|
|
243
|
-
"not as the tank's total heat content."
|
|
243
|
+
"not as the tank's total heat content. During a TOU window that "
|
|
244
|
+
"raises hp_upper_on_temp_setting, the reference is 2 degC "
|
|
245
|
+
"higher (about 108.5 degF)."
|
|
244
246
|
),
|
|
245
247
|
json_schema_extra={
|
|
246
248
|
"unit_of_measurement": "Wh",
|
|
@@ -252,9 +254,10 @@ class DeviceStatus(NavienBaseModel):
|
|
|
252
254
|
description=(
|
|
253
255
|
"Energy still NEEDED to bring the tank up to the setpoint, in "
|
|
254
256
|
"Watt-hours - a heating deficit, not stored energy. It falls as "
|
|
255
|
-
"the tank heats and reaches zero at the setpoint
|
|
256
|
-
"
|
|
257
|
-
"
|
|
257
|
+
"the tank heats and reaches zero at the setpoint - or, during a "
|
|
258
|
+
"TOU window that raises hp_upper_on_temp_setting, about 2 degC "
|
|
259
|
+
"short of it. Despite the protocol name 'availableEnergyCapacity' "
|
|
260
|
+
"it is the inverse of available energy."
|
|
258
261
|
),
|
|
259
262
|
json_schema_extra={
|
|
260
263
|
"unit_of_measurement": "Wh",
|
|
@@ -276,8 +279,8 @@ class DeviceStatus(NavienBaseModel):
|
|
|
276
279
|
recirc_error_status: int = Field(description="Recirculation error status")
|
|
277
280
|
current_inst_power: float = Field(
|
|
278
281
|
description=(
|
|
279
|
-
"Current instantaneous power consumption
|
|
280
|
-
"
|
|
282
|
+
"Current instantaneous power consumption of the whole unit "
|
|
283
|
+
"in Watts, including heating element power when active"
|
|
281
284
|
),
|
|
282
285
|
json_schema_extra={
|
|
283
286
|
"unit_of_measurement": "W",
|
|
@@ -958,12 +961,19 @@ class DeviceStatus(NavienBaseModel):
|
|
|
958
961
|
estimate of what can actually be drawn - water colder than that
|
|
959
962
|
still holds heat, but not heat you can wash with.
|
|
960
963
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
964
|
+
Usually robust: ``full_recovery_energy`` is bimodal, taking one
|
|
965
|
+
of two values 2 degC apart at a fixed setpoint, but both fields
|
|
966
|
+
shift together so the difference is unaffected. Checked against
|
|
967
|
+
the tank thermistors over 12275 samples, the implied tank
|
|
965
968
|
temperature agrees to a standard deviation of 0.57 degF.
|
|
966
969
|
|
|
970
|
+
The exception is a Time-of-Use window, when
|
|
971
|
+
``hp_upper_on_temp_setting`` is raised to about the setpoint.
|
|
972
|
+
There ``energy_to_setpoint`` reaches 0 while the tank is still up
|
|
973
|
+
to about 2 degC short. This value then equals
|
|
974
|
+
``full_recovery_energy`` and under-reads by up to about 560 Wh on
|
|
975
|
+
a 65-gallon tank. In that case, use the thermistors.
|
|
976
|
+
|
|
967
977
|
Returns:
|
|
968
978
|
Drawable energy in Watt-hours, clamped at zero.
|
|
969
979
|
"""
|
nwp500/mqtt/control.py
CHANGED
|
@@ -27,7 +27,7 @@ weekly reservation) have no method.
|
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
29
|
import logging
|
|
30
|
-
from collections.abc import Awaitable, Callable, Sequence
|
|
30
|
+
from collections.abc import Awaitable, Callable, Mapping, Sequence
|
|
31
31
|
from datetime import UTC, datetime
|
|
32
32
|
from typing import Any
|
|
33
33
|
|
|
@@ -120,6 +120,120 @@ def validate_recirculation_schedule(schedule: RecirculationSchedule) -> None:
|
|
|
120
120
|
)
|
|
121
121
|
|
|
122
122
|
|
|
123
|
+
RESERVATION_ENTRY_FIELDS = ("enable", "week", "hour", "min", "mode", "param")
|
|
124
|
+
# DhwOperationSetting ids are contiguous (1-6), so a range check covers them.
|
|
125
|
+
_DHW_MODE_MIN = min(DhwOperationSetting)
|
|
126
|
+
_DHW_MODE_MAX = max(DhwOperationSetting)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def validate_reservation_entries(
|
|
130
|
+
reservations: Sequence[Mapping[str, Any]],
|
|
131
|
+
features: DeviceFeature | None = None,
|
|
132
|
+
) -> None:
|
|
133
|
+
"""Check raw reservation entries before they are written to the device.
|
|
134
|
+
|
|
135
|
+
:class:`~nwp500.models.ReservationEntry` accepts any integers so that
|
|
136
|
+
device read-backs always parse; writes are held to what the device
|
|
137
|
+
accepts. Each entry must carry the six protocol fields as plain
|
|
138
|
+
integers (a ``bool`` or ``float`` is rejected): ``enable`` 1 (off) or
|
|
139
|
+
2 (on), ``week`` a day bitfield (2-254, bit 0 clear), ``hour`` 0-23,
|
|
140
|
+
``min`` 0-59 and ``mode`` a :class:`~nwp500.enums.DhwOperationSetting`
|
|
141
|
+
id.
|
|
142
|
+
|
|
143
|
+
``param`` is the setpoint in half-degrees Celsius. With ``features``
|
|
144
|
+
it must lie within the range the heater reports,
|
|
145
|
+
``dhw_temperature_min_raw`` to ``dhw_temperature_max_raw``; without
|
|
146
|
+
them, only within the single byte the protocol carries (0-255).
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
reservations: Raw entry dicts, as passed to ``update_reservations``.
|
|
150
|
+
features: The device's feature data, for its setpoint limits.
|
|
151
|
+
|
|
152
|
+
Raises:
|
|
153
|
+
ParameterValidationError: On a missing field, a non-integer field,
|
|
154
|
+
or a bad ``enable`` or ``week``.
|
|
155
|
+
RangeValidationError: On ``hour``, ``min``, ``mode`` or ``param``
|
|
156
|
+
outside its range.
|
|
157
|
+
"""
|
|
158
|
+
for index, entry in enumerate(reservations, start=1):
|
|
159
|
+
_check_reservation_entry(index, entry)
|
|
160
|
+
if features is not None:
|
|
161
|
+
_check_reservation_setpoints(reservations, features)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _check_reservation_entry(index: int, entry: Mapping[str, Any]) -> None:
|
|
165
|
+
"""Check one entry's fields, except ``param`` against device limits."""
|
|
166
|
+
for name in RESERVATION_ENTRY_FIELDS:
|
|
167
|
+
if name not in entry:
|
|
168
|
+
raise ParameterValidationError(
|
|
169
|
+
f"entry {index}: missing field {name!r}",
|
|
170
|
+
parameter=f"reservation[{index}].{name}",
|
|
171
|
+
)
|
|
172
|
+
if not _is_int(entry[name]):
|
|
173
|
+
raise ParameterValidationError(
|
|
174
|
+
f"entry {index}: {name} must be an integer",
|
|
175
|
+
parameter=f"reservation[{index}].{name}",
|
|
176
|
+
value=entry[name],
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
enable = entry["enable"]
|
|
180
|
+
if enable not in (1, 2):
|
|
181
|
+
raise ParameterValidationError(
|
|
182
|
+
f"entry {index}: enable must be 1 (off) or 2 (on), got {enable}",
|
|
183
|
+
parameter=f"reservation[{index}].enable",
|
|
184
|
+
value=enable,
|
|
185
|
+
)
|
|
186
|
+
week = entry["week"]
|
|
187
|
+
if not (0 < week <= 254 and week % 2 == 0):
|
|
188
|
+
raise ParameterValidationError(
|
|
189
|
+
f"entry {index}: week={week} is not a day bitfield "
|
|
190
|
+
"(Sun=128 .. Sat=2, at least one day, bit 0 clear)",
|
|
191
|
+
parameter=f"reservation[{index}].week",
|
|
192
|
+
value=week,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
ranges = [
|
|
196
|
+
("hour", 0, 23),
|
|
197
|
+
("min", 0, 59),
|
|
198
|
+
("mode", int(_DHW_MODE_MIN), int(_DHW_MODE_MAX)),
|
|
199
|
+
("param", 0, 255),
|
|
200
|
+
]
|
|
201
|
+
for name, low, high in ranges:
|
|
202
|
+
value = entry[name]
|
|
203
|
+
if not low <= value <= high:
|
|
204
|
+
raise RangeValidationError(
|
|
205
|
+
f"entry {index}: {name} must be between {low} and {high}, "
|
|
206
|
+
f"got {value}",
|
|
207
|
+
field=f"reservation[{index}].{name}",
|
|
208
|
+
value=value,
|
|
209
|
+
min_value=low,
|
|
210
|
+
max_value=high,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _check_reservation_setpoints(
|
|
215
|
+
reservations: Sequence[Mapping[str, Any]], features: DeviceFeature
|
|
216
|
+
) -> None:
|
|
217
|
+
"""Hold each entry's ``param`` to the setpoint range the heater reports.
|
|
218
|
+
|
|
219
|
+
Both ``param`` and the feature limits are half-degrees Celsius, so they
|
|
220
|
+
compare without conversion.
|
|
221
|
+
"""
|
|
222
|
+
low = features.dhw_temperature_min_raw
|
|
223
|
+
high = features.dhw_temperature_max_raw
|
|
224
|
+
for index, entry in enumerate(reservations, start=1):
|
|
225
|
+
param = entry["param"]
|
|
226
|
+
if not low <= param <= high:
|
|
227
|
+
raise RangeValidationError(
|
|
228
|
+
f"entry {index}: param={param} (half-degrees C) is outside "
|
|
229
|
+
f"the device's setpoint range {low}-{high}",
|
|
230
|
+
field=f"reservation[{index}].param",
|
|
231
|
+
value=param,
|
|
232
|
+
min_value=low,
|
|
233
|
+
max_value=high,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
123
237
|
class MqttDeviceController:
|
|
124
238
|
"""
|
|
125
239
|
Manages device control commands for Navien devices.
|
|
@@ -569,6 +683,27 @@ class MqttDeviceController:
|
|
|
569
683
|
[preferred_to_half_celsius(temperature)],
|
|
570
684
|
)
|
|
571
685
|
|
|
686
|
+
async def _get_reservation_limits(self, device: Device) -> DeviceFeature:
|
|
687
|
+
"""Fetch the feature data that carries the device's setpoint range.
|
|
688
|
+
|
|
689
|
+
Mirrors :func:`~nwp500.command_decorators.requires_capability`: a
|
|
690
|
+
failed fetch (a feature-response timeout, a lost connection) is
|
|
691
|
+
reported as :class:`DeviceCapabilityError`, like a missing result.
|
|
692
|
+
"""
|
|
693
|
+
capability = "dhw_temperature_setting_use"
|
|
694
|
+
message = "Unable to validate reservation temperatures"
|
|
695
|
+
try:
|
|
696
|
+
features = await self._get_device_features(device)
|
|
697
|
+
except DeviceCapabilityError:
|
|
698
|
+
raise
|
|
699
|
+
except Exception as e:
|
|
700
|
+
raise DeviceCapabilityError(capability, f"{message}: {e!s}") from e
|
|
701
|
+
if features is None:
|
|
702
|
+
raise DeviceCapabilityError(
|
|
703
|
+
capability, f"{message}: device features not available."
|
|
704
|
+
)
|
|
705
|
+
return features
|
|
706
|
+
|
|
572
707
|
async def update_reservations(
|
|
573
708
|
self,
|
|
574
709
|
device: Device,
|
|
@@ -579,6 +714,11 @@ class MqttDeviceController:
|
|
|
579
714
|
"""
|
|
580
715
|
Update programmed reservations for temperature/mode changes.
|
|
581
716
|
|
|
717
|
+
Every entry is checked before anything is sent (see
|
|
718
|
+
:func:`validate_reservation_entries`). Each ``param`` is held to
|
|
719
|
+
the setpoint range the heater reports in its feature data, which is
|
|
720
|
+
requested from the device if it is not cached yet.
|
|
721
|
+
|
|
582
722
|
Args:
|
|
583
723
|
device: Device object
|
|
584
724
|
reservations: List of reservation entries
|
|
@@ -586,7 +726,21 @@ class MqttDeviceController:
|
|
|
586
726
|
|
|
587
727
|
Returns:
|
|
588
728
|
Publish packet ID
|
|
729
|
+
|
|
730
|
+
Raises:
|
|
731
|
+
ParameterValidationError: If an entry has a missing or
|
|
732
|
+
non-integer field, or a bad ``enable`` or ``week``.
|
|
733
|
+
RangeValidationError: If an entry's ``hour``, ``min`` or
|
|
734
|
+
``mode`` is out of range, or its ``param`` is outside the
|
|
735
|
+
device's setpoint range.
|
|
736
|
+
DeviceCapabilityError: If the device's feature data, and so
|
|
737
|
+
its setpoint range, is not available.
|
|
589
738
|
"""
|
|
739
|
+
validate_reservation_entries(reservations)
|
|
740
|
+
if reservations:
|
|
741
|
+
features = await self._get_reservation_limits(device)
|
|
742
|
+
_check_reservation_setpoints(reservations, features)
|
|
743
|
+
|
|
590
744
|
# See docs/reference/protocol/mqtt_protocol.rst "Reservations" for the
|
|
591
745
|
# command code (16777226) and the reservation object fields
|
|
592
746
|
# (enable, week, hour, min, mode, param).
|
nwp500/mqtt/subscriptions.py
CHANGED
|
@@ -40,7 +40,12 @@ from ..mqtt_events import (
|
|
|
40
40
|
from ..topic_builder import MqttTopicBuilder
|
|
41
41
|
from .state_tracker import DeviceStateTracker
|
|
42
42
|
from .types import QoS, to_awscrt_qos
|
|
43
|
-
from .utils import
|
|
43
|
+
from .utils import (
|
|
44
|
+
get_response_data,
|
|
45
|
+
redact_mac,
|
|
46
|
+
redact_topic,
|
|
47
|
+
topic_matches_pattern,
|
|
48
|
+
)
|
|
44
49
|
|
|
45
50
|
if TYPE_CHECKING:
|
|
46
51
|
from ..device_info_cache import MqttDeviceInfoCache
|
|
@@ -614,13 +619,45 @@ class MqttSubscriptionManager:
|
|
|
614
619
|
"""
|
|
615
620
|
|
|
616
621
|
def handler(topic: str, message: dict[str, Any]) -> None:
|
|
622
|
+
# Every branch that declines a message says so at debug level.
|
|
623
|
+
# Silence here is expensive to diagnose: a caller watching for a
|
|
624
|
+
# reply that never reaches its callback cannot tell a dropped
|
|
625
|
+
# message from one the cloud never sent, and the two want
|
|
626
|
+
# opposite fixes. The logs are the only evidence either way.
|
|
627
|
+
#
|
|
628
|
+
# Each log is guarded: redact_topic runs a dozen regexes, and
|
|
629
|
+
# these branches are the normal case for a handler registered
|
|
630
|
+
# under the device wildcard, which sees every message for that
|
|
631
|
+
# device and keeps only its own.
|
|
617
632
|
if topic_suffix and not topic.endswith(topic_suffix):
|
|
633
|
+
if _logger.isEnabledFor(logging.DEBUG):
|
|
634
|
+
_logger.debug(
|
|
635
|
+
"Ignoring a message on %s: the topic does not end "
|
|
636
|
+
"with %r, which this handler requires",
|
|
637
|
+
redact_topic(topic),
|
|
638
|
+
topic_suffix,
|
|
639
|
+
)
|
|
618
640
|
return
|
|
619
641
|
try:
|
|
620
642
|
data = get_response_data(message, key)
|
|
621
643
|
if not data:
|
|
644
|
+
if _logger.isEnabledFor(logging.DEBUG):
|
|
645
|
+
_logger.debug(
|
|
646
|
+
"Ignoring a message on %s: no %s data to parse "
|
|
647
|
+
"as %s",
|
|
648
|
+
redact_topic(topic),
|
|
649
|
+
f"{key!r}" if key else "response",
|
|
650
|
+
model.__name__,
|
|
651
|
+
)
|
|
622
652
|
return
|
|
623
653
|
if only_mac and not _same_device(data, message, only_mac):
|
|
654
|
+
if _logger.isEnabledFor(logging.DEBUG):
|
|
655
|
+
_logger.debug(
|
|
656
|
+
"Ignoring a message on %s: it names another "
|
|
657
|
+
"device, not %s",
|
|
658
|
+
redact_topic(topic),
|
|
659
|
+
redact_mac(only_mac),
|
|
660
|
+
)
|
|
624
661
|
return
|
|
625
662
|
|
|
626
663
|
parsed = parse(data) if parse else model.model_validate(data)
|
nwp500/reservations.py
CHANGED
|
@@ -17,6 +17,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
17
17
|
from .converters import device_bool_from_python
|
|
18
18
|
from .encoding import build_reservation_entry, encode_week_bitfield
|
|
19
19
|
from .models import ReservationEntry, ReservationSchedule
|
|
20
|
+
from .mqtt.control import validate_reservation_entries
|
|
20
21
|
|
|
21
22
|
if TYPE_CHECKING:
|
|
22
23
|
from .models import Device
|
|
@@ -119,7 +120,22 @@ async def update_reservations_confirmed(
|
|
|
119
120
|
what was just written. This avoids resolving on a stale/unrelated
|
|
120
121
|
``rsv/rd`` message (e.g. from a concurrent read or a previous
|
|
121
122
|
write) that happens to arrive in the same window.
|
|
123
|
+
|
|
124
|
+
Entries are checked before anything is sent; see
|
|
125
|
+
:func:`nwp500.mqtt.control.validate_reservation_entries`.
|
|
126
|
+
|
|
127
|
+
Raises:
|
|
128
|
+
ParameterValidationError: If an entry has a missing or non-integer
|
|
129
|
+
field, or a bad ``enable`` or ``week``.
|
|
130
|
+
RangeValidationError: If an entry's ``hour``, ``min`` or ``mode``
|
|
131
|
+
is out of range, or its ``param`` is outside the device's
|
|
132
|
+
setpoint range.
|
|
122
133
|
"""
|
|
134
|
+
# Structural checks first, so a bad entry neither subscribes nor builds
|
|
135
|
+
# a coerced expectation (``ReservationEntry`` turns ``True`` into 1).
|
|
136
|
+
# The setpoint range is checked by ``update_reservations`` against the
|
|
137
|
+
# device's feature data, still before anything is published.
|
|
138
|
+
validate_reservation_entries(reservations)
|
|
123
139
|
expected = ReservationSchedule(
|
|
124
140
|
reservationUse=device_bool_from_python(enabled),
|
|
125
141
|
reservation=[ReservationEntry(**entry) for entry in reservations],
|
|
@@ -16,7 +16,7 @@ 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=il5f0xVM0Kxanp5WsgpYwLjUgSgnVP-8hVurIh-aix0,13381
|
|
20
20
|
nwp500/temperature.py,sha256=O9pyjFaMstzfKxWtQTUDNP12YOJbS9rSwBH76s4p4MY,7982
|
|
21
21
|
nwp500/topic_builder.py,sha256=AmFFY-_b6qpS3VRzuGSyNYv6CPYDc6y-LrNDoWVz9s0,4137
|
|
22
22
|
nwp500/tou_schedule.py,sha256=sPwceU3sXbEqJCrFMsNZsZRg4DE4TsROkgVN-qXzlk0,3675
|
|
@@ -39,7 +39,7 @@ nwp500/models/feature.py,sha256=7z1pDoW0LQGxv8__AlQn5M35lZRKgeybgwfnw2OCKIM,1543
|
|
|
39
39
|
nwp500/models/firmware.py,sha256=cyp9fnewmenvQnsDcTXAsCz-298tqrdswOs1CSZ4duc,1490
|
|
40
40
|
nwp500/models/mqtt_models.py,sha256=1Etfn0lqiWvoUsW2niT6wXdZtNvN0sGjn7F1uPJQ9yU,774
|
|
41
41
|
nwp500/models/schedule.py,sha256=qQzWJP7Ik1x47GRYv6tfWeF2Ja4ONVkbLPkhK-mEjcs,11605
|
|
42
|
-
nwp500/models/status.py,sha256=
|
|
42
|
+
nwp500/models/status.py,sha256=Riq42GD56n7UPALTF3tdOrlRziLJOH2lZwK4wWmv5UE,38358
|
|
43
43
|
nwp500/models/tou.py,sha256=wwxhYwCSY4JIWgVDa2SlwUzlCEDqqRBN994NrkqdVg8,7018
|
|
44
44
|
nwp500/mqtt/__init__.py,sha256=WYdBfpW-3tSLdFMObDWCX4pcLm_IqRBDluJqkPnQBN4,974
|
|
45
45
|
nwp500/mqtt/_control_commands.py,sha256=NksQ7Fku_4Vswmyi3LONwse33MN9hJUFOF4qhdw1-80,8775
|
|
@@ -47,17 +47,17 @@ nwp500/mqtt/_device_subscriptions.py,sha256=rnTHQQ4l9i-pv1VAqnxOr106CFyS3yLA0I1n
|
|
|
47
47
|
nwp500/mqtt/client.py,sha256=vEj5mZscGR5u-XePnmqvpDqGlYcL68tb_K_pe1SuVIQ,49284
|
|
48
48
|
nwp500/mqtt/command_queue.py,sha256=wiNxNzQe6psMqa0PQ3wzatf9N6IDrtDwpl4DPq1kDdk,6517
|
|
49
49
|
nwp500/mqtt/connection.py,sha256=fNrOtBJxsqk054YDUmipnwI3yfBHx1anXb_07e0X9XU,17712
|
|
50
|
-
nwp500/mqtt/control.py,sha256=
|
|
50
|
+
nwp500/mqtt/control.py,sha256=VRoX80Nm5hEOAr-13vHG9ve1OUNVfNTQI5zceBG4flA,47381
|
|
51
51
|
nwp500/mqtt/diagnostics.py,sha256=NDkPExS4FObGCWJXAhENqNQUm8Bda5GDBR7_o3ylSAo,15723
|
|
52
52
|
nwp500/mqtt/periodic.py,sha256=WKyyO61yTZJfOm9JAtBp6Onyk-XNj0U544sYMCKvTZw,14729
|
|
53
53
|
nwp500/mqtt/reconnection.py,sha256=g9OuLlf1lVx_pbV6d7QHq7IcCflKkypPYSLSDTp58tQ,16499
|
|
54
54
|
nwp500/mqtt/state_tracker.py,sha256=uZlDFNYn7DOXmXPbk-8jXva_QKEvLES9T_BYbQSkhvM,6364
|
|
55
|
-
nwp500/mqtt/subscriptions.py,sha256=
|
|
55
|
+
nwp500/mqtt/subscriptions.py,sha256=lBO4IM_5bQbexhARy-PNf36aKNJNx9iiWQMgiMe5Y_0,39853
|
|
56
56
|
nwp500/mqtt/types.py,sha256=86R7gwsc1XabA_ABWeALeC5EImlTziFFn1DrP1x8Yy4,1605
|
|
57
57
|
nwp500/mqtt/utils.py,sha256=qXafc9GDpVY9o9o8DQLKBM8t0E401j3EPpZ0rC4snCY,13415
|
|
58
|
-
nwp500_python-9.4.
|
|
59
|
-
nwp500_python-9.4.
|
|
60
|
-
nwp500_python-9.4.
|
|
61
|
-
nwp500_python-9.4.
|
|
62
|
-
nwp500_python-9.4.
|
|
63
|
-
nwp500_python-9.4.
|
|
58
|
+
nwp500_python-9.4.3.dist-info/licenses/LICENSE.txt,sha256=EUOsypJ3iEqJ-9tdo_TSZHwB0Rd-7p0JzToEI5CMBJU,1085
|
|
59
|
+
nwp500_python-9.4.3.dist-info/METADATA,sha256=LWySo-xhXgEEApUxOXin2dM-DDlFTySXKVCt1Z_dIR4,5091
|
|
60
|
+
nwp500_python-9.4.3.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
61
|
+
nwp500_python-9.4.3.dist-info/entry_points.txt,sha256=p8vGDEbUIOT_TzDG-XnJ3lw-9yaeHrDPNN7fAx4-78I,52
|
|
62
|
+
nwp500_python-9.4.3.dist-info/top_level.txt,sha256=DRPIenxKnssWeaetq428gYfwrKjLGN1z2NOtztIvxZE,7
|
|
63
|
+
nwp500_python-9.4.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|