async def fetch_data(self): """Fetch current versions from Github. Is a coroutine. """ url = URL_OPPIO_VERSION.format(channel=self.channel) machine = self.sys_machine or "default" try: _LOGGER.info("Fetching update data from %s", url) async with self.sys_websession.get(url, timeout=10) as request: data = await request.json(content_type=None) except (aiohttp.ClientError, asyncio.TimeoutError) as err: _LOGGER.warning("Can't fetch versions from %s: %s", url, err) raise UpdaterError() from err except json.JSONDecodeError as err: _LOGGER.warning("Can't parse versions from %s: %s", url, err) raise UpdaterError() from err # data valid? if not data or data.get(ATTR_CHANNEL) != self.channel: _LOGGER.warning("Invalid data from %s", url) raise UpdaterError() try: # Update supervisor version self._data[ATTR_SUPERVISOR] = AwesomeVersion(data["supervisor"]) # Update Open Peer Power core version self._data[ATTR_OPENPEERPOWER] = AwesomeVersion( data["openpeerpower"][machine]) # Update OppOS version if self.sys_oppos.board: self._data[ATTR_OPPOS] = AwesomeVersion( data["oppos"][self.sys_oppos.board]) self._data[ATTR_OTA] = data["ota"] # Update Open Peer Power plugins self._data[ATTR_CLI] = AwesomeVersion(data["cli"]) self._data[ATTR_DNS] = AwesomeVersion(data["dns"]) self._data[ATTR_AUDIO] = AwesomeVersion(data["audio"]) self._data[ATTR_OBSERVER] = AwesomeVersion(data["observer"]) self._data[ATTR_MULTICAST] = AwesomeVersion(data["multicast"]) # Update images for that versions self._data[ATTR_IMAGE][ATTR_OPENPEERPOWER] = data["image"]["core"] self._data[ATTR_IMAGE][ATTR_SUPERVISOR] = data["image"][ "supervisor"] self._data[ATTR_IMAGE][ATTR_AUDIO] = data["image"]["audio"] self._data[ATTR_IMAGE][ATTR_CLI] = data["image"]["cli"] self._data[ATTR_IMAGE][ATTR_DNS] = data["image"]["dns"] self._data[ATTR_IMAGE][ATTR_OBSERVER] = data["image"]["observer"] self._data[ATTR_IMAGE][ATTR_MULTICAST] = data["image"]["multicast"] except KeyError as err: _LOGGER.warning("Can't process version data: %s", err) raise UpdaterError() from err else: self.save_data()
def version(self) -> Optional[AwesomeVersion]: """Return version of Docker image.""" if LABEL_VERSION not in self.meta_labels: return None return AwesomeVersion(self.meta_labels[LABEL_VERSION])
def release_url(self) -> str | None: """URL to the full release notes of the latest version available.""" version = AwesomeVersion(self.latest_version) if version.dev or version.strategy == AwesomeVersionStrategy.UNKNOWN: return "https://github.com/home-assistant/supervisor/commits/main" return f"https://github.com/home-assistant/supervisor/releases/tag/{version}"
def release_url(self) -> str | None: """URL to the full release notes of the latest version available.""" version = AwesomeVersion(self.latest_version) if version.dev: return "https://github.com/home-assistant/core/commits/dev" return f"https://{'rc' if version.beta else 'www'}.home-assistant.io/latest-release-notes/"
def new(data: Dict[str, Any]): """Create a object from docker info.""" return DockerInfo(AwesomeVersion(data["ServerVersion"]), data["Driver"], data["LoggingDriver"])
def version_left_higher_then_right(left: str, right: str) -> bool: """Return a bool if source is newer than target, will also be true if identical.""" return AwesomeVersion(left) >= AwesomeVersion(right)
def test_simple(): handlers = CompareHandlers(AwesomeVersion("2"), AwesomeVersion("1")) assert handlers.check() handlers = CompareHandlers(AwesomeVersion("1"), AwesomeVersion("2")) assert not handlers.check()
) if TYPE_CHECKING: from . import Recorder _RecorderT = TypeVar("_RecorderT", bound="Recorder") _P = ParamSpec("_P") _LOGGER = logging.getLogger(__name__) RETRIES = 3 QUERY_RETRY_WAIT = 0.1 SQLITE3_POSTFIXES = ["", "-wal", "-shm"] DEFAULT_YIELD_STATES_ROWS = 32768 MIN_VERSION_MARIA_DB = AwesomeVersion("10.3.0", AwesomeVersionStrategy.SIMPLEVER) MIN_VERSION_MYSQL = AwesomeVersion("8.0.0", AwesomeVersionStrategy.SIMPLEVER) MIN_VERSION_PGSQL = AwesomeVersion("12.0", AwesomeVersionStrategy.SIMPLEVER) MIN_VERSION_SQLITE = AwesomeVersion("3.31.0", AwesomeVersionStrategy.SIMPLEVER) # This is the maximum time after the recorder ends the session # before we no longer consider startup to be a "restart" and we # should do a check on the sqlite3 database. MAX_RESTART_TIME = timedelta(minutes=10) # Retry when one of the following MySQL errors occurred: RETRYABLE_MYSQL_ERRORS = (1205, 1206, 1213) # 1205: Lock wait timeout exceeded; try restarting transaction # 1206: The total number of locks exceeds the lock table size # 1213: Deadlock found when trying to get lock; try restarting transaction
def version(self) -> AwesomeVersion | None: """Return the version of the integration.""" if "version" not in self.manifest: return None return AwesomeVersion(self.manifest["version"])
def test_sectons(): handlers = CompareHandlers(AwesomeVersion("1.2.3.4.5b0"), AwesomeVersion("1.2b0")) assert handlers.check()
CONF_PORT, CONF_SERVER, DATA_LIFX_MANAGER, DOMAIN as LIFX_DOMAIN, ) _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = timedelta(seconds=10) DISCOVERY_INTERVAL = 60 MESSAGE_TIMEOUT = 1.0 MESSAGE_RETRIES = 8 UNAVAILABLE_GRACE = 90 FIX_MAC_FW = AwesomeVersion("3.70") SWITCH_PRODUCT_IDS = [70, 71, 89] SERVICE_LIFX_SET_STATE = "set_state" ATTR_INFRARED = "infrared" ATTR_ZONES = "zones" ATTR_POWER = "power" LIFX_SET_STATE_SCHEMA = cv.make_entity_service_schema({ **LIGHT_TURN_ON_SCHEMA, ATTR_INFRARED: vol.All(vol.Coerce(int), vol.Clamp(min=0, max=255)), ATTR_ZONES: vol.All(cv.ensure_list, [cv.positive_int]), ATTR_POWER:
async def _block_till_run(self, version: AwesomeVersion) -> None: """Block until Home-Assistant is booting up or startup timeout.""" # Skip landingpage if version == LANDINGPAGE: return _LOGGER.info("Wait until Home Assistant is ready") # Manage timeouts timeout: bool = True start_time = time.monotonic() with suppress(AwesomeVersionException): # Version provide early stage UI if version >= AwesomeVersion("0.112.0"): _LOGGER.debug("Disable startup timeouts - early UI") timeout = False # Database migration migration_progress = False migration_file = Path(self.sys_config.path_homeassistant, ".migration_progress") # PIP installation pip_progress = False pip_file = Path(self.sys_config.path_homeassistant, ".pip_progress") while True: await asyncio.sleep(5) # 1: Check if Container is is_running if not await self.instance.is_running(): _LOGGER.error("Home Assistant has crashed!") break # 2: Check if API response if await self.sys_homeassistant.api.check_api_state(): _LOGGER.info("Detect a running Home Assistant instance") self._error_state = False return # 3: Running DB Migration if migration_file.exists(): if not migration_progress: migration_progress = True _LOGGER.info("Home Assistant record migration in progress") continue if migration_progress: migration_progress = False # Reset start time start_time = time.monotonic() _LOGGER.info("Home Assistant record migration done") # 4: Running PIP installation if pip_file.exists(): if not pip_progress: pip_progress = True _LOGGER.info("Home Assistant pip installation in progress") continue if pip_progress: pip_progress = False # Reset start time start_time = time.monotonic() _LOGGER.info("Home Assistant pip installation done") # 5: Timeout if (timeout and time.monotonic() - start_time > self.sys_homeassistant.wait_boot): _LOGGER.warning( "Don't wait anymore on Home Assistant startup!") break self._error_state = True raise HomeAssistantCrashError()
@property @final def state(self) -> str | None: """Return the entity state.""" if (installed_version := self.installed_version) is None or ( latest_version := self.latest_version) is None: return None if latest_version == self.__skipped_version: return STATE_OFF if latest_version == installed_version: return STATE_OFF try: newer = AwesomeVersion(latest_version) > installed_version return STATE_ON if newer else STATE_OFF except AwesomeVersionCompareException: # Can't compare versions, already tried exact match return STATE_ON @final @property def state_attributes(self) -> dict[str, Any] | None: """Return state attributes.""" if (release_summary := self.release_summary) is not None: release_summary = release_summary[:255] # If entity supports progress, return the in_progress value. # Otherwise, we use the internal progress value. if self.supported_features & UpdateEntityFeature.PROGRESS:
def test_strategy_match(version, strategy): """Test that the version matches the expected strategy.""" assert AwesomeVersion(version).strategy == strategy
DBUS_ATTR_VERSION, DBUS_IFACE_NM, DBUS_NAME_NM, DBUS_OBJECT_BASE, DBUS_OBJECT_NM, DeviceType, ) from ..interface import DBusInterface from ..utils import dbus_connected from .dns import NetworkManagerDNS from .interface import NetworkInterface from .settings import NetworkManagerSettings _LOGGER: logging.Logger = logging.getLogger(__name__) MINIMAL_VERSION = AwesomeVersion("1.14.6") class NetworkManager(DBusInterface): """Handle D-Bus interface for Network Manager. https://developer.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.html """ name = DBUS_NAME_NM def __init__(self) -> None: """Initialize Properties.""" self._dns: NetworkManagerDNS = NetworkManagerDNS() self._settings: NetworkManagerSettings = NetworkManagerSettings() self._interfaces: dict[str, NetworkInterface] = {}
}, extra=vol.REMOVE_EXTRA, ), vol.Optional(ATTR_OTA): vol.Url(), }, extra=vol.REMOVE_EXTRA, ) # pylint: disable=no-value-for-parameter SCHEMA_SUPERVISOR_CONFIG = vol.Schema( { vol.Optional(ATTR_TIMEZONE): validate_timezone, vol.Optional(ATTR_LAST_BOOT): str, vol.Optional( ATTR_VERSION, default=AwesomeVersion(SUPERVISOR_VERSION) ): version_tag, vol.Optional(ATTR_IMAGE): docker_image, vol.Optional( ATTR_ADDONS_CUSTOM_LIST, default=["https://github.com/hassio-addons/repository"], ): repositories, vol.Optional(ATTR_WAIT_BOOT, default=5): wait_boot, vol.Optional(ATTR_LOGGING, default=LogLevel.INFO): vol.Coerce(LogLevel), vol.Optional(ATTR_DEBUG, default=False): vol.Boolean(), vol.Optional(ATTR_DEBUG_BLOCK, default=False): vol.Boolean(), vol.Optional(ATTR_DIAGNOSTICS, default=None): vol.Maybe(vol.Boolean()), }, extra=vol.REMOVE_EXTRA, )
def version(self) -> AwesomeVersion: """Return if connectivity check is enabled.""" return AwesomeVersion(self.properties[DBUS_ATTR_VERSION])
def async_discover_single_value( value: ZwaveValue, device: DeviceEntry, discovered_value_ids: dict[str, set[str]] ) -> Generator[ZwaveDiscoveryInfo, None, None]: """Run discovery on a single ZWave value and return matching schema info.""" discovered_value_ids[device.id].add(value.value_id) for schema in DISCOVERY_SCHEMAS: # check manufacturer_id if (schema.manufacturer_id is not None and value.node.manufacturer_id not in schema.manufacturer_id): continue # check product_id if (schema.product_id is not None and value.node.product_id not in schema.product_id): continue # check product_type if (schema.product_type is not None and value.node.product_type not in schema.product_type): continue # check firmware_version_range if schema.firmware_version_range is not None and ( (schema.firmware_version_range.min is not None and schema.firmware_version_range.min_ver > AwesomeVersion( value.node.firmware_version)) or (schema.firmware_version_range.max is not None and schema.firmware_version_range.max_ver < AwesomeVersion( value.node.firmware_version))): continue # check firmware_version if (schema.firmware_version is not None and value.node.firmware_version not in schema.firmware_version): continue # check device_class_basic if not check_device_class(value.node.device_class.basic, schema.device_class_basic): continue # check device_class_generic if not check_device_class(value.node.device_class.generic, schema.device_class_generic): continue # check device_class_specific if not check_device_class(value.node.device_class.specific, schema.device_class_specific): continue # check primary value if not check_value(value, schema.primary_value): continue # check additional required values if schema.required_values is not None and not all( any( check_value(val, val_scheme) for val in value.node.values.values()) for val_scheme in schema.required_values): continue # check for values that may not be present if schema.absent_values is not None and any( any( check_value(val, val_scheme) for val in value.node.values.values()) for val_scheme in schema.absent_values): continue # resolve helper data from template resolved_data = None additional_value_ids_to_watch = set() if schema.data_template: try: resolved_data = schema.data_template.resolve_data(value) except UnknownValueData as err: LOGGER.error( "Discovery for value %s on device '%s' (%s) will be skipped: %s", value, device.name_by_user or device.name, value.node, err, ) continue additional_value_ids_to_watch = schema.data_template.value_ids_to_watch( resolved_data) # all checks passed, this value belongs to an entity yield ZwaveDiscoveryInfo( node=value.node, primary_value=value, assumed_state=schema.assumed_state, platform=schema.platform, platform_hint=schema.hint, platform_data_template=schema.data_template, platform_data=resolved_data, additional_value_ids_to_watch=additional_value_ids_to_watch, entity_registry_enabled_default=schema. entity_registry_enabled_default, ) if not schema.allow_multi: # return early since this value may not be discovered by other schemas/platforms return
def version(self) -> AwesomeVersion: """Return version of running Home Assistant.""" return AwesomeVersion(SUPERVISOR_VERSION)