feat: parse power measurements in http-power and add a power read CLI command#790
Conversation
📝 WalkthroughWalkthroughHTTP Power driver's dummy ChangesHTTP Power JSON Parsing and CLI Integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| url: "http://192.168.1.65/cm?cmnd=Status%2010" # Tasmota | ||
| voltage_path: "StatusSNS.ENERGY.Voltage" | ||
| current_path: "StatusSNS.ENERGY.Current" | ||
| ``` |
There was a problem hiding this comment.
Can you provide the examples for the shelly? I have one and would love to test it :)
| @base.command() | ||
| def read(): | ||
| """Read power measurements""" | ||
| for reading in self.read(): |
There was a problem hiding this comment.
for the CLI we should probably perform a single read. Otherwise this will remain in a loop reading powers without any throttling.
Another option is to provide a number of readings, and the interval between them, may be default to 1s ?
mangelajo
left a comment
There was a problem hiding this comment.
just a couple of comments but looks great :) thank you so much!
| try: | ||
| return float(value) | ||
| except (TypeError, ValueError): | ||
| raise ValueError(f"value at {key!r} is not numeric: {value!r}") from None |
There was a problem hiding this comment.
The error message includes {value!r}, which dumps the full Python repr of whatever sits at the targeted path. Can a device response contain sensitive fields that should not leak into logs or client-visible error messages? If so consider including only the type name instead:
raise ValueError(f"value at {key!r} is not numeric (got {type(value).__name__})") from NoneThere was a problem hiding this comment.
That is a good comment, I did not think of sensitive data, but we can never be sure.
| from jumpstarter.driver import Driver, export | ||
|
|
||
|
|
||
| def _json_path(data, path: str): |
There was a problem hiding this comment.
Annotations missing:
def _json_path(data: Any, path: str) -> Any:
def _extract_reading(data: Any, path: Optional[str], default_key: str) -> float:d0897d8 to
e2fd76c
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
python/packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py (1)
67-75: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
count/intervaldon’t control individual readings when the stream is continuous.At Line 68,
for reading in self.read()can consume an unbounded stream, so the outer counter (i) may never increment. That makes--countand--intervalineffective for live streams.Suggested fix
- `@click.option`("--count", "-n", default=1, help="Number of readings (0 = infinite)", show_default=True) - `@click.option`("--interval", "-i", default=1.0, help="Seconds between readings", show_default=True) + `@click.option`( + "--count", + "-n", + type=click.IntRange(min=0), + default=1, + help="Number of readings (0 = infinite)", + show_default=True, + ) + `@click.option`( + "--interval", + "-i", + type=click.FloatRange(min=0.0), + default=1.0, + help="Seconds between readings", + show_default=True, + ) def read(count, interval): """Read power measurements""" - i = 0 - while count == 0 or i < count: - for reading in self.read(): - click.echo( - f"voltage={reading.voltage} V current={reading.current} A " - f"apparent_power={reading.apparent_power} VA" - ) - i += 1 - if (count == 0 or i < count) and interval > 0: - time.sleep(interval) + for i, reading in enumerate(self.read(), start=1): + click.echo( + f"voltage={reading.voltage} V current={reading.current} A " + f"apparent_power={reading.apparent_power} VA" + ) + if count > 0 and i >= count: + break + if interval > 0: + time.sleep(interval)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@python/packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py` around lines 67 - 75, The while loop at line 67 uses a counter `i` to control iterations, but the inner `for reading in self.read()` consumes an unbounded stream without respecting the count limit, making the `--count` and `--interval` parameters ineffective for continuous streams. Add a break condition inside the for loop (right after the click.echo call) to check if we have reached the count limit by comparing `i` with `count`, and increment `i` for each individual reading processed rather than only after the entire stream is consumed. This ensures each reading respects the count constraint.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@python/packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py`:
- Around line 67-75: The while loop at line 67 uses a counter `i` to control
iterations, but the inner `for reading in self.read()` consumes an unbounded
stream without respecting the count limit, making the `--count` and `--interval`
parameters ineffective for continuous streams. Add a break condition inside the
for loop (right after the click.echo call) to check if we have reached the count
limit by comparing `i` with `count`, and increment `i` for each individual
reading processed rather than only after the entire stream is consumed. This
ensures each reading respects the count constraint.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a64bfb55-d9dd-42d6-bbec-e3520a4ac85e
📒 Files selected for processing (6)
python/packages/jumpstarter-driver-http-power/README.mdpython/packages/jumpstarter-driver-http-power/examples/exporter.yamlpython/packages/jumpstarter-driver-http-power/jumpstarter_driver_http_power/driver.pypython/packages/jumpstarter-driver-http-power/jumpstarter_driver_http_power/driver_test.pypython/packages/jumpstarter-driver-power/jumpstarter_driver_power/client.pypython/packages/jumpstarter-driver-power/jumpstarter_driver_power/client_test.py
💤 Files with no reviewable changes (1)
- python/packages/jumpstarter-driver-http-power/examples/exporter.yaml
✅ Files skipped from review due to trivial changes (1)
- python/packages/jumpstarter-driver-http-power/README.md
🚧 Files skipped from review as they are similar to previous changes (2)
- python/packages/jumpstarter-driver-power/jumpstarter_driver_power/client_test.py
- python/packages/jumpstarter-driver-http-power/jumpstarter_driver_http_power/driver.py
Previously the http-power driver's
read()always returned dummy(0.0, 0.0)values.read()now parses the JSON the read endpoint returns and pulls voltage/current out of it. By default it looks for top-levelvoltage/currentkeys, but you can pointvoltage_path/current_pathat a dotted path (emeter.voltage,StatusSNS.ENERGY.Voltage,meters.0.voltage) for devices that nest them.Tested against a real Shelly Plug S Gen3.