fix: source get_device_in from Device/{contract} endpoint (reCAPTCHA-gated DeviceIn) - #269
fix: source get_device_in from Device/{contract} endpoint (reCAPTCHA-gated DeviceIn)#269Minitour wants to merge 1 commit into
Conversation
The DeviceIn/{contract_id} endpoint now returns HTTP 400 ("Token should be
provide") unless a reCAPTCHA token is supplied via the RecaptchToken header,
which isn't feasible for headless clients (GuyKh/iec-custom-component#462).
get_device_in now sources the device list from the reCAPTCHA-free
Device/{contract_id} endpoint and adapts it to DeviceInResponse, keeping the
same return shape (meter_kind defaults to "Consumption"). DeviceInResponse and
DeviceInDevice fields are defaulted so they remain backward-compatible with the
legacy DeviceIn payload.
Co-authored-by: Cursor <cursoragent@cursor.com>
PR Summary by QodoFix get_device_in by sourcing devices from reCAPTCHA-free Device endpoint
AI Description
Diagram
High-Level Assessment
Files changed (3)
|
Code Review by Qodo
1. Inactive devices returned
|
| devices = await get_devices(session, token, contract_id) | ||
| device_in_devices = [ | ||
| DeviceInDevice( | ||
| is_active=device.is_active, | ||
| device_type=device.device_type, | ||
| device_number=device.device_number, | ||
| device_code=device.device_code, | ||
| meter_kind=DEFAULT_METER_KIND, | ||
| ) | ||
| for device in (devices or []) | ||
| ] |
There was a problem hiding this comment.
1. Inactive devices returned 🐞 Bug ≡ Correctness
data.get_device_in() now adapts the full /api/Device/{contract_id} list without filtering
inactive devices, even though IecClient.get_device_in() is documented as returning active devices.
Callers that select the first returned device (as in example.py) may start using an inactive meter
and fail downstream operations (e.g., remote reading).
Agent Prompt
### Issue description
`data.get_device_in()` returns all devices from `get_devices()` (including inactive). This conflicts with the documented expectation that `get_device_in()` provides “active devices”, and can break callers that choose the first device.
### Issue Context
- `IecClient.get_device_in()` states it returns active devices.
- `example.py` selects `device_in.devices[0]` without checking `is_active`.
### Fix Focus Areas
- Filter returned devices to active only **or** sort active devices first (preserving the old behavior expectation).
- If you choose to keep inactive devices in the response for completeness, ensure the ordering (or documentation) prevents common “pick first” callers from selecting an inactive meter.
- iec_api/data.py[427-455]
- example.py[70-85]
- iec_api/iec_client.py[670-678]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
@GuyKh I didn't find your comment. I only see the qodo bot. |
My comment was about It obfuscates the actual API behavior; And transforms one object to the other, just to preserve the library API. |
|
Makes sense. Until we meet again, cheers 🍻 |
Summary
get_device_incallsGET /api/DeviceIn/{contract_id}, which now returnsHTTP 400 {"Error":"Token should be provide","Code":400}unless a reCAPTCHA token is supplied via aRecaptchTokenheader. That header can't be produced by a headless client, which breaks device/meter enumeration for downstream consumers (e.g. the Home Assistant integration — see GuyKh/iec-custom-component#462).This PR re-sources the same device list from the reCAPTCHA-free
GET /api/Device/{contract_id}endpoint and adapts it toDeviceInResponse, so the public API (IecClient.get_device_in()/data.get_device_in()) keeps the exact same return shape. No caller changes are required.Details
data.get_device_innow delegates toget_devices(Device/{contract_id}) and maps eachDeviceinto aDeviceInDevice.Device/{contract_id}returns the same fields as the old endpoint (isActive,deviceType,deviceNumber,deviceCode) exceptmeterKind, someter_kinddefaults to"Consumption"(the value theRemoteReadingRangerequest already defaults to viaSmartMeter).DeviceInDevice/DeviceInResponsefields are given defaults so the models stay backward-compatible with the legacyDeviceInpayload (existingfrom_dictparsing still works).GET_DEVICE_IN_URLis kept but annotated as deprecated.Verification
Confirmed live against a real account:
GET /api/DeviceIn/{contract}→400 "Token should be provide"GET /api/Device/{contract}→200 [{"isActive":true,"deviceType":3,"deviceNumber":"...","deviceCode":"503", ...}]End-to-end,
data.get_device_in(session, token, contract_id)now returns a populatedDeviceInResponse(status=0,is_active=True, one device withmeter_kind="Consumption"), and the Home Assistant integration fetches devices successfully.Test plan
get_device_inreturns devices for a contract with smart meter(s)device_number/device_codeDeviceInResponse.from_dict(...)still parses the old payloadFixes GuyKh/iec-custom-component#462