Skip to content

Add Tasmota firmware update availability support#166910

Draft
scop wants to merge 1 commit intodevfrom
scop-tasmota-update
Draft

Add Tasmota firmware update availability support#166910
scop wants to merge 1 commit intodevfrom
scop-tasmota-update

Conversation

@scop
Copy link
Copy Markdown
Member

@scop scop commented Mar 30, 2026

Proposed change

Support displaying availability of Tasmota firmware updates. Use latest GitHub release as the latest ref.

Reopen of #159789

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

Additional information

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:

@home-assistant
Copy link
Copy Markdown

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

Code owner commands

Code owners of tasmota 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 tasmota 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, problem in config, problem in device, feature-request) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) on the pull request.

@scop scop force-pushed the scop-tasmota-update branch from 43ec52b to 5e7e299 Compare March 30, 2026 20:08
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

Add firmware update availability reporting to the Tasmota integration by checking the latest GitHub release and exposing it via the update platform.

Changes:

  • Add a new Tasmota update platform entity intended to compare device sw_version vs latest GitHub release.
  • Introduce a DataUpdateCoordinator that fetches the latest Tasmota GitHub release daily.
  • Add aiogithubapi as an integration requirement and wire Platform.UPDATE into Tasmota platforms.

Reviewed changes

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

Show a summary per file
File Description
requirements_test_all.txt Notes that aiogithubapi is also used by homeassistant.components.tasmota.
requirements_all.txt Notes that aiogithubapi is also used by homeassistant.components.tasmota.
homeassistant/components/tasmota/update.py Adds the Tasmota firmware UpdateEntity and setup logic.
homeassistant/components/tasmota/manifest.json Adds aiogithubapi dependency for Tasmota.
homeassistant/components/tasmota/coordinator.py Adds coordinator to fetch the latest Tasmota GitHub release.
homeassistant/components/tasmota/const.py Adds Platform.UPDATE to the forwarded platforms list.

Comment on lines +25 to +27
coordinator = TasmotaLatestReleaseUpdateCoordinator(hass, config_entry)
await coordinator.async_config_entry_first_refresh()

Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Avoid failing Tasmota config entry setup when the GitHub check is temporarily unavailable by not using async_config_entry_first_refresh() here (it raises ConfigEntryNotReady on failure); instead do a non-blocking async_refresh() (or catch ConfigEntryNotReady) and let the entity report available=False until data is fetched.

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +32
device_registry = dr.async_get(hass)
devices = device_registry.devices.get_devices_for_config_entry_id(
config_entry.entry_id
)
async_add_entities(TasmotaUpdateEntity(coordinator, device) for device in devices)
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Ensure update entities are created for devices discovered after startup by subscribing to device registry updates (or another discovery signal) instead of only snapshotting devices once during setup.

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +52
self.coordinator = coordinator
self.device_entry = device_entry
self._attr_unique_id = f"{device_entry.id}_update"

Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Make the entity react to coordinator updates by registering a listener (or inheriting from CoordinatorEntity) so the state changes when the latest release data refreshes.

Copilot uses AI. Check for mistakes.
Comment on lines +50 to +56
self.device_entry = device_entry
self._attr_unique_id = f"{device_entry.id}_update"

@property
def installed_version(self) -> str | None:
"""Return the installed version."""
return self.device_entry.sw_version # type:ignore[union-attr]
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Don't hold on to a frozen DeviceEntry snapshot for installed_version; fetch the current device entry from the device registry by id (or subscribe to device-registry updates) so installed_version reflects updated sw_version values.

Suggested change
self.device_entry = device_entry
self._attr_unique_id = f"{device_entry.id}_update"
@property
def installed_version(self) -> str | None:
"""Return the installed version."""
return self.device_entry.sw_version # type:ignore[union-attr]
self._device_id = device_entry.id
self._attr_unique_id = f"{device_entry.id}_update"
@property
def installed_version(self) -> str | None:
"""Return the installed version."""
device_registry = dr.async_get(self.hass)
if not (device := device_registry.async_get(self._device_id)):
return None
return device.sw_version # type:ignore[union-attr]

Copilot uses AI. Check for mistakes.
Comment on lines +54 to +78
def installed_version(self) -> str | None:
"""Return the installed version."""
return self.device_entry.sw_version # type:ignore[union-attr]

@property
def latest_version(self) -> str:
"""Return the latest version."""
return self.coordinator.data.tag_name.removeprefix("v")

@property
def release_url(self) -> str:
"""Return the release URL."""
return self.coordinator.data.html_url

@property
def release_summary(self) -> str:
"""Return the release summary."""
return self.coordinator.data.name

def release_notes(self) -> str | None:
"""Return the release notes."""
if not self.coordinator.data.body:
return None
return re.sub(
r"^<picture>.*?</picture>", "", self.coordinator.data.body, flags=re.DOTALL
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Handle coordinator.data being unavailable by returning None for latest_version/release metadata and gating available on coordinator.last_update_success; otherwise attribute access like self.coordinator.data.tag_name will raise when the GitHub fetch fails.

Suggested change
def installed_version(self) -> str | None:
"""Return the installed version."""
return self.device_entry.sw_version # type:ignore[union-attr]
@property
def latest_version(self) -> str:
"""Return the latest version."""
return self.coordinator.data.tag_name.removeprefix("v")
@property
def release_url(self) -> str:
"""Return the release URL."""
return self.coordinator.data.html_url
@property
def release_summary(self) -> str:
"""Return the release summary."""
return self.coordinator.data.name
def release_notes(self) -> str | None:
"""Return the release notes."""
if not self.coordinator.data.body:
return None
return re.sub(
r"^<picture>.*?</picture>", "", self.coordinator.data.body, flags=re.DOTALL
def available(self) -> bool:
"""Return if entity is available."""
return bool(self.coordinator.last_update_success and self.coordinator.data)
@property
def installed_version(self) -> str | None:
"""Return the installed version."""
return self.device_entry.sw_version # type:ignore[union-attr]
@property
def latest_version(self) -> str | None:
"""Return the latest version."""
if not self.coordinator.data:
return None
return self.coordinator.data.tag_name.removeprefix("v")
@property
def release_url(self) -> str | None:
"""Return the release URL."""
if not self.coordinator.data:
return None
return self.coordinator.data.html_url
@property
def release_summary(self) -> str | None:
"""Return the release summary."""
if not self.coordinator.data:
return None
return self.coordinator.data.name
def release_notes(self) -> str | None:
"""Return the release notes."""
if not self.coordinator.data or not self.coordinator.data.body:
return None
return re.sub(
r"^<picture>.*?</picture>",
"",
self.coordinator.data.body,
flags=re.DOTALL,

Copilot uses AI. Check for mistakes.
@property
def installed_version(self) -> str | None:
"""Return the installed version."""
return self.device_entry.sw_version # type:ignore[union-attr]
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Remove the # type:ignore[union-attr] (and fix the type: ignore syntax if you keep one); DeviceEntry.sw_version already exists and is str | None, which matches this property’s return type.

Suggested change
return self.device_entry.sw_version # type:ignore[union-attr]
return self.device_entry.sw_version

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +32
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Tasmota update entities."""
coordinator = TasmotaLatestReleaseUpdateCoordinator(hass, config_entry)
await coordinator.async_config_entry_first_refresh()

device_registry = dr.async_get(hass)
devices = device_registry.devices.get_devices_for_config_entry_id(
config_entry.entry_id
)
async_add_entities(TasmotaUpdateEntity(coordinator, device) for device in devices)
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Add test coverage for the new update platform (entity creation, availability when GitHub fetch fails, and version comparison) since the integration already has extensive tests under tests/components/tasmota/.

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

Choose a reason for hiding this comment

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

This is very much broken/WIP, but already here for communicating some intent.

@scop
Copy link
Copy Markdown
Member Author

scop commented Mar 30, 2026

@emontnemery here's the current state of the work on this. I may have a bit of time to work more on this this week, and practically none at least two weeks after that. Do feel free to push here whatever you feel appropriate, if you have time and interest!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants