Skip to content

Remove request_tou_settings; there is no MQTT TOU read - #125

Merged
eman merged 2 commits into
mainfrom
remove/mqtt-tou-read
Aug 29, 2026
Merged

Remove request_tou_settings; there is no MQTT TOU read#125
eman merged 2 commits into
mainfrom
remove/mqtt-tou-read

Conversation

@eman

@eman eman commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Breaking: removes NavienMqttClient.request_tou_settings().

Evidence

The method published CommandCode.TOU_RESERVATION (0x02000007) to ctrl/tou/rd with only controllerSerialNumber, then waited for a reply on res/tou/rd. That reply never comes.

Tested against a live device with TOU fully provisioned — program_reservation_use true, valid controller serial, touStatus: 1, and a PG&E "Electric Vehicle EV (Sch) Rate A" plan that REST /device/tou returns in full. The request publishes; nothing comes back in 45 seconds.

The reason is in the vendor app:

// TouScheduleViewmodel.java:427
navilinkApplication.setPublishMgppControlTou("ctrl/tou/rd",
    KDEnum.DeviceControlMGPP.TOU_RESERVATION, controllerSerialNumber,
    i /* on/off flag */, arrayList /* the intervals */, ...);

ctrl/tou/rd + TOU_RESERVATION is the write — what configure_tou_schedule publishes, and what the app publishes from its TOU editor. The device answers on res/tou/rd to confirm such a write; it does not answer a request carrying no schedule. The app reads TOU over REST: /device/tou and /device/tou/convert are the only TOU reads in any of the HAR captures in reference/.

Beyond returning nothing, the call published a write-shaped command with no schedule attached. This device ignores it — touStatus was unchanged across repeated calls — but a firmware that took it at face value could read it as "store an empty TOU schedule". Not a risk worth carrying for a call that returns nothing.

Changes

  • request_tou_settings removed from mqtt/control.py and mqtt/_control_commands.py.
  • Docstrings on subscribe_tou_response (both layers) and TOUReservationSchedule now say the payload is a write confirmation and point at the REST read.
  • docs/how-to/optimize-tou.rst: the "MQTT: Request TOU Settings" section becomes "REST: Read the Current TOU Schedule", with a note on why there is no MQTT read; Example 3 is rewritten against get_tou_info (and uses TOUSchedule.intervals' raw dicts, which the old example got wrong).
  • docs/reference/python_api/mqtt_client.rst: method entry dropped, subscribe_tou_response description corrected.
  • examples/advanced/tou_schedule.py: drops the bogus read-back after the write.
  • examples/advanced/firmware_payload_capture.py: drops its TOU step — a read-only capture tool should never have been publishing a control command.

Migration

NavienAPIClient.get_tou_info(mac_address, additional_value, controller_id) returns the stored plan (rate name, utility, ZIP, seasonal pricing intervals) and needs no MQTT connection. subscribe_tou_response is unaffected and still delivers write confirmations. set_tou_enabled (TOU_ON/TOU_OFF, 0x0200002B/2C) is a separate path and is untouched — that's what the Home Assistant TOU switch uses, which is why it kept working while the read never did.

Tests

tests/test_public_api.py::test_mqtt_tou_read_is_gone follows the existing "removed things stay removed" pattern and records why. Full suite: 709 passed.

https://claude.ai/code/session_01XVj9BYvuLj7Th3iFUVoeCn

@eman
eman requested a balanced review from Copilot August 29, 2026 14:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The REST example is not executable as written, and two public docstrings still describe TOU responses as reads.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Removes the unsafe, nonfunctional MQTT TOU read API and directs schedule reads to REST.

Changes:

  • Removes request_tou_settings from both MQTT layers.
  • Reframes tou/rd as write-confirmation only.
  • Updates tests, examples, documentation, and migration notes.
File summaries
File Description
CHANGELOG.rst Documents the breaking removal and migration.
src/nwp500/mqtt/control.py Removes the device-level command.
src/nwp500/mqtt/_control_commands.py Removes the public delegation method.
src/nwp500/mqtt/subscriptions.py Updates TOU subscription documentation.
src/nwp500/mqtt/_device_subscriptions.py Updates delegated subscription documentation.
src/nwp500/models/tou.py Clarifies write-confirmation model semantics.
docs/how-to/optimize-tou.rst Replaces MQTT reads with a REST example.
docs/reference/python_api/mqtt_client.rst Removes the obsolete API reference.
examples/advanced/tou_schedule.py Removes the invalid read-back request.
examples/advanced/firmware_payload_capture.py Stops issuing a TOU write-shaped request.
tests/test_public_api.py Guards the removed public API surface.
Review details
  • Files reviewed: 11/11 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +202 to +205
:class:`~nwp500.models.TOUReservationSchedule` when the device
confirms a TOU write (triggered by :meth:`configure_tou_schedule`).
There is no MQTT read to subscribe to; to read the current schedule,
use the REST :meth:`~nwp500.NavienAPIClient.get_tou_info`.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6d7fd96 — the summary line now reads "Subscribe to Time-of-Use schedule write confirmations with automatic parsing." Done on both layers, and docs/reference/python_api/mqtt_client.rst had the same stale summary ("Subscribe to parsed TOU schedule responses"), which is corrected too.

Comment on lines +675 to +679
tou_info = await api_client.get_tou_info(
mac_address=device.device_info.mac_address,
additional_value=device.device_info.additional_value,
controller_id=controller_serial,
)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both halves were wrong, and fixed in 6d7fd96.

There is no REST source for the controller serial — I checked the HAR captures, and the vendor app sends /device/tou a controllerId it already holds. The only thing that publishes it is the MQTT device-info response (DeviceFeature.controller_serial_number).

So the section no longer claims the whole flow is MQTT-free. It now says the read itself is REST but is keyed by the controller serial, which is a hardware property: fetch it once, cache it, and every later read is pure REST. Example 3 is self-contained — a get_controller_serial() helper that connects, reads device info and disconnects, and a check_tou_settings(controller_serial=None) that skips it when you pass a cached value. The parameter list for get_tou_info says where the serial comes from.

Separately: Example 1 read it as feature.controllerSerialNumber, which the model has no such attribute for. Corrected to controller_serial_number while in the same file.

Comment on lines +819 to +823
:class:`~nwp500.models.TOUReservationSchedule` when the device
confirms a TOU write (triggered by
:meth:`~nwp500.NavienMqttClient.configure_tou_schedule`). There is no
MQTT read to subscribe to; to read the current schedule, use the REST
:meth:`~nwp500.NavienAPIClient.get_tou_info`.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6d7fd96 — the summary line now reads "Subscribe to Time-of-Use schedule write confirmations with automatic parsing." Done on both layers, and docs/reference/python_api/mqtt_client.rst had the same stale summary ("Subscribe to parsed TOU schedule responses"), which is corrected too.

eman added 2 commits August 29, 2026 07:37
request_tou_settings published a TOU_RESERVATION message to ctrl/tou/rd
carrying only controllerSerialNumber, then waited for a reply on
res/tou/rd. That reply never comes. On a live device with TOU fully
provisioned - program_reservation_use true, valid controller serial, and
a PG&E plan the REST API returns in full - the request produced silence
for 45 seconds.

ctrl/tou/rd with that command code is the *write*. It is what
configure_tou_schedule publishes, and what the vendor app publishes from
its TOU editor (TouScheduleViewmodel.setPublishMgppControlTou passes the
on/off flag and the interval list). The device replies on res/tou/rd to
confirm a write; it does not answer a request with no schedule in it.
The vendor app reads TOU over REST - /device/tou and /device/tou/convert
are the only TOU reads in any of the HAR captures.

So the method could not have worked, and it published a write-shaped
command while pretending to read. This device ignores it - touStatus was
unchanged across repeated calls - but a firmware that took it at face
value could read it as "store an empty TOU schedule", which is not a
risk worth carrying for a call that returns nothing.

Reads go through NavienAPIClient.get_tou_info. subscribe_tou_response
keeps working for write confirmations, and set_tou_enabled is a separate
command pair and is untouched. The docs and the two examples that called
it are updated, including the payload-capture example, which should not
have been publishing a control command during a read-only capture.

Claude-Session: https://claude.ai/code/session_01XVj9BYvuLj7Th3iFUVoeCn
Two subscribe_tou_response docstrings kept a summary line calling these
'schedule read responses' while the body below said write confirmations;
the summaries now say write confirmations too, as does the method entry
in the client reference.

The rewritten Example 3 used an undefined controller_serial while
claiming no MQTT connection was needed. Both halves are now honest: the
read is REST, but it is keyed by the controller serial, and the MQTT
device-info response is the only thing that publishes it. The example
fetches it once in a helper and takes a cached value; the parameter list
says where it comes from. No REST endpoint returns it - the vendor app
sends /device/tou a serial it already holds.

Example 1 read it as feature.controllerSerialNumber, an attribute the
model does not have; corrected while in the same file.

Claude-Session: https://claude.ai/code/session_01XVj9BYvuLj7Th3iFUVoeCn
@eman
eman force-pushed the remove/mqtt-tou-read branch from 4279a09 to 4d96cd5 Compare August 29, 2026 14:37
@eman
eman merged commit c732a24 into main Aug 29, 2026
7 checks passed
@eman
eman deleted the remove/mqtt-tou-read branch August 29, 2026 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants