@7365admin1/core 3.136.2 → 3.138.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/CHANGELOG.md +12 -0
- package/dist/index.d.ts +60 -1
- package/dist/index.js +507 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +615 -152
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/e2e/harness.mjs +6 -0
- package/test/e2e/service-provider-invite-email-new-company.e2e.test.mjs +139 -0
- package/test/ounitech-api-2025-10.test.mjs +51 -8
- package/test/service-provider-invite.test.mjs +9 -0
package/dist/index.js
CHANGED
|
@@ -759,6 +759,7 @@ __export(src_exports, {
|
|
|
759
759
|
robotTypesVisibleTo: () => robotTypesVisibleTo,
|
|
760
760
|
rtspUrl: () => rtspUrl,
|
|
761
761
|
safeTopic: () => safeTopic,
|
|
762
|
+
sanitizeCompanyName: () => sanitizeCompanyName,
|
|
762
763
|
scaleFromMeasurement: () => scaleFromMeasurement,
|
|
763
764
|
schema: () => schema,
|
|
764
765
|
schemaAppSlugNotification: () => schemaAppSlugNotification,
|
|
@@ -9824,6 +9825,9 @@ function serviceProviderNatureFor(app) {
|
|
|
9824
9825
|
const nature = PROVIDER_NATURE_ALIASES[key] ?? key;
|
|
9825
9826
|
return nature !== "property_management_agency" && SERVICE_TYPE_LABELS[nature] ? nature : "";
|
|
9826
9827
|
}
|
|
9828
|
+
function sanitizeCompanyName(value) {
|
|
9829
|
+
return String(value ?? "").replace(/[\u0000-\u001f\u007f<>]/g, " ").replace(/\s+/g, " ").trim().slice(0, 120).trim();
|
|
9830
|
+
}
|
|
9827
9831
|
|
|
9828
9832
|
// src/utils/invite-actor.util.ts
|
|
9829
9833
|
var import_node_server_utils28 = require("@7365admin1/node-server-utils");
|
|
@@ -11354,7 +11358,7 @@ function robotFaultText(robotName, fault) {
|
|
|
11354
11358
|
}
|
|
11355
11359
|
return {
|
|
11356
11360
|
title: "Robot fault",
|
|
11357
|
-
body: `${robotName || "A robot"} reports a fault: ${fault.message || fault.code}.`
|
|
11361
|
+
body: `${robotName || "A robot"} reports a fault: ${String(fault.message || fault.code).replace(/\.\s*$/, "")}.`
|
|
11358
11362
|
};
|
|
11359
11363
|
}
|
|
11360
11364
|
function robotConnectionText(robotName, online, minutes) {
|
|
@@ -25555,10 +25559,36 @@ function useServiceProviderInviteService() {
|
|
|
25555
25559
|
);
|
|
25556
25560
|
}
|
|
25557
25561
|
const user = await getUserById(actor.id).catch(() => null);
|
|
25558
|
-
const
|
|
25562
|
+
const { orgId } = await createProviderCompany({
|
|
25563
|
+
invite,
|
|
25564
|
+
nature,
|
|
25565
|
+
user,
|
|
25566
|
+
companyName,
|
|
25567
|
+
country
|
|
25568
|
+
});
|
|
25569
|
+
const result = await accept({ id, userId: actor.id, org: orgId });
|
|
25570
|
+
return {
|
|
25571
|
+
...result,
|
|
25572
|
+
orgId,
|
|
25573
|
+
nature,
|
|
25574
|
+
companyName: (await getOrgById(orgId).catch(() => null))?.name ?? "",
|
|
25575
|
+
siteId: invite.metadata?.siteId?.toString?.() ?? "",
|
|
25576
|
+
siteName: String(invite.metadata?.siteName ?? "")
|
|
25577
|
+
};
|
|
25578
|
+
}
|
|
25579
|
+
async function createProviderCompany({
|
|
25580
|
+
invite,
|
|
25581
|
+
nature,
|
|
25582
|
+
user,
|
|
25583
|
+
companyName,
|
|
25584
|
+
country
|
|
25585
|
+
}) {
|
|
25586
|
+
const inviteeId = String(user?._id ?? "");
|
|
25587
|
+
const name = sanitizeCompanyName(companyName) || sanitizeCompanyName(invite.metadata?.name) || sanitizeCompanyName(user?.name);
|
|
25559
25588
|
if (!name)
|
|
25560
25589
|
throw new import_node_server_utils61.BadRequestError("Enter your company's name.");
|
|
25561
25590
|
const email = String(invite.email ?? "").trim().toLowerCase();
|
|
25591
|
+
const actor = { id: inviteeId };
|
|
25562
25592
|
const orgId = String(
|
|
25563
25593
|
await addOrg({
|
|
25564
25594
|
name,
|
|
@@ -25600,15 +25630,9 @@ function useServiceProviderInviteService() {
|
|
|
25600
25630
|
await useUserRepo().updateUserFieldById({ _id: actor.id, field: "defaultOrg", value: orgId }).catch(() => void 0);
|
|
25601
25631
|
import_node_server_utils61.logger.log({
|
|
25602
25632
|
level: "info",
|
|
25603
|
-
message: `[sp-invite] invite=${
|
|
25633
|
+
message: `[sp-invite] invite=${String(invite._id)} new company org=${orgId} nature=${nature}`
|
|
25604
25634
|
});
|
|
25605
|
-
|
|
25606
|
-
return {
|
|
25607
|
-
...result,
|
|
25608
|
-
orgId,
|
|
25609
|
-
nature,
|
|
25610
|
-
siteId: invite.metadata?.siteId?.toString?.() ?? ""
|
|
25611
|
-
};
|
|
25635
|
+
return { orgId, name };
|
|
25612
25636
|
}
|
|
25613
25637
|
async function wrongJoinContext(id, userId) {
|
|
25614
25638
|
const actor = await actorFor(userId);
|
|
@@ -25677,7 +25701,8 @@ function useServiceProviderInviteService() {
|
|
|
25677
25701
|
async function repairWrongJoin({ id, userId }) {
|
|
25678
25702
|
const { actor, invite, senderOrg, user } = await wrongJoinContext(id, userId);
|
|
25679
25703
|
const rows = await wrongJoinRows(invite, senderOrg, user);
|
|
25680
|
-
|
|
25704
|
+
const defaultOrgIsSender = !!user?._id && String(user?.defaultOrg ?? "") === senderOrg;
|
|
25705
|
+
if (!rows.length && !defaultOrgIsSender) {
|
|
25681
25706
|
return {
|
|
25682
25707
|
message: "Nothing to repair: this account is not in the sender's organisation.",
|
|
25683
25708
|
changed: 0
|
|
@@ -25686,12 +25711,15 @@ function useServiceProviderInviteService() {
|
|
|
25686
25711
|
const { updateMemberStatus } = useMemberRepo();
|
|
25687
25712
|
for (const row of rows)
|
|
25688
25713
|
await updateMemberStatus(row._id, { status: "inactive" });
|
|
25714
|
+
if (defaultOrgIsSender)
|
|
25715
|
+
await setDefaultOrg(String(user._id), "");
|
|
25689
25716
|
await _transition({
|
|
25690
25717
|
id,
|
|
25691
25718
|
from: [],
|
|
25692
25719
|
event: {
|
|
25693
25720
|
...event("wrong-join-repaired", actor),
|
|
25694
|
-
memberIds: rows.map((row) => row._id)
|
|
25721
|
+
memberIds: rows.map((row) => row._id),
|
|
25722
|
+
...defaultOrgIsSender ? { defaultOrgWas: senderOrg } : {}
|
|
25695
25723
|
}
|
|
25696
25724
|
});
|
|
25697
25725
|
import_node_server_utils61.logger.log({
|
|
@@ -25712,12 +25740,170 @@ function useServiceProviderInviteService() {
|
|
|
25712
25740
|
const ids2 = (repaired.memberIds ?? []).filter((m) => import_mongodb56.ObjectId.isValid(m));
|
|
25713
25741
|
for (const m of ids2)
|
|
25714
25742
|
await updateMemberStatus(m, { status: "active" }).catch(() => 0);
|
|
25743
|
+
const { user } = await wrongJoinContext(id, userId);
|
|
25744
|
+
if (repaired.defaultOrgWas && user?._id && !String(user.defaultOrg ?? "")) {
|
|
25745
|
+
await setDefaultOrg(String(user._id), String(repaired.defaultOrgWas));
|
|
25746
|
+
}
|
|
25715
25747
|
await _transition({ id, from: [], event: event("wrong-join-undone", actor) });
|
|
25716
25748
|
return {
|
|
25717
25749
|
message: `Undone: ${ids2.length} membership${ids2.length === 1 ? "" : "s"} active again.`,
|
|
25718
25750
|
changed: ids2.length
|
|
25719
25751
|
};
|
|
25720
25752
|
}
|
|
25753
|
+
async function setDefaultOrg(userId, orgId) {
|
|
25754
|
+
if (!orgId) {
|
|
25755
|
+
await import_node_server_utils61.useAtlas.getDb()?.collection("users").updateOne(
|
|
25756
|
+
{ _id: new import_mongodb56.ObjectId(userId) },
|
|
25757
|
+
{ $unset: { defaultOrg: "" }, $set: { updatedAt: (/* @__PURE__ */ new Date()).toISOString() } }
|
|
25758
|
+
);
|
|
25759
|
+
await (0, import_node_server_utils61.useCache)("users").delNamespace().catch(() => void 0);
|
|
25760
|
+
return;
|
|
25761
|
+
}
|
|
25762
|
+
await useUserRepo().updateUserFieldById({ _id: userId, field: "defaultOrg", value: orgId }).catch(() => void 0);
|
|
25763
|
+
}
|
|
25764
|
+
async function finishSetupContext(id, userId) {
|
|
25765
|
+
const ctx = await wrongJoinContext(id, userId);
|
|
25766
|
+
const { invite, user, senderOrg } = ctx;
|
|
25767
|
+
const nature = serviceProviderNatureFor(invite.metadata?.app);
|
|
25768
|
+
const existing = user?._id && nature ? await getByCreatorAndNature(String(user._id), nature).catch(() => null) : null;
|
|
25769
|
+
const stillInSender = await wrongJoinRows(invite, senderOrg, user);
|
|
25770
|
+
let blocked = "";
|
|
25771
|
+
if (!user?._id) {
|
|
25772
|
+
blocked = "This email has no account yet. The invitee signs up from the invitation email.";
|
|
25773
|
+
} else if (!nature) {
|
|
25774
|
+
blocked = "This invitation does not say which service the company provides.";
|
|
25775
|
+
} else if (invite.deletedAt) {
|
|
25776
|
+
blocked = "This invitation was deleted by the sender.";
|
|
25777
|
+
} else if (invite.status === "awaiting-approval" /* AWAITING_APPROVAL */) {
|
|
25778
|
+
blocked = "Approve the invitation first.";
|
|
25779
|
+
} else if (invite.status !== "pending" /* PENDING */) {
|
|
25780
|
+
blocked = `This invitation is ${serviceProviderInviteLabel(invite.status)}, not waiting for an answer.`;
|
|
25781
|
+
} else if (isServiceProviderInviteExpired(invite)) {
|
|
25782
|
+
blocked = "This invitation has expired. Press Resend email first.";
|
|
25783
|
+
} else if (stillInSender.length) {
|
|
25784
|
+
blocked = "The account is still inside the sender's organisation. Press Check account, then Repair, first.";
|
|
25785
|
+
}
|
|
25786
|
+
return { ...ctx, nature, existing, blocked };
|
|
25787
|
+
}
|
|
25788
|
+
function lastFinishSetup(invite) {
|
|
25789
|
+
const history = invite.history ?? [];
|
|
25790
|
+
const last = [...history].reverse().find((e) => ["finish-setup-applied", "finish-setup-undone"].includes(e?.action));
|
|
25791
|
+
return last?.action === "finish-setup-applied" ? last : null;
|
|
25792
|
+
}
|
|
25793
|
+
async function finishSetupPreview({
|
|
25794
|
+
id,
|
|
25795
|
+
userId,
|
|
25796
|
+
companyName
|
|
25797
|
+
}) {
|
|
25798
|
+
const { invite, user, senderOrg, nature, existing, blocked } = await finishSetupContext(id, userId);
|
|
25799
|
+
const [sender, currentDefault] = await Promise.all([
|
|
25800
|
+
getOrgById(senderOrg).catch(() => null),
|
|
25801
|
+
user?.defaultOrg ? getOrgById(String(user.defaultOrg)).catch(() => null) : null
|
|
25802
|
+
]);
|
|
25803
|
+
const applied = lastFinishSetup(invite);
|
|
25804
|
+
return {
|
|
25805
|
+
invitationId: id,
|
|
25806
|
+
email: String(invite.email ?? ""),
|
|
25807
|
+
account: user?._id ? String(user.name || user.email || "") : "",
|
|
25808
|
+
service: nature,
|
|
25809
|
+
serviceLabel: nature ? serviceTypeLabel(nature) : "",
|
|
25810
|
+
senderOrgName: String(sender?.name ?? ""),
|
|
25811
|
+
siteName: String(invite.metadata?.siteName ?? ""),
|
|
25812
|
+
companyName: existing ? String(existing.name ?? "") : sanitizeCompanyName(companyName) || sanitizeCompanyName(invite.metadata?.name) || sanitizeCompanyName(user?.name),
|
|
25813
|
+
usesExistingCompany: !!existing,
|
|
25814
|
+
currentDefaultOrgName: String(currentDefault?.name ?? ""),
|
|
25815
|
+
status: String(invite.status ?? ""),
|
|
25816
|
+
canApply: !blocked,
|
|
25817
|
+
blocked,
|
|
25818
|
+
applied: applied ? { at: applied.at, by: applied.byName, orgId: applied.orgId, companyName: applied.companyName } : null
|
|
25819
|
+
};
|
|
25820
|
+
}
|
|
25821
|
+
async function finishSetupApply({
|
|
25822
|
+
id,
|
|
25823
|
+
userId,
|
|
25824
|
+
companyName,
|
|
25825
|
+
country
|
|
25826
|
+
}) {
|
|
25827
|
+
const { actor, invite, user, nature, existing, blocked } = await finishSetupContext(id, userId);
|
|
25828
|
+
if (blocked)
|
|
25829
|
+
throw new import_node_server_utils61.BadRequestError(blocked);
|
|
25830
|
+
const defaultOrgWas = String(user.defaultOrg ?? "");
|
|
25831
|
+
let orgId = existing ? String(existing._id) : "";
|
|
25832
|
+
let name = existing ? String(existing.name ?? "") : "";
|
|
25833
|
+
if (!orgId) {
|
|
25834
|
+
({ orgId, name } = await createProviderCompany({ invite, nature, user, companyName, country }));
|
|
25835
|
+
} else {
|
|
25836
|
+
await setDefaultOrg(String(user._id), orgId);
|
|
25837
|
+
}
|
|
25838
|
+
const result = await useCustomerSiteService().addViaInvite(
|
|
25839
|
+
id,
|
|
25840
|
+
{ id: actor.id, name: actor.name },
|
|
25841
|
+
orgId
|
|
25842
|
+
);
|
|
25843
|
+
await _transition({
|
|
25844
|
+
id,
|
|
25845
|
+
from: [],
|
|
25846
|
+
event: {
|
|
25847
|
+
...event("finish-setup-applied", actor),
|
|
25848
|
+
orgId,
|
|
25849
|
+
companyName: name,
|
|
25850
|
+
created: !existing,
|
|
25851
|
+
defaultOrgWas
|
|
25852
|
+
}
|
|
25853
|
+
});
|
|
25854
|
+
if (result.accepted)
|
|
25855
|
+
await notifyInviter(invite, true, name);
|
|
25856
|
+
const siteName = String(invite.metadata?.siteName ?? "");
|
|
25857
|
+
import_node_server_utils61.logger.log({
|
|
25858
|
+
level: "info",
|
|
25859
|
+
message: `[sp-invite] finish-setup invite=${id} org=${orgId} created=${!existing} by=${actor.id}`
|
|
25860
|
+
});
|
|
25861
|
+
return {
|
|
25862
|
+
message: `${name} is set up and linked to ${siteName || "the site"}.`,
|
|
25863
|
+
orgId,
|
|
25864
|
+
companyName: name,
|
|
25865
|
+
nature,
|
|
25866
|
+
created: !existing
|
|
25867
|
+
};
|
|
25868
|
+
}
|
|
25869
|
+
async function finishSetupUndo({ id, userId }) {
|
|
25870
|
+
const { actor, invite, user } = await wrongJoinContext(id, userId);
|
|
25871
|
+
const applied = lastFinishSetup(invite);
|
|
25872
|
+
if (!applied)
|
|
25873
|
+
throw new import_node_server_utils61.BadRequestError("There is no Finish setup to undo on this invitation.");
|
|
25874
|
+
const orgId = String(applied.orgId ?? "");
|
|
25875
|
+
const siteId = invite.metadata?.siteId?.toString?.() ?? "";
|
|
25876
|
+
const db2 = import_node_server_utils61.useAtlas.getDb();
|
|
25877
|
+
if (!db2 || !import_mongodb56.ObjectId.isValid(orgId))
|
|
25878
|
+
throw new import_node_server_utils61.BadRequestError("Nothing to undo.");
|
|
25879
|
+
if (import_mongodb56.ObjectId.isValid(siteId)) {
|
|
25880
|
+
await db2.collection("customer.sites").updateMany(
|
|
25881
|
+
{ org: new import_mongodb56.ObjectId(orgId), site: new import_mongodb56.ObjectId(siteId), status: "active" },
|
|
25882
|
+
{ $set: { status: "inactive", updatedAt: (/* @__PURE__ */ new Date()).toISOString() } }
|
|
25883
|
+
);
|
|
25884
|
+
await (0, import_node_server_utils61.useCache)("customer.sites").delNamespace().catch(() => void 0);
|
|
25885
|
+
}
|
|
25886
|
+
if (applied.created) {
|
|
25887
|
+
const { updateMemberStatus } = useMemberRepo();
|
|
25888
|
+
const members = await db2.collection("members").find({ org: { $in: [new import_mongodb56.ObjectId(orgId), orgId] }, status: "active" }).toArray();
|
|
25889
|
+
for (const m of members) {
|
|
25890
|
+
await updateMemberStatus(m._id.toString(), { status: "inactive" });
|
|
25891
|
+
}
|
|
25892
|
+
await useOrgRepo().updateStatusById(orgId, "deleted").catch(() => void 0);
|
|
25893
|
+
}
|
|
25894
|
+
if (user?._id && String(user.defaultOrg ?? "") === orgId) {
|
|
25895
|
+
await setDefaultOrg(String(user._id), String(applied.defaultOrgWas ?? ""));
|
|
25896
|
+
}
|
|
25897
|
+
await _transition({
|
|
25898
|
+
id,
|
|
25899
|
+
from: ["complete" /* COMPLETE */],
|
|
25900
|
+
to: "pending" /* PENDING */,
|
|
25901
|
+
event: event("finish-setup-undone", actor)
|
|
25902
|
+
});
|
|
25903
|
+
return {
|
|
25904
|
+
message: "Undone: the site is off the company's list and the invitation is open again."
|
|
25905
|
+
};
|
|
25906
|
+
}
|
|
25721
25907
|
async function notifyInviter(invite, accepted, providerName) {
|
|
25722
25908
|
try {
|
|
25723
25909
|
const to = invite.metadata?.invitedBy?.toString?.() ?? "";
|
|
@@ -25931,6 +26117,9 @@ function useServiceProviderInviteService() {
|
|
|
25931
26117
|
wrongJoinPreview,
|
|
25932
26118
|
repairWrongJoin,
|
|
25933
26119
|
undoWrongJoin,
|
|
26120
|
+
finishSetupPreview,
|
|
26121
|
+
finishSetupApply,
|
|
26122
|
+
finishSetupUndo,
|
|
25934
26123
|
linkSite,
|
|
25935
26124
|
listReceived,
|
|
25936
26125
|
getForProvider,
|
|
@@ -64524,7 +64713,7 @@ function robotManifest(brand, reportedModel) {
|
|
|
64524
64713
|
const b = robotBrandFor(brand);
|
|
64525
64714
|
const reported = typeof reportedModel === "string" ? reportedModel.trim().slice(0, 60) : "";
|
|
64526
64715
|
if (!b)
|
|
64527
|
-
return { capabilities: {}, profile: { model: reported, modelKnown: false, alarms: [], makerApp: "", makerAppOnly: [] } };
|
|
64716
|
+
return { capabilities: {}, profile: { model: reported, modelKnown: false, alarms: [], makerApp: "", makerAppOnly: [], clearFault: [] } };
|
|
64528
64717
|
const prefix = modelKey(b.brand);
|
|
64529
64718
|
let key = modelKey(reported);
|
|
64530
64719
|
if (prefix && key.startsWith(prefix) && key !== prefix)
|
|
@@ -64537,7 +64726,8 @@ function robotManifest(brand, reportedModel) {
|
|
|
64537
64726
|
modelKnown: Boolean(m),
|
|
64538
64727
|
alarms: [...m?.alarms ?? b.info?.alarms ?? []],
|
|
64539
64728
|
makerApp: m?.makerApp ?? b.info?.makerApp ?? "",
|
|
64540
|
-
makerAppOnly: [...m?.makerAppOnly ?? b.info?.makerAppOnly ?? []]
|
|
64729
|
+
makerAppOnly: [...m?.makerAppOnly ?? b.info?.makerAppOnly ?? []],
|
|
64730
|
+
clearFault: [...m?.clearFault ?? b.info?.clearFault ?? []]
|
|
64541
64731
|
}
|
|
64542
64732
|
};
|
|
64543
64733
|
}
|
|
@@ -65370,6 +65560,246 @@ function useRobotCloudService(credentials, options = {}) {
|
|
|
65370
65560
|
// src/services/ounitech.adapter.ts
|
|
65371
65561
|
var import_axios4 = __toESM(require("axios"));
|
|
65372
65562
|
var import_node_server_utils164 = require("@7365admin1/node-server-utils");
|
|
65563
|
+
|
|
65564
|
+
// src/utils/ounitech-codes.util.ts
|
|
65565
|
+
var CLEAR = "Clear the fault (press and release the emergency stop)";
|
|
65566
|
+
var SUPPORT = "If it keeps happening, call Ounitech support";
|
|
65567
|
+
var DOCK = "Push it onto its charging dock so it can find its position again";
|
|
65568
|
+
var WAIT = "Wait until it is ready, then try again";
|
|
65569
|
+
var WHEEL = "Clear anything caught in the wheel and check the floor for holes";
|
|
65570
|
+
var NONE = "Normal when the emergency stop is pressed or released - no action needed";
|
|
65571
|
+
var T = {
|
|
65572
|
+
"100": ["Charging dock is over-heating", "Unplug the dock and let it cool down"],
|
|
65573
|
+
"101": ["Charging dock's clean-water tank is empty", "Fill the dock's clean-water tank"],
|
|
65574
|
+
"102": ["Charging dock's waste-water tank is full", "Drain the dock (unlock the dock, long-press Drain)"],
|
|
65575
|
+
"103": ["Charging dock valve or pump fault", `Check the pump connections. ${SUPPORT}`],
|
|
65576
|
+
"104": ["Charging dock valve or pump fault", `Check the pump connections. ${SUPPORT}`],
|
|
65577
|
+
"105": ["Charging dock valve or pump fault", `Check the pump connections. ${SUPPORT}`],
|
|
65578
|
+
"106": ["Charging dock valve or pump fault", `Check the pump connections. ${SUPPORT}`],
|
|
65579
|
+
"107": ["Charging dock valve or pump fault", `Check the pump connections. ${SUPPORT}`],
|
|
65580
|
+
"200": ["Battery not responding (flat for a long time)", "Charge it on the dock, or take the battery out and put it back"],
|
|
65581
|
+
"204": ["Wheel driver board offline", `Check the wheel wiring. ${SUPPORT}`],
|
|
65582
|
+
"205": ["Side-brush driver offline (side brush won't turn)", "Check the side-brush motor wire"],
|
|
65583
|
+
"206": ["Button panel offline (handle lights off)", `Check the handle's cable contact. ${SUPPORT}`],
|
|
65584
|
+
"207": ["Vacuum fan stalled", "Clean the vacuum pipe, HEPA filter, dust bag and tray"],
|
|
65585
|
+
"209": ["Vacuum fan fault", `${CLEAR}. ${SUPPORT}`],
|
|
65586
|
+
"210": ["Roller mop motor fault", "Take out the roller and clear anything tangled in it"],
|
|
65587
|
+
"211": ["Roller brush motor fault", "Take out the roller and clear anything tangled in it"],
|
|
65588
|
+
"212": ["Left side-brush motor fault", "Clear anything tangled in the side brush"],
|
|
65589
|
+
"213": ["Right side-brush motor fault", "Clear anything tangled in the side brush"],
|
|
65590
|
+
"214": ["Clean-water pump fault", `Re-plug the pump board. ${SUPPORT}`],
|
|
65591
|
+
"218": ["Waste-water pump fault", `Re-plug the pump board. ${SUPPORT}`],
|
|
65592
|
+
"219": ["Mop lift motor fault", `Restart the robot (power switch at the back). ${SUPPORT}`],
|
|
65593
|
+
"220": ["Brush lift motor fault", `Restart the robot (power switch at the back). ${SUPPORT}`],
|
|
65594
|
+
"221": ["Wheel fault", WHEEL],
|
|
65595
|
+
"222": ["Wheel fault", WHEEL],
|
|
65596
|
+
"223": ["Wheel stalled", WHEEL],
|
|
65597
|
+
"224": ["Wheel stalled", WHEEL],
|
|
65598
|
+
"228": ["Cooling fan fault", "Call Ounitech support"],
|
|
65599
|
+
"233": ["Battery short circuit", "Stop using the robot and call Ounitech support to replace the battery"],
|
|
65600
|
+
"234": ["Battery over-charged", "Take it off charge and call Ounitech support"],
|
|
65601
|
+
"245": ["Water-suction fan stalled", "Unclog the suction pipe and replace the filter"],
|
|
65602
|
+
"248": ["Squeegee motor fault", `Check the squeegee cable and motor. ${SUPPORT}`],
|
|
65603
|
+
"250": ["Battery full"],
|
|
65604
|
+
"251": ["Battery low"],
|
|
65605
|
+
"252": ["Battery too low to charge", "Call Ounitech support"],
|
|
65606
|
+
"255": ["Battery charging current high"],
|
|
65607
|
+
"256": ["Battery over-charged", "Take it off charge and call Ounitech support"],
|
|
65608
|
+
"257": ["Battery over 70 \xB0C", "Stop charging and let it cool down"],
|
|
65609
|
+
"302": ["Robot can't load its map", "Call Ounitech support"],
|
|
65610
|
+
"303": ["The map's area layout is damaged", "Re-edit the areas in the Ounitech app and save"],
|
|
65611
|
+
"304": ["The area layout doesn't match the map", "In the Ounitech app, switch to another floor map and back"],
|
|
65612
|
+
"305": ["Lost contact with its motion controller", "Restart the robot (power switch at the back)"],
|
|
65613
|
+
"306": ["Robot couldn't receive the chosen area", `${CLEAR}, then send the task again`],
|
|
65614
|
+
"307": ["Robot is finding its position again", "If the site has changed, push it onto its charging dock"],
|
|
65615
|
+
"308": ["Robot couldn't find its position", DOCK],
|
|
65616
|
+
"310": ["No signal from the charging dock", `Check the dock is powered. ${SUPPORT}`],
|
|
65617
|
+
"312": ["Couldn't dock", "Check the dock hasn't been moved"],
|
|
65618
|
+
"314": ["Couldn't dock - trying again", "Check the dock hasn't been moved"],
|
|
65619
|
+
"315": ["Couldn't dock - trying again", "Check the dock hasn't been moved"],
|
|
65620
|
+
"316": ["Lost the wireless link to its charging dock", "Check the dock is powered"],
|
|
65621
|
+
"319": ["Robot was moved while charging", "Press the emergency stop and push it back onto the dock"],
|
|
65622
|
+
"401": ["Battery below 10% - going to charge"],
|
|
65623
|
+
"403": ["Robot has left its charging dock"],
|
|
65624
|
+
"404": ["Charging dock reports a fault", "See the dock's other alerts"],
|
|
65625
|
+
"405": ["Couldn't call the lift"],
|
|
65626
|
+
"407": ["Lift is full"],
|
|
65627
|
+
"409": ["Couldn't switch map", "Call Ounitech support"],
|
|
65628
|
+
"410": ["Internal communication problem", SUPPORT],
|
|
65629
|
+
"415": ["Gyro data fault", `Check the cables. ${SUPPORT}`],
|
|
65630
|
+
"416": ["Wheel odometer data fault", `Check the cables. ${SUPPORT}`],
|
|
65631
|
+
"417": ["Laser scanner data fault", `Check the cables. ${SUPPORT}`],
|
|
65632
|
+
"418": ["Couldn't dock - trying again", "Check the dock hasn't been moved"],
|
|
65633
|
+
"419": ["Couldn't dock - trying again", "Check the dock hasn't been moved"],
|
|
65634
|
+
"420": ["Docking failed several times", "Check the dock's position and the robot's position"],
|
|
65635
|
+
"423": ["Dust box blocked", "Empty the dust box and clear the pipe"],
|
|
65636
|
+
"431": ["Robot's waste-water tank is high - returning to empty"],
|
|
65637
|
+
"432": ["Robot's clean water is low - returning to refill"],
|
|
65638
|
+
"433": ["Robot over-heating while charging", "Let it cool down"],
|
|
65639
|
+
"443": ["No charging current", "Seat the battery and clean the charging contacts"],
|
|
65640
|
+
"445": ["Area laser fault", "Call Ounitech support"],
|
|
65641
|
+
"446": ["Area laser board offline", "Call Ounitech support"],
|
|
65642
|
+
"448": ["Wheel drawing too much current", WHEEL],
|
|
65643
|
+
"449": ["Wheel over-current fault", `${WHEEL}, then clear the fault (press and release the emergency stop)`],
|
|
65644
|
+
"450": ["Wheels slipping", "Check the wheels"],
|
|
65645
|
+
"453": ["Robot tried to leave a no-go area", DOCK],
|
|
65646
|
+
"454": ["Robot hasn't moved for a long time", "Clear its path and wipe its sensors"],
|
|
65647
|
+
"457": ["Left bumper hit something", "Tap the bumper if it stays stuck"],
|
|
65648
|
+
"458": ["Right bumper hit something", "Tap the bumper if it stays stuck"],
|
|
65649
|
+
"459": ["Water suction blocked", "Clean the suction pipe and filters"],
|
|
65650
|
+
"460": ["Front camera fault", `Check the camera cable. ${SUPPORT}`],
|
|
65651
|
+
"462": ["Left camera fault", `Check the camera cable. ${SUPPORT}`],
|
|
65652
|
+
"464": ["Right camera fault", `Check the camera cable. ${SUPPORT}`],
|
|
65653
|
+
"469": ["The map's floor doesn't match the floor it senses", "Check it is on the right floor; the air-pressure sensor may need recalibrating"],
|
|
65654
|
+
"470": ["Gyro offline", "Call Ounitech support"],
|
|
65655
|
+
"471": ["Roller motor needs servicing", "Book a service"],
|
|
65656
|
+
"472": ["Fan needs servicing", "Book a service"],
|
|
65657
|
+
"500": ["Scheduled job skipped: floor can't be reached", "Cross-floor jobs need lift control"],
|
|
65658
|
+
"501": ["Scheduled job skipped: robot not ready to clean", "Check battery, water, tanks and dust box"],
|
|
65659
|
+
"502": ["Waited over 20 minutes for the lift - going to charge"],
|
|
65660
|
+
"503": ["Scheduled job done - going to charge"],
|
|
65661
|
+
"504": ["Job done - going to charge"],
|
|
65662
|
+
"505": ["Job cancelled - going to charge"],
|
|
65663
|
+
"506": ["Couldn't leave the charging dock", "Check the wheels"],
|
|
65664
|
+
"507": ["Clean water low - returning to refill, then continuing"],
|
|
65665
|
+
"508": ["Waste water high - returning to empty, then continuing"],
|
|
65666
|
+
"509": ["Dust box missing", "Fit the dust box"],
|
|
65667
|
+
"510": ["Clean-water tank missing", "Fit the clean-water tank"],
|
|
65668
|
+
"511": ["Waste-water tank missing", "Fit the waste-water tank"],
|
|
65669
|
+
"512": ["Tank cover missing", "Fit the tank cover"],
|
|
65670
|
+
"513": ["Top cover open", "Close the top cover"],
|
|
65671
|
+
"514": ["Handle is up (manual mode) - the app can't start a job", "Lower the handle"],
|
|
65672
|
+
"515": ["Battery below 10% - can't clean", "Let it charge"],
|
|
65673
|
+
"517": ["Robot is starting up - not ready yet", WAIT],
|
|
65674
|
+
"518": ["Robot is shutting down"],
|
|
65675
|
+
"519": ["Robot is resuming - not ready yet", WAIT],
|
|
65676
|
+
"520": ["Robot is finding its position - not ready yet", WAIT],
|
|
65677
|
+
"521": ["Robot is downloading its map - not ready yet", WAIT],
|
|
65678
|
+
"522": ["Couldn't resume: clean water below half", "Wait for it to refill"],
|
|
65679
|
+
"523": ["Couldn't resume: waste water too high", "Wait for it to empty"],
|
|
65680
|
+
"524": ["Charging dock doesn't match this robot", "Re-scan the dock's QR code in the Ounitech app"],
|
|
65681
|
+
"525": ["Scheduled time ended before the job finished", "Give the schedule a longer time window"],
|
|
65682
|
+
"527": ["Software updating", "Don't use the robot until it finishes"],
|
|
65683
|
+
"528": ["Software updating", "Don't use the robot until it finishes"],
|
|
65684
|
+
"529": ["Software updating", "Don't use the robot until it finishes"],
|
|
65685
|
+
"530": ["Software updating", "Don't use the robot until it finishes"],
|
|
65686
|
+
"531": ["Manual mode - the app's command was refused", "Lower the handle"],
|
|
65687
|
+
"532": ["Finding its starting position"],
|
|
65688
|
+
"534": ["Finding its starting position after a map switch"],
|
|
65689
|
+
"535": ["Areas updated"],
|
|
65690
|
+
"536": ["Virtual walls updated"],
|
|
65691
|
+
"537": ["Safe zones updated"],
|
|
65692
|
+
"538": ["Map saved"],
|
|
65693
|
+
"539": ["Couldn't save the map", "Save it again"],
|
|
65694
|
+
"540": ["Cliff sensor switched on"],
|
|
65695
|
+
"541": ["Cliff sensor switched off"],
|
|
65696
|
+
"542": ["Stopped at a drop (cliff sensor)", `Pull it back by the handle, ${CLEAR.charAt(0).toLowerCase()}${CLEAR.slice(1)}, and add a virtual wall there`],
|
|
65697
|
+
"543": ["Slowing down near a drop (cliff sensor)", "Add a virtual wall there if it keeps happening"],
|
|
65698
|
+
"544": ["Scheduled shutdown soon - returning to the dock"],
|
|
65699
|
+
"545": ["Edge cleaning started"],
|
|
65700
|
+
"546": ["Edge cleaning finished"],
|
|
65701
|
+
"549": ["Asking to switch map"],
|
|
65702
|
+
"552": ["No floor switch needed"],
|
|
65703
|
+
"553": ["Area can't be reached - job ended", "In the Ounitech app, switch map and back, or re-save an area"],
|
|
65704
|
+
"554": ["Sensor calibration"],
|
|
65705
|
+
"555": ["Sensor calibration - clear obstacles around the robot"],
|
|
65706
|
+
"556": ["Sensor calibration - clear obstacles around the robot"],
|
|
65707
|
+
"557": ["Slow zones updated"],
|
|
65708
|
+
"560": ["Fan stalled - returning to the dock", "Press and release the emergency stop, then check the fan"],
|
|
65709
|
+
"561": ["Couldn't reconnect to the charging dock", "Check the dock"],
|
|
65710
|
+
"562": ["Scheduled shutdown soon - no new jobs"],
|
|
65711
|
+
"563": ["Robot is in a fault state", "Find the cause, clear it (press and release the emergency stop), and check the robot"],
|
|
65712
|
+
"564": ["Fault cleared - robot on standby"],
|
|
65713
|
+
"565": ["Handling waste water"],
|
|
65714
|
+
"566": ["Waste water handled"],
|
|
65715
|
+
"567": ["Fan stalled", "Clear the fan, then resume"],
|
|
65716
|
+
"568": ["No charging dock on this floor", "Set the dock's floor in the Ounitech app"],
|
|
65717
|
+
"570": ["Finding its starting position - can't start yet", WAIT],
|
|
65718
|
+
"571": ["Stopped too long - side brush switched off", "Pause, then resume"],
|
|
65719
|
+
"572": ["Lift ride took too long - job cancelled", "Push it back onto its charging dock"],
|
|
65720
|
+
"573": ["Camera saw a drop", "Add a virtual wall 1 m from the drop"],
|
|
65721
|
+
"574": ["Map updating", WAIT],
|
|
65722
|
+
"575": ["Air-pressure sensor reading invalid", "Recalibrate the air-pressure sensor"],
|
|
65723
|
+
"576": ["Air-pressure sensor calibration"],
|
|
65724
|
+
"577": ["Air-pressure sensor calibration"],
|
|
65725
|
+
"578": ["Air-pressure sensor calibration"],
|
|
65726
|
+
"581": ["Taught route started"],
|
|
65727
|
+
"582": ["Taught route ended"],
|
|
65728
|
+
"583": ["Taught route cleared"],
|
|
65729
|
+
"585": ["An area's positioning mode changed"],
|
|
65730
|
+
"587": ["Battery discharging while on charge", "Seat the battery and clean the charging contacts"],
|
|
65731
|
+
"588": ["Switched off with the power button"],
|
|
65732
|
+
"589": ["Switched off by the timed power schedule"],
|
|
65733
|
+
"590": ["Switched on"],
|
|
65734
|
+
"591": ["Switched on with the power button"],
|
|
65735
|
+
"592": ["Switched on by the timed power schedule"],
|
|
65736
|
+
"593": ["Still finding its position - can't take commands yet", WAIT],
|
|
65737
|
+
"600": ["Left wheel driver offline", NONE],
|
|
65738
|
+
"601": ["Left wheel driver reset", NONE],
|
|
65739
|
+
"603": ["Left wheel drawing too much current", WHEEL],
|
|
65740
|
+
"604": ["Left wheel drawing too much current", WHEEL],
|
|
65741
|
+
"605": ["Left wheel stalled", WHEEL],
|
|
65742
|
+
"620": ["Right wheel driver offline", NONE],
|
|
65743
|
+
"621": ["Right wheel driver reset", NONE],
|
|
65744
|
+
"622": ["Right wheel driver error", "Call Ounitech support"],
|
|
65745
|
+
"623": ["Right wheel drawing too much current", WHEEL],
|
|
65746
|
+
"624": ["Right wheel drawing too much current", WHEEL],
|
|
65747
|
+
"625": ["Right wheel stalled", WHEEL],
|
|
65748
|
+
"640": ["A part is overdue for replacement - won't clean", "Replace the part"],
|
|
65749
|
+
"641": ["A running job is blocking the schedule", "Cancel the job or wait"],
|
|
65750
|
+
"642": ["Can't find its position while charging", "Put it on standby first"],
|
|
65751
|
+
"643": ["Can't reset the dock while charging", "Put it on standby first"],
|
|
65752
|
+
"644": ["Can't set its starting position while charging", "Put it on standby first"],
|
|
65753
|
+
"700": ["Vacuum button pressed"],
|
|
65754
|
+
"701": ["Standard button pressed"],
|
|
65755
|
+
"702": ["Dust-mop button pressed"],
|
|
65756
|
+
"703": ["Cancel button pressed"],
|
|
65757
|
+
"704": ["Strong button pressed"],
|
|
65758
|
+
"705": ["Mop button pressed"],
|
|
65759
|
+
"706": ["Pause button pressed"],
|
|
65760
|
+
"707": ["Recall button pressed"],
|
|
65761
|
+
"708": ["Pause button held"],
|
|
65762
|
+
"709": ["Pause button held"],
|
|
65763
|
+
"710": ["Wheel drawing too much current", WHEEL],
|
|
65764
|
+
"711": ["Wheel over-heating", "Let it cool and check the wheel"],
|
|
65765
|
+
"712": ["Wheel kept drawing too much current", `${WHEEL}, then clear the fault (press and release the emergency stop)`],
|
|
65766
|
+
"713": ["Switching lift - timed power paused"],
|
|
65767
|
+
"731": ["Robot locked by the dealer", "Ask the dealer"],
|
|
65768
|
+
"732": ["Voice file updated"],
|
|
65769
|
+
"733": ["Switched off from the app"],
|
|
65770
|
+
"737": ["Laser scanner motion-sensor fault", "Call Ounitech support"],
|
|
65771
|
+
"738": ["Can't start: clean water low", "Fill the clean-water tank"],
|
|
65772
|
+
"739": ["Can't start: waste water high", "Empty the waste-water tank"],
|
|
65773
|
+
"760": ["Map file missing", "Call Ounitech support"],
|
|
65774
|
+
"761": ["Map file damaged", "Call Ounitech support"],
|
|
65775
|
+
"762": ["Started from the wrong place (it was pushed)", "Push it back onto its charging dock"],
|
|
65776
|
+
"775": ["Moved by a person during a lift ride", "Cancel the job and push it back onto its charging dock"],
|
|
65777
|
+
"776": ["Boxed in", `Clear its path, wipe its sensors. ${DOCK}`],
|
|
65778
|
+
"777": ["Can't reach its target", `Clear its path, wipe its sensors. ${DOCK}`],
|
|
65779
|
+
"778": ["Couldn't dock - recovering by itself", "Check the dock's sensor and that the dock hasn't moved"],
|
|
65780
|
+
"779": ["Robot settings changed"],
|
|
65781
|
+
"780": ["Top cover open", "Close the top cover"],
|
|
65782
|
+
"781": ["Dust box not fitted", "Fit the dust box"],
|
|
65783
|
+
"782": ["Waste-water tank not fitted", "Fit the waste-water tank"],
|
|
65784
|
+
"785": ["Position ready"]
|
|
65785
|
+
};
|
|
65786
|
+
var _a;
|
|
65787
|
+
for (const c of [...range(740, 759), 763, 764, 783, 784])
|
|
65788
|
+
T[_a = String(c)] ?? (T[_a] = ["Sensor or camera calibration", "Recalibrate if it failed"]);
|
|
65789
|
+
var _a2;
|
|
65790
|
+
for (const c of range(765, 773))
|
|
65791
|
+
T[_a2 = String(c)] ?? (T[_a2] = ["QR code return-point / position scan", "Rescan if it failed"]);
|
|
65792
|
+
T["774"] ?? (T["774"] = ["Taught route saved"]);
|
|
65793
|
+
function range(a, b) {
|
|
65794
|
+
return Array.from({ length: b - a + 1 }, (_, i) => a + i);
|
|
65795
|
+
}
|
|
65796
|
+
var OUNITECH_CODES = T;
|
|
65797
|
+
function ounitechCodeWords(code) {
|
|
65798
|
+
const c = OUNITECH_CODES[code];
|
|
65799
|
+
return c ? c[1] ? `${c[0]}. ${c[1]}.` : c[0] : null;
|
|
65800
|
+
}
|
|
65801
|
+
|
|
65802
|
+
// src/services/ounitech.adapter.ts
|
|
65373
65803
|
var OUNITECH_BRAND = "ounitech";
|
|
65374
65804
|
var OUNITECH_CAPABILITIES = {
|
|
65375
65805
|
// Pose in the chapter-6 robot list (metres) and the tracking stream.
|
|
@@ -65398,8 +65828,9 @@ var OUNITECH_CAPABILITIES = {
|
|
|
65398
65828
|
// taskState carries the job and its %, shown in the status words; there is
|
|
65399
65829
|
// no command id to follow a job WE started, so completion is not claimed.
|
|
65400
65830
|
processCompletion: false,
|
|
65831
|
+
// The manual: "Combined cleaning" across floors exists in the Ounitech app and needs its
|
|
65832
|
+
// lift-control module (code 500 = floor unreachable); no API call starts one.
|
|
65401
65833
|
multiFloor: false,
|
|
65402
|
-
// plans can name floors; no API starts a cross-floor job
|
|
65403
65834
|
// `start-spot-cleaning` takes a PIXEL polygon while poses are metres, and
|
|
65404
65835
|
// whether y is flipped between the two is not documented. Hidden until one
|
|
65405
65836
|
// real robot confirms the frame.
|
|
@@ -65555,9 +65986,9 @@ function ounitechFault(list) {
|
|
|
65555
65986
|
}
|
|
65556
65987
|
function faultWords(a) {
|
|
65557
65988
|
if (!a.code)
|
|
65558
|
-
return { code: "error", message: a.message.slice(0,
|
|
65559
|
-
const words =
|
|
65560
|
-
return { code: a.code.slice(0, 60), message: words
|
|
65989
|
+
return { code: "error", message: a.message.slice(0, 300) };
|
|
65990
|
+
const words = alertWords(a.code) ?? a.vendorText ?? a.message;
|
|
65991
|
+
return { code: a.code.slice(0, 60), message: `${a.code} \xB7 ${words}`.slice(0, 300) };
|
|
65561
65992
|
}
|
|
65562
65993
|
function ounitechMapShare(map, x, y, clamp4 = false) {
|
|
65563
65994
|
const w = map?.width, h = map?.height, r = map?.resolution;
|
|
@@ -65705,22 +66136,8 @@ function ounitechTaskRecord(t, maps = [], tz = 480) {
|
|
|
65705
66136
|
floorNo: Number.isInteger(t.floorNo) ? t.floorNo : map?.floorNo ?? null
|
|
65706
66137
|
};
|
|
65707
66138
|
}
|
|
65708
|
-
var
|
|
65709
|
-
|
|
65710
|
-
"206": "Button panel offline",
|
|
65711
|
-
// 306 "接收定点指定区域故障": seen live 30 Sep right after start-area-cleaning on an area with no centre point.
|
|
65712
|
-
"306": "Robot couldn't receive the chosen cleaning area",
|
|
65713
|
-
"540": "Floor check: cliff sensor switched on",
|
|
65714
|
-
"563": "Robot is in a fault state",
|
|
65715
|
-
"588": "Switched off with the power button",
|
|
65716
|
-
"589": "Switched off by the timed power schedule",
|
|
65717
|
-
"593": "Still finding its position - try again shortly",
|
|
65718
|
-
"601": "Left wheel driver reset",
|
|
65719
|
-
"620": "Right wheel driver offline",
|
|
65720
|
-
"621": "Right wheel driver reset",
|
|
65721
|
-
"734": "IMU zero offset too large",
|
|
65722
|
-
"785": "Localisation ready"
|
|
65723
|
-
};
|
|
66139
|
+
var VENDOR_TEXT_EN = { "734": "IMU zero offset too large" };
|
|
66140
|
+
var alertWords = (code) => code ? ounitechCodeWords(code) ?? VENDOR_TEXT_EN[code] ?? null : null;
|
|
65724
66141
|
var ALERT_LEVEL_WORDS = { \u63D0\u793A: "Notice", \u8B66\u544A: "Warning", \u6545\u969C: "Fault", \u9519\u8BEF: "Error", \u4E25\u91CD: "Critical" };
|
|
65725
66142
|
function ounitechAlertRecord(a, tz = 480) {
|
|
65726
66143
|
const raw = String(a.errMsg ?? a.errCode ?? "").trim();
|
|
@@ -65736,7 +66153,7 @@ function ounitechAlertRecord(a, tz = 480) {
|
|
|
65736
66153
|
code,
|
|
65737
66154
|
level: lv ? ALERT_LEVEL_WORDS[lv[1].trim()] ?? lv[1].trim() : null,
|
|
65738
66155
|
// An untranslated Chinese text keeps its code up front, so the row is never only Chinese.
|
|
65739
|
-
message:
|
|
66156
|
+
message: alertWords(code) ?? (/[㐀-鿿]/.test(rest || raw) && code ? `Robot alert ${code}: ${rest || raw}` : rest || raw),
|
|
65740
66157
|
vendorText: rest || raw,
|
|
65741
66158
|
resolved,
|
|
65742
66159
|
at: ounitechTime(a.createTime, tz)
|
|
@@ -66114,7 +66531,7 @@ function createOunitechAdapter(credentials, options) {
|
|
|
66114
66531
|
const blind = command.areas.filter((n) => centre.has(n) && !centre.get(n));
|
|
66115
66532
|
if (blind.length) {
|
|
66116
66533
|
throw new import_node_server_utils164.BadRequestError(
|
|
66117
|
-
`${blind.map((n) => `"${n}"`).join(", ")} has no centre point on the robot's map, so the robot can't be sent there (it stops with fault 306). Use Clean all, or redraw the area in the Ounitech app
|
|
66534
|
+
`${blind.map((n) => `"${n}"`).join(", ")} has no centre point on the robot's map, so the robot can't be sent there (it stops with fault 306). ` + (blind.includes("default") ? `"default" is the whole map before it is divided into areas. Use Clean all, or divide the map in the Ounitech app (Control -> Divide areas).` : "Use Clean all, or redraw the area in the Ounitech app.")
|
|
66118
66535
|
);
|
|
66119
66536
|
}
|
|
66120
66537
|
await cloud.startAreaCleaning(robotSn, command.areas, options2);
|
|
@@ -66377,7 +66794,17 @@ var OUNITECH_BRAND_DEFINITION = {
|
|
|
66377
66794
|
{ name: "Q3-G", codes: ["Q3G", "Q3-G"] },
|
|
66378
66795
|
{ name: "Q3-W", codes: ["Q3W", "Q3-W"] }
|
|
66379
66796
|
],
|
|
66380
|
-
info: {
|
|
66797
|
+
info: {
|
|
66798
|
+
makerApp: "the Ounitech app",
|
|
66799
|
+
makerAppOnly: ["pausing or stopping a job", "cleaning across floors", "changing maps, areas and schedules"],
|
|
66800
|
+
// Q3-G User Manual / Troubleshooting Checklist (2026-09-30). The API has no documented way to clear an Error.
|
|
66801
|
+
clearFault: [
|
|
66802
|
+
'Press the red emergency stop on top of the robot, then turn it clockwise to release it. It says "emergency stop button bounced" and the fault clears.',
|
|
66803
|
+
"If it has lost its position: lift the handle, push it onto its charging dock until the metal contacts touch, then press and release the emergency stop. It relocates by itself.",
|
|
66804
|
+
"Still stuck: restart it with the power switch at the back. It is ready when only the child-lock light stays on.",
|
|
66805
|
+
"Handle buttons not responding: press Child lock 3 times to unlock them."
|
|
66806
|
+
]
|
|
66807
|
+
},
|
|
66381
66808
|
create: (credentials) => createOunitechAdapter(credentials)
|
|
66382
66809
|
};
|
|
66383
66810
|
function registerOunitechBrand() {
|
|
@@ -71669,7 +72096,7 @@ function toBuildingYaml(name, rmf) {
|
|
|
71669
72096
|
}
|
|
71670
72097
|
const lifts = {};
|
|
71671
72098
|
for (const l of rmf.lifts) {
|
|
71672
|
-
const
|
|
72099
|
+
const range2 = liftFloorRange(l, rmf.levels);
|
|
71673
72100
|
lifts[l.name] = {
|
|
71674
72101
|
x: l.x,
|
|
71675
72102
|
y: l.y,
|
|
@@ -71677,9 +72104,9 @@ function toBuildingYaml(name, rmf) {
|
|
|
71677
72104
|
width: l.widthM,
|
|
71678
72105
|
depth: l.depthM,
|
|
71679
72106
|
reference_floor_name: l.referenceFloor,
|
|
71680
|
-
initial_floor_name:
|
|
71681
|
-
lowest_floor:
|
|
71682
|
-
highest_floor:
|
|
72107
|
+
initial_floor_name: range2.initial,
|
|
72108
|
+
lowest_floor: range2.lowest,
|
|
72109
|
+
highest_floor: range2.highest,
|
|
71683
72110
|
doors: Object.fromEntries(
|
|
71684
72111
|
l.doors.map((d) => [d.name, { door_type: LIFT_DOOR_TYPE_INT[d.type] ?? 2, x: d.x, y: d.y, width: d.widthM, motion_axis_orientation: d.axisYawRad }])
|
|
71685
72112
|
),
|
|
@@ -71729,13 +72156,13 @@ function toNavGraphs(name, rmf) {
|
|
|
71729
72156
|
const out = {};
|
|
71730
72157
|
const elevation = new Map(rmf.levels.map((l) => [l.name, l.elevationM]));
|
|
71731
72158
|
const shafts = rmf.lifts.map((l) => {
|
|
71732
|
-
const
|
|
72159
|
+
const range2 = liftFloorRange(l, rmf.levels);
|
|
71733
72160
|
return {
|
|
71734
72161
|
lift: l,
|
|
71735
72162
|
verts: liftShaft(l),
|
|
71736
72163
|
// lift.py: no lowest / highest floor = no bound; a lift with no level_doors marks nothing.
|
|
71737
|
-
lo: elevation.get(
|
|
71738
|
-
hi: elevation.get(
|
|
72164
|
+
lo: elevation.get(range2.lowest) ?? -Infinity,
|
|
72165
|
+
hi: elevation.get(range2.highest) ?? Infinity,
|
|
71739
72166
|
hasDoors: l.levelDoors.length > 0
|
|
71740
72167
|
};
|
|
71741
72168
|
});
|
|
@@ -101293,6 +101720,9 @@ function useServiceProviderInviteController() {
|
|
|
101293
101720
|
wrongJoinPreview: _wrongJoinPreview,
|
|
101294
101721
|
repairWrongJoin: _repairWrongJoin,
|
|
101295
101722
|
undoWrongJoin: _undoWrongJoin,
|
|
101723
|
+
finishSetupPreview: _finishSetupPreview,
|
|
101724
|
+
finishSetupApply: _finishSetupApply,
|
|
101725
|
+
finishSetupUndo: _finishSetupUndo,
|
|
101296
101726
|
linkSite: _linkSite,
|
|
101297
101727
|
listReceived: _listReceived,
|
|
101298
101728
|
getForProvider: _getForProvider
|
|
@@ -101500,6 +101930,35 @@ function useServiceProviderInviteController() {
|
|
|
101500
101930
|
const wrongJoinPreview = wrongJoin("preview");
|
|
101501
101931
|
const wrongJoinRepair = wrongJoin("repair");
|
|
101502
101932
|
const wrongJoinUndo = wrongJoin("undo");
|
|
101933
|
+
function finishSetup(action) {
|
|
101934
|
+
return async (req, res, next) => {
|
|
101935
|
+
try {
|
|
101936
|
+
await requireConsolePermission(req, "sp-approvals", "approve-sp");
|
|
101937
|
+
const { error, value } = import_joi149.default.object({
|
|
101938
|
+
companyName: import_joi149.default.string().trim().max(200).optional().allow("", null),
|
|
101939
|
+
country: import_joi149.default.string().trim().max(100).optional().allow("", null)
|
|
101940
|
+
}).validate(action === "preview" ? req.query ?? {} : req.body ?? {}, {
|
|
101941
|
+
stripUnknown: true
|
|
101942
|
+
});
|
|
101943
|
+
if (error)
|
|
101944
|
+
throw new import_node_server_utils294.BadRequestError(`Invalid input: ${error.message}`);
|
|
101945
|
+
const args = {
|
|
101946
|
+
id: inviteId(req),
|
|
101947
|
+
userId: callerId5(req),
|
|
101948
|
+
companyName: value.companyName ?? "",
|
|
101949
|
+
country: value.country ?? ""
|
|
101950
|
+
};
|
|
101951
|
+
res.json(
|
|
101952
|
+
action === "preview" ? await _finishSetupPreview(args) : action === "apply" ? await _finishSetupApply(args) : await _finishSetupUndo(args)
|
|
101953
|
+
);
|
|
101954
|
+
} catch (error) {
|
|
101955
|
+
next(error);
|
|
101956
|
+
}
|
|
101957
|
+
};
|
|
101958
|
+
}
|
|
101959
|
+
const finishSetupPreview = finishSetup("preview");
|
|
101960
|
+
const finishSetupApply = finishSetup("apply");
|
|
101961
|
+
const finishSetupUndo = finishSetup("undo");
|
|
101503
101962
|
async function linkSite(req, res, next) {
|
|
101504
101963
|
try {
|
|
101505
101964
|
res.json(
|
|
@@ -101554,6 +102013,9 @@ function useServiceProviderInviteController() {
|
|
|
101554
102013
|
wrongJoinPreview,
|
|
101555
102014
|
wrongJoinRepair,
|
|
101556
102015
|
wrongJoinUndo,
|
|
102016
|
+
finishSetupPreview,
|
|
102017
|
+
finishSetupApply,
|
|
102018
|
+
finishSetupUndo,
|
|
101557
102019
|
linkSite,
|
|
101558
102020
|
received,
|
|
101559
102021
|
getOne
|
|
@@ -108766,6 +109228,7 @@ function usePersonalEmergencyChainController() {
|
|
|
108766
109228
|
robotTypesVisibleTo,
|
|
108767
109229
|
rtspUrl,
|
|
108768
109230
|
safeTopic,
|
|
109231
|
+
sanitizeCompanyName,
|
|
108769
109232
|
scaleFromMeasurement,
|
|
108770
109233
|
schema,
|
|
108771
109234
|
schemaAppSlugNotification,
|