Tap the card on the charger and the number fills itself in
The enrolment the Anker app does, done here: 0108 a2=7 opens the reader, 0908 brings back the UID. The frames this sends are byte-for-byte the ones the app was captured sending — checksum included — which is what the new test asserts. Adding and removing now write the charger as well as the account: the device write is the app's own message, the account write is the inferred one that carries the name, and either may fail without the other. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4c73d4ac05
commit
7176867eb3
13 files changed
+473
-19
No files matched your search
@@ -345,6 +345,13 @@ export const api = {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ cardNumber, cardName }),
|
||||
}),
|
||||
// Opens the charger's own card reader and waits for a tap — the request is in
|
||||
// flight for the whole twenty-second window, and answers whether or not a card
|
||||
// arrived.
|
||||
scanAnkerRfidCard: (sn) =>
|
||||
request(`/integrations/anker-solix/chargers/${encodeURIComponent(sn)}/rfid-cards/scan`, {
|
||||
method: "POST",
|
||||
}),
|
||||
deleteAnkerRfidCard: (sn, cardNumber) =>
|
||||
request(
|
||||
`/integrations/anker-solix/chargers/${encodeURIComponent(sn)}/rfid-cards/${encodeURIComponent(cardNumber)}`,
|
||||
|
||||
@@ -69,6 +69,10 @@
|
||||
"none": "Ingen kort er godkendt til denne lader.",
|
||||
"unsupported": "Tjenesten, som denne lader kommer fra, rapporterer ikke RFID-kort.",
|
||||
"add": "Tilføj kort",
|
||||
"tap": "Hold kortet mod laderen",
|
||||
"tapping": "Hold kortet mod læseren… {n}s",
|
||||
"tapHint": "Læseren er åben. Hold kortet mod laderen.",
|
||||
"tapNone": "Der blev ikke holdt et kort mod læseren, før den lukkede.",
|
||||
"addTitle": "Tilføj et kort",
|
||||
"remove": "Fjern",
|
||||
"removeConfirm": "Fjern {name} fra denne lader?",
|
||||
|
||||
@@ -338,6 +338,10 @@
|
||||
"none": "No cards are authorised on this charger.",
|
||||
"unsupported": "The service this charger came from does not report RFID cards.",
|
||||
"add": "Add card",
|
||||
"tap": "Tap card at the charger",
|
||||
"tapping": "Hold the card against the reader… {n}s",
|
||||
"tapHint": "The reader is open. Hold the card against the charger.",
|
||||
"tapNone": "No card was tapped before the reader closed.",
|
||||
"addTitle": "Add a card",
|
||||
"remove": "Remove",
|
||||
"removeConfirm": "Remove {name} from this charger?",
|
||||
|
||||
@@ -69,6 +69,10 @@
|
||||
"none": "Na tej ładowarce nie autoryzowano żadnej karty.",
|
||||
"unsupported": "Usługa, z której pochodzi ta ładowarka, nie zgłasza kart RFID.",
|
||||
"add": "Dodaj kartę",
|
||||
"tap": "Przyłóż kartę do ładowarki",
|
||||
"tapping": "Przytrzymaj kartę przy czytniku… {n}s",
|
||||
"tapHint": "Czytnik jest otwarty. Przytrzymaj kartę przy ładowarce.",
|
||||
"tapNone": "Nie przyłożono karty, zanim czytnik się zamknął.",
|
||||
"addTitle": "Dodaj kartę",
|
||||
"remove": "Usuń",
|
||||
"removeConfirm": "Usunąć {name} z tej ładowarki?",
|
||||
|
||||
@@ -1242,7 +1242,8 @@ const rfidCards = computed(() => {
|
||||
return rfidCardsFrom(view?.attrs);
|
||||
});
|
||||
|
||||
const rfidBusy = ref(""); // the card being written, or "new" while one is added
|
||||
const rfidBusy = ref(""); // the card being written, "new", or "scan" while the reader is open
|
||||
const rfidCountdown = ref(0); // seconds left of the reader's window, while it is open
|
||||
const rfidError = ref("");
|
||||
const newCardNumber = ref("");
|
||||
const newCardName = ref("");
|
||||
@@ -1272,6 +1273,35 @@ async function addRfidCard() {
|
||||
}
|
||||
}
|
||||
|
||||
// Asking the charger to read a card, which is what the Anker app's second way of
|
||||
// adding one does: the reader opens for twenty seconds, and whatever is held
|
||||
// against it comes back as a number. Nothing is written by this — the card lands
|
||||
// in the form, and adding it is still a decision.
|
||||
async function scanRfidCard() {
|
||||
const sn = detailSn.value;
|
||||
if (!sn || rfidBusy.value) return;
|
||||
rfidBusy.value = "scan";
|
||||
rfidError.value = "";
|
||||
rfidCountdown.value = 20;
|
||||
const tick = setInterval(() => {
|
||||
rfidCountdown.value = Math.max(0, rfidCountdown.value - 1);
|
||||
}, 1000);
|
||||
try {
|
||||
const res = await api.scanAnkerRfidCard(sn);
|
||||
if (res?.tapped && res.card) {
|
||||
newCardNumber.value = res.card;
|
||||
} else {
|
||||
rfidError.value = t("charging.rfid.tapNone");
|
||||
}
|
||||
} catch (e) {
|
||||
rfidError.value = e.message;
|
||||
} finally {
|
||||
clearInterval(tick);
|
||||
rfidCountdown.value = 0;
|
||||
rfidBusy.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
// Removing one asks first — a card that is gone can only be put back by whoever
|
||||
// still has it in their hand.
|
||||
async function removeRfidCard(card) {
|
||||
@@ -1877,6 +1907,18 @@ onMounted(async () => {
|
||||
{{ rfidBusy === "new" ? t("common.loading") : t("charging.rfid.add") }}
|
||||
</button>
|
||||
</div>
|
||||
<!-- The other way to fill that field in: hold the card against the
|
||||
charger. The reader opens for twenty seconds and the number
|
||||
arrives on its own, which beats reading it off the card. -->
|
||||
<button
|
||||
type="button"
|
||||
class="dh-btn dh-btn-ghost mt-2 w-full text-xs"
|
||||
:disabled="rfidBusy !== ''"
|
||||
@click="scanRfidCard"
|
||||
>
|
||||
{{ rfidBusy === "scan" ? t("charging.rfid.tapping", { n: rfidCountdown }) : t("charging.rfid.tap") }}
|
||||
</button>
|
||||
<p v-if="rfidBusy === 'scan'" class="mt-1 text-[11px] text-muted">{{ t("charging.rfid.tapHint") }}</p>
|
||||
</form>
|
||||
<p class="mt-3 text-[11px] text-muted">{{ t("charging.rfid.inferred") }}</p>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user