lavva.exalushome
Version:
Library implementing communication and abstraction layers for ExalusHome system
759 lines (758 loc) • 99 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Api } from "../../Api";
import { DataFrame, Method, Status } from "../../DataFrame";
import { ControllerExtensionsService } from "../Controller/ControllerExtensionsService";
import { DevicesService } from "../Devices/DevicesService";
import { ResponseResult } from "../FieldChangeResult";
import { CannotGetRuntimeErrorCode, CannotGetRuntimeInfo, UpdatesProvider } from "./UpdatesProvider";
import { ControllerUpdatesInfo, ControllerUpdateType, ControllerVersion, DeviceUpdateShortInfo, UpdateHistory } from "./IUpdateInfo";
import { ProviderType, UpdateInfo, UpdateTypes, UpdatesProviderNotFound } from "./IUpdatesProvider";
import { BulkUpdateResult, BulkUpdateStatus, DownloadProgressInfo, DownloadTask, UpdateBranch, UpdateErrorCode, UpdateProgressInfo, UpdateState, UpdateStatus, UserInput } from './IUpdatesService';
import { AppStateService } from "../AppStateService";
import { LoggerService } from "../Logging/LoggerService";
import { ExalusConnectionService } from "../ExalusConnectionService";
import { Helpers, ParseObjToMap } from "../../Helpers";
import { SessionService } from "../Session/SessionService";
export class UpdatesService {
constructor() {
this._controllerUpdates = [];
this._updateRequestTimeout = 120000;
this._oldUpdateVersion = 5;
this._oldUpdateBuild = 56;
this._updateOfflineStageTime = 11;
this._updateProviders = Api.Get(UpdatesProvider.ServiceName);
this._logger = Api.Get(LoggerService.ServiceName);
this._connection = Api.Get(ExalusConnectionService.ServiceName);
this._appStateService = Api.Get(AppStateService.ServiceName);
this._extensionService = Api.Get(ControllerExtensionsService.ServiceName);
}
GetAutomaticControllerUpdateDownloadStatusAsync() {
return __awaiter(this, void 0, void 0, function* () {
const ver = (yield this.getRuntimeInfoAsync());
if (ver instanceof ResponseResult) {
return ver;
}
const swVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[0]);
const buildVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[1]);
if (swVerInt < this._oldUpdateVersion || (swVerInt === this._oldUpdateVersion && buildVerInt <= this._oldUpdateBuild))
return new ResponseResult(UpdateErrorCode.FeatureUnsupportedInCurrentVersion, `Cannot get update branch, feature unsupported in current version. Current version: ${ver}`);
let result = yield this._connection.SendAndWaitForResponseAsync(new GetAutomaticUpdatesStatusRequest(), 10000, false);
if (result.Status == Status.OK && result.Data)
return true;
else
return false;
});
}
GetServiceName() {
return UpdatesService.ServiceName;
}
CheckDeviceVersionAsync(dev) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
let device;
if (typeof dev == 'string') {
const result = yield Api.Get(DevicesService.ServiceName).GetDevice(dev);
if (result != null)
device = result;
else
return new ResponseResult(UpdateErrorCode.CannotFindDevice, `Device with specified GUID ${dev} does not exists.`);
}
else {
device = dev;
}
if (device.ProtocolGuid == null)
return new ResponseResult(UpdateErrorCode.UnsupportedDevice, `Device does not contains data about protocol GUID.`);
const softwareInfo = yield ((_a = (yield this._updateProviders.GetUpdatesProvidersByProtocolAsync(device.ProtocolGuid, ProviderType.Device)).first()) === null || _a === void 0 ? void 0 : _a.GetSoftwareVersionAsync(device));
if (softwareInfo.Type != null)
return softwareInfo;
return softwareInfo;
});
}
CheckDeviceUpdateAsync(dev) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
let device;
if (typeof dev == 'string') {
const result = yield Api.Get(DevicesService.ServiceName).GetDevice(dev);
if (result != null)
device = result;
else
return new ResponseResult(UpdateErrorCode.CannotFindDevice, `Device with specified GUID ${dev} does not exists.`);
}
else {
device = dev;
}
if (device.ProtocolGuid == null)
return new ResponseResult(UpdateErrorCode.UnsupportedDevice, `Device does not contains data about protocol GUID.`);
const updateInfo = yield ((_a = (yield this._updateProviders.GetUpdatesProvidersByProtocolAsync(device.ProtocolGuid, ProviderType.Device)).first()) === null || _a === void 0 ? void 0 : _a.GetAvailableUpdateAsync(device));
if (updateInfo.Type != null)
return updateInfo;
const result = new DeviceUpdate();
result.CurrentResourceVersion = updateInfo.CurrentResourceVersion;
result.NewResourceVersion = updateInfo.Update.Version;
result.UpdatedResourceName = updateInfo.UpdatedResourceName;
result.UpdateResourceGuid = updateInfo.Update.ResourceGuid;
result.UpdateDescription = new Map([["pl-PL", updateInfo.Update.Description]]);
let protocol = "n/a";
try {
protocol = (yield this._extensionService.GetProtocolInfoAsync(device.ProtocolGuid)).first().Name;
}
catch (error) {
this._logger.Warning(`Cannot get protocol name! error: ${error}`);
}
result.Protocol = protocol;
result.ProtocolGuid = device.ProtocolGuid;
if ((yield Api.Get(DevicesService.ServiceName).GetDevicesAsync()).any(dev => dev.ModelGuid == "952ddc14-76c4-4f47-b0bc-5d106c1f9b02"))
result.IsUpdateSafe = false;
return result;
});
}
CheckDeviceUpdatesBulkAsync(reloadCache) {
return __awaiter(this, void 0, void 0, function* () {
const updateProviders = yield (yield this._updateProviders.GetUpdatesProvidersAsync(ProviderType.Device));
const result = [];
for (var provider of updateProviders) {
const updateInfo = yield provider.CheckUpdateBulkAsync(reloadCache);
if (updateInfo instanceof ResponseResult)
return updateInfo;
updateInfo.map((d) => __awaiter(this, void 0, void 0, function* () {
const update = new DeviceUpdate();
update.CurrentResourceVersion = d.CurrentResourceVersion;
update.NewResourceVersion = d.Update.Version;
update.UpdatedResourceName = d.UpdatedResourceName;
update.UpdateDescription = d.UpdateDescription;
update.IsUpdateSafe = true;
let protocol = "n/a";
try {
protocol = (yield this._extensionService.GetProtocolInfoAsync(provider.ProtocolGuid)).first().Name;
}
catch (error) {
this._logger.Warning(`Cannot get protocol name! error: ${error}`);
}
update.Protocol = protocol;
update.ProtocolGuid = provider.ProtocolGuid;
update.UpdateResourceGuid = d.Update.ResourceGuid;
result.push(update);
}));
}
return result;
});
}
InstallDeviceUpdateAsync(dev_1, updateProgress_1, updateAction_1) {
return __awaiter(this, arguments, void 0, function* (dev, updateProgress, updateAction, force = false) {
var _a;
let device;
if (typeof dev == 'string') {
const result = yield Api.Get(DevicesService.ServiceName).GetDevice(dev);
if (result != null)
device = result;
else
return new ResponseResult(UpdateErrorCode.CannotFindDevice, `Device with specified GUID ${dev} does not exists.`);
}
else {
device = dev;
}
if (device.ProtocolGuid == null)
return new ResponseResult(UpdateErrorCode.UnsupportedDevice, `Device does not contains data about protocol GUID.`);
const updateInfo = yield ((_a = (yield this._updateProviders.GetUpdatesProvidersByProtocolAsync(device.ProtocolGuid, ProviderType.Device)).first()) === null || _a === void 0 ? void 0 : _a.GetAvailableUpdateAsync(device));
if (updateInfo.Type != null)
return updateInfo;
this._appStateService.DisallowHibernation();
const updateInstallResult = yield (yield this._updateProviders.GetUpdatesProvidersByProtocolAsync(device.ProtocolGuid, ProviderType.Device)).first().InstallUpdateAsync(updateInfo, updateProgress, updateAction, force);
if (updateInstallResult.Type != null) {
this._appStateService.AllowHibernation();
return updateInstallResult;
}
this._appStateService.AllowHibernation();
return Status.OK;
});
}
InstallDevicesUpdateBulkAsync(deviceGuids, updateProgress, updateAction) {
return __awaiter(this, void 0, void 0, function* () {
const grouppedDevs = (yield Api.Get(DevicesService.ServiceName).GetDevicesAsync()).where(d => deviceGuids.includes(d.Guid)).groupBy(d => d.ProtocolGuid);
const result = [];
this._appStateService.DisallowHibernation();
const grpCnt = grouppedDevs.count();
for (let i = 0; i < grpCnt; i++) {
const grp = grouppedDevs.elementAt(i);
const totalStartRange = i * (100 / grpCnt);
const totalEndRange = (i + 1) * (100 / grpCnt);
const progressFunctionWrapper = (progress) => {
progress.PercentageTotal = Math.floor((progress.PercentageTotal / 100) * (totalEndRange - totalStartRange) + totalStartRange);
updateProgress(progress);
};
const updateProvider = yield (yield this._updateProviders.GetUpdatesProvidersByProtocolAsync(grp.key, ProviderType.Device));
const r = yield updateProvider.first().InstalUpdateBulkAsync(grp.select(d => d.Guid).toArray(), progressFunctionWrapper, updateAction);
if (r instanceof ResponseResult)
return r;
result.push(r);
}
this._appStateService.AllowHibernation();
if (result.length == 1)
return result[0];
else {
const newBulkResult = new BulkUpdateResult();
if (result.some(r => r.DevicesUpdateResults.count() > 0) && result.any(r => r.BulkUpdateStatus != BulkUpdateStatus.Success)) {
newBulkResult.BulkUpdateStatus = BulkUpdateStatus.PartialSuccess;
}
else if (result.any(r => r.BulkUpdateStatus != BulkUpdateStatus.Success)) {
newBulkResult.BulkUpdateStatus = result.first().BulkUpdateStatus;
}
else {
newBulkResult.BulkUpdateStatus = BulkUpdateStatus.Success;
}
for (const r of result) {
r.DevicesUpdateResults.forEach((value, key) => {
newBulkResult.DevicesUpdateResults.set(key, value);
});
}
return newBulkResult;
}
});
}
CheckControllerSoftwareUpdateAvailabilityAsync() {
return __awaiter(this, void 0, void 0, function* () {
const user = Api.Get(SessionService.ServiceName).User;
if (user == null)
return false;
//Very old software, does not contains information about software version (before 15.02.2024)
if (user.SoftwareVersion == "" || user.SoftwareVersion == null)
return true;
//Old software, containse information about software version but does not supports API that gets updates in backgroud (new way)
const [major, build] = user.SoftwareVersion.split('.');
if (parseFloat(major) < this._oldUpdateVersion || (parseFloat(major) == this._oldUpdateVersion && parseFloat(build) <= this._oldUpdateBuild))
return true;
const result = yield this._connection.SendAndWaitForResponseAsync(new PendingUpdatesRequest(), 10000, false);
if (result == null || result.Status != Status.OK || result.Data == null)
return false;
const [majorPening, buildPending] = result.Data.Update.Version.ContainerSoftwareVersion.toString().split('.');
if (parseInt(majorPening) > parseInt(major) || (parseInt(majorPening) == parseInt(major) && parseInt(buildPending) > parseInt(build)))
return true;
else
return false;
});
}
CheckDevicesUpdatesAvailabilityAsync() {
return __awaiter(this, void 0, void 0, function* () {
const user = Api.Get(SessionService.ServiceName).User;
if (user == null)
return [];
//Very old software, does not contains information about software version (before 15.02.2024)
if (user.SoftwareVersion == "" || user.SoftwareVersion == null)
return [];
//Old software, containse information about software version but does not supports API that gets updates in backgroud (new way)
const [major, build] = user.SoftwareVersion.split('.');
if (parseFloat(major) < 6 || (parseFloat(major) == 6 && parseFloat(build) <= 4))
return [];
const result = yield this._connection.SendAndWaitForResponseAsync(new CheckDevicesUpdatesRequest(), 10000, false);
const fResult = [];
if (result == null || (result === null || result === void 0 ? void 0 : result.Status) != Status.OK || (result === null || result === void 0 ? void 0 : result.Data) == null)
return [];
result === null || result === void 0 ? void 0 : result.Data.forEach(d => {
if (!d.IsAvailable)
return;
const info = new DeviceUpdateShortInfo();
info.DeviceGuid = d.DeviceGuid;
info.CurrentVersion = d.CurrentVersion;
info.NewVersion = d.NewVersion;
fResult.push(info);
});
return fResult;
});
}
CheckControllerUpdatesAsync() {
return __awaiter(this, void 0, void 0, function* () {
this._controllerUpdates = [];
let updateProviderServices = [];
const updates = new ControllerUpdatesInfo();
const runtimeInfo = yield this.getRuntimeInfoAsync();
if (runtimeInfo instanceof ResponseResult) {
const error = new ControllerUpdateNotAvailableInfo();
error.UpdateType = ControllerUpdateType.SoftwareUpdate;
error.ResponseResult = runtimeInfo;
updates.UpdatesNotAvailable.push(error);
return updates;
}
const swVerInt = parseInt(runtimeInfo.SoftwareVersion.toString().split('.')[0]);
const buildVerInt = parseInt(runtimeInfo.SoftwareVersion.toString().split('.')[1]);
try {
updateProviderServices = yield this._updateProviders.GetUpdatesProvidersAsync(ProviderType.Controller);
}
catch (error) {
if (error instanceof UpdatesProviderNotFound) {
this._logger.Warning(`Cannot get update providers for controller protocols! Avaliable only controller software updates!`);
}
else
throw error;
}
//Radio updates
for (let i = 0; i < updateProviderServices.length; i++) {
const result = yield updateProviderServices[i].GetAvailableRadioUpdateAsync();
if (result.Type != null) {
const error = new ControllerUpdateNotAvailableInfo();
error.ResponseResult = result;
error.UpdateType = ControllerUpdateType.RadioUpdate;
let protocol = "n/a";
try {
protocol = (yield this._extensionService.GetProtocolInfoAsync(updateProviderServices[i].ProtocolGuid)).first().Name;
}
catch (error) {
this._logger.Warning(`Cannot get protocol name! error: ${error}`);
}
error.UpdatedResourceName = new Map([["pl-PL", protocol], ["en-US", protocol]]);
error.CurrentResourceVersion = yield updateProviderServices[i].GetCurrentRadioVersionAsync();
updates.UpdatesNotAvailable.push(error);
}
else {
const updateInfo = result;
const update = new ControllerUpdate();
update.UpdatedResourceName = new Map([["pl-PL", updateInfo.UpdatedResourceName], ["en-US", updateInfo.UpdatedResourceName]]);
update.CurrentResourceVersion = updateInfo.CurrentResourceVersion;
update.NewResourceVersion = updateInfo.Update.Version;
update.UpdateDescription = new Map([["pl-PL", updateInfo.Update.Description]]);
update.UpdateType = ControllerUpdateType.RadioUpdate;
update.UpdateIdentifier = Helpers.GenerateUUID();
update.UpdateIsDownloading = false;
update.DownloadProgress = 100;
update.UpdateCreationTime = null;
updates.UpdatesAvailable.push(update);
this._controllerUpdates.push(Object.assign(Object.assign({}, updateInfo), { UpdateIdentifier: update.UpdateIdentifier, ProviderName: updateProviderServices[i].GetUpdateProviderName(), UpdateType: ControllerUpdateType.RadioUpdate }));
}
}
let isNewWayUpdate = true;
if (swVerInt < this._oldUpdateVersion || (swVerInt === this._oldUpdateVersion && buildVerInt <= this._oldUpdateBuild))
isNewWayUpdate = false;
//Software updates (new way)
if (isNewWayUpdate) {
const result = yield this._connection.SendAndWaitForResponseAsync(new PendingUpdatesRequest(), 15000, false);
if (result == null || (result.Status != Status.OK && result.Status != Status.NoData))
throw (new Error(`Cannot get pending updates! Response is empty or status is not OK. Status: ${result === null || result === void 0 ? void 0 : result.Status}`));
if (result.Data != null) {
const updateInfo = new UpdateInfo();
updateInfo.CurrentResourceVersion = `${runtimeInfo.RuntimeVersion}.${runtimeInfo.SoftwareVersion}`;
updateInfo.UpdateDescription = ParseObjToMap(result.Data.Update.UpdateDescription);
updateInfo.UpdatedResourceName = ParseObjToMap(result.Data.Update.UpdateName).get("en-US");
const update = new ControllerUpdate();
update.UpdatedResourceName = ParseObjToMap(result.Data.Update.UpdateName);
update.UpdateDescription = ParseObjToMap(result.Data.Update.UpdateDescription);
update.CurrentResourceVersion = `${runtimeInfo.RuntimeVersion}.${runtimeInfo.SoftwareVersion}`;
update.NewResourceVersion = `${result.Data.Update.Version.BaseRuntime}.${result.Data.Update.Version.ContainerSoftwareVersion}`;
update.UpdateType = ControllerUpdateType.SoftwareUpdate;
update.UpdateIdentifier = result.Data.Update.Guid;
update.UpdateIsDownloading = (result.Data.UpdateStep == UpdateStep.DownloadingInstaller
|| result.Data.UpdateStep == UpdateStep.DownloadingUpdate
|| result.Data.UpdateStep == UpdateStep.CheckingUpdate) ? true : false;
update.DownloadProgress = result.Data.DownloadProgress;
update.UpdateCreationTime = result.Data.Update.Creationtime;
updates.UpdatesAvailable.push(update);
this._controllerUpdates.push(Object.assign(Object.assign({}, updateInfo), { UpdateIdentifier: update.UpdateIdentifier, ProviderName: UpdatesService.ServiceName, UpdateType: ControllerUpdateType.SoftwareUpdate }));
}
else {
//If no updates - returns status NoData
const result = yield this._connection.SendAndWaitForResponseAsync(new CheckUpdatesRequest(), 15000, false);
if (result == null)
throw new Error(`Cannot check updates! Response is empty.`);
switch (result.Status) {
case Status.NoData:
const error = new ControllerUpdateNotAvailableInfo();
error.UpdateType = ControllerUpdateType.SoftwareUpdate;
error.UpdatedResourceName = new Map([["pl-PL", "Oprogramowanie kontrolera"], ["en-US", "Controller software"]]);
error.ResponseResult = new ResponseResult(UpdateErrorCode.DeviceIsUpToDate, `Updates not found, controller is already up-to-date.`);
updates.UpdatesNotAvailable.push(error);
break;
case Status.OK:
if (result.Data == null)
throw new Error(`Cannot check updates! Response data is empty.`);
const updateInfo = new UpdateInfo();
updateInfo.CurrentResourceVersion = `${runtimeInfo.RuntimeVersion}.${runtimeInfo.SoftwareVersion}`;
updateInfo.UpdateDescription = ParseObjToMap(result.Data.UpdateDescription);
updateInfo.UpdatedResourceName = ParseObjToMap(result.Data.UpdateName).get("en-US");
const update = new ControllerUpdate();
update.UpdatedResourceName = ParseObjToMap(result.Data.UpdateName);
update.UpdateDescription = ParseObjToMap(result.Data.UpdateDescription);
update.CurrentResourceVersion = `${runtimeInfo.RuntimeVersion}.${runtimeInfo.SoftwareVersion}`;
update.UpdateType = ControllerUpdateType.SoftwareUpdate;
update.UpdateIdentifier = result.Data.Guid;
update.NewResourceVersion = `${result.Data.Version.BaseRuntime}.${result.Data.Version.ContainerSoftwareVersion}`;
update.UpdateIsDownloading = false;
update.DownloadProgress = 0;
update.UpdateCreationTime = result.Data.Creationtime;
updates.UpdatesAvailable.push(update);
this._controllerUpdates.push(Object.assign(Object.assign({}, updateInfo), { UpdateIdentifier: update.UpdateIdentifier, ProviderName: UpdatesService.ServiceName, UpdateType: ControllerUpdateType.SoftwareUpdate }));
break;
default:
throw new Error(`Cannot check updates! Response status is not OK. Status: ${result.Status}`);
}
}
}
//Software updates (old way)
else {
const url = `https://exalus-updates.tr7.pl/software/${runtimeInfo.UpdateChannel}/${runtimeInfo.RuntimeVersion}/${runtimeInfo.SoftwareVersion}/update.json`;
try {
const response = yield fetch(url);
let updateData;
let updateInfo = new UpdateInfo();
const error = new ControllerUpdateNotAvailableInfo();
error.UpdateType = ControllerUpdateType.SoftwareUpdate;
error.UpdatedResourceName = new Map([["pl-PL", "Oprogramowanie kontrolera"], ["en-US", "Controller software"]]);
switch (response.status) {
case 200:
if (response.body != null)
updateData = (yield response.json());
break;
case 404:
error.ResponseResult = new ResponseResult(UpdateErrorCode.DeviceIsUpToDate, `Updates not found, controller is already up-to-date.`);
updates.UpdatesNotAvailable.push(error);
break;
default:
error.ResponseResult = new ResponseResult(UpdateErrorCode.CannotGetUpdates, `Response status code does not indicate success. Status: ${response.status}`);
updates.UpdatesNotAvailable.push(error);
break;
}
if (updateData != null) {
updateInfo.CurrentResourceVersion = `${runtimeInfo.RuntimeVersion}.${runtimeInfo.SoftwareVersion}`;
updateInfo.Update = updateData;
const update = new ControllerUpdate();
update.UpdatedResourceName = new Map([["pl-PL", "Oprogramowanie kontrolera"], ["en-US", "Controller software"]]);
update.CurrentResourceVersion = updateInfo.CurrentResourceVersion;
update.NewResourceVersion = updateInfo.Update.Version;
update.UpdateDescription = new Map([["pl-PL", updateInfo.Update.Description]]);
update.UpdateType = ControllerUpdateType.SoftwareUpdate;
update.UpdateIdentifier = Helpers.GenerateUUID();
update.DownloadProgress = 100;
update.UpdateIsDownloading = false;
update.UpdateCreationTime = null;
updates.UpdatesAvailable.push(update);
this._controllerUpdates.push(Object.assign(Object.assign({}, updateInfo), { UpdateIdentifier: update.UpdateIdentifier, ProviderName: UpdatesService.ServiceName, UpdateType: ControllerUpdateType.SoftwareUpdate }));
}
else if (response.status == 200)
throw (new Error(`Cannot get Update Data from response. Status: ${response.status}`));
}
catch (err) {
const runtimeInfo = (yield this.getRuntimeInfoAsync());
if (runtimeInfo instanceof ResponseResult) {
const error = new ControllerUpdateNotAvailableInfo();
error.UpdateType = ControllerUpdateType.SoftwareUpdate;
error.UpdatedResourceName = new Map([["pl-PL", "Oprogramowanie kontrolera"], ["en-US", "Controller software"]]);
error.ResponseResult = runtimeInfo;
updates.UpdatesNotAvailable.push(error);
}
else {
const error = new ControllerUpdateNotAvailableInfo();
error.UpdateType = ControllerUpdateType.SoftwareUpdate;
error.UpdatedResourceName = new Map([["pl-PL", "Oprogramowanie kontrolera"], ["en-US", "Controller software"]]);
error.ResponseResult = new ResponseResult(UpdateErrorCode.CannotGetUpdates, `Cannot get available updates! ${err}`);
error.CurrentResourceVersion = `${runtimeInfo.RuntimeVersion}.${runtimeInfo.SoftwareVersion}`;
updates.UpdatesNotAvailable.push(error);
}
}
}
return updates;
});
}
CheckControllerVersionAsync() {
return __awaiter(this, void 0, void 0, function* () {
const updateProviderServices = yield this._updateProviders.GetUpdatesProvidersAsync(ProviderType.Controller);
const runtimeInfo = (yield this.getRuntimeInfoAsync());
if (runtimeInfo instanceof ResponseResult) {
return runtimeInfo;
}
const result = [];
let version;
//Software updates
version = new ControllerVersion();
version.Resource = "Controller software";
version.Type = ControllerUpdateType.SoftwareUpdate;
version.Version = `${runtimeInfo.RuntimeVersion}.${runtimeInfo.SoftwareVersion}`;
result.push(version);
//Radio updates
for (let i = 0; i < updateProviderServices.length; i++) {
version = new ControllerVersion();
version.Version = yield updateProviderServices[i].GetCurrentRadioVersionAsync();
version.Type = ControllerUpdateType.RadioUpdate;
let protocol = "n/a";
try {
protocol = (yield this._extensionService.GetProtocolInfoAsync(updateProviderServices[i].ProtocolGuid)).first().Name;
}
catch (error) {
this._logger.Warning(`Cannot get protocol name! error: ${error}`);
}
version.Resource = protocol;
result.push(version);
}
return result;
});
}
GetControllerUpdateBranchAsync() {
return __awaiter(this, void 0, void 0, function* () {
const ver = (yield this.getRuntimeInfoAsync());
if (ver instanceof ResponseResult) {
return ver;
}
const swVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[0]);
const buildVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[1]);
if (swVerInt < this._oldUpdateVersion || (swVerInt === this._oldUpdateVersion && buildVerInt <= this._oldUpdateBuild))
return new ResponseResult(UpdateErrorCode.FeatureUnsupportedInCurrentVersion, `Cannot get update branch, feature unsupported in current version. Current version: ${ver}`);
const result = yield this._connection.SendAndWaitForResponseAsync(new GetUpdateBranchRequest(), 10000, false);
if (result == null || (result === null || result === void 0 ? void 0 : result.Status) != Status.OK)
return new ResponseResult(UpdateErrorCode.CannotGetUpdatesBranch, `Cannot get update branch, response is empty or status is not OK. Status: ${result === null || result === void 0 ? void 0 : result.Status}`);
switch (result.Data) {
case 0:
return UpdateBranch.Public;
case 1:
return UpdateBranch.Beta;
case 2:
return UpdateBranch.Development;
default:
return new ResponseResult(UpdateErrorCode.CannotGetUpdatesBranch, `Cannot get update branch, unknown branch number. Branch number: ${result.Data}`);
}
});
}
SetControllerUpdateBranchAsync(updateBranch) {
return __awaiter(this, void 0, void 0, function* () {
const ver = (yield this.getRuntimeInfoAsync());
if (ver instanceof ResponseResult) {
return ver;
}
const swVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[0]);
const buildVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[1]);
if (swVerInt < this._oldUpdateVersion || (swVerInt === this._oldUpdateVersion && buildVerInt <= this._oldUpdateBuild))
return new ResponseResult(UpdateErrorCode.FeatureUnsupportedInCurrentVersion, `Cannot set update branch, feature unsupported in current version. Current version: ${ver}`);
const result = yield this._connection.SendAndWaitForResponseAsync(new SetUpdateBranchRequest(updateBranch), 10000, false);
if (result == null || (result === null || result === void 0 ? void 0 : result.Status) != Status.OK)
return new ResponseResult(UpdateErrorCode.CannotSetUpdatesBranch, `Cannot set update branch, response is empty or status is not OK. Status: ${result === null || result === void 0 ? void 0 : result.Status}`);
return Status.OK;
});
}
EnableAutomaticControllerUpdateDonwloadAsync() {
return __awaiter(this, void 0, void 0, function* () {
const ver = (yield this.getRuntimeInfoAsync());
if (ver instanceof ResponseResult) {
return ver;
}
const swVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[0]);
const buildVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[1]);
if (swVerInt < this._oldUpdateVersion || (swVerInt === this._oldUpdateVersion && buildVerInt <= this._oldUpdateBuild))
return new ResponseResult(UpdateErrorCode.FeatureUnsupportedInCurrentVersion, `Cannot enable automatic updates download, feature unsupported in current version. Current version: ${ver}`);
const result = yield this._connection.SendAndWaitForResponseAsync(new EnableAutomaticUpdatesRequest(), 10000, false);
if (result == null || (result === null || result === void 0 ? void 0 : result.Status) != Status.OK)
return new ResponseResult(UpdateErrorCode.CannotSetUpdatesBranch, `Cannot enable automatic updates download, response is empty or status is not OK. Status: ${result === null || result === void 0 ? void 0 : result.Status}`);
return Status.OK;
});
}
DisableAutomaticControllerUpdateDonwloadAsync() {
return __awaiter(this, void 0, void 0, function* () {
const ver = (yield this.getRuntimeInfoAsync());
if (ver instanceof ResponseResult) {
return ver;
}
const swVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[0]);
const buildVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[1]);
if (swVerInt < this._oldUpdateVersion || (swVerInt === this._oldUpdateVersion && buildVerInt <= this._oldUpdateBuild))
return new ResponseResult(UpdateErrorCode.FeatureUnsupportedInCurrentVersion, `Cannot disable automatic update download, feature unsupported in current version. Current version: ${ver}`);
const result = yield this._connection.SendAndWaitForResponseAsync(new DisableAutomaticUpdatesRequest(), 10000, false);
if (result == null || (result === null || result === void 0 ? void 0 : result.Status) != Status.OK)
return new ResponseResult(UpdateErrorCode.CannotSetUpdatesBranch, `Cannot disable automatic update download, response is empty or status is not OK. Status: ${result === null || result === void 0 ? void 0 : result.Status}`);
return Status.OK;
});
}
GetControllerUpdatesHistoryAsync() {
return __awaiter(this, void 0, void 0, function* () {
const ver = (yield this.getRuntimeInfoAsync());
if (ver instanceof ResponseResult) {
return ver;
}
const swVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[0]);
const buildVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[1]);
if (swVerInt < this._oldUpdateVersion || (swVerInt === this._oldUpdateVersion && buildVerInt <= this._oldUpdateBuild))
return new ResponseResult(UpdateErrorCode.FeatureUnsupportedInCurrentVersion, `Cannot disable automatic update download, feature unsupported in current version. Current version: ${ver}`);
const result = yield this._connection.SendAndWaitForResponseAsync(new GetUpdatesHistoryRequest(), 10000, false);
if (result == null)
return new ResponseResult(UpdateErrorCode.CannotGetUpdates, `Cannot get updates history, response is empty.`);
switch (result.Status) {
case Status.OK:
if (result.Data != null)
return result.Data.map(d => {
const update = new UpdateHistory();
update.UpdatedResourceName = ParseObjToMap(d.UpdateName);
update.UpdateDescription = ParseObjToMap(d.UpdateDescription);
update.UpdateCreationTime = d.Creationtime;
update.UpdateChannel = d.UpdateChannel;
update.Version = `${d.Version.BaseRuntime}.${d.Version.ContainerSoftwareVersion}`;
return update;
});
else
return new ResponseResult(UpdateErrorCode.CannotGetUpdates, `Cannot get updates history, response data is empty.`);
case Status.NoData:
return [];
default:
return new ResponseResult(UpdateErrorCode.CannotGetUpdates, `Cannot get updates history, response status is not OK. Status: ${result.Status}`);
}
});
}
InstallControllerUpdateAsync(updateProgress, downloadProgress, guidOrAction, downloadOnlyOrAction, updateAction) {
return __awaiter(this, void 0, void 0, function* () {
const regGuid = new RegExp("^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$");
const numberOfUpdates = this._controllerUpdates.length;
const ver = (yield this.getRuntimeInfoAsync());
if (ver instanceof ResponseResult) {
return ver;
}
const swVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[0]);
const buildVerInt = parseInt(ver.SoftwareVersion.toString().split('.')[1]);
//Check if any updates are avaliable
if (this._controllerUpdates.length == 0)
return new ResponseResult(UpdateErrorCode.CannotGetUpdates, `Cannot find any updates, make sure you check for available updates by calling CheckControllerUpdateAsync().`);
//Install single update
if ((typeof guidOrAction === 'string') && regGuid.test(guidOrAction)) {
let updateResult;
const updateToInstall = this._controllerUpdates.find(update => update.UpdateIdentifier == guidOrAction);
if (updateToInstall == null)
return new ResponseResult(UpdateErrorCode.CannotGetUpdates, `Update with requested guid (${guidOrAction}) not found.`);
this._logger.Debug(`Installing single update type: ${updateToInstall.Update.UpdateType}, resource: ${updateToInstall.UpdatedResourceName}, version: ${updateToInstall.Update.Version}`);
this._appStateService.DisallowHibernation();
switch (updateToInstall.UpdateType) {
case ControllerUpdateType.RadioUpdate:
if (downloadOnlyOrAction)
return new ResponseResult(UpdateErrorCode.UpdateAlreadyDownloaded, `Update already downloaded set flag downloadOnly to false to install it.`);
updateResult = yield (yield this._updateProviders.GetUpdatesProviderAsync(updateToInstall.ProviderName)).InstallRadioUpdateAsync(updateToInstall, updateProgress, updateAction);
this._appStateService.AllowHibernation();
if (updateResult == Status.OK)
this._controllerUpdates = this._controllerUpdates.filter(update => update.UpdateIdentifier != guidOrAction);
return updateResult;
case ControllerUpdateType.SoftwareUpdate:
//Old way
if (swVerInt < this._oldUpdateVersion || (swVerInt === this._oldUpdateVersion && buildVerInt <= this._oldUpdateBuild)) {
if (downloadOnlyOrAction)
return new ResponseResult(UpdateErrorCode.UpdateAlreadyDownloaded, `Update already downloaded set flag downloadOnly to false to install it.`);
updateResult = yield this.UpdateControllerUsingScriptAsync(updateToInstall.Update, updateProgress);
this._appStateService.AllowHibernation();
if (updateResult == Status.OK)
this._controllerUpdates = this._controllerUpdates.filter(update => update.UpdateIdentifier != guidOrAction);
}
//New way
else {
updateResult = yield this.UpdateControllerUsingApiAsync(updateProgress, downloadProgress, downloadOnlyOrAction);
this._appStateService.AllowHibernation();
if (updateResult == Status.OK)
this._controllerUpdates = this._controllerUpdates.filter(update => update.UpdateIdentifier != guidOrAction);
}
return updateResult;
default:
return new ResponseResult(UpdateErrorCode.CannotProcessUpdate, `Update with type ${updateToInstall.UpdateType} is not supported by UpdateService.`);
}
}
//Install all updates
else {
//Handle update progress with multiple updates.
const updateNumberRef = [1];
const updateProgressWrapper = (progressInfo) => {
progressInfo.UpdateNumber = updateNumberRef[0];
progressInfo.AvailableUpdates = numberOfUpdates;
updateProgress(progressInfo);
};
const timeout = (ms) => __awaiter(this, void 0, void 0, function* () {
return new Promise(resolve => setTimeout(resolve, ms));
});
//First insall radio updates
let updatesToInstall = this._controllerUpdates.where(update => update.UpdateType == ControllerUpdateType.RadioUpdate).toArray();
let result = [];
this._appStateService.DisallowHibernation();
for (let i = 0; i < updatesToInstall.length; i++) {
const updateResult = yield (yield this._updateProviders.GetUpdatesProviderAsync(updatesToInstall[i].ProviderName)).InstallRadioUpdateAsync(updatesToInstall[i], updateProgressWrapper, guidOrAction);
//Wait between updates
yield timeout(5000);
if (updateResult == Status.OK)
this._controllerUpdates = this._controllerUpdates.filter(update => update.UpdateIdentifier != updatesToInstall[i].UpdateIdentifier);
result.push(updateResult);
updateNumberRef[0]++;
}
//Install other updates (in the future)
//....//
//
//Install controller update
const controlerUpdate = this._controllerUpdates.find(update => update.UpdateType == ControllerUpdateType.SoftwareUpdate);
if (controlerUpdate != null) {
//Old way
if (swVerInt < this._oldUpdateVersion || (swVerInt === this._oldUpdateVersion && buildVerInt <= this._oldUpdateBuild)) {
const updateResult = yield this.UpdateControllerUsingScriptAsync(controlerUpdate.Update, updateProgressWrapper);
if (updateResult == Status.OK)
this._controllerUpdates = this._controllerUpdates.filter(update => update.UpdateIdentifier != controlerUpdate.UpdateIdentifier);
result.push(updateResult);
updateNumberRef[0]++;
}
//New way
else {
const updateResult = yield this.UpdateControllerUsingApiAsync(updateProgressWrapper, downloadProgress, downloadOnlyOrAction);
this._appStateService.AllowHibernation();
if (updateResult == Status.OK)
this._controllerUpdates = this._controllerUpdates.filter(update => update.UpdateIdentifier != guidOrAction);
result.push(updateResult);
updateNumberRef[0]++;
}
}
const resultWithError = result.where(r => r instanceof ResponseResult).toArray();
if (resultWithError != null && resultWithError.length != 0) {
let errors = "";
resultWithError.forEach(err => errors += `${err.Type}, `);
this._appStateService.AllowHibernation();
return new ResponseResult(UpdateErrorCode.UpdatesPartiallyInstalled, `Update installation finished - not all updates were successfully installed. Installation error codes: ${errors}`);
}
this._appStateService.AllowHibernation();
return Status.OK;
}
});
}
UpdateControllerUsingScriptAsync(update, updateProgress) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e, _f;
switch (update.UpdateType) {
case UpdateTypes.Container:
case UpdateTypes.ContainerSoftware:
try {
yield ((_a = this._connection) === null || _a === void 0 ? void 0 : _a.SendAndHandleResponseAsync(new ScriptControllerUpdateRequest(update.DownloadUri), this._updateRequestTimeout, (resp) => {
const progressInfo = new UpdateProgressInfo();
progressInfo.UpdatedResourceName = new Map([["pl-PL", "Oprogramowanie kontrolera"], ["en-US", "Controller software"]]);
switch (resp.Status) {
case Status.MultiDataResponseStart:
progressInfo.Status = UpdateStatus.PreparingForUpgrade;
break;
case Status.MultiDataResponse:
if (resp.Data == null) {
this._logger.Warning(UpdatesService.ServiceName, `Recived update response without data! URI: ${update.DownloadUri}`);
return;
}
progressInfo.Percentage = Math.round(resp.Data.Progress);
switch (resp.Data.State) {
case UpdateState.CheckingApplicability:
case UpdateState.CheckingIntegrity:
case UpdateState.CheckingPackageIntegrity:
progressInfo.Status = UpdateStatus.CheckingPackage;
break;
case UpdateState.CreatingBackup:
case UpdateState.MigratingData:
case UpdateState.Removing:
progressInfo.Status = UpdateStatus.MovingData;
break;
case UpdateState.Deploying:
case UpdateState.Installing:
progressInfo.Status = UpdateStatus.Upgrading;
break;
case UpdateState.Downloading:
progressInfo.Status = UpdateStatus.DownloadingPackage;
break;
case UpdateState.Error:
progressInfo.Status = UpdateStatus.Error;
break;
case UpdateState.Installed:
progressInfo.Status = UpdateStatus.Upgraded;
break;
case UpdateState.PreparingSystem: