Skip to content

Add Lafayette Utilities System (LUS) - #240

Merged
tronikos merged 3 commits into
tronikos:mainfrom
marcusjames2:add-lus
Nov 4, 2026
Merged

tronikos merged 3 commits into
tronikos:mainfrom
marcusjames2:add-lus

Conversation

@marcusjames2

Copy link
Copy Markdown
Contributor

LUS (Lafayette, Louisiana) runs the Opower DSS portal at dss-lus.opower.com. Login is the same as SMECO's: the credentials are posted to identity-management-v1/cws/v1/lus/login, which returns a sessionToken. Everything after login uses the existing DSS support unchanged.

Tested with a live account (python -m opower --utility lus): login, the bill forecast, and daily electric reads with cost, including the 11/1 DST change.

Water and wastewater not included. LUS also has hourly water and wastewater meters. multi-account-v1 lists them only with ?X-DS-4930-uom-enable=true, as the LUS portal sends; wastewater's meterType is WASTE_WATER; and reads are in gallons. Since UnitOfMeasure has no gallons and the Home Assistant coordinator records non-electric meters as CCF, they would be imported about 748× too high, so this PR adds electric only. I'm happy to follow up with gallons support if that's welcome.

🤖 Generated with Claude Code

LUS (Lafayette, Louisiana) runs the Opower DSS portal at
dss-lus.opower.com for electric, water and wastewater. Login posts the
credentials to the identity-management login API, which returns a
sessionToken, the same flow as SMECO.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/opower/utilities/lus.py Outdated
if response.status in (400, 401, 403):
raise InvalidAuth("Username/Password are invalid")
response.raise_for_status()
result = await response.json()

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

SMECO is catching Exception here and raises InvalidAuth. Do you need to do the same?

@marcusjames2 marcusjames2 Nov 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, thanks. Added in cc856c6 (latest commit): a non-JSON reply now raises an error instead of escaping as a decode error. I used CannotConnect rather than InvalidAuth there, since a non-JSON reply means the portal returned something like a maintenance page rather than rejected the credentials, and InvalidAuth would make Home Assistant start reauth. Rejected credentials (400/401/403) and a reply without a sessionToken still raise InvalidAuth. Happy to switch to InvalidAuth to match SMECO if you prefer.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

CannotConnect is retryable. Home Assistant will keep retrying it forever introducing unnecessary load to the website. If this ever fails it's most likely because the utility stopped using Opower or the API changed and we will need to change the code to handle it. So it's better to return a non retryable error. We might need a separate error to distinguish this but for now InvalidAuth seems better.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, thanks. Changed in 321bd17: a non-JSON reply now raises InvalidAuth (non-retryable), like SMECO, and the test checks for it. (Also, the commit I mentioned in my earlier reply should have read cc856c6.)

marcusjames2 and others added 2 commits November 3, 2026 18:57
A reply that isn't JSON (e.g. a maintenance page) now raises
CannotConnect instead of a raw decode error, and a JSON reply that
isn't an object is treated like one without a sessionToken.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CannotConnect is retried indefinitely. A non-JSON reply most likely
means the login API changed and needs a code change, so raise the
non-retryable InvalidAuth, as SMECO does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tronikos
tronikos merged commit cd55c9a into tronikos:main Nov 4, 2026
3 checks passed
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