Skip to content

Add minutes and energy sensors to Nuheat#160751

Closed
eric wants to merge 1 commit intohome-assistant:devfrom
eric:nuheat-add-energy-sensor
Closed

Add minutes and energy sensors to Nuheat#160751
eric wants to merge 1 commit intohome-assistant:devfrom
eric:nuheat-add-energy-sensor

Conversation

@eric
Copy link
Copy Markdown

@eric eric commented Jan 11, 2026

Proposed change

The Nuheat API has the number of minutes the floor was on, which can be used to calculate an estimated energy usage. If the settings have been set on the Nuheat server, it will use that to calculate the energy usage, but if not, they can be provided in Home Assistant settings.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

Copilot AI review requested due to automatic review settings January 11, 2026 23:34
Copy link
Copy Markdown
Contributor

@home-assistant home-assistant Bot left a comment

Choose a reason for hiding this comment

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

Hi @eric

It seems you haven't yet signed a CLA. Please do so here.

Once you do that we will be able to review and accept this pull request.

Thanks!

@home-assistant
Copy link
Copy Markdown
Contributor

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

@home-assistant
Copy link
Copy Markdown
Contributor

Hey there @tstabrawa, mind taking a look at this pull request as it has been labeled with an integration (nuheat) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of nuheat can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign nuheat Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component) on the pull request.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds energy monitoring capabilities to the NuHeat integration by introducing sensors for heating time and energy consumption. The energy data is fetched from the NuHeat API, with an option to calculate energy usage from user-provided floor area and watt density when API data is unavailable.

Changes:

  • Added new sensor platform with heating time (minutes) and energy (kWh) sensors
  • Implemented energy data coordinator to fetch data from the NuHeat API
  • Added config flow options to allow users to configure floor area and watt density for energy calculations
  • Extended test coverage for energy data handling scenarios

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
homeassistant/components/nuheat/init.py Added energy data coordinator, NuHeatEnergyData dataclass, and logic to fetch/calculate energy from API or user options
homeassistant/components/nuheat/sensor.py New sensor platform implementation with energy and heating time entities
homeassistant/components/nuheat/const.py Added sensor platform and new configuration constants for floor area and watt density
homeassistant/components/nuheat/config_flow.py Added options flow for configuring energy calculation parameters
homeassistant/components/nuheat/climate.py Updated to handle new tuple structure in runtime data
homeassistant/components/nuheat/strings.json Added translations for sensor entities and options flow
tests/components/nuheat/mocks.py Added mock energy data and session creation helper
tests/components/nuheat/test_init.py Added tests for energy sensor creation and calculation scenarios
tests/components/nuheat/test_config_flow.py Added test for options flow
tests/components/nuheat/test_climate.py Updated tests to mock energy API endpoint

async def _async_update_energy_data() -> NuHeatEnergyData:
"""Fetch energy data from NuHeat API."""
# Access session ID from library internals (no public API available)
session_id: str = thermostat._session._session_id # noqa: SLF001
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

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

Accessing private attributes through multiple levels of indirection (thermostat._session._session_id) is fragile and tightly couples the code to library internals. Consider checking if the nuheat library exposes a public API for session management, or store the session ID separately during initialization to avoid repeated private attribute access.

Copilot uses AI. Check for mistakes.
Comment thread homeassistant/components/nuheat/__init__.py Outdated
Comment thread homeassistant/components/nuheat/__init__.py Outdated
Comment thread homeassistant/components/nuheat/config_flow.py Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Comment thread homeassistant/components/nuheat/__init__.py Outdated
Comment on lines +20 to +34
"title": "Connect to the NuHeat"
}
}
},
"options": {
"step": {
"init": {
"description": "The NuHeat API only reports energy usage if you have configured watt density in the NuHeat app. If you haven't done that, you can enter your floor specifications here to calculate energy from heating time instead.",
"data": {
"floor_area": "Heated floor area (square feet)",
"watt_density": "Watt density (watts per square foot)"
},
"data_description": {
"floor_area": "The total area covered by your heated floor mat. Check your installation documentation or measure the heated area.",
"watt_density": "Power output per square foot. Most NuHeat mats are 12 watts/sqft. Check your mat's specifications if unsure."
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The description should use sentence case consistently. Change 'NuHeat' references to lowercase 'Nuheat' except at the beginning of sentences, per Home Assistant writing style guidelines.

Suggested change
"title": "Connect to the NuHeat"
}
}
},
"options": {
"step": {
"init": {
"description": "The NuHeat API only reports energy usage if you have configured watt density in the NuHeat app. If you haven't done that, you can enter your floor specifications here to calculate energy from heating time instead.",
"data": {
"floor_area": "Heated floor area (square feet)",
"watt_density": "Watt density (watts per square foot)"
},
"data_description": {
"floor_area": "The total area covered by your heated floor mat. Check your installation documentation or measure the heated area.",
"watt_density": "Power output per square foot. Most NuHeat mats are 12 watts/sqft. Check your mat's specifications if unsure."
"title": "Connect to Nuheat"
}
}
},
"options": {
"step": {
"init": {
"description": "The Nuheat API only reports energy usage if you have configured watt density in the Nuheat app. If you haven't done that, you can enter your floor specifications here to calculate energy from heating time instead.",
"data": {
"floor_area": "Heated floor area (square feet)",
"watt_density": "Watt density (watts per square foot)"
},
"data_description": {
"floor_area": "The total area covered by your heated floor mat. Check your installation documentation or measure the heated area.",
"watt_density": "Power output per square foot. Most Nuheat mats are 12 watts/sqft. Check your mat's specifications if unsure."

Copilot uses AI. Check for mistakes.
Comment on lines +159 to +166
floor_area = entry.options.get(CONF_FLOOR_AREA)
if floor_area:
watt_density = entry.options.get(CONF_WATT_DENSITY, DEFAULT_WATT_DENSITY)
# Power (watts) = floor_area (sqft) * watt_density (watts/sqft)
# Energy (kWh) = power (watts) * time (minutes) / 60 / 1000
power_watts = floor_area * watt_density
calculated_kwh = (power_watts * total_minutes) / 60 / 1000
energy_kwh: float | None = calculated_kwh
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The conditional logic prioritizes user-configured floor area over API-provided kWh data. This may be unexpected behavior. Consider documenting this precedence decision or validating that this is the intended priority.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is intentional.

@eric eric force-pushed the nuheat-add-energy-sensor branch from 892cc1a to 9e056ec Compare January 12, 2026 00:10
Copy link
Copy Markdown
Contributor

@tstabrawa tstabrawa left a comment

Choose a reason for hiding this comment

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

@eric First of all, I love that you want to improve the Nuheat integration with this feature. This is one that I've also wanted to see in the past, and I commend your efforts to add it, especially considering how much effort appears to have gone into it.

Unfortunately, directly accessing the device's API breaks one of the integration rules for Home Assistant. Specifically:

4. Communication with devices/services

  1. All API specific code has to be part of a third party library hosted on PyPi. Home Assistant should only interact with objects and not make direct calls to the API.

I don't expect that the core Home Assistant dev's would merge this PR as a result.

In theory, the way to avoid breaking this rule would be to contribue to the upstream API project. Unfortunately, that project appears to have gone unmaintained for a few years now, so I'm not sure how much success you'd have getting a PR through there either. So far, the Nuheat integration has been stable without any upstream activity, at the cost of not really being able to add new features (such as #122675).

If there's sufficient interest in the community, probably the only way forward would be for someone to fork the upstream repo and pick up maintenance on that. I've considered doing the same in the past and have also considered re-architecting it using aiohttp, but I just can't commit the amount of time needed to get started, let alone to maintain it. If you feel like you have the time and expertise to take something like that on, I would be fine with a PR that involves switching the upstream PyPi dependency to a new project that you maintain (though I may try to rope you in to help out as a code-owner of the integration, too).

Comment on lines +112 to +139
async def _async_update_energy_data() -> NuHeatEnergyData:
"""Fetch energy data from NuHeat API."""
now = dt_util.now()
date_str = now.strftime("%Y-%m-%d")

url = "https://mynuheat.com/api/energyusage"
params: dict[str, Any] = {
"sessionid": _get_session_id(thermostat),
"serialnumber": serial_number,
"view": "day",
"date": date_str,
"history": "0",
"calc": "yes",
"weekstart": "sunday",
}

try:
async with session.get(url, params=params) as response:
if response.status == 401:
_LOGGER.debug("Session expired, re-authenticating")
await hass.async_add_executor_job(_reauthenticate, thermostat)
params["sessionid"] = _get_session_id(thermostat)
async with session.get(url, params=params) as retry_response:
retry_response.raise_for_status()
data = await retry_response.json()
else:
response.raise_for_status()
data = await response.json()
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.

Unfortunately, directly accessing the device's API breaks one of the integration rules for Home Assistant. Specifically:

4. Communication with devices/services

  1. All API specific code has to be part of a third party library hosted on PyPi. Home Assistant should only interact with objects and not make direct calls to the API.

@joostlek
Copy link
Copy Markdown
Member

You're right! We would have declined this PR for that exact reason.

So I will put it back to draft to keep the discussion available. I also want to offer my help, if needed. Like I am happy to help with making sure the forked library has a way to easily release new versions, as that will decrease maintenance costs. I am not willing to fork it as I am not a user, but I am definitely able to help you get started :)

@joostlek joostlek marked this pull request as draft January 12, 2026 14:35
@eric eric force-pushed the nuheat-add-energy-sensor branch from 9e056ec to 14fcd05 Compare January 13, 2026 00:19
@eric
Copy link
Copy Markdown
Author

eric commented Jan 13, 2026

This pull request has been updated and now depends on broox/python-nuheat#21. I don't have the capacity at the moment to take on ownership of any of this, but the code is all here and is solving my problem for myself in my local setup.

@github-actions
Copy link
Copy Markdown

There hasn't been any activity on this pull request recently. This pull request has been automatically marked as stale because of that and will be closed if no further activity occurs within 7 days.
If you are the author of this PR, please leave a comment if you want to keep it open. Also, please rebase your PR onto the latest dev branch to ensure that it's up to date with the latest changes.
Thank you for your contribution!

@github-actions github-actions Bot added the stale label Mar 15, 2026
@eric
Copy link
Copy Markdown
Author

eric commented Mar 16, 2026

Now what?

@joostlek
Copy link
Copy Markdown
Member

Euh yea it's a bit meh that the development on the library has gone stale. We could try to reach out via email to the developer to ask to merge and release. Otherwise we'd need to fork (for which we need to make sure the original library owner is indeed not active anymore)

@github-actions github-actions Bot removed the stale label Mar 16, 2026
@emontnemery
Copy link
Copy Markdown
Contributor

emontnemery commented Apr 2, 2026

There's no point in having this PR open because it can't be merged for the reasons discussed above.

There are two options to make it mergable:

  1. Make a fork of the library adding the energy calculation and make a new version of this PR which relies on the energy calculation in the library
  2. Make a smaller version of this PR which only adds the sensor which tracks usage time. Users can then use a sensor helper, for example the integration helper, to calculate energy use.

@eric please open a new PR based on one of those two options if you still want to get this merged.

@emontnemery emontnemery closed this Apr 2, 2026
@eric
Copy link
Copy Markdown
Author

eric commented Apr 2, 2026

Great job, team.

@github-actions github-actions Bot locked and limited conversation to collaborators Apr 3, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants