mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 03:01:21 +02:00
fix(backup): read the printer's verdict before counting a K-profile restored (#2656)
`18938a10` on `dev` changed `set_kprofiles_batch` from returning a `bool` to returning the sequence_id it published the command under, and moved the verdict to a separate `await client.await_cali_ack(seq)` returning `(ok, detail)`. Every caller in `api/routes/kprofiles.py` was updated with it. `_restore_kprofiles` was not — it still did `sent = client.set_kprofiles_batch(...)` and branched on `if sent:`. A sequence_id string is truthy, so that compiled, passed, and silently made the restore the one path left in the codebase that reports a refused K-profile write as saved — exactly the defect `18938a10` closed everywhere else. Keep the sequence_id, await the ack per batch, and route an explicit refusal into `tally.failed` with a new `kprofilesRefused` note carrying the printer's own `reason`. Reusing `kprofilesSendFailed` would have been wrong: the command was sent, and the printer answered. Silence still counts restored. That is `await_cali_ack`'s own contract and the maintainer's rule — no answer is not evidence of refusal, and firmware predating the ack never answers. An exception reading the ack degrades the same way rather than inventing a failure out of a write that most likely landed. `kprofilesAckUnreliable` is reworded to match: a refusal is now believed, so the caveat narrows to what is genuinely left uncertain. The ack is only worth reading at all because `18938a10` also changed the payload's `tray_id` from `-1` to `0` — single-nozzle firmware answered `result: "fail"` to `-1` on writes that demonstrably applied. This restore builds no `tray_id` of its own, so it inherits that fix for free. Tests: 4 regression (the ack is awaited for the returned sequence_id; a refused batch counts failed and surfaces the printer's reason; one refused nozzle does not condemn the other; the reworded caveat) + 3 controls (a silent printer still counts restored; an unreadable ack does not fail the batch; `None` keeps the existing send-failed path and awaits nothing). All four confirmed failing against the pre-fix service.
This commit is contained in:
@@ -1473,9 +1473,12 @@ class GitHubRestoreService:
|
||||
# the profile occupying a slot, so writing is always an overwrite on the
|
||||
# printer side.
|
||||
tally.note("kprofilesAlwaysOverwrite", "K-profiles always overwrite the matching slot on the printer")
|
||||
# A refusal is now believed and counted failed (#2718 made the ack worth
|
||||
# reading), but silence still counts restored, so the caveat stands —
|
||||
# narrowed to what is actually left uncertain.
|
||||
tally.note(
|
||||
"kprofilesAckUnreliable",
|
||||
"The printer's acknowledgement is not reliable — verify the profiles on the printer",
|
||||
"A printer that does not answer still counts as restored — verify the profiles on the printer",
|
||||
)
|
||||
|
||||
for serial, entries in sorted(by_serial.items()):
|
||||
@@ -1565,14 +1568,12 @@ class GitHubRestoreService:
|
||||
)
|
||||
|
||||
try:
|
||||
sent = client.set_kprofiles_batch(profile_dicts, nozzle)
|
||||
seq = client.set_kprofiles_batch(profile_dicts, nozzle)
|
||||
except Exception as e:
|
||||
logger.warning("K-profile restore failed for %s nozzle %s: %s", serial, nozzle, e)
|
||||
sent = False
|
||||
seq = None
|
||||
|
||||
if sent:
|
||||
tally.restored += len(profile_dicts)
|
||||
else:
|
||||
if not seq:
|
||||
tally.failed += len(profile_dicts)
|
||||
tally.note(
|
||||
"kprofilesSendFailed",
|
||||
@@ -1581,6 +1582,44 @@ class GitHubRestoreService:
|
||||
printer=printer.name,
|
||||
serial=serial,
|
||||
)
|
||||
continue
|
||||
|
||||
# What came back is the sequence_id the command was published
|
||||
# under, not a verdict (#2718) — a truthy string only means the
|
||||
# command left the building. The printer answers separately, and
|
||||
# every other caller of this API now reads that answer; without
|
||||
# this the restore would be the one path left that reports a
|
||||
# refused write as saved.
|
||||
ok, detail = await self._kprofile_ack(client, seq, serial, nozzle)
|
||||
if ok:
|
||||
tally.restored += len(profile_dicts)
|
||||
else:
|
||||
tally.failed += len(profile_dicts)
|
||||
tally.note(
|
||||
"kprofilesRefused",
|
||||
f"{printer.name} ({serial}) refused the {nozzle} profiles: {detail}",
|
||||
nozzle=nozzle,
|
||||
printer=printer.name,
|
||||
serial=serial,
|
||||
reason=detail,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
async def _kprofile_ack(client, seq: str, serial: str, nozzle: str) -> tuple[bool, str]:
|
||||
"""Read the printer's verdict on one batch write.
|
||||
|
||||
``await_cali_ack`` already treats silence as success — no answer is not
|
||||
evidence of refusal, and firmware that predates the ack never answers at
|
||||
all. An exception reading it is the same situation one layer up, so it
|
||||
degrades the same way rather than turning a write that most likely
|
||||
landed into a reported failure.
|
||||
"""
|
||||
try:
|
||||
ok, detail = await client.await_cali_ack(seq)
|
||||
return bool(ok), str(detail or "")
|
||||
except Exception as e:
|
||||
logger.warning("Could not read the K-profile ack for %s nozzle %s: %s", serial, nozzle, e)
|
||||
return True, ""
|
||||
|
||||
@staticmethod
|
||||
async def _current_kprofile_index(client, nozzle: str, serial: str) -> list:
|
||||
|
||||
@@ -1236,10 +1236,17 @@ class TestRestoreKprofiles:
|
||||
"""One profile as the printer currently reports it."""
|
||||
return SimpleNamespace(slot_id=slot_id, filament_id=filament_id, name=name, setting_id=setting_id)
|
||||
|
||||
def _client(self, live=None, sent=True):
|
||||
def _client(self, live=None, sent="7", ack=(True, "")):
|
||||
"""A connected printer client.
|
||||
|
||||
``set_kprofiles_batch`` returns the sequence_id it published under, not
|
||||
a success flag (#2718), and the verdict arrives separately from
|
||||
``await_cali_ack`` as ``(ok, detail)``.
|
||||
"""
|
||||
client = MagicMock()
|
||||
client.state.connected = True
|
||||
client.set_kprofiles_batch = MagicMock(return_value=sent)
|
||||
client.await_cali_ack = AsyncMock(return_value=ack)
|
||||
client.get_kprofiles = AsyncMock(return_value=list(live or []))
|
||||
return client
|
||||
|
||||
@@ -1266,9 +1273,7 @@ class TestRestoreKprofiles:
|
||||
@pytest.mark.asyncio
|
||||
async def test_sends_batch_to_connected_printer(self, db_session, printer_factory):
|
||||
printer = await printer_factory(serial_number="00M09A123456789")
|
||||
client = MagicMock()
|
||||
client.state.connected = True
|
||||
client.set_kprofiles_batch = MagicMock(return_value=True)
|
||||
client = self._client()
|
||||
tally = _CategoryTally()
|
||||
|
||||
with patch("backend.app.services.github_restore.printer_manager") as manager:
|
||||
@@ -1293,9 +1298,10 @@ class TestRestoreKprofiles:
|
||||
manager.get_client = MagicMock(return_value=client)
|
||||
await _service()._restore_kprofiles(db_session, self._payload(), tally)
|
||||
|
||||
# The printer does answer extrusion_cali_set, but it reports "fail" on
|
||||
# writes that land, so the note must not promise either way.
|
||||
# A refusal is now read and counted failed, so the caveat is narrowed to
|
||||
# what is genuinely left uncertain: a printer that never answers.
|
||||
assert any("verify the profiles on the printer" in note for note in _messages(tally))
|
||||
assert any("does not answer still counts as restored" in note for note in _messages(tally))
|
||||
assert not any("without acknowledgement" in note for note in _messages(tally))
|
||||
assert any("always overwrite" in note for note in _messages(tally))
|
||||
|
||||
@@ -1560,10 +1566,10 @@ class TestRestoreKprofiles:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_publish_failure_counts_as_failed(self, db_session, printer_factory):
|
||||
# None is what set_kprofiles_batch returns when it could not publish —
|
||||
# a disconnected client. There is no ack to wait for in that case.
|
||||
await printer_factory(serial_number="00M09A123456789")
|
||||
client = MagicMock()
|
||||
client.state.connected = True
|
||||
client.set_kprofiles_batch = MagicMock(return_value=False)
|
||||
client = self._client(sent=None)
|
||||
tally = _CategoryTally()
|
||||
|
||||
with patch("backend.app.services.github_restore.printer_manager") as manager:
|
||||
@@ -1572,6 +1578,9 @@ class TestRestoreKprofiles:
|
||||
|
||||
assert tally.failed == 1
|
||||
assert tally.restored == 0
|
||||
assert "kprofilesSendFailed" in _codes(tally)
|
||||
assert "kprofilesRefused" not in _codes(tally), "nothing was sent, so the printer refused nothing"
|
||||
client.await_cali_ack.assert_not_awaited()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_publish_exception_is_contained(self, db_session, printer_factory):
|
||||
@@ -1587,13 +1596,96 @@ class TestRestoreKprofiles:
|
||||
|
||||
assert tally.failed == 1
|
||||
|
||||
# --- the printer's verdict decides the tally, not the publish ------------
|
||||
#
|
||||
# #2718 changed set_kprofiles_batch from returning a bool to returning the
|
||||
# sequence_id it published under. A sequence_id string is truthy, so a
|
||||
# restore that branches on the return value alone reports every refused
|
||||
# write as saved — the defect that fix closed in every other caller.
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_awaits_the_ack_for_the_sequence_id_it_was_given(self, db_session, printer_factory):
|
||||
await printer_factory(serial_number="00M09A123456789")
|
||||
client = self._client(sent="4211")
|
||||
tally = _CategoryTally()
|
||||
|
||||
with patch("backend.app.services.github_restore.printer_manager") as manager:
|
||||
manager.get_client = MagicMock(return_value=client)
|
||||
await _service()._restore_kprofiles(db_session, self._payload(), tally)
|
||||
|
||||
client.await_cali_ack.assert_awaited_once_with("4211")
|
||||
assert tally.restored == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_refused_batch_counts_failed_not_restored(self, db_session, printer_factory):
|
||||
await printer_factory(serial_number="00M09A123456789", name="Shelf Printer")
|
||||
client = self._client(ack=(False, "invalid tray_id"))
|
||||
tally = _CategoryTally()
|
||||
|
||||
with patch("backend.app.services.github_restore.printer_manager") as manager:
|
||||
manager.get_client = MagicMock(return_value=client)
|
||||
await _service()._restore_kprofiles(db_session, self._payload(), tally)
|
||||
|
||||
assert tally.restored == 0
|
||||
assert tally.failed == 1
|
||||
assert "kprofilesRefused" in _codes(tally)
|
||||
assert "kprofilesSendFailed" not in _codes(tally), "it was sent — the printer answered no"
|
||||
note = next(n for n in tally.notes if n["code"] == "kprofilesRefused")
|
||||
assert note["params"]["reason"] == "invalid tray_id", "the printer's own reason has to survive"
|
||||
assert "Shelf Printer" in note["message"] and "invalid tray_id" in note["message"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_silent_printer_still_counts_restored(self, db_session, printer_factory):
|
||||
# maziggy's rule, and await_cali_ack's own contract: no answer is not
|
||||
# evidence of refusal. Firmware that predates the ack never answers.
|
||||
await printer_factory(serial_number="00M09A123456789")
|
||||
client = self._client(ack=(True, "no acknowledgement from printer"))
|
||||
tally = _CategoryTally()
|
||||
|
||||
with patch("backend.app.services.github_restore.printer_manager") as manager:
|
||||
manager.get_client = MagicMock(return_value=client)
|
||||
await _service()._restore_kprofiles(db_session, self._payload(), tally)
|
||||
|
||||
assert tally.restored == 1
|
||||
assert tally.failed == 0
|
||||
assert "kprofilesRefused" not in _codes(tally)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_an_unreadable_ack_does_not_fail_the_batch(self, db_session, printer_factory):
|
||||
# Same situation one layer up: the write most likely landed, so this
|
||||
# degrades the way a timeout does rather than inventing a failure.
|
||||
await printer_factory(serial_number="00M09A123456789")
|
||||
client = self._client()
|
||||
client.await_cali_ack = AsyncMock(side_effect=RuntimeError("mqtt down"))
|
||||
tally = _CategoryTally()
|
||||
|
||||
with patch("backend.app.services.github_restore.printer_manager") as manager:
|
||||
manager.get_client = MagicMock(return_value=client)
|
||||
await _service()._restore_kprofiles(db_session, self._payload(), tally)
|
||||
|
||||
assert tally.restored == 1
|
||||
assert tally.failed == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_one_refused_nozzle_does_not_condemn_the_other(self, db_session, printer_factory):
|
||||
await printer_factory(serial_number="00M09A123456789")
|
||||
payload = {**self._payload(nozzle="0.4"), **self._payload(nozzle="0.8")}
|
||||
client = self._client()
|
||||
client.await_cali_ack = AsyncMock(side_effect=[(False, "busy"), (True, "")])
|
||||
tally = _CategoryTally()
|
||||
|
||||
with patch("backend.app.services.github_restore.printer_manager") as manager:
|
||||
manager.get_client = MagicMock(return_value=client)
|
||||
await _service()._restore_kprofiles(db_session, payload, tally)
|
||||
|
||||
assert tally.restored == 1
|
||||
assert tally.failed == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_each_nozzle_is_sent_separately(self, db_session, printer_factory):
|
||||
await printer_factory(serial_number="00M09A123456789")
|
||||
payload = {**self._payload(nozzle="0.4"), **self._payload(nozzle="0.8")}
|
||||
client = MagicMock()
|
||||
client.state.connected = True
|
||||
client.set_kprofiles_batch = MagicMock(return_value=True)
|
||||
client = self._client()
|
||||
tally = _CategoryTally()
|
||||
|
||||
with patch("backend.app.services.github_restore.printer_manager") as manager:
|
||||
|
||||
@@ -4922,12 +4922,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}} bleiben ausgeschaltet - die jeweils benötigten Zugangsdaten lassen sich nicht aus einem Backup wiederherstellen und sind auf dieser Instanz nicht hinterlegt, ein Einschalten würde die Integration also ohne Authentifizierung lassen',
|
||||
settingsMqttRelayFailed: 'MQTT-Einstellungen wiederhergestellt, aber das Relay konnte nicht neu verbunden werden - Bambuddy neu starten',
|
||||
kprofilesAlwaysOverwrite: 'K-Profile überschreiben immer den passenden Slot auf dem Drucker',
|
||||
kprofilesAckUnreliable: 'Die Bestätigung des Druckers ist nicht zuverlässig - überprüfen Sie die Profile am Drucker',
|
||||
kprofilesAckUnreliable: 'Ein Drucker, der nicht antwortet, zählt weiterhin als wiederhergestellt - überprüfen Sie die Profile am Drucker',
|
||||
kprofilesPrinterMissing: 'Kein Drucker mit der Seriennummer {{serial}} - übersprungen',
|
||||
kprofilesPrinterOffline: '{{printer}} ({{serial}}) ist nicht verbunden - übersprungen',
|
||||
kprofilesUnknownNozzle: 'Unerwarteter Düsendurchmesser {{nozzle}} für {{serial}} - unverändert gesendet',
|
||||
kprofilesUnmatched: '{{count}} Profile für {{nozzle}} hatten kein Gegenstück auf {{printer}} - als neue Profile hinzugefügt',
|
||||
kprofilesSendFailed: '{{nozzle}}-Profile konnten nicht an {{printer}} ({{serial}}) gesendet werden',
|
||||
kprofilesRefused: '{{printer}} ({{serial}}) hat die {{nozzle}}-Profile abgelehnt: {{reason}}',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -4970,12 +4970,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}} left switched off - the credential each one needs cannot be restored from a backup and this instance has none stored, so switching them on would leave the integration unauthenticated',
|
||||
settingsMqttRelayFailed: 'MQTT settings restored, but the relay could not be reconnected - restart Bambuddy',
|
||||
kprofilesAlwaysOverwrite: 'K-profiles always overwrite the matching slot on the printer',
|
||||
kprofilesAckUnreliable: 'The printer\'s acknowledgement is not reliable - verify the profiles on the printer',
|
||||
kprofilesAckUnreliable: 'A printer that does not answer still counts as restored - verify the profiles on the printer',
|
||||
kprofilesPrinterMissing: 'No printer with serial {{serial}} - skipped',
|
||||
kprofilesPrinterOffline: '{{printer}} ({{serial}}) is not connected - skipped',
|
||||
kprofilesUnknownNozzle: 'Unexpected nozzle diameter {{nozzle}} for {{serial}} - sent as-is',
|
||||
kprofilesUnmatched: '{{count}} profile(s) for {{nozzle}} had no counterpart on {{printer}} - added as new profiles',
|
||||
kprofilesSendFailed: 'Failed to send {{nozzle}} profiles to {{printer}} ({{serial}})',
|
||||
kprofilesRefused: '{{printer}} ({{serial}}) refused the {{nozzle}} profiles: {{reason}}',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -4930,12 +4930,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}} se han dejado desactivados - la credencial que cada uno necesita no puede restaurarse desde una copia y esta instancia no tiene ninguna guardada, así que activarlos dejaría la integración sin autenticación',
|
||||
settingsMqttRelayFailed: 'Ajustes MQTT restaurados, pero no se pudo reconectar el relé - reinicia Bambuddy',
|
||||
kprofilesAlwaysOverwrite: 'Los perfiles K siempre sobrescriben la ranura correspondiente en la impresora',
|
||||
kprofilesAckUnreliable: 'La confirmación de la impresora no es fiable - verifica los perfiles en la impresora',
|
||||
kprofilesAckUnreliable: 'Una impresora que no responde sigue contando como restaurada - verifica los perfiles en la impresora',
|
||||
kprofilesPrinterMissing: 'No hay ninguna impresora con el número de serie {{serial}} - omitido',
|
||||
kprofilesPrinterOffline: '{{printer}} ({{serial}}) no está conectada - omitido',
|
||||
kprofilesUnknownNozzle: 'Diámetro de boquilla inesperado {{nozzle}} para {{serial}} - enviado tal cual',
|
||||
kprofilesUnmatched: '{{count}} perfiles para {{nozzle}} no tenían equivalente en {{printer}} - añadidos como perfiles nuevos',
|
||||
kprofilesSendFailed: 'No se pudieron enviar los perfiles de {{nozzle}} a {{printer}} ({{serial}})',
|
||||
kprofilesRefused: '{{printer}} ({{serial}}) rechazó los perfiles de {{nozzle}}: {{reason}}',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -4911,12 +4911,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}} laissés désactivés - l\'identifiant dont chacun a besoin ne peut pas être restauré depuis une sauvegarde et cette instance n\'en a aucun enregistré ; les activer laisserait donc l\'intégration sans authentification',
|
||||
settingsMqttRelayFailed: 'Réglages MQTT restaurés, mais le relais n\'a pas pu être reconnecté - redémarrez Bambuddy',
|
||||
kprofilesAlwaysOverwrite: 'Les profils K écrasent toujours l\'emplacement correspondant sur l\'imprimante',
|
||||
kprofilesAckUnreliable: 'L\'accusé de réception de l\'imprimante n\'est pas fiable - vérifiez les profils sur l\'imprimante',
|
||||
kprofilesAckUnreliable: 'Une imprimante qui ne répond pas compte quand même comme restaurée - vérifiez les profils sur l\'imprimante',
|
||||
kprofilesPrinterMissing: 'Aucune imprimante avec le numéro de série {{serial}} - ignoré',
|
||||
kprofilesPrinterOffline: '{{printer}} ({{serial}}) n\'est pas connectée - ignoré',
|
||||
kprofilesUnknownNozzle: 'Diamètre de buse inattendu {{nozzle}} pour {{serial}} - envoyé tel quel',
|
||||
kprofilesUnmatched: '{{count}} profils pour {{nozzle}} n\'avaient pas d\'équivalent sur {{printer}} - ajoutés comme nouveaux profils',
|
||||
kprofilesSendFailed: 'Impossible d\'envoyer les profils {{nozzle}} à {{printer}} ({{serial}})',
|
||||
kprofilesRefused: '{{printer}} ({{serial}}) a refusé les profils {{nozzle}} : {{reason}}',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -4910,12 +4910,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}} lasciati disattivati - la credenziale richiesta da ciascuno non può essere ripristinata da un backup e questa istanza non ne ha nessuna salvata, quindi attivarli lascerebbe l\'integrazione senza autenticazione',
|
||||
settingsMqttRelayFailed: 'Impostazioni MQTT ripristinate, ma il relay non è stato riconnesso - riavvia Bambuddy',
|
||||
kprofilesAlwaysOverwrite: 'I profili K sovrascrivono sempre lo slot corrispondente sulla stampante',
|
||||
kprofilesAckUnreliable: 'La conferma della stampante non è affidabile - verifica i profili sulla stampante',
|
||||
kprofilesAckUnreliable: 'Una stampante che non risponde conta comunque come ripristinata - verifica i profili sulla stampante',
|
||||
kprofilesPrinterMissing: 'Nessuna stampante con numero di serie {{serial}} - saltato',
|
||||
kprofilesPrinterOffline: '{{printer}} ({{serial}}) non è connessa - saltato',
|
||||
kprofilesUnknownNozzle: 'Diametro ugello inatteso {{nozzle}} per {{serial}} - inviato così com\'è',
|
||||
kprofilesUnmatched: '{{count}} profili per {{nozzle}} non avevano corrispondenza su {{printer}} - aggiunti come nuovi profili',
|
||||
kprofilesSendFailed: 'Impossibile inviare i profili {{nozzle}} a {{printer}} ({{serial}})',
|
||||
kprofilesRefused: '{{printer}} ({{serial}}) ha rifiutato i profili {{nozzle}}: {{reason}}',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -4922,12 +4922,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}} はオフのままにしました - 各項目に必要な認証情報はバックアップから復元できず、このインスタンスにも保存されていないため、オンにすると連携が未認証のままになります',
|
||||
settingsMqttRelayFailed: 'MQTT 設定を復元しましたが、リレーを再接続できませんでした - Bambuddy を再起動してください',
|
||||
kprofilesAlwaysOverwrite: 'K プロファイルは常にプリンター側の該当スロットを上書きします',
|
||||
kprofilesAckUnreliable: 'プリンターの応答は信頼できません - プロファイルはプリンター側で確認してください',
|
||||
kprofilesAckUnreliable: '応答しないプリンターも復元済みとして数えます - プロファイルはプリンター側で確認してください',
|
||||
kprofilesPrinterMissing: 'シリアル {{serial}} のプリンターがありません - スキップしました',
|
||||
kprofilesPrinterOffline: '{{printer}} ({{serial}}) は接続されていません - スキップしました',
|
||||
kprofilesUnknownNozzle: '{{serial}} に想定外のノズル径 {{nozzle}} - そのまま送信しました',
|
||||
kprofilesUnmatched: '{{nozzle}} 用のプロファイル {{count}} 件は {{printer}} に該当がありませんでした - 新規プロファイルとして追加しました',
|
||||
kprofilesSendFailed: '{{nozzle}} のプロファイルを {{printer}} ({{serial}}) に送信できませんでした',
|
||||
kprofilesRefused: '{{printer}} ({{serial}}) が {{nozzle}} のプロファイルを拒否しました: {{reason}}',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -4687,12 +4687,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}}을(를) 꺼진 상태로 두었습니다 - 각 항목에 필요한 자격 증명은 백업에서 복원할 수 없고 이 인스턴스에도 저장되어 있지 않아, 켜면 연동이 인증 없이 열립니다',
|
||||
settingsMqttRelayFailed: 'MQTT 설정을 복원했지만 릴레이를 다시 연결하지 못했습니다 - Bambuddy를 재시작하세요',
|
||||
kprofilesAlwaysOverwrite: 'K 프로파일은 항상 프린터의 해당 슬롯을 덮어씁니다',
|
||||
kprofilesAckUnreliable: '프린터의 응답은 신뢰할 수 없습니다 - 프린터에서 프로파일을 확인하세요',
|
||||
kprofilesAckUnreliable: '응답하지 않는 프린터도 복원됨으로 집계됩니다 - 프린터에서 프로파일을 확인하세요',
|
||||
kprofilesPrinterMissing: '시리얼 {{serial}}인 프린터가 없습니다 - 건너뛰었습니다',
|
||||
kprofilesPrinterOffline: '{{printer}}({{serial}})이(가) 연결되어 있지 않습니다 - 건너뛰었습니다',
|
||||
kprofilesUnknownNozzle: '{{serial}}의 예상치 못한 노즐 직경 {{nozzle}} - 그대로 전송했습니다',
|
||||
kprofilesUnmatched: '{{nozzle}}용 프로파일 {{count}}개가 {{printer}}에 대응 항목이 없습니다 - 새 프로파일로 추가했습니다',
|
||||
kprofilesSendFailed: '{{nozzle}} 프로파일을 {{printer}}({{serial}})에 보내지 못했습니다',
|
||||
kprofilesRefused: '{{printer}}({{serial}})이(가) {{nozzle}} 프로파일을 거부했습니다: {{reason}}',
|
||||
},
|
||||
},
|
||||
history: '기록',
|
||||
|
||||
@@ -4910,12 +4910,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}} ficaram desligados - a credencial que cada um precisa não pode ser restaurada de um backup e esta instância não tem nenhuma armazenada, então ligá-los deixaria a integração sem autenticação',
|
||||
settingsMqttRelayFailed: 'Configurações MQTT restauradas, mas o relay não pôde ser reconectado - reinicie o Bambuddy',
|
||||
kprofilesAlwaysOverwrite: 'Os perfis K sempre sobrescrevem o slot correspondente na impressora',
|
||||
kprofilesAckUnreliable: 'A confirmação da impressora não é confiável - verifique os perfis na impressora',
|
||||
kprofilesAckUnreliable: 'Uma impressora que não responde ainda conta como restaurada - verifique os perfis na impressora',
|
||||
kprofilesPrinterMissing: 'Nenhuma impressora com o número de série {{serial}} - ignorado',
|
||||
kprofilesPrinterOffline: '{{printer}} ({{serial}}) não está conectada - ignorado',
|
||||
kprofilesUnknownNozzle: 'Diâmetro de bico inesperado {{nozzle}} para {{serial}} - enviado como está',
|
||||
kprofilesUnmatched: '{{count}} perfis para {{nozzle}} não tinham correspondente em {{printer}} - adicionados como novos perfis',
|
||||
kprofilesSendFailed: 'Não foi possível enviar os perfis de {{nozzle}} para {{printer}} ({{serial}})',
|
||||
kprofilesRefused: '{{printer}} ({{serial}}) recusou os perfis de {{nozzle}}: {{reason}}',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -4679,12 +4679,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}} оставлены выключенными - нужные им учётные данные нельзя восстановить из резервной копии, и в этом экземпляре они не сохранены, поэтому включение оставило бы интеграцию без аутентификации',
|
||||
settingsMqttRelayFailed: 'Настройки MQTT восстановлены, но переподключить реле не удалось - перезапустите Bambuddy',
|
||||
kprofilesAlwaysOverwrite: 'K-профили всегда перезаписывают соответствующий слот на принтере',
|
||||
kprofilesAckUnreliable: 'Подтверждение принтера ненадёжно - проверьте профили на принтере',
|
||||
kprofilesAckUnreliable: 'Принтер, который не отвечает, всё равно считается восстановленным - проверьте профили на принтере',
|
||||
kprofilesPrinterMissing: 'Нет принтера с серийным номером {{serial}} - пропущено',
|
||||
kprofilesPrinterOffline: '{{printer}} ({{serial}}) не подключён - пропущено',
|
||||
kprofilesUnknownNozzle: 'Неожиданный диаметр сопла {{nozzle}} для {{serial}} - отправлено как есть',
|
||||
kprofilesUnmatched: 'Профилей для {{nozzle}} без соответствия на {{printer}}: {{count}} - добавлены как новые профили',
|
||||
kprofilesSendFailed: 'Не удалось отправить профили {{nozzle}} на {{printer}} ({{serial}})',
|
||||
kprofilesRefused: '{{printer}} ({{serial}}) отклонил профили {{nozzle}}: {{reason}}',
|
||||
},
|
||||
},
|
||||
history: "История",
|
||||
|
||||
@@ -4900,12 +4900,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}} kapalı bırakıldı - her birinin ihtiyaç duyduğu kimlik bilgisi bir yedekten geri yüklenemez ve bu örnekte kayıtlı değil, dolayısıyla açmak entegrasyonu kimlik doğrulamasız bırakırdı',
|
||||
settingsMqttRelayFailed: 'MQTT ayarları geri yüklendi ancak röle yeniden bağlanamadı - Bambuddy\'yi yeniden başlatın',
|
||||
kprofilesAlwaysOverwrite: 'K profilleri yazıcıdaki eşleşen yuvanın her zaman üzerine yazar',
|
||||
kprofilesAckUnreliable: 'Yazıcının onayı güvenilir değil - profilleri yazıcıda doğrulayın',
|
||||
kprofilesAckUnreliable: 'Yanıt vermeyen bir yazıcı yine de geri yüklendi sayılır - profilleri yazıcıda doğrulayın',
|
||||
kprofilesPrinterMissing: '{{serial}} seri numaralı yazıcı yok - atlandı',
|
||||
kprofilesPrinterOffline: '{{printer}} ({{serial}}) bağlı değil - atlandı',
|
||||
kprofilesUnknownNozzle: '{{serial}} için beklenmeyen nozul çapı {{nozzle}} - olduğu gibi gönderildi',
|
||||
kprofilesUnmatched: '{{nozzle}} için {{count}} profilin {{printer}} üzerinde karşılığı yoktu - yeni profil olarak eklendi',
|
||||
kprofilesSendFailed: '{{nozzle}} profilleri {{printer}} ({{serial}}) yazıcısına gönderilemedi',
|
||||
kprofilesRefused: '{{printer}} ({{serial}}) {{nozzle}} profillerini reddetti: {{reason}}',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -4965,12 +4965,13 @@ export default {
|
||||
settingsCompanionSkipped: "{{keys}} залишено вимкненими - потрібні їм облікові дані не можна відновити з резервної копії, і в цьому екземплярі вони не збережені, тож увімкнення залишило б інтеграцію без автентифікації",
|
||||
settingsMqttRelayFailed: "Налаштування MQTT відновлено, але реле не вдалося перепідключити - перезапустіть Bambuddy",
|
||||
kprofilesAlwaysOverwrite: "K-профілі завжди перезаписують відповідний слот на принтері",
|
||||
kprofilesAckUnreliable: "Підтвердження принтера ненадійне - перевірте профілі на принтері",
|
||||
kprofilesAckUnreliable: "Принтер, який не відповідає, усе одно вважається відновленим - перевірте профілі на принтері",
|
||||
kprofilesPrinterMissing: "Немає принтера із серійним номером {{serial}} - пропущено",
|
||||
kprofilesPrinterOffline: "{{printer}} ({{serial}}) не підключено - пропущено",
|
||||
kprofilesUnknownNozzle: "Неочікуваний діаметр сопла {{nozzle}} для {{serial}} - надіслано як є",
|
||||
kprofilesUnmatched: "Профілів для {{nozzle}} без відповідника на {{printer}}: {{count}} - додано як нові профілі",
|
||||
kprofilesSendFailed: "Не вдалося надіслати профілі {{nozzle}} на {{printer}} ({{serial}})",
|
||||
kprofilesRefused: "{{printer}} ({{serial}}) відхилив профілі {{nozzle}}: {{reason}}",
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -4910,12 +4910,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}} 保持关闭 - 它们各自所需的凭据无法从备份恢复,本实例也没有存储,开启会让集成处于未认证状态',
|
||||
settingsMqttRelayFailed: 'MQTT 设置已恢复,但中继无法重新连接 - 请重启 Bambuddy',
|
||||
kprofilesAlwaysOverwrite: 'K 值配置总是覆盖打印机上对应的槽位',
|
||||
kprofilesAckUnreliable: '打印机的确认信息不可靠 - 请在打印机上核对配置',
|
||||
kprofilesAckUnreliable: '打印机不回应时仍计为已恢复 - 请在打印机上核对配置',
|
||||
kprofilesPrinterMissing: '没有序列号为 {{serial}} 的打印机 - 已跳过',
|
||||
kprofilesPrinterOffline: '{{printer}}({{serial}})未连接 - 已跳过',
|
||||
kprofilesUnknownNozzle: '{{serial}} 的喷嘴直径 {{nozzle}} 不在预期范围内 - 已原样发送',
|
||||
kprofilesUnmatched: '{{nozzle}} 的 {{count}} 个配置在 {{printer}} 上没有对应项 - 已作为新配置添加',
|
||||
kprofilesSendFailed: '无法将 {{nozzle}} 的配置发送到 {{printer}}({{serial}})',
|
||||
kprofilesRefused: '{{printer}}({{serial}})拒绝了 {{nozzle}} 的配置:{{reason}}',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -4910,12 +4910,13 @@ export default {
|
||||
settingsCompanionSkipped: '{{keys}} 維持關閉 - 它們各自所需的憑證無法從備份還原,本執行個體也沒有儲存,開啟會讓整合處於未驗證狀態',
|
||||
settingsMqttRelayFailed: 'MQTT 設定已還原,但中繼無法重新連線 - 請重新啟動 Bambuddy',
|
||||
kprofilesAlwaysOverwrite: 'K 值設定檔一律覆寫印表機上對應的插槽',
|
||||
kprofilesAckUnreliable: '印表機的確認訊息不可靠 - 請在印表機上核對設定檔',
|
||||
kprofilesAckUnreliable: '印表機未回應時仍計為已還原 - 請在印表機上核對設定檔',
|
||||
kprofilesPrinterMissing: '沒有序號為 {{serial}} 的印表機 - 已略過',
|
||||
kprofilesPrinterOffline: '{{printer}}({{serial}})未連線 - 已略過',
|
||||
kprofilesUnknownNozzle: '{{serial}} 的噴嘴直徑 {{nozzle}} 不在預期範圍內 - 已原樣傳送',
|
||||
kprofilesUnmatched: '{{nozzle}} 的 {{count}} 個設定檔在 {{printer}} 上沒有對應項 - 已新增為新設定檔',
|
||||
kprofilesSendFailed: '無法將 {{nozzle}} 的設定檔傳送到 {{printer}}({{serial}})',
|
||||
kprofilesRefused: '{{printer}}({{serial}})拒絕了 {{nozzle}} 的設定檔:{{reason}}',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user