Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions homeassistant/components/updater/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,25 @@
import async_timeout
import voluptuous as vol

from homeassistant.const import ATTR_FRIENDLY_NAME, __version__ as current_version
from homeassistant.const import __version__ as current_version
from homeassistant.helpers import event
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers import discovery
from homeassistant.helpers.dispatcher import async_dispatcher_send
import homeassistant.helpers.config_validation as cv
import homeassistant.util.dt as dt_util

_LOGGER = logging.getLogger(__name__)

ATTR_RELEASE_NOTES = "release_notes"
ATTR_NEWEST_VERSION = "newest_version"

CONF_REPORTING = "reporting"
CONF_COMPONENT_REPORTING = "include_used_components"

DOMAIN = "updater"

ENTITY_ID = "updater.updater"
DISPATCHER_REMOTE_UPDATE = "updater_remote_update"

UPDATER_URL = "https://updater.home-assistant.io/"
UPDATER_UUID_FILE = ".uuid"
Expand All @@ -47,6 +50,16 @@
)


class Updater:
"""Updater class for data exchange."""

def __init__(self, update_available: bool, newest_version: str, release_notes: str):
"""Initialize attributes."""
self.update_available = update_available
self.release_notes = release_notes
self.newest_version = newest_version


def _create_uuid(hass, filename=UPDATER_UUID_FILE):
"""Create UUID and save it in a file."""
with open(hass.config.path(filename), "w") as fptr:
Expand All @@ -73,6 +86,10 @@ async def async_setup(hass, config):
# This component only makes sense in release versions
_LOGGER.info("Running on 'dev', only analytics will be submitted")

hass.async_create_task(
discovery.async_load_platform(hass, "binary_sensor", DOMAIN, {}, config)
)

config = config.get(DOMAIN, {})
if config.get(CONF_REPORTING):
huuid = await hass.async_add_job(_load_uuid, hass)
Expand All @@ -88,7 +105,7 @@ async def check_new_version(now):
if result is None:
return

newest, releasenotes = result
newest, release_notes = result

# Skip on dev
if newest is None or "dev" in current_version:
Expand All @@ -99,18 +116,17 @@ async def check_new_version(now):
newest = hass.components.hassio.get_homeassistant_version()

# Validate version
update_available = False
if StrictVersion(newest) > StrictVersion(current_version):
_LOGGER.info("The latest available version is %s", newest)
hass.states.async_set(
ENTITY_ID,
newest,
{
ATTR_FRIENDLY_NAME: "Update Available",
ATTR_RELEASE_NOTES: releasenotes,
},
)
_LOGGER.info("The latest available version of Home Assistant is %s", newest)
update_available = True
elif StrictVersion(newest) == StrictVersion(current_version):
_LOGGER.info("You are on the latest version (%s) of Home Assistant", newest)
elif StrictVersion(newest) < StrictVersion(current_version):
_LOGGER.debug("Local version is newer than the latest version (%s)", newest)

updater = Updater(update_available, newest, release_notes)
async_dispatcher_send(hass, DISPATCHER_REMOTE_UPDATE, updater)
Comment thread
Santobert marked this conversation as resolved.

# Update daily, start 1 hour after startup
_dt = dt_util.utcnow() + timedelta(hours=1)
Expand Down Expand Up @@ -151,7 +167,7 @@ async def get_newest_version(hass, huuid, include_components):
info_object,
)
except (asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Could not contact Home Assistant Update to check " "for updates")
_LOGGER.error("Could not contact Home Assistant Update to check for updates")
return None

try:
Expand Down
81 changes: 81 additions & 0 deletions homeassistant/components/updater/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""Support for Home Assistant Updater binary sensors."""

from homeassistant.core import callback
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.helpers.dispatcher import async_dispatcher_connect

from . import ATTR_NEWEST_VERSION, ATTR_RELEASE_NOTES, DISPATCHER_REMOTE_UPDATE, Updater


async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the updater binary sensors."""
async_add_entities([UpdaterBinary()])


class UpdaterBinary(BinarySensorDevice):
"""Representation of an updater binary sensor."""

def __init__(self):
"""Initialize the binary sensor."""
self._update_available = None
self._release_notes = None
self._newest_version = None
self._unsub_dispatcher = None

@property
def name(self) -> str:
"""Return the name of the binary sensor, if any."""
return "Updater"

@property
def unique_id(self) -> str:
"""Return a unique ID."""
return "updater"

@property
def is_on(self) -> bool:
"""Return true if the binary sensor is on."""
return self._update_available

@property
def available(self) -> bool:
"""Return True if entity is available."""
return self._update_available is not None

@property
def should_poll(self) -> bool:
"""Return True if entity has to be polled for state."""
return False

@property
def device_state_attributes(self) -> dict:
"""Return the optional state attributes."""
data = super().device_state_attributes
if data is None:
data = {}
if self._release_notes:
data[ATTR_RELEASE_NOTES] = self._release_notes
if self._newest_version:
data[ATTR_NEWEST_VERSION] = self._newest_version
return data

async def async_added_to_hass(self):
"""Register update dispatcher."""

@callback
def async_state_update(updater: Updater):
"""Update callback."""
self._newest_version = updater.newest_version
self._release_notes = updater.release_notes
self._update_available = updater.update_available
self.async_schedule_update_ha_state()

self._unsub_dispatcher = async_dispatcher_connect(
self.hass, DISPATCHER_REMOTE_UPDATE, async_state_update
)

async def async_will_remove_from_hass(self):
"""Register update dispatcher."""
if self._unsub_dispatcher is not None:
self._unsub_dispatcher()
self._unsub_dispatcher = None
Loading