1
0
mirror of https://github.com/natekspencer/hacs-oasis_mini.git synced 2025-12-06 18:44:14 -05:00
Files
hacs-oasis_mini/custom_components/oasis_mini/update.py
Nathan Spencer 379b6f67f2 Swap out direct HTTP connection with server MQTT connection to handle firmware 2.60+ (#98)
* Switch to using mqtt

* Better mqtt handling when connection is interrupted

* Get track info from the cloud when playlist or index changes

* Add additional helpers

* Dynamically handle devices and other enhancements

* 📝 Add docstrings to `mqtt`

Docstrings generation was requested by @natekspencer.

* https://github.com/natekspencer/hacs-oasis_mini/pull/98#issuecomment-3568450288

The following files were modified:

* `custom_components/oasis_mini/__init__.py`
* `custom_components/oasis_mini/binary_sensor.py`
* `custom_components/oasis_mini/button.py`
* `custom_components/oasis_mini/config_flow.py`
* `custom_components/oasis_mini/coordinator.py`
* `custom_components/oasis_mini/entity.py`
* `custom_components/oasis_mini/helpers.py`
* `custom_components/oasis_mini/image.py`
* `custom_components/oasis_mini/light.py`
* `custom_components/oasis_mini/media_player.py`
* `custom_components/oasis_mini/number.py`
* `custom_components/oasis_mini/pyoasiscontrol/clients/cloud_client.py`
* `custom_components/oasis_mini/pyoasiscontrol/clients/http_client.py`
* `custom_components/oasis_mini/pyoasiscontrol/clients/mqtt_client.py`
* `custom_components/oasis_mini/pyoasiscontrol/clients/transport.py`
* `custom_components/oasis_mini/pyoasiscontrol/device.py`
* `custom_components/oasis_mini/pyoasiscontrol/utils.py`
* `custom_components/oasis_mini/select.py`
* `custom_components/oasis_mini/sensor.py`
* `custom_components/oasis_mini/switch.py`
* `custom_components/oasis_mini/update.py`
* `update_tracks.py`

* Fix formatting in transport.py

* Replace tabs with spaces

* Use tuples instead of sets for descriptors

* Encode svg in image entity

* Fix iot_class

* Fix tracks list url

* Ensure update_tracks closes the connection

* Fix number typing and docstring

* Fix docstring in update_tracks

* Cache playlist based on type

* Fix formatting in device.py

* Add missing async_send_auto_clean_command to http client

* Propagate UnauthenticatedError from async_get_track_info

* Adjust exceptions

* Move create_client outside of try block in config_flow

* Formatting

* Address PR comments

* Formatting

* Add noqa: ARG001 on unused hass

* Close cloud/MQTT clients if initial coordinator refresh fails.

* Address PR again

* PR fixes

* Pass config entry to coordinator

* Remove async_timeout (thanks ChatGPT... not)

* Address PR

* Replace magic numbers for status code

* Update autoplay wording/ordering

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-11-24 01:09:23 -07:00

121 lines
4.1 KiB
Python

"""Oasis device update entity."""
from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
from homeassistant.components.update import (
UpdateDeviceClass,
UpdateEntity,
UpdateEntityDescription,
UpdateEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import OasisDeviceConfigEntry, setup_platform_from_coordinator
from .entity import OasisDeviceEntity
from .pyoasiscontrol import OasisDevice
from .pyoasiscontrol.const import STATUS_UPDATING
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(hours=6)
async def async_setup_entry(
hass: HomeAssistant, # noqa: ARG001
entry: OasisDeviceConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""
Set up update entities for Oasis devices from a configuration entry.
Parameters:
hass (HomeAssistant): Home Assistant core instance.
entry (OasisDeviceConfigEntry): Config entry containing runtime data used to create device update entities.
async_add_entities (AddEntitiesCallback): Callback to add created entities to Home Assistant.
"""
def make_entities(new_devices: list[OasisDevice]):
"""
Create update entities for the given Oasis devices.
Parameters:
new_devices (list[OasisDevice]): Devices to create update entities for.
Returns:
list: A list of OasisDeviceUpdateEntity instances corresponding to each device.
"""
return [
OasisDeviceUpdateEntity(entry.runtime_data, device, DESCRIPTOR)
for device in new_devices
]
setup_platform_from_coordinator(entry, async_add_entities, make_entities, True)
DESCRIPTOR = UpdateEntityDescription(
key="software", device_class=UpdateDeviceClass.FIRMWARE
)
class OasisDeviceUpdateEntity(OasisDeviceEntity, UpdateEntity):
"""Oasis device update entity."""
_attr_supported_features = (
UpdateEntityFeature.INSTALL | UpdateEntityFeature.PROGRESS
)
@property
def in_progress(self) -> bool | int:
"""Update installation progress."""
if self.device.status_code == STATUS_UPDATING:
return self.device.download_progress
return False
@property
def installed_version(self) -> str:
"""Version installed and in use."""
return self.device.software_version
@property
def should_poll(self) -> bool:
"""Set polling to True."""
return True
async def async_install(
self, version: str | None, backup: bool, **kwargs: Any
) -> None:
"""
Trigger installation of the latest available update on the device.
If the latest available version matches the device's currently installed software version, no action is taken. Otherwise an upgrade is started on the device.
Parameters:
version (str | None): Ignored by this implementation; the entity uses its known latest version.
backup (bool): Ignored by this implementation.
**kwargs: Additional keyword arguments are ignored.
"""
if self.latest_version == self.device.software_version:
return
await self.device.async_upgrade()
async def async_update(self) -> None:
"""
Refreshes this entity's latest software metadata.
Fetches the latest software details from the coordinator's cloud client and updates
the entity's `latest_version`, `release_summary`, and `release_url` attributes.
If no software details are returned, the entity's attributes are left unchanged.
"""
client = self.coordinator.cloud_client
if not (software := await client.async_get_latest_software_details()):
_LOGGER.warning("Unable to get latest software details")
return
self._attr_latest_version = software["version"]
self._attr_release_summary = software["description"]
self._attr_release_url = f"https://app.grounded.so/software/{software['id']}"