@robotical/webapp-types 3.18.6 → 3.18.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -59,7 +59,11 @@ export default class ApplicationManager {
59
59
  getDeviceInfo(): Promise<void>;
60
60
  updateRaftNameInAllStates(raftId: string, name: string): Promise<boolean>;
61
61
  private getConnectedRaftName;
62
- private isRaftActive;
62
+ /**
63
+ * Whether this exact RAFT object still owns its ID and is not already in
64
+ * the asynchronous disconnect pipeline.
65
+ */
66
+ isRaftActive(raft: RAFT): boolean;
63
67
  private addConnectedRaftToContext;
64
68
  private publishLocalVerifiedConnection;
65
69
  private confirmQrVerifiedConnection;
@@ -59,6 +59,7 @@ import LEDLightsOrQRVerificationModal from "../../components/modals/LEDLightsOrQ
59
59
  import { CarouselNavigatorManager } from "../../utils/CarouselNavigatorManager";
60
60
  import i18n from "../../i18n";
61
61
  import InUnpluggedModalContent from "../../components/modals/InUnplugged";
62
+ import { cogKeepAwakeManager, } from "../../features/cog-keep-awake";
62
63
  var SHOW_LOGS = true;
63
64
  var TAG = "ApplicationManager";
64
65
  var UNSUPPORTED_WEB_BLUETOOTH_BROWSER_MESSAGE = "This browser can't connect to Marty or Cog over Bluetooth. Please use Chrome or Edge, or use the iPad app.";
@@ -230,6 +231,10 @@ var ApplicationManager = /** @class */ (function () {
230
231
  });
231
232
  });
232
233
  };
234
+ /**
235
+ * Whether this exact RAFT object still owns its ID and is not already in
236
+ * the asynchronous disconnect pipeline.
237
+ */
233
238
  ApplicationManager.prototype.isRaftActive = function (raft) {
234
239
  return this.connectedRafts[raft.id] === raft && !this._disconnectPromises.has(raft);
235
240
  };
@@ -902,6 +907,10 @@ var ApplicationManager = /** @class */ (function () {
902
907
  if (inFlightDisconnect) {
903
908
  return inFlightDisconnect;
904
909
  }
910
+ if (raft.type === RaftTypeE.COG) {
911
+ // Stop scheduling before any asynchronous disconnect work starts.
912
+ cogKeepAwakeManager.removeCog(raft);
913
+ }
905
914
  var operation = Promise.resolve().then(function () { return __awaiter(_this, void 0, void 0, function () {
906
915
  return __generator(this, function (_a) {
907
916
  switch (_a.label) {
@@ -16,6 +16,10 @@ declare class PublishedDataAnalyser extends EventEmitter {
16
16
  private cog;
17
17
  cogState: CogState;
18
18
  private pubSub;
19
+ private accelerometerActivityBaseline;
20
+ private lastAccelerometerActivityAt;
21
+ private static readonly ACCELEROMETER_ACTIVITY_DEADBAND;
22
+ private static readonly ACCELEROMETER_ACTIVITY_RATE_LIMIT_MS;
19
23
  rotationDetection: RotationDetection;
20
24
  shakeDetector: ShakeDetector;
21
25
  buttonClickDetection: ButtonClickDetection;
@@ -58,6 +62,7 @@ declare class PublishedDataAnalyser extends EventEmitter {
58
62
  setObjectSense(objectSense: CogState["objectSense"]): void;
59
63
  setLightSense(lightSense: CogState["lightSense"]): void;
60
64
  setSteering(steering: CogState["steering"]): void;
65
+ private recordAccelerometerActivity;
61
66
  }
62
67
  declare class SteeringDetection {
63
68
  private DELAY_FOR_NEXT_STEERING;
@@ -53,6 +53,7 @@ import { createRaftPubSubscriptionHelper } from "../raft-subscription-helpers";
53
53
  import { isVersionGreater_errorCatching } from "../../../utils/helpers/compare-version";
54
54
  import EventEmitter from "events";
55
55
  import Logger from "../../../services/logger/Logger";
56
+ import userActivityTracker from "../../../features/cog-keep-awake/UserActivityTracker";
56
57
  var SHOW_LOGS = true;
57
58
  var TAG = "PublishedDataAnalyser";
58
59
  var PublishedDataAnalyser = /** @class */ (function (_super) {
@@ -60,6 +61,8 @@ var PublishedDataAnalyser = /** @class */ (function (_super) {
60
61
  function PublishedDataAnalyser(cog) {
61
62
  var _this = _super.call(this) || this;
62
63
  _this.cog = cog;
64
+ _this.accelerometerActivityBaseline = null;
65
+ _this.lastAccelerometerActivityAt = Number.NEGATIVE_INFINITY;
63
66
  _this.rotationDetection = new RotationDetection();
64
67
  _this.shakeDetector = new ShakeDetector();
65
68
  _this.buttonClickDetection = new ButtonClickDetection();
@@ -141,6 +144,8 @@ var PublishedDataAnalyser = /** @class */ (function (_super) {
141
144
  var gyroData = data.gyroscope;
142
145
  var lightData = data.light;
143
146
  var isMoving;
147
+ if (accelData)
148
+ this.recordAccelerometerActivity(accelData);
144
149
  if (accelData)
145
150
  isMoving = this.shakeDetector.detectShake(accelData.ax, accelData.ay, accelData.az, Date.now(), this);
146
151
  accelData && this.tiltDetection.detectTilt(accelData.ax, accelData.ay, accelData.az, isMoving, this, this.cog.getRaftVersion());
@@ -151,20 +156,32 @@ var PublishedDataAnalyser = /** @class */ (function (_super) {
151
156
  accelData && this.steeringDetection.detectSteering(accelData, this);
152
157
  };
153
158
  PublishedDataAnalyser.prototype.setTilt = function (tilt) {
159
+ var hasChanged = this.cogState.tilt !== tilt;
154
160
  this.cogState.tilt = tilt;
155
161
  this.emit(this.eventsMap.tilt[tilt]);
162
+ if (hasChanged)
163
+ userActivityTracker.recordActivity();
156
164
  };
157
165
  PublishedDataAnalyser.prototype.setMovementType = function (movementType) {
166
+ var hasChanged = this.cogState.movementType !== movementType;
158
167
  this.cogState.movementType = movementType;
159
168
  this.emit(this.eventsMap.movementType[movementType]);
169
+ if (hasChanged && movementType !== "none")
170
+ userActivityTracker.recordActivity();
160
171
  };
161
172
  PublishedDataAnalyser.prototype.setRotation = function (rotation) {
173
+ var hasChanged = this.cogState.rotation !== rotation;
162
174
  this.cogState.rotation = rotation;
163
175
  this.emit(this.eventsMap.rotation[rotation]);
176
+ if (hasChanged && rotation !== "none")
177
+ userActivityTracker.recordActivity();
164
178
  };
165
179
  PublishedDataAnalyser.prototype.setButtonClick = function (buttonClick) {
180
+ var hasChanged = this.cogState.buttonClick !== buttonClick;
166
181
  this.cogState.buttonClick = buttonClick;
167
182
  this.emit(this.eventsMap.buttonClick[buttonClick]);
183
+ if (hasChanged && buttonClick !== "none")
184
+ userActivityTracker.recordActivity();
168
185
  };
169
186
  PublishedDataAnalyser.prototype.setObjectSense = function (objectSense) {
170
187
  this.cogState.objectSense = objectSense;
@@ -175,9 +192,39 @@ var PublishedDataAnalyser = /** @class */ (function (_super) {
175
192
  this.emit(this.eventsMap.lightSense[lightSense]);
176
193
  };
177
194
  PublishedDataAnalyser.prototype.setSteering = function (steering) {
195
+ var hasChanged = this.cogState.steering !== steering;
178
196
  this.cogState.steering = steering;
179
197
  this.emit(this.eventsMap.steering[steering]);
198
+ if (hasChanged)
199
+ userActivityTracker.recordActivity();
200
+ };
201
+ PublishedDataAnalyser.prototype.recordAccelerometerActivity = function (accelerometer) {
202
+ var currentSample = {
203
+ ax: accelerometer.ax,
204
+ ay: accelerometer.ay,
205
+ az: accelerometer.az,
206
+ };
207
+ if (!this.accelerometerActivityBaseline) {
208
+ this.accelerometerActivityBaseline = currentSample;
209
+ return;
210
+ }
211
+ var delta = Math.hypot(currentSample.ax - this.accelerometerActivityBaseline.ax, currentSample.ay - this.accelerometerActivityBaseline.ay, currentSample.az - this.accelerometerActivityBaseline.az);
212
+ if (delta < PublishedDataAnalyser.ACCELEROMETER_ACTIVITY_DEADBAND) {
213
+ return;
214
+ }
215
+ // Keep the comparison anchored to the latest meaningful sample even
216
+ // when activity reporting is rate-limited. Otherwise a quick move and
217
+ // return can be reported again a second later while the Cog is still.
218
+ this.accelerometerActivityBaseline = currentSample;
219
+ var now = Date.now();
220
+ if (now - this.lastAccelerometerActivityAt < PublishedDataAnalyser.ACCELEROMETER_ACTIVITY_RATE_LIMIT_MS) {
221
+ return;
222
+ }
223
+ this.lastAccelerometerActivityAt = now;
224
+ userActivityTracker.recordActivity(now);
180
225
  };
226
+ PublishedDataAnalyser.ACCELEROMETER_ACTIVITY_DEADBAND = 0.12;
227
+ PublishedDataAnalyser.ACCELEROMETER_ACTIVITY_RATE_LIMIT_MS = 1000;
181
228
  return PublishedDataAnalyser;
182
229
  }(EventEmitter));
183
230
  var SteeringDetection = /** @class */ (function () {
@@ -0,0 +1,53 @@
1
+ import type { CogKeepAwakeCog, CogKeepAwakeRuntime, CogKeepAwakeSnapshot } from "./types";
2
+ export type CogKeepAwakeManagerOptions = {
3
+ runtime?: Partial<CogKeepAwakeRuntime>;
4
+ keepAwakeIntervalMs?: number;
5
+ idleTimeoutMs?: number;
6
+ promptTimeoutMs?: number;
7
+ retryDelayMs?: number;
8
+ fastRetryLimit?: number;
9
+ schedulerIntervalMs?: number;
10
+ };
11
+ /**
12
+ * Owns keep-awake state independently from React so timing, connection races,
13
+ * and prompt decisions can be tested deterministically.
14
+ */
15
+ export declare class CogKeepAwakeManager {
16
+ private runtime;
17
+ private readonly sessions;
18
+ private readonly listeners;
19
+ private activePrompt;
20
+ private scheduler;
21
+ private wasVisible;
22
+ private nextPromptRevision;
23
+ private readonly keepAwakeIntervalMs;
24
+ private readonly idleTimeoutMs;
25
+ private readonly promptTimeoutMs;
26
+ private readonly retryDelayMs;
27
+ private readonly fastRetryLimit;
28
+ private readonly schedulerIntervalMs;
29
+ constructor(options?: CogKeepAwakeManagerOptions);
30
+ setRuntime(runtime: Partial<CogKeepAwakeRuntime>): void;
31
+ start(): void;
32
+ stop(): void;
33
+ syncCogs(cogs: Iterable<CogKeepAwakeCog>): void;
34
+ addCog(cog: CogKeepAwakeCog): void;
35
+ removeCog(cogOrId: CogKeepAwakeCog | string): void;
36
+ subscribe(listener: (snapshot: CogKeepAwakeSnapshot) => void): () => void;
37
+ getSnapshot(): CogKeepAwakeSnapshot;
38
+ resolvePrompt(keepAwake: boolean, expectedRevision?: number): boolean;
39
+ deferPrompt(): void;
40
+ tick(): void;
41
+ private activateVerifiedSession;
42
+ private sendSignal;
43
+ private openPromptIfPossible;
44
+ private isSessionCurrentAndVerified;
45
+ private isSessionIdle;
46
+ private isConfirmedUsb;
47
+ private getCogName;
48
+ private removeSessionFromPrompt;
49
+ private buildPromptSnapshot;
50
+ private emitSnapshot;
51
+ }
52
+ export declare const cogKeepAwakeManager: CogKeepAwakeManager;
53
+ export default cogKeepAwakeManager;
@@ -0,0 +1,495 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
13
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
14
+ return new (P || (P = Promise))(function (resolve, reject) {
15
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
18
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
19
+ });
20
+ };
21
+ var __generator = (this && this.__generator) || function (thisArg, body) {
22
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
23
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
24
+ function verb(n) { return function (v) { return step([n, v]); }; }
25
+ function step(op) {
26
+ if (f) throw new TypeError("Generator is already executing.");
27
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
28
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
29
+ if (y = 0, t) op = [op[0] & 2, t.value];
30
+ switch (op[0]) {
31
+ case 0: case 1: t = op; break;
32
+ case 4: _.label++; return { value: op[1], done: false };
33
+ case 5: _.label++; y = op[1]; op = [0]; continue;
34
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
35
+ default:
36
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
37
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
38
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
39
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
40
+ if (t[2]) _.ops.pop();
41
+ _.trys.pop(); continue;
42
+ }
43
+ op = body.call(thisArg, _);
44
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
45
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
+ }
47
+ };
48
+ import { RaftConnEvent } from "@robdobsn/raftjs";
49
+ import { KEEP_AWAKE_FAST_RETRY_LIMIT, KEEP_AWAKE_INTERVAL_MS, KEEP_AWAKE_RETRY_DELAY_MS, KEEP_AWAKE_SCHEDULER_INTERVAL_MS, PROMPT_TIMEOUT_MS, USER_IDLE_TIMEOUT_MS, } from "./constants";
50
+ var defaultRuntime = {
51
+ now: function () { return Date.now(); },
52
+ getLastActivityAt: function () { return Date.now(); },
53
+ isVisible: function () {
54
+ return typeof document === "undefined" ||
55
+ (!document.hidden && document.visibilityState !== "hidden");
56
+ },
57
+ isCurrentCog: function () { return true; },
58
+ canPrompt: function () { return true; },
59
+ };
60
+ /**
61
+ * Owns keep-awake state independently from React so timing, connection races,
62
+ * and prompt decisions can be tested deterministically.
63
+ */
64
+ var CogKeepAwakeManager = /** @class */ (function () {
65
+ function CogKeepAwakeManager(options) {
66
+ if (options === void 0) { options = {}; }
67
+ var _a, _b, _c, _d, _e, _f;
68
+ this.sessions = new Map();
69
+ this.listeners = new Set();
70
+ this.activePrompt = null;
71
+ this.scheduler = null;
72
+ this.wasVisible = null;
73
+ this.nextPromptRevision = 0;
74
+ this.runtime = __assign(__assign({}, defaultRuntime), options.runtime);
75
+ this.keepAwakeIntervalMs =
76
+ (_a = options.keepAwakeIntervalMs) !== null && _a !== void 0 ? _a : KEEP_AWAKE_INTERVAL_MS;
77
+ this.idleTimeoutMs = (_b = options.idleTimeoutMs) !== null && _b !== void 0 ? _b : USER_IDLE_TIMEOUT_MS;
78
+ this.promptTimeoutMs = (_c = options.promptTimeoutMs) !== null && _c !== void 0 ? _c : PROMPT_TIMEOUT_MS;
79
+ this.retryDelayMs = (_d = options.retryDelayMs) !== null && _d !== void 0 ? _d : KEEP_AWAKE_RETRY_DELAY_MS;
80
+ this.fastRetryLimit =
81
+ (_e = options.fastRetryLimit) !== null && _e !== void 0 ? _e : KEEP_AWAKE_FAST_RETRY_LIMIT;
82
+ this.schedulerIntervalMs =
83
+ (_f = options.schedulerIntervalMs) !== null && _f !== void 0 ? _f : KEEP_AWAKE_SCHEDULER_INTERVAL_MS;
84
+ }
85
+ CogKeepAwakeManager.prototype.setRuntime = function (runtime) {
86
+ this.runtime = __assign(__assign({}, this.runtime), runtime);
87
+ if (!this.runtime.canPrompt()) {
88
+ this.deferPrompt();
89
+ return;
90
+ }
91
+ this.tick();
92
+ };
93
+ CogKeepAwakeManager.prototype.start = function () {
94
+ var _this = this;
95
+ if (this.scheduler !== null) {
96
+ return;
97
+ }
98
+ this.scheduler = setInterval(function () { return _this.tick(); }, this.schedulerIntervalMs);
99
+ this.tick();
100
+ };
101
+ CogKeepAwakeManager.prototype.stop = function () {
102
+ if (this.scheduler !== null) {
103
+ clearInterval(this.scheduler);
104
+ this.scheduler = null;
105
+ }
106
+ for (var _i = 0, _a = Array.from(this.sessions.values()); _i < _a.length; _i++) {
107
+ var session = _a[_i];
108
+ this.removeCog(session.cog);
109
+ }
110
+ this.activePrompt = null;
111
+ this.wasVisible = null;
112
+ this.emitSnapshot();
113
+ };
114
+ CogKeepAwakeManager.prototype.syncCogs = function (cogs) {
115
+ var nextCogs = new Set(cogs);
116
+ for (var _i = 0, _a = Array.from(this.sessions.values()); _i < _a.length; _i++) {
117
+ var session = _a[_i];
118
+ if (!nextCogs.has(session.cog)) {
119
+ this.removeCog(session.cog);
120
+ }
121
+ }
122
+ for (var _b = 0, _c = Array.from(nextCogs); _b < _c.length; _b++) {
123
+ var cog = _c[_b];
124
+ this.addCog(cog);
125
+ }
126
+ this.tick();
127
+ };
128
+ CogKeepAwakeManager.prototype.addCog = function (cog) {
129
+ var _this = this;
130
+ if (this.sessions.has(cog)) {
131
+ return;
132
+ }
133
+ for (var _i = 0, _a = Array.from(this.sessions.values()); _i < _a.length; _i++) {
134
+ var session_1 = _a[_i];
135
+ if (session_1.cog.id === cog.id && session_1.cog !== cog) {
136
+ this.removeCog(session_1.cog);
137
+ }
138
+ }
139
+ var observer = {
140
+ notify: function (_eventType, eventEnum) {
141
+ if (eventEnum === RaftConnEvent.CONN_VERIFIED_CORRECT) {
142
+ var currentSession = _this.sessions.get(cog);
143
+ if ((currentSession === null || currentSession === void 0 ? void 0 : currentSession.state) === "waiting-for-verification") {
144
+ _this.activateVerifiedSession(currentSession);
145
+ }
146
+ return;
147
+ }
148
+ if (eventEnum === RaftConnEvent.CONN_DISCONNECTED ||
149
+ eventEnum === RaftConnEvent.CONN_REJECTED) {
150
+ _this.removeCog(cog);
151
+ }
152
+ },
153
+ };
154
+ var onUsb = this.isConfirmedUsb(cog);
155
+ var session = {
156
+ cog: cog,
157
+ state: cog.hasVerifiedConnection()
158
+ ? onUsb
159
+ ? "paused-usb"
160
+ : "armed"
161
+ : "waiting-for-verification",
162
+ nextSignalAt: Number.POSITIVE_INFINITY,
163
+ idleGraceUntil: Number.NEGATIVE_INFINITY,
164
+ consecutiveFailures: 0,
165
+ signalGeneration: 0,
166
+ wasOnUsb: onUsb,
167
+ observer: observer,
168
+ };
169
+ this.sessions.set(cog, session);
170
+ cog.subscribe(observer, ["conn"]);
171
+ if (cog.hasVerifiedConnection() && !onUsb) {
172
+ this.activateVerifiedSession(session);
173
+ }
174
+ };
175
+ CogKeepAwakeManager.prototype.removeCog = function (cogOrId) {
176
+ var sessionsToRemove = Array.from(this.sessions.values()).filter(function (session) {
177
+ return typeof cogOrId === "string"
178
+ ? session.cog.id === cogOrId
179
+ : session.cog === cogOrId;
180
+ });
181
+ for (var _i = 0, sessionsToRemove_1 = sessionsToRemove; _i < sessionsToRemove_1.length; _i++) {
182
+ var session = sessionsToRemove_1[_i];
183
+ session.cog.unsubscribe(session.observer);
184
+ this.sessions.delete(session.cog);
185
+ this.removeSessionFromPrompt(session);
186
+ }
187
+ };
188
+ CogKeepAwakeManager.prototype.subscribe = function (listener) {
189
+ var _this = this;
190
+ this.listeners.add(listener);
191
+ return function () { return _this.listeners.delete(listener); };
192
+ };
193
+ CogKeepAwakeManager.prototype.getSnapshot = function () {
194
+ return { prompt: this.buildPromptSnapshot() };
195
+ };
196
+ CogKeepAwakeManager.prototype.resolvePrompt = function (keepAwake, expectedRevision) {
197
+ if (!this.activePrompt ||
198
+ (expectedRevision !== undefined &&
199
+ this.activePrompt.revision !== expectedRevision)) {
200
+ return false;
201
+ }
202
+ var promptedSessions = Array.from(this.activePrompt.sessions);
203
+ this.activePrompt = null;
204
+ for (var _i = 0, promptedSessions_1 = promptedSessions; _i < promptedSessions_1.length; _i++) {
205
+ var session = promptedSessions_1[_i];
206
+ if (this.sessions.get(session.cog) !== session ||
207
+ session.state !== "prompting") {
208
+ continue;
209
+ }
210
+ if (!keepAwake) {
211
+ session.state = "opted-out";
212
+ continue;
213
+ }
214
+ if (!this.runtime.isVisible() ||
215
+ !this.isSessionCurrentAndVerified(session)) {
216
+ session.state = "needs-confirmation";
217
+ continue;
218
+ }
219
+ if (this.isConfirmedUsb(session.cog)) {
220
+ session.wasOnUsb = true;
221
+ session.state = "paused-usb";
222
+ continue;
223
+ }
224
+ session.state = "armed";
225
+ void this.sendSignal(session);
226
+ }
227
+ this.emitSnapshot();
228
+ return true;
229
+ };
230
+ CogKeepAwakeManager.prototype.deferPrompt = function () {
231
+ if (!this.activePrompt) {
232
+ return;
233
+ }
234
+ for (var _i = 0, _a = Array.from(this.activePrompt.sessions); _i < _a.length; _i++) {
235
+ var session = _a[_i];
236
+ if (this.sessions.get(session.cog) === session &&
237
+ session.state === "prompting") {
238
+ session.state = "needs-confirmation";
239
+ }
240
+ }
241
+ this.activePrompt = null;
242
+ this.emitSnapshot();
243
+ };
244
+ CogKeepAwakeManager.prototype.tick = function () {
245
+ var now = this.runtime.now();
246
+ var isVisible = this.runtime.isVisible();
247
+ var resumedFromHidden = this.wasVisible === false && isVisible;
248
+ this.wasVisible = isVisible;
249
+ if (this.activePrompt && now >= this.activePrompt.expiresAt) {
250
+ this.resolvePrompt(false, this.activePrompt.revision);
251
+ }
252
+ for (var _i = 0, _a = Array.from(this.sessions.values()); _i < _a.length; _i++) {
253
+ var session = _a[_i];
254
+ if (!this.runtime.isCurrentCog(session.cog)) {
255
+ this.removeCog(session.cog);
256
+ continue;
257
+ }
258
+ // A No/timeout decision is a connection-lifetime latch. Power-source
259
+ // changes must never silently re-enable keep-awake for that Cog object.
260
+ if (session.state === "opted-out") {
261
+ continue;
262
+ }
263
+ var onUsb = this.isConfirmedUsb(session.cog);
264
+ if (onUsb) {
265
+ session.wasOnUsb = true;
266
+ if (session.state !== "waiting-for-verification") {
267
+ session.state = "paused-usb";
268
+ this.removeSessionFromPrompt(session);
269
+ }
270
+ continue;
271
+ }
272
+ if (session.wasOnUsb) {
273
+ session.wasOnUsb = false;
274
+ if (session.cog.hasVerifiedConnection()) {
275
+ // Unplugging the Cog is itself physical activity and starts the
276
+ // battery-only firmware timeout, so protect it immediately.
277
+ session.idleGraceUntil = now + this.idleTimeoutMs;
278
+ this.activateVerifiedSession(session);
279
+ }
280
+ continue;
281
+ }
282
+ if (session.state === "waiting-for-verification") {
283
+ if (session.cog.hasVerifiedConnection()) {
284
+ this.activateVerifiedSession(session);
285
+ }
286
+ continue;
287
+ }
288
+ if (session.state === "sending" ||
289
+ session.state === "prompting" ||
290
+ session.state === "needs-confirmation" ||
291
+ session.state === "paused-usb") {
292
+ continue;
293
+ }
294
+ if (this.isSessionIdle(session, now)) {
295
+ session.state = "needs-confirmation";
296
+ continue;
297
+ }
298
+ if (now >= session.nextSignalAt) {
299
+ if (!isVisible || resumedFromHidden) {
300
+ session.state = "needs-confirmation";
301
+ }
302
+ else {
303
+ void this.sendSignal(session);
304
+ }
305
+ }
306
+ }
307
+ this.openPromptIfPossible(now);
308
+ };
309
+ CogKeepAwakeManager.prototype.activateVerifiedSession = function (session) {
310
+ if (this.sessions.get(session.cog) !== session) {
311
+ return;
312
+ }
313
+ if (this.isConfirmedUsb(session.cog)) {
314
+ session.wasOnUsb = true;
315
+ session.state = "paused-usb";
316
+ return;
317
+ }
318
+ if (!this.runtime.isVisible() || !this.runtime.isCurrentCog(session.cog)) {
319
+ session.state = "needs-confirmation";
320
+ this.openPromptIfPossible(this.runtime.now());
321
+ return;
322
+ }
323
+ if (this.isSessionIdle(session, this.runtime.now())) {
324
+ session.state = "needs-confirmation";
325
+ this.openPromptIfPossible(this.runtime.now());
326
+ return;
327
+ }
328
+ // In particular, make a USB -> battery transition eligible to send. The
329
+ // regular send guard correctly rejects sessions left in paused-usb.
330
+ session.state = "armed";
331
+ void this.sendSignal(session);
332
+ };
333
+ CogKeepAwakeManager.prototype.sendSignal = function (session) {
334
+ return __awaiter(this, void 0, void 0, function () {
335
+ var signalGeneration, response, _a, result, succeeded, now;
336
+ return __generator(this, function (_b) {
337
+ switch (_b.label) {
338
+ case 0:
339
+ if (this.sessions.get(session.cog) !== session ||
340
+ session.state !== "armed" ||
341
+ !this.isSessionCurrentAndVerified(session)) {
342
+ return [2 /*return*/];
343
+ }
344
+ session.state = "sending";
345
+ signalGeneration = ++session.signalGeneration;
346
+ _b.label = 1;
347
+ case 1:
348
+ _b.trys.push([1, 3, , 4]);
349
+ return [4 /*yield*/, session.cog.sendRestMessage("audio/vol")];
350
+ case 2:
351
+ response = _b.sent();
352
+ return [3 /*break*/, 4];
353
+ case 3:
354
+ _a = _b.sent();
355
+ response = null;
356
+ return [3 /*break*/, 4];
357
+ case 4:
358
+ if (this.sessions.get(session.cog) !== session ||
359
+ session.state !== "sending" ||
360
+ session.signalGeneration !== signalGeneration) {
361
+ return [2 /*return*/];
362
+ }
363
+ if (!this.isSessionCurrentAndVerified(session)) {
364
+ this.removeCog(session.cog);
365
+ return [2 /*return*/];
366
+ }
367
+ if (this.isConfirmedUsb(session.cog)) {
368
+ session.wasOnUsb = true;
369
+ session.state = "paused-usb";
370
+ return [2 /*return*/];
371
+ }
372
+ result = response;
373
+ succeeded = typeof (result === null || result === void 0 ? void 0 : result.rslt) === "string" && result.rslt.toLowerCase() === "ok";
374
+ now = this.runtime.now();
375
+ if (succeeded) {
376
+ session.consecutiveFailures = 0;
377
+ session.state = "armed";
378
+ session.nextSignalAt = now + this.keepAwakeIntervalMs;
379
+ return [2 /*return*/];
380
+ }
381
+ session.consecutiveFailures += 1;
382
+ session.state = "armed";
383
+ session.nextSignalAt =
384
+ now +
385
+ (session.consecutiveFailures <= this.fastRetryLimit
386
+ ? this.retryDelayMs
387
+ : this.keepAwakeIntervalMs);
388
+ return [2 /*return*/];
389
+ }
390
+ });
391
+ });
392
+ };
393
+ CogKeepAwakeManager.prototype.openPromptIfPossible = function (now) {
394
+ var _this = this;
395
+ if (!this.runtime.isVisible() || !this.runtime.canPrompt()) {
396
+ return;
397
+ }
398
+ var sessions = Array.from(this.sessions.values()).filter(function (session) {
399
+ return session.state === "needs-confirmation" &&
400
+ _this.isSessionCurrentAndVerified(session) &&
401
+ !_this.isConfirmedUsb(session.cog);
402
+ });
403
+ if (sessions.length === 0) {
404
+ return;
405
+ }
406
+ if (this.activePrompt) {
407
+ for (var _i = 0, sessions_1 = sessions; _i < sessions_1.length; _i++) {
408
+ var session = sessions_1[_i];
409
+ session.state = "prompting";
410
+ this.activePrompt.sessions.add(session);
411
+ }
412
+ this.activePrompt.expiresAt = Math.max(this.activePrompt.expiresAt, now + this.promptTimeoutMs);
413
+ this.activePrompt.revision = ++this.nextPromptRevision;
414
+ this.emitSnapshot();
415
+ return;
416
+ }
417
+ for (var _a = 0, sessions_2 = sessions; _a < sessions_2.length; _a++) {
418
+ var session = sessions_2[_a];
419
+ session.state = "prompting";
420
+ }
421
+ this.activePrompt = {
422
+ revision: ++this.nextPromptRevision,
423
+ sessions: new Set(sessions),
424
+ expiresAt: now + this.promptTimeoutMs,
425
+ };
426
+ this.emitSnapshot();
427
+ };
428
+ CogKeepAwakeManager.prototype.isSessionCurrentAndVerified = function (session) {
429
+ return (this.sessions.get(session.cog) === session &&
430
+ this.runtime.isCurrentCog(session.cog) &&
431
+ session.cog.hasVerifiedConnection());
432
+ };
433
+ CogKeepAwakeManager.prototype.isSessionIdle = function (session, now) {
434
+ return (now >= session.idleGraceUntil &&
435
+ now - this.runtime.getLastActivityAt() >= this.idleTimeoutMs);
436
+ };
437
+ CogKeepAwakeManager.prototype.isConfirmedUsb = function (cog) {
438
+ var _a, _b;
439
+ var usb = (_b = (_a = cog.raftStateInfo) === null || _a === void 0 ? void 0 : _a.power) === null || _b === void 0 ? void 0 : _b.usb;
440
+ return typeof usb === "string" && usb.toLowerCase() === "yes";
441
+ };
442
+ CogKeepAwakeManager.prototype.getCogName = function (cog) {
443
+ var _a, _b;
444
+ try {
445
+ return ((_b = (_a = cog.getFriendlyName) === null || _a === void 0 ? void 0 : _a.call(cog)) === null || _b === void 0 ? void 0 : _b.trim()) || cog.id || "Cog";
446
+ }
447
+ catch (_c) {
448
+ return cog.id || "Cog";
449
+ }
450
+ };
451
+ CogKeepAwakeManager.prototype.removeSessionFromPrompt = function (session) {
452
+ var _a;
453
+ if (!((_a = this.activePrompt) === null || _a === void 0 ? void 0 : _a.sessions.delete(session))) {
454
+ return;
455
+ }
456
+ if (this.activePrompt.sessions.size === 0) {
457
+ this.activePrompt = null;
458
+ }
459
+ this.emitSnapshot();
460
+ };
461
+ CogKeepAwakeManager.prototype.buildPromptSnapshot = function () {
462
+ var _this = this;
463
+ if (!this.activePrompt) {
464
+ return null;
465
+ }
466
+ var devices = Array.from(this.activePrompt.sessions).map(function (session) { return ({
467
+ id: session.cog.id,
468
+ name: _this.getCogName(session.cog),
469
+ }); });
470
+ return {
471
+ revision: this.activePrompt.revision,
472
+ devices: devices,
473
+ deviceIds: devices.map(function (_a) {
474
+ var id = _a.id;
475
+ return id;
476
+ }),
477
+ deviceNames: devices.map(function (_a) {
478
+ var name = _a.name;
479
+ return name;
480
+ }),
481
+ expiresAt: this.activePrompt.expiresAt,
482
+ };
483
+ };
484
+ CogKeepAwakeManager.prototype.emitSnapshot = function () {
485
+ var snapshot = this.getSnapshot();
486
+ for (var _i = 0, _a = Array.from(this.listeners); _i < _a.length; _i++) {
487
+ var listener = _a[_i];
488
+ listener(snapshot);
489
+ }
490
+ };
491
+ return CogKeepAwakeManager;
492
+ }());
493
+ export { CogKeepAwakeManager };
494
+ export var cogKeepAwakeManager = new CogKeepAwakeManager();
495
+ export default cogKeepAwakeManager;
@@ -0,0 +1,22 @@
1
+ type Cleanup = () => void;
2
+ /**
3
+ * Tracks interaction with the app without treating an open/backgrounded tab as
4
+ * proof that somebody is still present.
5
+ */
6
+ export declare class UserActivityTracker {
7
+ private readonly now;
8
+ private readonly isAppVisible;
9
+ private lastActivityAt;
10
+ constructor(now?: () => number, isAppVisible?: () => boolean);
11
+ getLastActivityAt(): number;
12
+ recordActivity(at?: number): boolean;
13
+ /**
14
+ * Attach activity listeners to either the main app window or a same-origin
15
+ * embedded editor window. The returned cleanup is safe to call repeatedly.
16
+ */
17
+ attachWindow(targetWindow: Window): Cleanup;
18
+ private isTopDocumentHidden;
19
+ }
20
+ declare const userActivityTracker: UserActivityTracker;
21
+ export { userActivityTracker };
22
+ export default userActivityTracker;
@@ -0,0 +1,177 @@
1
+ var DRAG_ACTIVITY_THROTTLE_MS = 1000;
2
+ /**
3
+ * Tracks interaction with the app without treating an open/backgrounded tab as
4
+ * proof that somebody is still present.
5
+ */
6
+ var UserActivityTracker = /** @class */ (function () {
7
+ function UserActivityTracker(now, isAppVisible) {
8
+ if (now === void 0) { now = function () { return Date.now(); }; }
9
+ if (isAppVisible === void 0) { isAppVisible = function () {
10
+ return typeof document === "undefined" ||
11
+ (!document.hidden && document.visibilityState !== "hidden");
12
+ }; }
13
+ this.now = now;
14
+ this.isAppVisible = isAppVisible;
15
+ this.lastActivityAt = this.now();
16
+ }
17
+ UserActivityTracker.prototype.getLastActivityAt = function () {
18
+ return this.lastActivityAt;
19
+ };
20
+ UserActivityTracker.prototype.recordActivity = function (at) {
21
+ if (at === void 0) { at = this.now(); }
22
+ if (!this.isAppVisible() || this.isTopDocumentHidden()) {
23
+ return false;
24
+ }
25
+ this.lastActivityAt = at;
26
+ return true;
27
+ };
28
+ /**
29
+ * Attach activity listeners to either the main app window or a same-origin
30
+ * embedded editor window. The returned cleanup is safe to call repeatedly.
31
+ */
32
+ UserActivityTracker.prototype.attachWindow = function (targetWindow) {
33
+ var _this = this;
34
+ var listeners = [];
35
+ var activePointerIds = new Set();
36
+ var mouseIsDown = false;
37
+ var touchIsActive = false;
38
+ var lastDragActivityAt = Number.NEGATIVE_INFINITY;
39
+ var addListener = function (type, listener, options) {
40
+ if (options === void 0) { options = { capture: true, passive: true }; }
41
+ targetWindow.addEventListener(type, listener, options);
42
+ listeners.push({ type: type, listener: listener, options: options });
43
+ };
44
+ var recordTrustedActivity = function (event) {
45
+ if (!event.isTrusted) {
46
+ return null;
47
+ }
48
+ var activityAt = _this.now();
49
+ return _this.recordActivity(activityAt) ? activityAt : null;
50
+ };
51
+ var activityListener = function (event) {
52
+ recordTrustedActivity(event);
53
+ };
54
+ var pointerDownListener = function (event) {
55
+ var activityAt = recordTrustedActivity(event);
56
+ if (activityAt === null) {
57
+ return;
58
+ }
59
+ var pointerId = event.pointerId;
60
+ activePointerIds.add(typeof pointerId === "number" ? pointerId : 0);
61
+ lastDragActivityAt = activityAt;
62
+ };
63
+ var pointerUpListener = function (event) {
64
+ var pointerId = event.pointerId;
65
+ if (typeof pointerId === "number") {
66
+ activePointerIds.delete(pointerId);
67
+ }
68
+ else {
69
+ activePointerIds.clear();
70
+ }
71
+ if (!activePointerIds.size) {
72
+ mouseIsDown = false;
73
+ touchIsActive = false;
74
+ }
75
+ };
76
+ var mouseDownListener = function (event) {
77
+ var activityAt = recordTrustedActivity(event);
78
+ if (activityAt === null) {
79
+ return;
80
+ }
81
+ mouseIsDown = true;
82
+ lastDragActivityAt = activityAt;
83
+ };
84
+ var touchStartListener = function (event) {
85
+ var activityAt = recordTrustedActivity(event);
86
+ if (activityAt === null) {
87
+ return;
88
+ }
89
+ touchIsActive = true;
90
+ lastDragActivityAt = activityAt;
91
+ };
92
+ var touchEndListener = function (event) {
93
+ var touches = event.touches;
94
+ touchIsActive = Boolean(touches === null || touches === void 0 ? void 0 : touches.length);
95
+ };
96
+ var pointerMoveListener = function (event) {
97
+ if (!activePointerIds.size && !mouseIsDown && !touchIsActive) {
98
+ return;
99
+ }
100
+ if (!event.isTrusted) {
101
+ return;
102
+ }
103
+ var activityAt = _this.now();
104
+ if (activityAt - lastDragActivityAt < DRAG_ACTIVITY_THROTTLE_MS) {
105
+ return;
106
+ }
107
+ if (_this.recordActivity(activityAt)) {
108
+ lastDragActivityAt = activityAt;
109
+ }
110
+ };
111
+ var resetPressedState = function () {
112
+ activePointerIds.clear();
113
+ mouseIsDown = false;
114
+ touchIsActive = false;
115
+ };
116
+ addListener("pointerdown", pointerDownListener);
117
+ addListener("pointerup", pointerUpListener);
118
+ addListener("pointercancel", pointerUpListener);
119
+ addListener("pointermove", pointerMoveListener);
120
+ // These also cover browsers/WebViews that do not expose Pointer Events.
121
+ addListener("mousedown", mouseDownListener);
122
+ addListener("mousemove", pointerMoveListener);
123
+ addListener("mouseup", function () {
124
+ mouseIsDown = false;
125
+ });
126
+ addListener("touchstart", touchStartListener);
127
+ addListener("touchmove", pointerMoveListener);
128
+ addListener("touchend", touchEndListener);
129
+ addListener("touchcancel", touchEndListener);
130
+ addListener("keydown", activityListener);
131
+ addListener("click", activityListener);
132
+ addListener("wheel", activityListener);
133
+ addListener("input", activityListener);
134
+ addListener("change", activityListener);
135
+ addListener("scroll", activityListener);
136
+ addListener("blur", resetPressedState, { capture: true });
137
+ var isCleanedUp = false;
138
+ return function () {
139
+ if (isCleanedUp) {
140
+ return;
141
+ }
142
+ isCleanedUp = true;
143
+ for (var _i = 0, listeners_1 = listeners; _i < listeners_1.length; _i++) {
144
+ var _a = listeners_1[_i], type = _a.type, listener = _a.listener, options = _a.options;
145
+ try {
146
+ targetWindow.removeEventListener(type, listener, options);
147
+ }
148
+ catch (_b) {
149
+ // The embedded document may have navigated to a different origin.
150
+ }
151
+ }
152
+ resetPressedState();
153
+ };
154
+ };
155
+ UserActivityTracker.prototype.isTopDocumentHidden = function () {
156
+ var _a, _b;
157
+ if (typeof document === "undefined") {
158
+ return false;
159
+ }
160
+ var topDocument = document;
161
+ if (typeof window !== "undefined") {
162
+ try {
163
+ topDocument = (_b = (_a = window.top) === null || _a === void 0 ? void 0 : _a.document) !== null && _b !== void 0 ? _b : document;
164
+ }
165
+ catch (_c) {
166
+ // A cross-origin parent is not expected in production, but the local
167
+ // document is still a safe fallback if this app is embedded elsewhere.
168
+ }
169
+ }
170
+ return topDocument.hidden || topDocument.visibilityState === "hidden";
171
+ };
172
+ return UserActivityTracker;
173
+ }());
174
+ export { UserActivityTracker };
175
+ var userActivityTracker = new UserActivityTracker();
176
+ export { userActivityTracker };
177
+ export default userActivityTracker;
@@ -0,0 +1,6 @@
1
+ export declare const KEEP_AWAKE_INTERVAL_MS: number;
2
+ export declare const USER_IDLE_TIMEOUT_MS: number;
3
+ export declare const PROMPT_TIMEOUT_MS: number;
4
+ export declare const KEEP_AWAKE_RETRY_DELAY_MS: number;
5
+ export declare const KEEP_AWAKE_FAST_RETRY_LIMIT = 2;
6
+ export declare const KEEP_AWAKE_SCHEDULER_INTERVAL_MS = 1000;
@@ -0,0 +1,6 @@
1
+ export var KEEP_AWAKE_INTERVAL_MS = 5 * 60 * 1000;
2
+ export var USER_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
3
+ export var PROMPT_TIMEOUT_MS = 60 * 1000;
4
+ export var KEEP_AWAKE_RETRY_DELAY_MS = 30 * 1000;
5
+ export var KEEP_AWAKE_FAST_RETRY_LIMIT = 2;
6
+ export var KEEP_AWAKE_SCHEDULER_INTERVAL_MS = 1000;
@@ -0,0 +1,4 @@
1
+ export { CogKeepAwakeManager, cogKeepAwakeManager, } from "./CogKeepAwakeManager";
2
+ export type { CogKeepAwakeManagerOptions } from "./CogKeepAwakeManager";
3
+ export * from "./constants";
4
+ export type { CogKeepAwakeCog, CogKeepAwakePrompt, CogKeepAwakeRuntime, CogKeepAwakeSnapshot, } from "./types";
@@ -0,0 +1,2 @@
1
+ export { CogKeepAwakeManager, cogKeepAwakeManager, } from "./CogKeepAwakeManager";
2
+ export * from "./constants";
@@ -0,0 +1,41 @@
1
+ export type CogKeepAwakeResult = {
2
+ rslt?: string;
3
+ [key: string]: unknown;
4
+ };
5
+ export type CogKeepAwakeObserver = {
6
+ notify: (eventType: string, eventEnum: unknown, eventName: string, eventData: unknown) => void;
7
+ };
8
+ export type CogKeepAwakeCog = {
9
+ id: string;
10
+ raftStateInfo?: {
11
+ power?: {
12
+ usb?: string;
13
+ };
14
+ } | null;
15
+ getFriendlyName?: () => string | undefined;
16
+ hasVerifiedConnection: () => boolean;
17
+ sendRestMessage: (message: string) => Promise<CogKeepAwakeResult | unknown>;
18
+ subscribe: (observer: CogKeepAwakeObserver, topics: string[]) => void;
19
+ unsubscribe: (observer: CogKeepAwakeObserver) => void;
20
+ };
21
+ export type CogKeepAwakeRuntime = {
22
+ now: () => number;
23
+ getLastActivityAt: () => number;
24
+ isVisible: () => boolean;
25
+ isCurrentCog: (cog: CogKeepAwakeCog) => boolean;
26
+ canPrompt: () => boolean;
27
+ };
28
+ export type CogKeepAwakePromptDevice = {
29
+ id: string;
30
+ name: string;
31
+ };
32
+ export type CogKeepAwakePrompt = {
33
+ revision: number;
34
+ devices: CogKeepAwakePromptDevice[];
35
+ deviceIds: string[];
36
+ deviceNames: string[];
37
+ expiresAt: number;
38
+ };
39
+ export type CogKeepAwakeSnapshot = {
40
+ prompt: CogKeepAwakePrompt | null;
41
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -140,6 +140,20 @@ var resources = {
140
140
  "more_info": "MORE INFORMATION",
141
141
  "reject": "NO, I DON'T ACCEPT"
142
142
  },
143
+ "cog_keep_awake": {
144
+ "device_list_label": "Cogs affected by this choice",
145
+ "explanation": "If you choose No or do not respond, keep-awake signals will stop and normal automatic power-off will follow.",
146
+ "no": "No",
147
+ "no_aria_label": "No, stop keeping Cog awake",
148
+ "question": "We haven't detected any activity. Do you want to keep Cog turned on?",
149
+ "question_multiple": "We haven't detected any activity. Do you want to keep these Cogs turned on?",
150
+ "question_named": "We haven't detected any activity. Do you want to keep {{deviceName}} turned on?",
151
+ "seconds_remaining_one": "{{seconds}} second remaining",
152
+ "seconds_remaining_other": "{{seconds}} seconds remaining",
153
+ "title": "Keep Cog turned on?",
154
+ "yes": "Yes",
155
+ "yes_aria_label": "Yes, keep Cog awake"
156
+ },
143
157
  "calibration_screen": {
144
158
  "colour_calibration": {
145
159
  "unplugged_modal_title": "Unplugged",
@@ -549,6 +563,20 @@ var resources = {
549
563
  "more_info": "ΠΕΡΙΣΣΟΤΕΡΕΣ ΠΛΗΡΟΦΟΡΙΕΣ",
550
564
  "reject": "ΟΧΙ, ΔΕΝ ΑΠΟΔΕΧΟΜΑΙ"
551
565
  },
566
+ "cog_keep_awake": {
567
+ "device_list_label": "Συσκευές Cog που επηρεάζονται από αυτή την επιλογή",
568
+ "explanation": "Αν επιλέξετε «Όχι» ή δεν απαντήσετε, τα σήματα διατήρησης σε λειτουργία θα σταματήσουν και θα ισχύσει η κανονική αυτόματη απενεργοποίηση.",
569
+ "no": "Όχι",
570
+ "no_aria_label": "Όχι, σταμάτησε να διατηρείς το Cog ενεργό",
571
+ "question": "Δεν εντοπίσαμε καμία δραστηριότητα. Θέλετε να παραμείνει το Cog ενεργοποιημένο;",
572
+ "question_multiple": "Δεν εντοπίσαμε καμία δραστηριότητα. Θέλετε να παραμείνουν αυτές οι συσκευές Cog ενεργοποιημένες;",
573
+ "question_named": "Δεν εντοπίσαμε καμία δραστηριότητα. Θέλετε να παραμείνει το {{deviceName}} ενεργοποιημένο;",
574
+ "seconds_remaining_one": "Απομένει {{seconds}} δευτερόλεπτο",
575
+ "seconds_remaining_other": "Απομένουν {{seconds}} δευτερόλεπτα",
576
+ "title": "Να παραμείνει το Cog ενεργοποιημένο;",
577
+ "yes": "Ναι",
578
+ "yes_aria_label": "Ναι, διατήρησε το Cog ενεργό"
579
+ },
552
580
  "calibration_screen": {
553
581
  "colour_calibration": {
554
582
  "unplugged_modal_title": "Unplugged",
@@ -941,6 +969,20 @@ var resources = {
941
969
  "more_info": "MEER INFORMATIE",
942
970
  "reject": "NEE, IK ACCEPTEER HET NIET"
943
971
  },
972
+ "cog_keep_awake": {
973
+ "device_list_label": "Cog-apparaten waarop deze keuze van toepassing is",
974
+ "explanation": "Als je Nee kiest of niet reageert, stoppen de signalen die Cog actief houden en volgt de normale automatische uitschakeling.",
975
+ "no": "Nee",
976
+ "no_aria_label": "Nee, stop met Cog actief te houden",
977
+ "question": "We hebben geen activiteit gedetecteerd. Wil je Cog ingeschakeld houden?",
978
+ "question_multiple": "We hebben geen activiteit gedetecteerd. Wil je deze Cogs ingeschakeld houden?",
979
+ "question_named": "We hebben geen activiteit gedetecteerd. Wil je {{deviceName}} ingeschakeld houden?",
980
+ "seconds_remaining_one": "Nog {{seconds}} seconde",
981
+ "seconds_remaining_other": "Nog {{seconds}} seconden",
982
+ "title": "Cog ingeschakeld houden?",
983
+ "yes": "Ja",
984
+ "yes_aria_label": "Ja, houd Cog actief"
985
+ },
944
986
  "calibration_screen": {
945
987
  "colour_calibration": {
946
988
  "unplugged_modal_title": "Unplugged",
@@ -1333,6 +1375,21 @@ var resources = {
1333
1375
  "more_info": "ULTERIORI INFORMAZIONI",
1334
1376
  "reject": "NO, NON ACCETTO"
1335
1377
  },
1378
+ "cog_keep_awake": {
1379
+ "device_list_label": "Cog interessati da questa scelta",
1380
+ "explanation": "Se scegli No o non rispondi, i segnali che mantengono Cog attivo si interromperanno e seguirà il normale spegnimento automatico.",
1381
+ "no": "No",
1382
+ "no_aria_label": "No, smetti di mantenere Cog attivo",
1383
+ "question": "Non abbiamo rilevato alcuna attività. Vuoi lasciare Cog acceso?",
1384
+ "question_multiple": "Non abbiamo rilevato alcuna attività. Vuoi lasciare accesi questi Cog?",
1385
+ "question_named": "Non abbiamo rilevato alcuna attività. Vuoi lasciare acceso {{deviceName}}?",
1386
+ "seconds_remaining_one": "{{seconds}} secondo rimanente",
1387
+ "seconds_remaining_many": "{{seconds}} secondi rimanenti",
1388
+ "seconds_remaining_other": "{{seconds}} secondi rimanenti",
1389
+ "title": "Lasciare Cog acceso?",
1390
+ "yes": "Sì",
1391
+ "yes_aria_label": "Sì, mantieni Cog attivo"
1392
+ },
1336
1393
  "calibration_screen": {
1337
1394
  "colour_calibration": {
1338
1395
  "unplugged_modal_title": "Unplugged",
@@ -1725,6 +1782,21 @@ var resources = {
1725
1782
  "more_info": "PLUS D'INFORMATIONS",
1726
1783
  "reject": "NON, JE N'ACCEPTE PAS"
1727
1784
  },
1785
+ "cog_keep_awake": {
1786
+ "device_list_label": "Cogs concernés par ce choix",
1787
+ "explanation": "Si vous choisissez Non ou ne répondez pas, les signaux de maintien en éveil s’arrêteront et l’arrêt automatique normal suivra.",
1788
+ "no": "Non",
1789
+ "no_aria_label": "Non, arrêter de maintenir Cog en éveil",
1790
+ "question": "Aucune activité n’a été détectée. Voulez-vous laisser Cog allumé ?",
1791
+ "question_multiple": "Aucune activité n’a été détectée. Voulez-vous laisser ces Cogs allumés ?",
1792
+ "question_named": "Aucune activité n’a été détectée. Voulez-vous laisser {{deviceName}} allumé ?",
1793
+ "seconds_remaining_one": "{{seconds}} seconde restante",
1794
+ "seconds_remaining_many": "{{seconds}} secondes restantes",
1795
+ "seconds_remaining_other": "{{seconds}} secondes restantes",
1796
+ "title": "Laisser Cog allumé ?",
1797
+ "yes": "Oui",
1798
+ "yes_aria_label": "Oui, maintenir Cog en éveil"
1799
+ },
1728
1800
  "calibration_screen": {
1729
1801
  "colour_calibration": {
1730
1802
  "unplugged_modal_title": "Unplugged",
@@ -2117,6 +2189,20 @@ var resources = {
2117
2189
  "more_info": "WEITERE INFORMATIONEN",
2118
2190
  "reject": "NEIN, ICH AKZEPTIERE NICHT"
2119
2191
  },
2192
+ "cog_keep_awake": {
2193
+ "device_list_label": "Von dieser Auswahl betroffene Cogs",
2194
+ "explanation": "Wenn Sie Nein wählen oder nicht antworten, werden keine Wachhaltesignale mehr gesendet und die normale automatische Abschaltung erfolgt.",
2195
+ "no": "Nein",
2196
+ "no_aria_label": "Nein, Cog nicht länger aktiv halten",
2197
+ "question": "Wir haben keine Aktivität erkannt. Soll Cog eingeschaltet bleiben?",
2198
+ "question_multiple": "Wir haben keine Aktivität erkannt. Sollen diese Cogs eingeschaltet bleiben?",
2199
+ "question_named": "Wir haben keine Aktivität erkannt. Soll {{deviceName}} eingeschaltet bleiben?",
2200
+ "seconds_remaining_one": "Noch {{seconds}} Sekunde",
2201
+ "seconds_remaining_other": "Noch {{seconds}} Sekunden",
2202
+ "title": "Cog eingeschaltet lassen?",
2203
+ "yes": "Ja",
2204
+ "yes_aria_label": "Ja, Cog aktiv halten"
2205
+ },
2120
2206
  "calibration_screen": {
2121
2207
  "colour_calibration": {
2122
2208
  "unplugged_modal_title": "Unplugged",
@@ -2509,6 +2595,20 @@ var resources = {
2509
2595
  "more_info": "ПОВЕЧЕ ИНФОРМАЦИЯ",
2510
2596
  "reject": "НЕ, НЕ ПРИЕМАМ"
2511
2597
  },
2598
+ "cog_keep_awake": {
2599
+ "device_list_label": "Cog устройства, засегнати от този избор",
2600
+ "explanation": "Ако изберете „Не“ или не отговорите, сигналите за поддържане на Cog активно ще спрат и ще последва нормалното автоматично изключване.",
2601
+ "no": "Не",
2602
+ "no_aria_label": "Не, спри да поддържаш Cog активно",
2603
+ "question": "Не открихме активност. Искате ли Cog да остане включено?",
2604
+ "question_multiple": "Не открихме активност. Искате ли тези Cog устройства да останат включени?",
2605
+ "question_named": "Не открихме активност. Искате ли {{deviceName}} да остане включено?",
2606
+ "seconds_remaining_one": "Остава {{seconds}} секунда",
2607
+ "seconds_remaining_other": "Остават {{seconds}} секунди",
2608
+ "title": "Cog да остане включено?",
2609
+ "yes": "Да",
2610
+ "yes_aria_label": "Да, поддържай Cog активно"
2611
+ },
2512
2612
  "calibration_screen": {
2513
2613
  "colour_calibration": {
2514
2614
  "unplugged_modal_title": "Unplugged",
@@ -2901,6 +3001,20 @@ var resources = {
2901
3001
  "more_info": "MERE INFORMATION",
2902
3002
  "reject": "NEJ, JEG ACCEPTERER IKKE"
2903
3003
  },
3004
+ "cog_keep_awake": {
3005
+ "device_list_label": "Cog-enheder, som dette valg gælder for",
3006
+ "explanation": "Hvis du vælger Nej eller ikke svarer, stopper signalerne, der holder Cog aktiv, og den normale automatiske slukning følger.",
3007
+ "no": "Nej",
3008
+ "no_aria_label": "Nej, stop med at holde Cog aktiv",
3009
+ "question": "Vi har ikke registreret nogen aktivitet. Vil du holde Cog tændt?",
3010
+ "question_multiple": "Vi har ikke registreret nogen aktivitet. Vil du holde disse Cogs tændt?",
3011
+ "question_named": "Vi har ikke registreret nogen aktivitet. Vil du holde {{deviceName}} tændt?",
3012
+ "seconds_remaining_one": "{{seconds}} sekund tilbage",
3013
+ "seconds_remaining_other": "{{seconds}} sekunder tilbage",
3014
+ "title": "Holde Cog tændt?",
3015
+ "yes": "Ja",
3016
+ "yes_aria_label": "Ja, hold Cog aktiv"
3017
+ },
2904
3018
  "calibration_screen": {
2905
3019
  "colour_calibration": {
2906
3020
  "unplugged_modal_title": "Unplugged",
@@ -3293,6 +3407,20 @@ var resources = {
3293
3407
  "more_info": "MER INFORMASJON",
3294
3408
  "reject": "NEI, JEG GODTAR IKKE"
3295
3409
  },
3410
+ "cog_keep_awake": {
3411
+ "device_list_label": "Cog-enheter som påvirkes av dette valget",
3412
+ "explanation": "Hvis du velger Nei eller ikke svarer, stopper signalene som holder Cog aktiv, og den vanlige automatiske avslåingen følger.",
3413
+ "no": "Nei",
3414
+ "no_aria_label": "Nei, slutt å holde Cog aktiv",
3415
+ "question": "Vi har ikke registrert noen aktivitet. Vil du la Cog være slått på?",
3416
+ "question_multiple": "Vi har ikke registrert noen aktivitet. Vil du la disse Cogs være slått på?",
3417
+ "question_named": "Vi har ikke registrert noen aktivitet. Vil du la {{deviceName}} være slått på?",
3418
+ "seconds_remaining_one": "{{seconds}} sekund igjen",
3419
+ "seconds_remaining_other": "{{seconds}} sekunder igjen",
3420
+ "title": "La Cog være slått på?",
3421
+ "yes": "Ja",
3422
+ "yes_aria_label": "Ja, hold Cog aktiv"
3423
+ },
3296
3424
  "calibration_screen": {
3297
3425
  "colour_calibration": {
3298
3426
  "unplugged_modal_title": "Unplugged",
@@ -3685,6 +3813,22 @@ var resources = {
3685
3813
  "more_info": "VÍCE INFORMACÍ",
3686
3814
  "reject": "NE, NEPŘIJÍMÁM"
3687
3815
  },
3816
+ "cog_keep_awake": {
3817
+ "device_list_label": "Zařízení Cog, kterých se tato volba týká",
3818
+ "explanation": "Pokud zvolíte Ne nebo neodpovíte, signály udržující Cog aktivní se zastaví a bude následovat běžné automatické vypnutí.",
3819
+ "no": "Ne",
3820
+ "no_aria_label": "Ne, přestat udržovat Cog aktivní",
3821
+ "question": "Nezaznamenali jsme žádnou aktivitu. Chcete ponechat Cog zapnutý?",
3822
+ "question_multiple": "Nezaznamenali jsme žádnou aktivitu. Chcete ponechat tato zařízení Cog zapnutá?",
3823
+ "question_named": "Nezaznamenali jsme žádnou aktivitu. Chcete ponechat {{deviceName}} zapnutý?",
3824
+ "seconds_remaining_one": "Zbývá {{seconds}} sekunda",
3825
+ "seconds_remaining_few": "Zbývají {{seconds}} sekundy",
3826
+ "seconds_remaining_many": "Zbývá {{seconds}} sekundy",
3827
+ "seconds_remaining_other": "Zbývá {{seconds}} sekund",
3828
+ "title": "Ponechat Cog zapnutý?",
3829
+ "yes": "Ano",
3830
+ "yes_aria_label": "Ano, udržovat Cog aktivní"
3831
+ },
3688
3832
  "calibration_screen": {
3689
3833
  "colour_calibration": {
3690
3834
  "unplugged_modal_title": "Unplugged",
@@ -4077,6 +4221,21 @@ var resources = {
4077
4221
  "more_info": "MÁS INFORMACIÓN",
4078
4222
  "reject": "NO, NO ACEPTO"
4079
4223
  },
4224
+ "cog_keep_awake": {
4225
+ "device_list_label": "Dispositivos Cog afectados por esta elección",
4226
+ "explanation": "Si eliges No o no respondes, dejarán de enviarse las señales que mantienen Cog activo y se aplicará el apagado automático normal.",
4227
+ "no": "No",
4228
+ "no_aria_label": "No, dejar de mantener Cog activo",
4229
+ "question": "No hemos detectado actividad. ¿Quieres mantener Cog encendido?",
4230
+ "question_multiple": "No hemos detectado actividad. ¿Quieres mantener estos Cogs encendidos?",
4231
+ "question_named": "No hemos detectado actividad. ¿Quieres mantener {{deviceName}} encendido?",
4232
+ "seconds_remaining_one": "Queda {{seconds}} segundo",
4233
+ "seconds_remaining_many": "Quedan {{seconds}} segundos",
4234
+ "seconds_remaining_other": "Quedan {{seconds}} segundos",
4235
+ "title": "¿Mantener Cog encendido?",
4236
+ "yes": "Sí",
4237
+ "yes_aria_label": "Sí, mantener Cog activo"
4238
+ },
4080
4239
  "calibration_screen": {
4081
4240
  "colour_calibration": {
4082
4241
  "unplugged_modal_title": "Unplugged",
@@ -4469,6 +4628,20 @@ var resources = {
4469
4628
  "more_info": "MER INFORMATION",
4470
4629
  "reject": "NEJ, JAG ACCEPTERAR INTE"
4471
4630
  },
4631
+ "cog_keep_awake": {
4632
+ "device_list_label": "Cog-enheter som påverkas av det här valet",
4633
+ "explanation": "Om du väljer Nej eller inte svarar slutar signalerna som håller Cog aktiv att skickas, och den vanliga automatiska avstängningen följer.",
4634
+ "no": "Nej",
4635
+ "no_aria_label": "Nej, sluta hålla Cog aktiv",
4636
+ "question": "Vi har inte registrerat någon aktivitet. Vill du låta Cog vara på?",
4637
+ "question_multiple": "Vi har inte registrerat någon aktivitet. Vill du låta dessa Cogs vara på?",
4638
+ "question_named": "Vi har inte registrerat någon aktivitet. Vill du låta {{deviceName}} vara på?",
4639
+ "seconds_remaining_one": "{{seconds}} sekund återstår",
4640
+ "seconds_remaining_other": "{{seconds}} sekunder återstår",
4641
+ "title": "Låta Cog vara på?",
4642
+ "yes": "Ja",
4643
+ "yes_aria_label": "Ja, håll Cog aktiv"
4644
+ },
4472
4645
  "calibration_screen": {
4473
4646
  "colour_calibration": {
4474
4647
  "unplugged_modal_title": "Unplugged",
@@ -4861,6 +5034,20 @@ var resources = {
4861
5034
  "more_info": "LISÄTIETOJA",
4862
5035
  "reject": "EI, EN HYVÄKSY"
4863
5036
  },
5037
+ "cog_keep_awake": {
5038
+ "device_list_label": "Cog-laitteet, joihin tämä valinta vaikuttaa",
5039
+ "explanation": "Jos valitset Ei tai et vastaa, Cogin aktiivisena pitävät signaalit lopetetaan ja normaali automaattinen virrankatkaisu jatkuu.",
5040
+ "no": "Ei",
5041
+ "no_aria_label": "Ei, lopeta Cogin pitäminen aktiivisena",
5042
+ "question": "Toimintaa ei ole havaittu. Haluatko pitää Cogin päällä?",
5043
+ "question_multiple": "Toimintaa ei ole havaittu. Haluatko pitää nämä Cog-laitteet päällä?",
5044
+ "question_named": "Toimintaa ei ole havaittu. Haluatko pitää laitteen {{deviceName}} päällä?",
5045
+ "seconds_remaining_one": "{{seconds}} sekunti jäljellä",
5046
+ "seconds_remaining_other": "{{seconds}} sekuntia jäljellä",
5047
+ "title": "Pidetäänkö Cog päällä?",
5048
+ "yes": "Kyllä",
5049
+ "yes_aria_label": "Kyllä, pidä Cog aktiivisena"
5050
+ },
4864
5051
  "calibration_screen": {
4865
5052
  "colour_calibration": {
4866
5053
  "unplugged_modal_title": "Unplugged",
@@ -5253,6 +5440,24 @@ var resources = {
5253
5440
  "more_info": "مزيد من المعلومات",
5254
5441
  "reject": "لا، لا أقبل"
5255
5442
  },
5443
+ "cog_keep_awake": {
5444
+ "device_list_label": "أجهزة Cog المتأثرة بهذا الاختيار",
5445
+ "explanation": "إذا اخترت «لا» أو لم تستجب، فسيتوقف إرسال الإشارات التي تُبقي Cog نشطًا وسيعمل إيقاف التشغيل التلقائي المعتاد.",
5446
+ "no": "لا",
5447
+ "no_aria_label": "لا، أوقف إبقاء Cog نشطًا",
5448
+ "question": "لم نكتشف أي نشاط. هل تريد إبقاء Cog قيد التشغيل؟",
5449
+ "question_multiple": "لم نكتشف أي نشاط. هل تريد إبقاء أجهزة Cog هذه قيد التشغيل؟",
5450
+ "question_named": "لم نكتشف أي نشاط. هل تريد إبقاء {{deviceName}} قيد التشغيل؟",
5451
+ "seconds_remaining_zero": "{{seconds}} ثانية متبقية",
5452
+ "seconds_remaining_one": "{{seconds}} ثانية متبقية",
5453
+ "seconds_remaining_two": "{{seconds}} ثانية متبقية",
5454
+ "seconds_remaining_few": "{{seconds}} ثوانٍ متبقية",
5455
+ "seconds_remaining_many": "{{seconds}} ثانية متبقية",
5456
+ "seconds_remaining_other": "{{seconds}} ثانية متبقية",
5457
+ "title": "هل تريد إبقاء Cog قيد التشغيل؟",
5458
+ "yes": "نعم",
5459
+ "yes_aria_label": "نعم، أبقِ Cog نشطًا"
5460
+ },
5256
5461
  "calibration_screen": {
5257
5462
  "colour_calibration": {
5258
5463
  "unplugged_modal_title": "Unplugged",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robotical/webapp-types",
3
- "version": "3.18.6",
3
+ "version": "3.18.7",
4
4
  "description": "Type definitions for the Application Manager",
5
5
  "main": "dist/application-manager.d.ts",
6
6
  "types": "dist/application-manager.d.ts",