@7365admin1/core 3.133.1 → 3.135.0

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.
package/dist/index.js CHANGED
@@ -323,6 +323,7 @@ __export(src_exports, {
323
323
  ROBOT_TYPES: () => ROBOT_TYPES,
324
324
  RobotCloudError: () => RobotCloudError,
325
325
  RobotCredentialsError: () => RobotCredentialsError,
326
+ SECRET_SETTING: () => SECRET_SETTING,
326
327
  SELF_SERVICE_RESEND_COOLDOWN_MS: () => SELF_SERVICE_RESEND_COOLDOWN_MS,
327
328
  SELF_SIGNUP_PLATFORM: () => SELF_SIGNUP_PLATFORM,
328
329
  SELF_SIGNUP_STATUS: () => SELF_SIGNUP_STATUS,
@@ -619,18 +620,26 @@ __export(src_exports, {
619
620
  orgRoleSets: () => orgRoleSets,
620
621
  orgSchema: () => orgSchema,
621
622
  ounitechAlertRecord: () => ounitechAlertRecord,
623
+ ounitechAppAction: () => ounitechAppAction,
622
624
  ounitechCleaning: () => ounitechCleaning,
623
625
  ounitechConsumable: () => ounitechConsumable,
624
626
  ounitechCurrentMap: () => ounitechCurrentMap,
625
627
  ounitechFault: () => ounitechFault,
628
+ ounitechFloorMapHistory: () => ounitechFloorMapHistory,
626
629
  ounitechJobWords: () => ounitechJobWords,
627
630
  ounitechMapShare: () => ounitechMapShare,
631
+ ounitechMapVersion: () => ounitechMapVersion,
628
632
  ounitechMapView: () => ounitechMapView,
633
+ ounitechObstacleImage: () => ounitechObstacleImage,
629
634
  ounitechPowerPlan: () => ounitechPowerPlan,
630
635
  ounitechReadings: () => ounitechReadings,
631
636
  ounitechRobotFromHomeRow: () => ounitechRobotFromHomeRow,
637
+ ounitechSettings: () => ounitechSettings,
638
+ ounitechShapes: () => ounitechShapes,
639
+ ounitechStatusLog: () => ounitechStatusLog,
632
640
  ounitechTaskRecord: () => ounitechTaskRecord,
633
641
  ounitechTime: () => ounitechTime,
642
+ ounitechTrack: () => ounitechTrack,
634
643
  overnight_parking_requests_namespace_collection: () => overnight_parking_requests_namespace_collection,
635
644
  padbotAccountKey: () => padbotAccountKey,
636
645
  padbotAlarmFault: () => padbotAlarmFault,
@@ -5431,7 +5440,7 @@ function useMemberRepo() {
5431
5440
  throw error;
5432
5441
  }
5433
5442
  }
5434
- async function updateRoleById(_id, role) {
5443
+ async function updateRoleById(_id, role, createdBy) {
5435
5444
  try {
5436
5445
  _id = new import_mongodb14.ObjectId(_id);
5437
5446
  } catch (error) {
@@ -5447,6 +5456,9 @@ function useMemberRepo() {
5447
5456
  role,
5448
5457
  updatedAt: /* @__PURE__ */ new Date()
5449
5458
  };
5459
+ if (createdBy && import_mongodb14.ObjectId.isValid(String(createdBy))) {
5460
+ updateValue.createdBy = new import_mongodb14.ObjectId(String(createdBy));
5461
+ }
5450
5462
  const res = await collection.updateOne({ _id }, { $set: updateValue });
5451
5463
  if (res.modifiedCount === 0) {
5452
5464
  throw new import_node_server_utils15.InternalServerError("Unable to update feedback status.");
@@ -9919,11 +9931,27 @@ async function siteInvitedPins(db2, userId, memberships) {
9919
9931
  const keys = /* @__PURE__ */ new Set();
9920
9932
  if (!pinned.length || !import_mongodb26.ObjectId.isValid(userId))
9921
9933
  return keys;
9934
+ const both3 = (ids2) => [...new Set(ids2.map(String))].flatMap((v) => [v, new import_mongodb26.ObjectId(v)]);
9935
+ const wanted = new Set(pinned.map((m) => pinKey(m.org, m.siteId)));
9936
+ const placed = await db2.collection("members").find(
9937
+ {
9938
+ user: new import_mongodb26.ObjectId(userId),
9939
+ status: { $ne: "deleted" },
9940
+ org: { $in: both3(pinned.map((m) => m.org)) },
9941
+ siteId: { $in: both3(pinned.map((m) => m.siteId)) },
9942
+ createdBy: { $type: "objectId", $ne: new import_mongodb26.ObjectId(userId) }
9943
+ },
9944
+ { projection: { org: 1, siteId: 1 } }
9945
+ ).toArray();
9946
+ for (const row of placed) {
9947
+ const key = pinKey(row.org, row.siteId);
9948
+ if (wanted.has(key))
9949
+ keys.add(key);
9950
+ }
9922
9951
  const user = await db2.collection("users").findOne({ _id: new import_mongodb26.ObjectId(userId) }, { projection: { email: 1 } });
9923
9952
  const email = String(user?.email ?? "").trim();
9924
9953
  if (!email)
9925
9954
  return keys;
9926
- const both3 = (ids2) => [...new Set(ids2.map(String))].flatMap((v) => [v, new import_mongodb26.ObjectId(v)]);
9927
9955
  const invites = await db2.collection("verifications").find(
9928
9956
  {
9929
9957
  email,
@@ -9939,7 +9967,6 @@ async function siteInvitedPins(db2, userId, memberships) {
9939
9967
  collation: { locale: "en", strength: 2 }
9940
9968
  }
9941
9969
  ).toArray();
9942
- const wanted = new Set(pinned.map((m) => pinKey(m.org, m.siteId)));
9943
9970
  for (const invite of invites) {
9944
9971
  const key = pinKey(invite.metadata?.org, invite.metadata?.siteId);
9945
9972
  if (wanted.has(key))
@@ -25783,7 +25810,8 @@ function useRoleRepo() {
25783
25810
  sort = {},
25784
25811
  type = "",
25785
25812
  org = "",
25786
- site = ""
25813
+ site = "",
25814
+ scope = ""
25787
25815
  }) {
25788
25816
  page2 = page2 > 0 ? page2 - 1 : 0;
25789
25817
  if (org && typeof org === "string" && org.length === 24) {
@@ -25805,6 +25833,9 @@ function useRoleRepo() {
25805
25833
  if (site) {
25806
25834
  query2.$or = [{ site }, { site: "" }, { site: null }];
25807
25835
  cacheOptions.site = site.toString();
25836
+ } else if (scope === "organisation") {
25837
+ query2.site = { $in: ["", null] };
25838
+ cacheOptions.scope = scope;
25808
25839
  }
25809
25840
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
25810
25841
  cacheOptions.sort = JSON.stringify(sort);
@@ -28241,7 +28272,9 @@ function useRoleController() {
28241
28272
  limit: import_joi28.default.number().integer().min(1).max(100).allow("", null).default(10),
28242
28273
  type: import_joi28.default.string().optional().allow("", null),
28243
28274
  org: import_joi28.default.string().hex().optional().allow("", null),
28244
- site: import_joi28.default.string().hex().optional().allow("", null)
28275
+ site: import_joi28.default.string().hex().optional().allow("", null),
28276
+ // "organisation": only the roles with no site (Organization app, service roles made once)
28277
+ scope: import_joi28.default.string().valid("organisation").optional().allow("", null)
28245
28278
  });
28246
28279
  const query2 = { ...req.query };
28247
28280
  const { error } = validation.validate(query2);
@@ -28256,18 +28289,19 @@ function useRoleController() {
28256
28289
  const type = req.query.type ?? "";
28257
28290
  const org = req.query.org ?? "";
28258
28291
  const site = req.query.site ?? "";
28292
+ const scope = req.query.scope ?? "";
28259
28293
  try {
28260
28294
  if (org) {
28261
28295
  await requireOrgReach(req, org);
28262
28296
  }
28263
- let data = await _getRoles({ search, page: page2, limit, type, org, site });
28264
- const listIsWholeOrg = org && !search && !site && page2 === 1;
28297
+ let data = await _getRoles({ search, page: page2, limit, type, org, site, scope });
28298
+ const listIsWholeOrg = org && !search && !site && !scope && page2 === 1;
28265
28299
  const listed = data?.items;
28266
28300
  if (listIsWholeOrg && listed?.length === 0) {
28267
28301
  const seeded = await healDefaultRoles(org).catch(() => 0);
28268
28302
  if (seeded > 0) {
28269
28303
  try {
28270
- data = await _getRoles({ search, page: page2, limit, type, org, site });
28304
+ data = await _getRoles({ search, page: page2, limit, type, org, site, scope });
28271
28305
  } catch (error2) {
28272
28306
  import_node_server_utils71.logger.log({ level: "error", message: error2.message });
28273
28307
  }
@@ -28715,7 +28749,8 @@ function useMemberService() {
28715
28749
  }
28716
28750
  }
28717
28751
  }
28718
- await _updateRoleById(id, role);
28752
+ const placedBy = member.siteId && !newRole?.site && callerId5 && import_mongodb60.ObjectId.isValid(callerId5) && String(member.user ?? "") !== callerId5 ? callerId5 : void 0;
28753
+ await _updateRoleById(id, role, placedBy);
28719
28754
  return { message: "Member role updated successfully." };
28720
28755
  }
28721
28756
  async function updateSiteById(id, siteId, siteName) {
@@ -28763,6 +28798,7 @@ function useMemberService() {
28763
28798
  var import_node_server_utils73 = require("@7365admin1/node-server-utils");
28764
28799
  var import_mongodb61 = require("mongodb");
28765
28800
  var EXCLUDED_TYPES = ["resident", PLATFORM_STAFF_MEMBER_TYPE];
28801
+ var ORG_ROLE_EXCLUDED = [...EXCLUDED_TYPES, "organization"];
28766
28802
  var both = (id) => [id, new import_mongodb61.ObjectId(id)];
28767
28803
  function useMemberSiteAccessService() {
28768
28804
  const { add, updateMemberStatus, updateRoleById } = useMemberRepo();
@@ -28802,10 +28838,13 @@ function useMemberSiteAccessService() {
28802
28838
  const roles = await db2().collection("roles").find({
28803
28839
  $or: [
28804
28840
  { _id: { $in: roleIds.map((r) => new import_mongodb61.ObjectId(r)) } },
28805
- { org: { $in: both(org) }, site: { $in: [...sites.keys()].flatMap(both) }, status: "active" }
28841
+ { org: { $in: both(org) }, site: { $in: [...sites.keys()].flatMap(both) }, status: "active" },
28842
+ // organisation-level service roles (no site; never the org-console type)
28843
+ { org: { $in: both(org) }, site: { $in: ["", null] }, status: "active", type: { $nin: ORG_ROLE_EXCLUDED } }
28806
28844
  ]
28807
28845
  }).project({ name: 1, type: 1, site: 1, org: 1, status: 1 }).toArray();
28808
28846
  const byId = new Map(roles.map((r) => [String(r._id), r]));
28847
+ const orgRoles = roles.filter((r) => !String(r.site ?? "") && String(r.org ?? "") === org && r.status === "active" && !ORG_ROLE_EXCLUDED.includes(r.type)).map((r) => ({ _id: String(r._id), name: r.name ?? "", type: r.type ?? "", scope: "organisation" })).sort((a, b) => a.name.localeCompare(b.name));
28809
28848
  const view = (m) => ({
28810
28849
  _id: String(m._id),
28811
28850
  role: String(m.role ?? ""),
@@ -28819,7 +28858,7 @@ function useMemberSiteAccessService() {
28819
28858
  ...s,
28820
28859
  membership: here[0] ?? null,
28821
28860
  memberships: here,
28822
- roles: roles.filter((r) => String(r.site ?? "") === s.site && String(r.org ?? "") === org && r.status === "active" && !EXCLUDED_TYPES.includes(r.type)).map((r) => ({ _id: String(r._id), name: r.name ?? "", type: r.type ?? "" }))
28861
+ roles: roles.filter((r) => String(r.site ?? "") === s.site && String(r.org ?? "") === org && r.status === "active" && !EXCLUDED_TYPES.includes(r.type)).map((r) => ({ _id: String(r._id), name: r.name ?? "", type: r.type ?? "", scope: "site" })).concat(orgRoles)
28823
28862
  };
28824
28863
  }).sort((a, b) => a.name.localeCompare(b.name));
28825
28864
  const orgWide = members.filter((m) => !String(m.siteId ?? "")).map(view);
@@ -28845,7 +28884,7 @@ function useMemberSiteAccessService() {
28845
28884
  throw new import_node_server_utils73.BadRequestError("A site is listed twice.");
28846
28885
  seen2.add(choice.site);
28847
28886
  if (!row.roles.some((r) => r._id === choice.role)) {
28848
- throw new import_node_server_utils73.BadRequestError(`Choose a role created for ${row.name || "that site"}.`);
28887
+ throw new import_node_server_utils73.BadRequestError(`Choose a role created for ${row.name || "that site"}, or one of the organisation's service roles.`);
28849
28888
  }
28850
28889
  }
28851
28890
  const userDoc = await db2().collection("users").findOne({ _id: new import_mongodb61.ObjectId(user) }, { projection: { email: 1, name: 1 } });
@@ -28859,10 +28898,11 @@ function useMemberSiteAccessService() {
28859
28898
  const choice = sites.find((c) => c.site === row.site);
28860
28899
  const role = choice ? row.roles.find((r) => r._id === choice.role) : null;
28861
28900
  const keep = role ? row.memberships.find((m) => m.type === role.type) : void 0;
28862
- if (role && keep && keep.role !== role._id) {
28901
+ if (role && keep && (keep.role !== role._id || role.scope === "organisation")) {
28863
28902
  reroles.push(async () => {
28864
- await updateRoleById(keep._id, role._id);
28865
- applied.changed.push(row.site);
28903
+ await updateRoleById(keep._id, role._id, actor);
28904
+ if (keep.role !== role._id)
28905
+ applied.changed.push(row.site);
28866
28906
  });
28867
28907
  } else if (role && !keep) {
28868
28908
  adds.push(async () => {
@@ -28871,7 +28911,7 @@ function useMemberSiteAccessService() {
28871
28911
  { projection: { _id: 1 } }
28872
28912
  );
28873
28913
  if (old) {
28874
- await updateRoleById(old._id, role._id);
28914
+ await updateRoleById(old._id, role._id, actor);
28875
28915
  await updateMemberStatus(old._id, { status: suspended ? "suspended" : "active" });
28876
28916
  } else {
28877
28917
  await add({
@@ -28885,6 +28925,8 @@ function useMemberSiteAccessService() {
28885
28925
  siteId: row.site,
28886
28926
  siteName: row.name,
28887
28927
  dateInvited: (/* @__PURE__ */ new Date()).toISOString(),
28928
+ // "placed at this site by the editor": confines an organisation role to it (siteInvitedPins)
28929
+ createdBy: actor,
28888
28930
  ...suspended ? { status: "suspended" } : {}
28889
28931
  });
28890
28932
  }
@@ -64262,6 +64304,17 @@ var ROBOT_CLOUD_PATHS = {
64262
64304
  workHours: "/portal/workhours/get-work-hours",
64263
64305
  haltPlans: "/portal/halt-plan/get",
64264
64306
  stationState: "/portal/detailstate/get-station-detail-state",
64307
+ // Read-only map layers and logs (2026-09-30: every read in the document is used).
64308
+ wallList: "/portal/maps/get-wall-list",
64309
+ safeZoneList: "/portal/maps/get-safezone-list",
64310
+ slowZoneList: "/portal/maps/get-slowzone-list",
64311
+ obstacleLayer: "/portal/obstacles/get-obstacle-layer",
64312
+ teachTrack: "/portal/teach/get-teach-track",
64313
+ mapVersions: "/portal/maps/get-map-version-list",
64314
+ floorMapHistory: "/portal/maps/v2405/get-floor-maplist",
64315
+ robotLog: "/portal/report/get-robot-log-list",
64316
+ userLog: "/portal/userlog/get-user-logs",
64317
+ cleaningSetting: "/portal/setting/get-cleaning-setting",
64265
64318
  mapList: "/portal/maps/get-map-list",
64266
64319
  fileUrl: "/portal/maps/get-file-url",
64267
64320
  tracking: "/portal/robots/tracking",
@@ -64562,7 +64615,17 @@ var STEP_NAMES = {
64562
64615
  [ROBOT_CLOUD_PATHS.haltPlans]: "the power schedule",
64563
64616
  [ROBOT_CLOUD_PATHS.stationState]: "the base station state",
64564
64617
  [ROBOT_CLOUD_PATHS.alertList]: "the alert history",
64565
- [ROBOT_CLOUD_PATHS.liftList]: "the lift list"
64618
+ [ROBOT_CLOUD_PATHS.liftList]: "the lift list",
64619
+ [ROBOT_CLOUD_PATHS.wallList]: "the virtual walls",
64620
+ [ROBOT_CLOUD_PATHS.safeZoneList]: "the safe zones",
64621
+ [ROBOT_CLOUD_PATHS.slowZoneList]: "the slow zones",
64622
+ [ROBOT_CLOUD_PATHS.obstacleLayer]: "the obstacle layer",
64623
+ [ROBOT_CLOUD_PATHS.teachTrack]: "the taught route",
64624
+ [ROBOT_CLOUD_PATHS.mapVersions]: "the map versions",
64625
+ [ROBOT_CLOUD_PATHS.floorMapHistory]: "the floor's map history",
64626
+ [ROBOT_CLOUD_PATHS.robotLog]: "the robot status log",
64627
+ [ROBOT_CLOUD_PATHS.userLog]: "the app action log",
64628
+ [ROBOT_CLOUD_PATHS.cleaningSetting]: "the robot settings"
64566
64629
  };
64567
64630
  var robotCloudStepName = (path5) => STEP_NAMES[path5] ?? path5;
64568
64631
  var SUCCESS_CODES = /* @__PURE__ */ new Set(["0", "200"]);
@@ -64782,6 +64845,40 @@ function useRobotCloudService(credentials, options = {}) {
64782
64845
  );
64783
64846
  return data.state ?? null;
64784
64847
  }
64848
+ async function getMapShapes(kind, mapId) {
64849
+ const [path5, list] = kind === "walls" ? [ROBOT_CLOUD_PATHS.wallList, "wallList"] : kind === "safe" ? [ROBOT_CLOUD_PATHS.safeZoneList, "safeZoneList"] : [ROBOT_CLOUD_PATHS.slowZoneList, "slowZoneList"];
64850
+ const data = await post(path5, { mapId }, list);
64851
+ return Array.isArray(data[list]) ? data[list] : [];
64852
+ }
64853
+ async function getObstacleLayer(robotId) {
64854
+ return post(ROBOT_CLOUD_PATHS.obstacleLayer, { robotId, version: 0 });
64855
+ }
64856
+ async function getTeachTrack(robotId, offset) {
64857
+ return post(ROBOT_CLOUD_PATHS.teachTrack, { robotId, offset }, "trackList");
64858
+ }
64859
+ async function getMapVersions(mapId) {
64860
+ return post(ROBOT_CLOUD_PATHS.mapVersions, { mapId }, "versionList");
64861
+ }
64862
+ async function getFloorMapHistory(sceneId, floorNo2) {
64863
+ return post(ROBOT_CLOUD_PATHS.floorMapHistory, { sceneId, floorNo: floorNo2 }, "mapList");
64864
+ }
64865
+ async function getRobotLog(robotId, sceneId, timeFrom, timeTo) {
64866
+ return post(
64867
+ ROBOT_CLOUD_PATHS.robotLog,
64868
+ { robotId: String(robotId), sceneId: String(sceneId), timeFrom, timeTo, key: "", pageNo: 1, pageSize: 50 },
64869
+ "logList"
64870
+ );
64871
+ }
64872
+ async function getUserLogs(robotId, startTime, endTime) {
64873
+ return post(
64874
+ ROBOT_CLOUD_PATHS.userLog,
64875
+ { robotId, pageNo: 1, pageSize: 50, startTime, endTime, keyword: "" },
64876
+ "actionList"
64877
+ );
64878
+ }
64879
+ async function getCleaningSetting(robotId) {
64880
+ return post(ROBOT_CLOUD_PATHS.cleaningSetting, { robotId });
64881
+ }
64785
64882
  async function getMaps(robotSn) {
64786
64883
  const data = await post(
64787
64884
  ROBOT_CLOUD_PATHS.mapList,
@@ -64867,6 +64964,14 @@ function useRobotCloudService(credentials, options = {}) {
64867
64964
  getHaltPlans,
64868
64965
  getStationState,
64869
64966
  getLiftList,
64967
+ getMapShapes,
64968
+ getObstacleLayer,
64969
+ getTeachTrack,
64970
+ getMapVersions,
64971
+ getFloorMapHistory,
64972
+ getRobotLog,
64973
+ getUserLogs,
64974
+ getCleaningSetting,
64870
64975
  getMaps,
64871
64976
  getFileUrls,
64872
64977
  getPlans,
@@ -65061,22 +65166,48 @@ function ounitechFault(list) {
65061
65166
  return { code: "error", message: String(list[0]).slice(0, 200) };
65062
65167
  return { code: "error", message: "The robot reports an error" };
65063
65168
  }
65064
- function ounitechMapShare(map, x, y) {
65169
+ function ounitechMapShare(map, x, y, clamp4 = false) {
65065
65170
  const w = map?.width, h = map?.height, r = map?.resolution;
65066
65171
  const wx = num(x), wy = num(y);
65067
65172
  if (!finite(w) || !finite(h) || !finite(r) || w <= 0 || h <= 0 || r <= 0 || wx === void 0 || wy === void 0)
65068
65173
  return null;
65069
- const sx = (wx - (num(map?.originX) ?? 0)) / r / w * 100;
65070
- const sy = 100 - (wy - (num(map?.originY) ?? 0)) / r / h * 100;
65174
+ let sx = (wx - (num(map?.originX) ?? 0)) / r / w * 100;
65175
+ let sy = 100 - (wy - (num(map?.originY) ?? 0)) / r / h * 100;
65176
+ if (clamp4)
65177
+ sx = Math.min(100, Math.max(0, sx)), sy = Math.min(100, Math.max(0, sy));
65071
65178
  if (sx < 0 || sx > 100 || sy < 0 || sy > 100)
65072
65179
  return null;
65073
65180
  return { x: Math.round(sx * 100) / 100, y: Math.round(sy * 100) / 100 };
65074
65181
  }
65075
- function ounitechMapView(m, imageUrl, partitionImageUrl, size) {
65182
+ var SHAPE_WORDS = { wall: "Virtual wall", safe: "Safe zone", slow: "Slow zone" };
65183
+ function ounitechShapes(map, kind, list) {
65184
+ return (Array.isArray(list) ? list : []).flatMap((z5, i) => {
65185
+ const points = (Array.isArray(z5?.pointList) ? z5.pointList : []).flatMap((p) => {
65186
+ const at2 = Array.isArray(p) ? ounitechMapShare(map, p[0], p[1], true) : null;
65187
+ return at2 ? [at2] : [];
65188
+ });
65189
+ const name = String(z5?.wallName ?? z5?.zoneName ?? "").trim();
65190
+ return points.length >= 2 ? [{ kind, name: name || `${SHAPE_WORDS[kind]} ${i + 1}`, points }] : [];
65191
+ });
65192
+ }
65193
+ function ounitechTrack(map, list) {
65194
+ const on = (Array.isArray(list) ? list : []).filter((p) => p && (p.mapid == null || String(p.mapid) === map?.mapId));
65195
+ const step = Math.max(1, Math.ceil(on.length / 500));
65196
+ return on.filter((_, i) => i % step === 0).flatMap((p) => {
65197
+ const at2 = ounitechMapShare(map, p.x, p.y, true);
65198
+ return at2 ? [at2] : [];
65199
+ });
65200
+ }
65201
+ function ounitechMapView(m, imageUrl, partitionImageUrl, size, shapes = {}) {
65076
65202
  const floorNo2 = Number.isInteger(m.floorNo) ? m.floorNo : null;
65077
65203
  const width = size?.width ?? null;
65078
65204
  const height = size?.height ?? null;
65079
65205
  const frame2 = { width, height, resolution: m.resolution, originX: m.originX, originY: m.originY };
65206
+ const zones = [
65207
+ ...ounitechShapes(frame2, "wall", Array.isArray(shapes.walls) && shapes.walls.length ? shapes.walls : m.wallList),
65208
+ ...ounitechShapes(frame2, "safe", shapes.safe),
65209
+ ...ounitechShapes(frame2, "slow", shapes.slow)
65210
+ ];
65080
65211
  const areas = (Array.isArray(m.areaList) ? m.areaList : []).filter((a) => typeof a?.areaName === "string" && a.areaName.trim()).map((a) => {
65081
65212
  const px = num(a.x), py = num(a.y);
65082
65213
  const at2 = width && height && px !== void 0 && py !== void 0 && (px > 0 || py > 0) && px <= width && py <= height ? { x: Math.round(px / width * 1e4) / 100, y: Math.round(py / height * 1e4) / 100 } : null;
@@ -65106,7 +65237,11 @@ function ounitechMapView(m, imageUrl, partitionImageUrl, size) {
65106
65237
  positionApproximate: true,
65107
65238
  // The raw areas, kept for older screens.
65108
65239
  areaList: areas.map(({ at: _at, ...a }) => a),
65109
- areas: areas.map((a) => ({ name: a.areaName, at: a.at }))
65240
+ areas: areas.map((a) => ({ name: a.areaName, at: a.at })),
65241
+ /** Virtual walls (the robot never crosses), safe zones and slow zones, drawn on the map. */
65242
+ zones,
65243
+ /** The route the robot was taught (get-teach-track), set per robot by getMaps. */
65244
+ taughtRoute: []
65110
65245
  };
65111
65246
  }
65112
65247
  function ounitechCurrentMap(maps, taskMapId, floorNo2) {
@@ -65333,10 +65468,115 @@ function ounitechPowerPlan(p) {
65333
65468
  enabled: p.enableFlag !== false
65334
65469
  };
65335
65470
  }
65471
+ function ounitechStatusLog(l, tz = 480) {
65472
+ const code = num(l.typeFlag);
65473
+ const words = code !== void 0 ? ROBOT_STATUS_NAMES[code] : void 0;
65474
+ return {
65475
+ at: ounitechTime(l.timeFrom, tz),
65476
+ status: words ? code === 15 || code === 16 ? `${words} (mid-job)` : words : String(l.typeName ?? "").trim() || `Status ${l.typeFlag ?? "?"}`
65477
+ };
65478
+ }
65479
+ var ACTION_WORDS = {
65480
+ \u539F\u5730\u505C\u6B62: "Stopped in place",
65481
+ \u4E0B\u53D1\u6E05\u6D01: "Started a cleaning job",
65482
+ \u793A\u6559\u8DEF\u5F84\u63A7\u5236: "Controlled a taught route",
65483
+ \u6682\u505C: "Paused",
65484
+ \u7EE7\u7EED: "Resumed",
65485
+ \u56DE\u5145: "Sent back to charge",
65486
+ \u7ED3\u675F\u6E05\u6D01: "Ended the cleaning job",
65487
+ \u5F00\u673A: "Switched on",
65488
+ \u5173\u673A: "Switched off"
65489
+ };
65490
+ function ounitechAppAction(a, tz = 480) {
65491
+ const raw = String(a.actionName ?? "").trim();
65492
+ return {
65493
+ at: ounitechTime(a.createTime, tz),
65494
+ action: ACTION_WORDS[raw] ?? (raw ? /[㐀-鿿]/.test(raw) ? `App action: ${raw}` : raw : "App action"),
65495
+ by: String(a.userName ?? "").trim() || null
65496
+ };
65497
+ }
65498
+ function ounitechMapVersion(v, tz = 480) {
65499
+ return { version: num(v.versionNo) ?? null, latest: v.latestFlag === true, createdAt: ounitechTime(v.createTime, tz) };
65500
+ }
65501
+ function ounitechFloorMapHistory(m, tz = 480) {
65502
+ const area = num(m.totalArea);
65503
+ return {
65504
+ name: String(m.mapName ?? "").trim() || `Map ${m.mapId ?? ""}`.trim(),
65505
+ areaM2: area !== void 0 ? Math.round(area * 10) / 10 : null,
65506
+ latest: m.latestFlag === true,
65507
+ inUse: m.enableFlag === true,
65508
+ createdAt: ounitechTime(m.createTime, tz),
65509
+ updatedAt: ounitechTime(m.updateTime, tz)
65510
+ };
65511
+ }
65512
+ var SETTINGS = {
65513
+ soundLevel: { group: "Sound", label: "Sound level", unit: "%" },
65514
+ enableWorkSound: { group: "Sound", label: "Sounds while working" },
65515
+ clean_speed_mode: { group: "Cleaning", label: "Cleaning speed" },
65516
+ fanLevel: { group: "Cleaning", label: "Suction level" },
65517
+ waterGear: { group: "Cleaning", label: "Water level" },
65518
+ planningWidth: { group: "Cleaning", label: "Cleaning path width" },
65519
+ alongWallFlag: { group: "Cleaning", label: "Edge cleaning" },
65520
+ alongWallDistance: { group: "Cleaning", label: "Edge distance" },
65521
+ enablePlanFlag: { group: "Cleaning", label: "Schedules switched on" },
65522
+ slowZoneSpeed: { group: "Cleaning", label: "Speed in slow zones", unit: "m/s" },
65523
+ minCleanBatteryPerc: { group: "Battery", label: "Lowest battery to start cleaning", unit: "%" },
65524
+ normalCleanBatteryPerc: { group: "Battery", label: "Normal cleaning battery", unit: "%" },
65525
+ backChargeThreshold: { group: "Battery", label: "Return-to-charge battery level", unit: "%" },
65526
+ chargeCompIntervalDay: { group: "Battery", label: "Full charge every", unit: "days" },
65527
+ obsAvoidSens: { group: "Safety", label: "Obstacle avoidance sensitivity" },
65528
+ obsDetectionHeight: { group: "Safety", label: "Obstacle detection height" },
65529
+ enableCliffDetect: { group: "Safety", label: "Cliff (drop) detection" },
65530
+ ultrasonicSwitch: { group: "Safety", label: "Ultrasonic sensors" },
65531
+ panicButtonMode: { group: "Safety", label: "Emergency button mode" },
65532
+ autoUpdateMap: { group: "Maps", label: "Update the map automatically" },
65533
+ autoUpdateVirtualWall: { group: "Maps", label: "Update virtual walls automatically" },
65534
+ autoUpdateZoneMap: { group: "Maps", label: "Update the area map automatically" },
65535
+ stationAutoDrain: { group: "Base station", label: "Station drains automatically" },
65536
+ language: { group: "Robot", label: "Robot language" },
65537
+ timeZone: { group: "Robot", label: "Robot time zone" }
65538
+ };
65539
+ var SECRET_SETTING = /wifi|pwd|password|secret|token/i;
65540
+ var LANG_WORDS = { zh: "Chinese", en: "English" };
65541
+ function ounitechSettings(s) {
65542
+ const out = [];
65543
+ for (const [key, v] of Object.entries(s ?? {})) {
65544
+ if (key === "ok" || key === "msg" || SECRET_SETTING.test(key) || v == null || typeof v === "object")
65545
+ continue;
65546
+ const raw = String(v).trim();
65547
+ if (raw === "")
65548
+ continue;
65549
+ const d = SETTINGS[key] ?? { group: "Other settings", label: key };
65550
+ let value;
65551
+ if (typeof v === "boolean")
65552
+ value = v ? "On" : "Off";
65553
+ else if (key === "language")
65554
+ value = LANG_WORDS[raw] ?? raw;
65555
+ else if (key === "timeZone" && num(raw) !== void 0)
65556
+ value = `UTC${num(raw) >= 0 ? "+" : ""}${raw}`;
65557
+ else
65558
+ value = d.unit ? `${raw} ${d.unit}` : raw;
65559
+ out.push({ key, group: d.group, label: d.label, value, attention: false });
65560
+ }
65561
+ const order = ["Cleaning", "Battery", "Safety", "Maps", "Sound", "Base station", "Robot", "Other settings"];
65562
+ return out.sort((a, b) => order.indexOf(a.group) - order.indexOf(b.group));
65563
+ }
65564
+ function ounitechObstacleImage(o) {
65565
+ const b64 = typeof o?.image === "string" ? o.image.replace(/^data:image\/\w+;base64,/, "").trim() : "";
65566
+ if (!b64 || !/^[A-Za-z0-9+/=\s]+$/.test(b64.slice(0, 200)))
65567
+ return null;
65568
+ if (b64.length > 15e5)
65569
+ return { version: num(o?.version) ?? null, image: null, tooLarge: true };
65570
+ const type = b64.startsWith("/9j/") ? "jpeg" : "png";
65571
+ return { version: num(o?.version) ?? null, image: `data:image/${type};base64,${b64}`, tooLarge: false };
65572
+ }
65336
65573
  var MAP_TTL_MS = 10 * 6e4;
65337
65574
  var RECORDS_TTL_MS = 2 * 6e4;
65338
65575
  var mapCache = /* @__PURE__ */ new Map();
65339
65576
  var recordsCache = /* @__PURE__ */ new Map();
65577
+ var SLOW_TTL_MS = 10 * 6e4;
65578
+ var slowCache = /* @__PURE__ */ new Map();
65579
+ var trackCache = /* @__PURE__ */ new Map();
65340
65580
  function cached2(cache3, field, key, ttl, load) {
65341
65581
  const hit = cache3.get(key);
65342
65582
  if (hit && Date.now() - hit.at < ttl)
@@ -65349,6 +65589,8 @@ function cached2(cache3, field, key, ttl, load) {
65349
65589
  function clearOunitechCaches() {
65350
65590
  mapCache.clear();
65351
65591
  recordsCache.clear();
65592
+ slowCache.clear();
65593
+ trackCache.clear();
65352
65594
  }
65353
65595
  var robotTime = (d) => robotCloudTimestamp(d, "yyyy-MM-dd HH:mm:ss", "Asia/Shanghai");
65354
65596
  function createOunitechAdapter(credentials, options) {
@@ -65362,12 +65604,15 @@ function createOunitechAdapter(credentials, options) {
65362
65604
  const keys = list.flatMap((m) => [m.backgroundFileKey, m.partitionFileKey]).filter((k) => typeof k === "string" && !!k);
65363
65605
  const files = keys.length ? await cloud.previewFiles(keys).catch(() => []) : [];
65364
65606
  const url2 = (k) => files.find((f) => f.fileKey === k)?.url || null;
65365
- return Promise.all(
65366
- list.map(async (m) => {
65367
- const imageUrl = url2(m.backgroundFileKey);
65368
- return ounitechMapView(m, imageUrl, url2(m.partitionFileKey), imageUrl ? await fetchPngSize(imageUrl) : null);
65369
- })
65370
- );
65607
+ const views = [];
65608
+ for (const m of list) {
65609
+ const imageUrl = url2(m.backgroundFileKey);
65610
+ const id = num(m.mapId);
65611
+ const layer = (kind) => id !== void 0 ? cloud.getMapShapes(kind, id).catch(() => []) : Promise.resolve([]);
65612
+ const shapes = { walls: await layer("walls"), safe: await layer("safe"), slow: await layer("slow") };
65613
+ views.push(ounitechMapView(m, imageUrl, url2(m.partitionFileKey), imageUrl ? await fetchPngSize(imageUrl) : null, shapes));
65614
+ }
65615
+ return views;
65371
65616
  });
65372
65617
  }
65373
65618
  async function placed(row, robot) {
@@ -65381,6 +65626,40 @@ function createOunitechAdapter(credentials, options) {
65381
65626
  robot.planPosition = robot.position ? ounitechMapShare(map, robot.position.x, robot.position.y) : null;
65382
65627
  return robot;
65383
65628
  }
65629
+ async function slowRecords(robotSn) {
65630
+ const row = await rowFor(robotSn);
65631
+ if (!row || !finite(row.robotId))
65632
+ return {};
65633
+ const robotId = row.robotId;
65634
+ const tz = finite(row.timezone) ? row.timezone : 480;
65635
+ const now = /* @__PURE__ */ new Date();
65636
+ const since = (days) => robotTime(new Date(now.getTime() - days * 864e5));
65637
+ const part = async (read2) => {
65638
+ try {
65639
+ return { items: await read2() };
65640
+ } catch (e) {
65641
+ return { items: [], error: String(e?.message ?? e).slice(0, 300) };
65642
+ }
65643
+ };
65644
+ const maps = finite(row.sceneId) ? await floorMaps(row.sceneId).catch(() => []) : [];
65645
+ const map = ounitechCurrentMap(maps, row.taskState?.mapId, Number.isInteger(row.floorNo) ? row.floorNo : null);
65646
+ const mapId = map ? num(map.mapId) : void 0;
65647
+ const floorNo2 = map?.floorNo ?? (Number.isInteger(row.floorNo) ? row.floorNo : null);
65648
+ const sceneId = finite(row.sceneId) ? row.sceneId : null;
65649
+ const statusLog = sceneId !== null ? await part(async () => ((await cloud.getRobotLog(robotId, sceneId, since(7), robotTime(now))).logList ?? []).map((l) => ounitechStatusLog(l, tz))) : { items: [] };
65650
+ const appActions = await part(async () => ((await cloud.getUserLogs(robotId, since(30), robotTime(now))).actionList ?? []).map((a) => ounitechAppAction(a, tz)));
65651
+ const settings = await part(async () => ounitechSettings(await cloud.getCleaningSetting(robotId)));
65652
+ const obstacles = await part(async () => {
65653
+ const o = ounitechObstacleImage(await cloud.getObstacleLayer(robotId));
65654
+ return o ? [o] : [];
65655
+ });
65656
+ const mapVersions = mapId !== void 0 ? await part(async () => ((await cloud.getMapVersions(mapId)).versionList ?? []).map((v) => ounitechMapVersion(v, tz))) : { items: [] };
65657
+ const mapHistory = sceneId !== null && floorNo2 !== null ? await part(async () => ((await cloud.getFloorMapHistory(sceneId, floorNo2)).mapList ?? []).map((m) => ounitechFloorMapHistory(m, tz))) : { items: [] };
65658
+ const mapLayers = {
65659
+ items: map ? [{ map: map.mapDisplayName || null, zones: map.zones.map((z5) => ({ kind: z5.kind, name: z5.name })) }] : []
65660
+ };
65661
+ return { statusLog, appActions, settings, obstacles, mapVersions, mapHistory, mapLayers };
65662
+ }
65384
65663
  async function rowFor(robotSn) {
65385
65664
  return (await cloud.getHomeRobots()).find((r) => r.robotSn === robotSn) ?? null;
65386
65665
  }
@@ -65444,7 +65723,18 @@ function createOunitechAdapter(credentials, options) {
65444
65723
  const row = await rowFor(robotSn);
65445
65724
  if (!row || !finite(row.sceneId))
65446
65725
  return [];
65447
- return floorMaps(row.sceneId);
65726
+ const maps = await floorMaps(row.sceneId);
65727
+ const robotId = row.robotId;
65728
+ if (!finite(robotId))
65729
+ return maps;
65730
+ const track = await cached2(
65731
+ trackCache,
65732
+ "value",
65733
+ `${options?.baseUrl ?? ""}|${credentials.appid}|${robotId}`,
65734
+ SLOW_TTL_MS,
65735
+ async () => (await cloud.getTeachTrack(robotId, robotTime(new Date(Date.now() - 365 * 864e5)))).trackList ?? []
65736
+ ).catch(() => []);
65737
+ return maps.map((m) => ({ ...m, taughtRoute: ounitechTrack(m, track) }));
65448
65738
  },
65449
65739
  async getMapImages(_robotSn, fileKeys) {
65450
65740
  return cloud.previewFiles(fileKeys);
@@ -65470,7 +65760,7 @@ function createOunitechAdapter(credentials, options) {
65470
65760
  * after another (gentler on the rate limit); each part carries its own error.
65471
65761
  */
65472
65762
  async getRecords(robotSn) {
65473
- return cached2(recordsCache, "value", `${options?.baseUrl ?? ""}|${credentials.appid}|${robotSn}`, RECORDS_TTL_MS, async () => {
65763
+ const fast = cached2(recordsCache, "value", `${options?.baseUrl ?? ""}|${credentials.appid}|${robotSn}`, RECORDS_TTL_MS, async () => {
65474
65764
  const row = await rowFor(robotSn);
65475
65765
  if (!row || !finite(row.robotId))
65476
65766
  return { supported: true, error: `Serial ${robotSn} is not on this Ounitech account.` };
@@ -65513,6 +65803,8 @@ function createOunitechAdapter(credentials, options) {
65513
65803
  });
65514
65804
  return { supported: true, readAt: now.toISOString(), tasks, alerts, consumables, power, lifts, readings, station };
65515
65805
  });
65806
+ const slow = await cached2(slowCache, "value", `${options?.baseUrl ?? ""}|${credentials.appid}|${robotSn}`, SLOW_TTL_MS, () => slowRecords(robotSn));
65807
+ return { ...slow, ...await fast };
65516
65808
  },
65517
65809
  /**
65518
65810
  * Staff Diagnose: every other read-only chapter-6 call, with the vendor's
@@ -65544,8 +65836,8 @@ function createOunitechAdapter(credentials, options) {
65544
65836
  return { count: list.length, robots: list.slice(0, 3).map((r) => ({ robotSn: r.robotSn, statusName: r.statusName })) };
65545
65837
  });
65546
65838
  await run("serial-based map list (chapter 2)", ROBOT_CLOUD_PATHS.mapList, { robotSn }, async () => {
65547
- const maps = await cloud.getMaps(robotSn);
65548
- return { count: maps.length, floors: maps.map((m) => m.floorNo) };
65839
+ const maps2 = await cloud.getMaps(robotSn);
65840
+ return { count: maps2.length, floors: maps2.map((m) => m.floorNo) };
65549
65841
  });
65550
65842
  const row = await rowFor(robotSn).catch(() => null);
65551
65843
  if (!row || !finite(row.robotId)) {
@@ -65597,6 +65889,50 @@ function createOunitechAdapter(credentials, options) {
65597
65889
  return (r.liftlist ?? []).map((l) => ({ liftno: l.liftno, liftname: l.liftname, floorno: l.floorno, onlineflag: l.onlineflag, enableflag: l.enableflag }));
65598
65890
  });
65599
65891
  }
65892
+ const maps = finite(row.sceneId) ? await floorMaps(row.sceneId).catch(() => []) : [];
65893
+ const map = ounitechCurrentMap(maps, row.taskState?.mapId, Number.isInteger(row.floorNo) ? row.floorNo : null);
65894
+ const mapId = map ? num(map.mapId) : void 0;
65895
+ if (mapId !== void 0) {
65896
+ await run("virtual walls (get-wall-list)", ROBOT_CLOUD_PATHS.wallList, { mapId }, async () => cut(await cloud.getMapShapes("walls", mapId)));
65897
+ await run("safe zones (get-safezone-list)", ROBOT_CLOUD_PATHS.safeZoneList, { mapId }, async () => cut(await cloud.getMapShapes("safe", mapId)));
65898
+ await run("slow zones (get-slowzone-list)", ROBOT_CLOUD_PATHS.slowZoneList, { mapId }, async () => cut(await cloud.getMapShapes("slow", mapId)));
65899
+ await run("map versions (get-map-version-list)", ROBOT_CLOUD_PATHS.mapVersions, { mapId }, async () => cut((await cloud.getMapVersions(mapId)).versionList ?? []));
65900
+ }
65901
+ if (finite(row.sceneId) && map?.floorNo != null) {
65902
+ const sceneId = row.sceneId;
65903
+ const floorNo2 = map.floorNo;
65904
+ await run(
65905
+ "floor map history (v2405/get-floor-maplist)",
65906
+ ROBOT_CLOUD_PATHS.floorMapHistory,
65907
+ { sceneId, floorNo: floorNo2 },
65908
+ async () => cut((await cloud.getFloorMapHistory(sceneId, floorNo2)).mapList ?? [])
65909
+ );
65910
+ }
65911
+ await run("obstacle layer (get-obstacle-layer)", ROBOT_CLOUD_PATHS.obstacleLayer, { robotId, version: 0 }, async () => {
65912
+ const o = await cloud.getObstacleLayer(robotId);
65913
+ return { version: o.version ?? null, imageChars: typeof o.image === "string" ? o.image.length : 0 };
65914
+ });
65915
+ await run("taught route (get-teach-track)", ROBOT_CLOUD_PATHS.teachTrack, { robotId, offset: window.timeFrom }, async () => {
65916
+ const r = await cloud.getTeachTrack(robotId, window.timeFrom);
65917
+ return { points: r.trackList?.length ?? 0, since: r.since ?? null, first: cut(r.trackList ?? []) };
65918
+ });
65919
+ if (finite(row.sceneId)) {
65920
+ const sceneId = row.sceneId;
65921
+ await run("robot status log, last 7 days (get-robot-log-list)", ROBOT_CLOUD_PATHS.robotLog, { robotId, sceneId, ...window }, async () => {
65922
+ const r = await cloud.getRobotLog(robotId, sceneId, window.timeFrom, window.timeTo);
65923
+ return { total: r.total ?? r.logList?.length ?? 0, latest: cut(r.logList ?? []) };
65924
+ });
65925
+ }
65926
+ await run("app action log, last 7 days (get-user-logs)", ROBOT_CLOUD_PATHS.userLog, { robotId, startTime: window.timeFrom, endTime: window.timeTo }, async () => {
65927
+ const r = await cloud.getUserLogs(robotId, window.timeFrom, window.timeTo);
65928
+ return { total: r.total ?? r.actionList?.length ?? 0, latest: cut((r.actionList ?? []).map(({ userPhone: _p, ...a }) => a)) };
65929
+ });
65930
+ await run(
65931
+ "robot settings (get-cleaning-setting)",
65932
+ ROBOT_CLOUD_PATHS.cleaningSetting,
65933
+ { robotId },
65934
+ async () => Object.fromEntries(Object.entries(await cloud.getCleaningSetting(robotId)).filter(([k]) => !SECRET_SETTING.test(k) && k !== "ok" && k !== "msg"))
65935
+ );
65600
65936
  steps.push({
65601
65937
  step: "current job (robot list taskState)",
65602
65938
  path: ROBOT_CLOUD_PATHS.homeRobotList,
@@ -107532,6 +107868,7 @@ function usePersonalEmergencyChainController() {
107532
107868
  ROBOT_TYPES,
107533
107869
  RobotCloudError,
107534
107870
  RobotCredentialsError,
107871
+ SECRET_SETTING,
107535
107872
  SELF_SERVICE_RESEND_COOLDOWN_MS,
107536
107873
  SELF_SIGNUP_PLATFORM,
107537
107874
  SELF_SIGNUP_STATUS,
@@ -107828,18 +108165,26 @@ function usePersonalEmergencyChainController() {
107828
108165
  orgRoleSets,
107829
108166
  orgSchema,
107830
108167
  ounitechAlertRecord,
108168
+ ounitechAppAction,
107831
108169
  ounitechCleaning,
107832
108170
  ounitechConsumable,
107833
108171
  ounitechCurrentMap,
107834
108172
  ounitechFault,
108173
+ ounitechFloorMapHistory,
107835
108174
  ounitechJobWords,
107836
108175
  ounitechMapShare,
108176
+ ounitechMapVersion,
107837
108177
  ounitechMapView,
108178
+ ounitechObstacleImage,
107838
108179
  ounitechPowerPlan,
107839
108180
  ounitechReadings,
107840
108181
  ounitechRobotFromHomeRow,
108182
+ ounitechSettings,
108183
+ ounitechShapes,
108184
+ ounitechStatusLog,
107841
108185
  ounitechTaskRecord,
107842
108186
  ounitechTime,
108187
+ ounitechTrack,
107843
108188
  overnight_parking_requests_namespace_collection,
107844
108189
  padbotAccountKey,
107845
108190
  padbotAlarmFault,