iobroker.shoppingroute 0.4.3 → 0.5.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.
Files changed (100) hide show
  1. package/README.md +150 -23
  2. package/README_DE.md +16 -1
  3. package/admin/backup-i18n.js +281 -0
  4. package/admin/backup-transfer.html +27 -65
  5. package/admin/custom/assets/_virtual_mf-localSharedImportMap___mfe_internal__ShoppingRouteAdminSet__mf_owner__1-DPLVD4vF.js +1 -0
  6. package/admin/custom/assets/helpers-CsVkgmeS.js +1 -0
  7. package/admin/custom/assets/hostInit-BOPgFMXR.js +1 -0
  8. package/admin/custom/assets/index-0cn00gCp.js +1 -0
  9. package/admin/custom/{markets/assets/index-2HOz3j7B.js → assets/index-12xv0FYP.js} +5 -5
  10. package/admin/custom/{review/assets/pendingShares-Cpf2lozX.js → assets/pendingShares-1oZLrQ_M.js} +2 -2
  11. package/admin/custom/assets/{route-editor-components-rW7iuBNT.js → route-editor-components-DXuRqdfo.js} +2 -2
  12. package/admin/custom/catalogManager/assets/_virtual_mf-localSharedImportMap___mfe_internal__ShoppingRouteCatalogSet__mf_owner__1-DINIg-s7.js +1 -0
  13. package/admin/custom/catalogManager/assets/_virtual_mf___mfe_internal__ShoppingRouteCatalogSet__mf_owner__270978611965008__loadShare__react__loadShare__.js-Bw3vMJzu.js +1 -0
  14. package/admin/custom/catalogManager/assets/_virtual_mf___mfe_internal__ShoppingRouteCatalogSet__mf_owner__270978611965008__prebuild__react__prebuild__-B27G_6sf.js +9 -0
  15. package/admin/custom/catalogManager/assets/catalog-manager-components-D2quJvPg.js +153 -0
  16. package/admin/custom/catalogManager/assets/helpers-CsVkgmeS.js +1 -0
  17. package/admin/custom/catalogManager/assets/hostInit-CX4Yac1I.js +1 -0
  18. package/admin/custom/catalogManager/assets/index-0cn00gCp.js +1 -0
  19. package/admin/custom/{productGroups/assets/index-2HOz3j7B.js → catalogManager/assets/index-12xv0FYP.js} +5 -5
  20. package/admin/custom/{assets/pendingShares-JrGCtzTF.js → catalogManager/assets/pendingShares-iypADtDA.js} +2 -2
  21. package/admin/custom/catalogManager/assets/vite-preload-helper-Dp1pzeXC.js +1 -0
  22. package/admin/custom/catalogManager/catalogManager.js +13 -0
  23. package/admin/custom/catalogManager/mf-manifest.json +1 -0
  24. package/admin/custom/catalogManager/mf-stats.json +1 -0
  25. package/admin/custom/markets/assets/_virtual_mf-localSharedImportMap___mfe_internal__ShoppingRouteMarketsSet__mf_owner__1-DWD9ARvx.js +1 -0
  26. package/admin/custom/markets/assets/helpers-CsVkgmeS.js +1 -0
  27. package/admin/custom/markets/assets/hostInit-O4vB_3aJ.js +1 -0
  28. package/admin/custom/markets/assets/index-0cn00gCp.js +1 -0
  29. package/admin/custom/{assets/index-2HOz3j7B.js → markets/assets/index-12xv0FYP.js} +5 -5
  30. package/admin/custom/markets/assets/{markets-editor-components-CUcyPBPl.js → markets-editor-components-BAsgTAV7.js} +3 -3
  31. package/admin/custom/markets/assets/pendingShares-mvCLhLzv.js +2 -0
  32. package/admin/custom/markets/marketsEditor.js +2 -2
  33. package/admin/custom/markets/mf-manifest.json +1 -1
  34. package/admin/custom/markets/mf-stats.json +1 -1
  35. package/admin/custom/mf-manifest.json +1 -1
  36. package/admin/custom/mf-stats.json +1 -1
  37. package/admin/custom/productGroups/assets/_virtual_mf-localSharedImportMap___mfe_internal__ShoppingRouteProductGroupsSet__mf_owner__1-CuVRE3L9.js +1 -0
  38. package/admin/custom/productGroups/assets/helpers-CsVkgmeS.js +1 -0
  39. package/admin/custom/productGroups/assets/hostInit-BO6YEhS7.js +1 -0
  40. package/admin/custom/productGroups/assets/index-0cn00gCp.js +1 -0
  41. package/admin/custom/{review/assets/index-2HOz3j7B.js → productGroups/assets/index-12xv0FYP.js} +5 -5
  42. package/admin/custom/productGroups/assets/{pendingShares-Cmc6I2xN.js → pendingShares-CnxCGNGe.js} +2 -2
  43. package/admin/custom/productGroups/assets/{product-groups-editor-components-CLVNuU40.js → product-groups-editor-components-B7aO0sA_.js} +2 -2
  44. package/admin/custom/productGroups/mf-manifest.json +1 -1
  45. package/admin/custom/productGroups/mf-stats.json +1 -1
  46. package/admin/custom/productGroups/productGroupsEditor.js +2 -2
  47. package/admin/custom/review/assets/_virtual_mf-localSharedImportMap___mfe_internal__ShoppingRouteReviewSet__mf_owner__1-FjU0cujc.js +1 -0
  48. package/admin/custom/review/assets/helpers-CsVkgmeS.js +1 -0
  49. package/admin/custom/review/assets/hostInit-B25pGNwS.js +1 -0
  50. package/admin/custom/review/assets/index-0cn00gCp.js +1 -0
  51. package/admin/custom/review/assets/index-12xv0FYP.js +7 -0
  52. package/admin/custom/{markets/assets/pendingShares-DaxGcdrd.js → review/assets/pendingShares-DIMEss5x.js} +2 -2
  53. package/admin/custom/review/assets/{review-editor-components-BrjR0Z85.js → review-editor-components-BD-k-o3i.js} +2 -2
  54. package/admin/custom/review/mf-manifest.json +1 -1
  55. package/admin/custom/review/mf-stats.json +1 -1
  56. package/admin/custom/review/reviewEditor.js +2 -2
  57. package/admin/custom/routeEditor.js +2 -2
  58. package/admin/custom/shoppingList/assets/_virtual_mf-localSharedImportMap___mfe_internal__ShoppingRouteShoppingListSet__mf_owner__1-a7n9bT1O.js +1 -0
  59. package/admin/custom/shoppingList/assets/helpers-CsVkgmeS.js +1 -0
  60. package/admin/custom/shoppingList/assets/hostInit-D9RbSpMP.js +1 -0
  61. package/admin/custom/shoppingList/assets/index-0cn00gCp.js +1 -0
  62. package/admin/custom/shoppingList/assets/index-12xv0FYP.js +7 -0
  63. package/admin/custom/shoppingList/assets/{pendingShares-DUnOFOkU.js → pendingShares-CrfC535i.js} +2 -2
  64. package/admin/custom/shoppingList/assets/{shopping-list-editor-components-DdXx3Xxu.js → shopping-list-editor-components-Q9aad4al.js} +2 -2
  65. package/admin/custom/shoppingList/mf-manifest.json +1 -1
  66. package/admin/custom/shoppingList/mf-stats.json +1 -1
  67. package/admin/custom/shoppingList/shoppingListEditor.js +2 -2
  68. package/admin/i18n/de.json +0 -6
  69. package/admin/i18n/en.json +0 -6
  70. package/admin/i18n/es.json +0 -6
  71. package/admin/i18n/fr.json +0 -6
  72. package/admin/i18n/it.json +0 -6
  73. package/admin/i18n/nl.json +0 -6
  74. package/admin/i18n/pl.json +0 -6
  75. package/admin/i18n/pt.json +0 -6
  76. package/admin/i18n/ru.json +0 -6
  77. package/admin/i18n/uk.json +0 -6
  78. package/admin/i18n/zh-cn.json +0 -6
  79. package/admin/jsonConfig.json +16 -17
  80. package/admin/jsonTab.json +18 -0
  81. package/build/lib/config-protection.js +50 -0
  82. package/build/main.js +126 -6
  83. package/io-package.json +84 -32
  84. package/package.json +2 -2
  85. package/admin/custom/assets/_virtual_mf-localSharedImportMap___mfe_internal__ShoppingRouteAdminSet__mf_owner__1-CifzZTJE.js +0 -1
  86. package/admin/custom/assets/hostInit-BFP_9qwB.js +0 -1
  87. package/admin/custom/assets/virtualExposes-D8D1innc.js +0 -1
  88. package/admin/custom/markets/assets/_virtual_mf-localSharedImportMap___mfe_internal__ShoppingRouteMarketsSet__mf_owner__1-CdXcBfjG.js +0 -1
  89. package/admin/custom/markets/assets/hostInit-CrDCY7SQ.js +0 -1
  90. package/admin/custom/markets/assets/virtualExposes-BhGZWOBB.js +0 -1
  91. package/admin/custom/productGroups/assets/_virtual_mf-localSharedImportMap___mfe_internal__ShoppingRouteProductGroupsSet__mf_owner__1-DXUHDGDp.js +0 -1
  92. package/admin/custom/productGroups/assets/hostInit-D8t8XJ7f.js +0 -1
  93. package/admin/custom/productGroups/assets/virtualExposes-Cci9-KkN.js +0 -1
  94. package/admin/custom/review/assets/_virtual_mf-localSharedImportMap___mfe_internal__ShoppingRouteReviewSet__mf_owner__1-CSWI0Knk.js +0 -1
  95. package/admin/custom/review/assets/hostInit-Bi90nPL8.js +0 -1
  96. package/admin/custom/review/assets/virtualExposes-C4BXYjQJ.js +0 -1
  97. package/admin/custom/shoppingList/assets/_virtual_mf-localSharedImportMap___mfe_internal__ShoppingRouteShoppingListSet__mf_owner__1-DRCeDtVc.js +0 -1
  98. package/admin/custom/shoppingList/assets/hostInit-usOXh1g1.js +0 -1
  99. package/admin/custom/shoppingList/assets/index-2HOz3j7B.js +0 -7
  100. package/admin/custom/shoppingList/assets/virtualExposes-9FoWCC98.js +0 -1
package/README.md CHANGED
@@ -1,45 +1,172 @@
1
1
  # ShoppingRoute for ioBroker
2
2
 
3
- ![ShoppingRoute](admin/shoppingroute.png)
3
+ <p align="center">
4
+ <img src="admin/shoppingroute.png" alt="ShoppingRoute" width="160">
5
+ </p>
4
6
 
5
- **Current version: 0.4.0**
7
+ <p align="center">
8
+ <strong>Smart Alexa shopping lists — sorted by store, product group and your real walking route.</strong>
9
+ </p>
6
10
 
7
- ShoppingRoute sorts Alexa shopping-list entries by market, product group and each store's individual walking route. It assigns visible two-digit keys such as `20> Bananas` and `40> ═════ ALDI ═════`; managed lists must therefore be set to **A–Z** in the Alexa app. ShoppingRoute reuses the local Alexa2 authentication for direct item updates, deletes and batch creates, while Alexa2 list states remain the external change trigger.
11
+ <p align="center">
12
+ <img src="http://iobroker.live/badges/shoppingroute-installed.svg" alt="ioBroker installations">
13
+ <img src="http://iobroker.live/badges/shoppingroute-stable.svg" alt="ioBroker stable installations">
14
+ <a href="https://www.npmjs.com/package/iobroker.shoppingroute"><img src="https://img.shields.io/npm/v/iobroker.shoppingroute.svg" alt="npm version"></a>
15
+ <a href="https://www.npmjs.com/package/iobroker.shoppingroute"><img src="https://img.shields.io/npm/dm/iobroker.shoppingroute.svg" alt="npm downloads"></a>
16
+ <a href="https://github.com/RaviniZib/ioBroker.shoppingroute/actions/workflows/test-and-release.yml"><img src="https://github.com/RaviniZib/ioBroker.shoppingroute/actions/workflows/test-and-release.yml/badge.svg" alt="Test and Release"></a>
17
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT license"></a>
18
+ </p>
8
19
 
9
- ## User guide / Bedienungsanleitung
20
+ > **Current version: 0.4.4**
21
+ > ShoppingRoute is available in the ioBroker **latest** repository.
10
22
 
11
- 🇬🇧 [**English user guide**](USER_GUIDE_EN.md)
12
- 🇩🇪 [**Deutsche Bedienungsanleitung**](BEDIENUNGSANLEITUNG_DE.md)
23
+ ## What ShoppingRoute does
13
24
 
14
- ## Highlights
25
+ ShoppingRoute turns an ordinary Alexa shopping list into a list that follows the way you actually shop.
15
26
 
16
- - multiple Alexa lists with per-list priority markets
17
- - global, per-list and temporary market priorities
18
- - market aliases and common market-name variants
19
- - optional automatically managed market headings such as `═════ ALDI ═════`
20
- - optional cross-market consolidation using a minimum-item threshold; explicit market requests are never moved
21
- - configurable product groups and store-specific routes
22
- - product catalogue with aliases, preferred and available markets
23
- - quantity parser for digits, number words, packs, half-kilo and `6x` forms
24
- - duplicate-resistant product learning
27
+ Instead of keeping items only in the order Alexa received them, the adapter can assign them to stores, product groups and a configurable walking route inside each store. It uses visible two-digit prefixes such as `20> Bananas` and optional market headings such as `40> ═════ ALDI ═════`.
28
+
29
+ That means a list can automatically become something like:
30
+
31
+ ```text
32
+ 10> Apples
33
+ 20> Bananas
34
+ 30> Bread
35
+ 40> ═════ ALDI ═════
36
+ 50> Milk
37
+ 60> Cheese
38
+ 70> Coffee
39
+ ```
40
+
41
+ Managed Alexa lists must be set to **A–Z** in the Alexa app. ShoppingRoute then controls the effective order through its prefixes.
42
+
43
+ ShoppingRoute reuses the local authentication of the ioBroker **Alexa2** adapter for direct item updates, deletions and batch creation. Alexa2 list states remain the external change trigger.
44
+
45
+ **Service / manufacturer reference:** ShoppingRoute works with Amazon Alexa shopping lists. Amazon documents Alexa lists as a customer feature of the Alexa ecosystem; customers can access Alexa shopping and to-do lists through the Alexa app, Amazon app and Amazon retail site. See the [official Amazon Alexa developer documentation](https://www.developer.amazon.com/en-US/docs/alexa/ask-overviews/deprecated-features.html#list-skills-and-alexa-shopping-and-to-do-lists).
46
+
47
+ ## Documentation
48
+
49
+ Choose your language:
50
+
51
+ - 🇬🇧 **English:** [User guide](USER_GUIDE_EN.md)
52
+ - 🇩🇪 **Deutsch:** [Bedienungsanleitung](BEDIENUNGSANLEITUNG_DE.md) · [Ausführliche README](README_DE.md)
53
+
54
+ Community and support:
55
+
56
+ - 🧪 [ioBroker tester forum – ShoppingRoute v0.4.4](https://forum.iobroker.net/topic/85510/test-adapter-shoppingroute-v0.4.4)
57
+ - 🐞 [GitHub issues](https://github.com/RaviniZib/ioBroker.shoppingroute/issues)
58
+
59
+ The Admin interface itself supports all 11 standard ioBroker Admin languages.
60
+
61
+ ## Main features
62
+
63
+ - dedicated **ShoppingRoute management page** in the ioBroker sidebar for shopping list, products, markets, product groups, routes, lists and review items
64
+ - catalogue changes are persisted at runtime and do not require an adapter restart
65
+ - market names are always stored in **UPPERCASE**, regardless of how they are entered, with all references normalized consistently
66
+ - defensive protection against accidental catalogue resets during Admin/update flows
67
+
68
+ ### Store and route based sorting
69
+
70
+ - configurable stores and store aliases
71
+ - individual walking route for every store
72
+ - product groups with independent sort order
73
+ - preferred and available markets per product
74
+ - global, per-list and temporary priority markets
75
+ - optional market headings such as `═════ ALDI ═════`
76
+ - optional cross-market consolidation using a minimum-item threshold
77
+ - explicit market requests are never moved to another store
78
+
79
+ ### Smart product handling
80
+
81
+ - product catalogue with aliases
82
+ - duplicate-resistant learning
25
83
  - review queue for unknown products
26
- - automatic/review/off learning modes
84
+ - automatic, review and off learning modes
27
85
  - category and alias suggestions
28
- - sorting preview before writes
29
- - incremental `00>`–`99>` prefix sorting with gap-preserving inserts and suffix-only rebuilds
30
- - direct Amazon responses plus one final list read as write confirmation
86
+ - quantity parsing for digits, number words, packs, half-kilo expressions and `6x` forms
87
+ - manual per-item market and position overrides
88
+
89
+ ### Alexa-safe list updates
90
+
91
+ - incremental `00>`–`99>` prefix sorting
92
+ - gap-preserving inserts to avoid unnecessary rewrites
93
+ - suffix-only rebuilds when a numeric gap is exhausted
94
+ - direct Amazon responses used to confirm operations
95
+ - final direct list read for verification
96
+ - exclusive transaction handling to prevent overlapping writes
97
+ - recovery journal for interrupted operations
31
98
  - API Safe Mode with configurable write-rate limiting
99
+ - bounded Alexa callbacks and polling with backoff
100
+
101
+ ### Admin and diagnostics
102
+
103
+ - interactive current shopping-list view
104
+ - drag & drop plus touch-friendly controls
105
+ - sorting preview before writes
32
106
  - local-only shopping statistics
33
- - configuration backup/restore
107
+ - configuration backup and restore
34
108
  - shareable market-route profiles
35
- - privacy-safe diagnostic/feedback report
109
+ - privacy-safe diagnostic and feedback report
36
110
  - Alexa2/alexa-remote2 direct-session diagnostics
37
111
  - Dry Run safety mode
38
112
 
39
- See `README_DE.md` for the detailed German documentation.
113
+ ## Requirements
114
+
115
+ - ioBroker with **Admin 8 or newer**
116
+ - **js-controller 7.1.0 or newer**
117
+ - a working ioBroker **Alexa2** adapter instance
118
+ - the Alexa shopping list managed by ShoppingRoute must be sorted **A–Z** in the Alexa app
119
+
120
+ ## How it works
121
+
122
+ 1. Alexa2 reports a shopping-list change to ioBroker.
123
+ 2. ShoppingRoute reads the affected list and normalizes the item names.
124
+ 3. Products are matched against aliases, product groups and market assignments.
125
+ 4. The adapter builds the best market and route plan.
126
+ 5. Only the required list changes are written back to Amazon.
127
+ 6. The final remote list is read again and verified.
128
+
129
+ The browser-based Admin interface never receives Alexa or Amazon credentials.
130
+
131
+ ## Privacy and safety
132
+
133
+ ShoppingRoute does not require its own Amazon login. It reuses the locally stored Alexa2 session and does not log authentication secrets.
134
+
135
+ Statistics are stored locally. The feedback report is designed to be privacy-safe, and Dry Run can be used to inspect planned changes without modifying the shopping list.
136
+
137
+ ## Installation
138
+
139
+ Install ShoppingRoute from the ioBroker **Adapters** page while using the **latest** repository.
140
+
141
+ After installation:
142
+
143
+ 1. create an instance of ShoppingRoute,
144
+ 2. select/configure the Alexa shopping list,
145
+ 3. define markets and product groups,
146
+ 4. configure the walking route for each store,
147
+ 5. set the managed Alexa list to **A–Z**,
148
+ 6. start with **Dry Run** if you want to inspect the result before enabling writes.
149
+
150
+ For all configuration details, see the [English user guide](USER_GUIDE_EN.md) or the [German user guide](BEDIENUNGSANLEITUNG_DE.md).
151
+
152
+ ## Feedback and support
153
+
154
+ ShoppingRoute is still young, so real-world feedback is especially valuable.
155
+
156
+ Please use the [ioBroker tester thread](https://forum.iobroker.net/topic/85510/test-adapter-shoppingroute-v0.4.4) for general testing feedback and the [GitHub issue tracker](https://github.com/RaviniZib/ioBroker.shoppingroute/issues) for reproducible bugs or feature requests.
40
157
 
41
158
  ## Changelog
42
159
 
160
+ ### 0.5.0 (2026-10-05)
161
+ - Add a dedicated ShoppingRoute management page in the ioBroker sidebar with a fixed header and direct management of shopping list, products, markets, product groups, routes, lists and review queue.
162
+ - Persist large catalogue data outside normal instance configuration so catalogue edits no longer require an adapter restart.
163
+ - Add defensive recovery and regression coverage for the destructive Admin/default reset reported in issue #59, including a successful real-instance reproduction test.
164
+ - Normalize every market name to UPPERCASE on create, rename, load and save, and update market references consistently.
165
+ - Refine the management UI with a stable sticky header, logo and shaded/zebra list blocks.
166
+
167
+ ### 0.4.4 (2026-09-25)
168
+ - (RaviniZib) Use `No Market` for new fallback-market defaults, add all 11 admin languages to the backup utility, and remove six unused translation keys. Existing market names, routes and product data remain unchanged.
169
+
43
170
  ### 0.4.3 (2026-09-25)
44
171
  - (RaviniZib) Route Alexa callback timeouts through ioBroker-managed timers so stalled callbacks stay bounded without plain Node.js `setTimeout()` calls in adapter source.
45
172
  - (RaviniZib) Keep `common.news` within the seven entries supported by the repository builder.
package/README_DE.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ![ShoppingRoute](admin/shoppingroute.png)
4
4
 
5
- **Aktuelle Version: 0.4.0**
5
+ **Aktuelle Version: 0.5.0**
6
6
 
7
7
  ShoppingRoute sortiert Alexa-Einkaufslisteneinträge nach Markt, Produktgruppe und dem individuellen Laufweg durch den jeweiligen Markt. Dazu vergibt es sichtbare zweistellige Schlüssel wie `20> Bananen` und `40> ═════ ALDI ═════`; verwaltete Listen müssen deshalb in der Alexa-App auf **A–Z** stehen. ShoppingRoute übernimmt lokal die Alexa2-Authentifizierung für direkte Updates, Deletes und Batch-Creates; Alexa2-Listenstates bleiben die Triggerquelle für externe Änderungen.
8
8
 
@@ -13,6 +13,11 @@ ShoppingRoute sortiert Alexa-Einkaufslisteneinträge nach Markt, Produktgruppe u
13
13
 
14
14
  ## Funktionen
15
15
 
16
+ - eigene **ShoppingRoute-Verwaltungsseite** in der ioBroker-Seitenleiste für Einkaufsliste, Artikel, Märkte, Produktgruppen, Laufwege, Listen und Prüfung
17
+ - Katalogänderungen werden zur Laufzeit gespeichert und benötigen keinen Adapter-Neustart
18
+ - Marktnamen werden unabhängig von der Eingabe automatisch in **GROSSBUCHSTABEN** gespeichert; alle Marktverweise werden konsistent mit normalisiert
19
+ - Schutz vor versehentlichem Zurücksetzen großer Katalogdaten durch Admin-/Update-Vorgänge
20
+
16
21
  - mehrere Alexa-Einkaufslisten mit eigenem Prioritätsmarkt
17
22
  - globale, listenbezogene und temporäre Marktpriorität
18
23
  - Markt-Aliase und automatische Erkennung häufiger Marktvarianten
@@ -73,6 +78,16 @@ ShoppingRoute wird unter der **[MIT-Lizenz](LICENSE)** veröffentlicht. Frühere
73
78
 
74
79
  ## Changelog
75
80
 
81
+ ### 0.5.0 (2026-10-05)
82
+ - Neue eigenständige ShoppingRoute-Verwaltungsseite in der ioBroker-Seitenleiste mit fester Kopfzeile sowie Einkaufsliste, Artikel-, Markt-, Produktgruppen-, Laufweg-, Listen- und Prüfverwaltung.
83
+ - Große Katalogdaten werden außerhalb der normalen Instanzkonfiguration als Laufzeitdaten gespeichert; Änderungen benötigen keinen Adapter-Neustart mehr.
84
+ - Schutzmechanismus und Regressionstests verhindern den in Issue #59 beobachteten Rückfall auf Paket-Standarddaten; zusätzlich auf einer realen ioBroker-Instanz geprüft.
85
+ - Marktnamen werden beim Anlegen, Umbenennen, Laden und Speichern immer in GROSSBUCHSTABEN normalisiert; Marktverweise werden konsistent mitgezogen.
86
+ - Überarbeitete Verwaltungsoberfläche mit festem Header, Logo und dezenter Block-/Zebra-Darstellung.
87
+
88
+ ### 0.4.4 (2026-09-25)
89
+ - (RaviniZib) `No Market` als Standard-Ausweichmarkt für neue Konfigurationen, Backup-Seite in allen 11 Admin-Sprachen und sechs ungenutzte Übersetzungsschlüssel entfernt. Vorhandene Marktnamen, Laufwege und Produktdaten bleiben unverändert.
90
+
76
91
  ### 0.4.3 (2026-09-25)
77
92
  - Alexa-Callback-Timeouts verwenden jetzt ioBroker-verwaltete Timer; hängende Aufrufe bleiben damit begrenzt, ohne nacktes Node.js-`setTimeout()` im Adapter-Quellcode.
78
93
  - `common.news` wird auf die vom Repository-Builder unterstützten sieben Einträge begrenzt.
@@ -0,0 +1,281 @@
1
+ 'use strict';
2
+
3
+ // Standalone backup utility: keep its translations separate from JSON Config keys.
4
+ (() => {
5
+ const translations = {
6
+ "de": {
7
+ "configTitle": "Komplette Konfiguration",
8
+ "configText": "Alle ShoppingRoute-Einstellungen direkt als JSON-Datei sichern oder aus einer Sicherungsdatei wiederherstellen.",
9
+ "downloadConfig": "↓ Sicherung herunterladen",
10
+ "uploadConfig": "↑ Sicherung wiederherstellen",
11
+ "marketTitle": "Marktprofil teilen",
12
+ "marketText": "Einen einzelnen Markt mit seinem Laufweg herunterladen oder ein Marktprofil aus einer JSON-Datei importieren.",
13
+ "downloadMarket": "↓ Marktprofil herunterladen",
14
+ "uploadMarket": "↑ Marktprofil importieren",
15
+ "loading": "Marktprofile werden geladen …",
16
+ "noMarkets": "Keine Marktprofile vorhanden",
17
+ "backupDownloaded": "Sicherung wurde heruntergeladen.",
18
+ "profileDownloaded": "Marktprofil wurde heruntergeladen.",
19
+ "configImportStarted": "Sicherung wurde übergeben. ShoppingRoute übernimmt die Konfiguration und startet anschließend neu.",
20
+ "profileImportStarted": "Marktprofil wurde übergeben und wird von ShoppingRoute übernommen.",
21
+ "invalidBackup": "Die gewählte Datei ist keine gültige JSON-Sicherung.",
22
+ "invalidProfile": "Die gewählte Datei ist kein gültiges ShoppingRoute-Marktprofil.",
23
+ "noInstance": "ShoppingRoute-Instanz konnte nicht ermittelt werden.",
24
+ "connectionError": "Verbindung zu ioBroker konnte nicht hergestellt werden.",
25
+ "exportError": "Sicherung konnte nicht erstellt werden.",
26
+ "profileError": "Marktprofile konnten nicht geladen werden.",
27
+ "title": "ShoppingRoute – Sicherung / Teilen",
28
+ "genericMarket": "Markt"
29
+ },
30
+ "en": {
31
+ "configTitle": "Complete configuration",
32
+ "configText": "Download all ShoppingRoute settings directly as a JSON file or restore them from a backup file.",
33
+ "downloadConfig": "↓ Download backup",
34
+ "uploadConfig": "↑ Restore backup",
35
+ "marketTitle": "Share market profile",
36
+ "marketText": "Download one market with its walking route or import a market profile from a JSON file.",
37
+ "downloadMarket": "↓ Download market profile",
38
+ "uploadMarket": "↑ Import market profile",
39
+ "loading": "Loading market profiles …",
40
+ "noMarkets": "No market profiles available",
41
+ "backupDownloaded": "Backup downloaded.",
42
+ "profileDownloaded": "Market profile downloaded.",
43
+ "configImportStarted": "Backup submitted. ShoppingRoute will apply the configuration and restart afterwards.",
44
+ "profileImportStarted": "Market profile submitted and will be applied by ShoppingRoute.",
45
+ "invalidBackup": "The selected file is not a valid JSON backup.",
46
+ "invalidProfile": "The selected file is not a valid ShoppingRoute market profile.",
47
+ "noInstance": "ShoppingRoute instance could not be determined.",
48
+ "connectionError": "Could not connect to ioBroker.",
49
+ "exportError": "Backup could not be created.",
50
+ "profileError": "Market profiles could not be loaded.",
51
+ "title": "ShoppingRoute – Backup / Share",
52
+ "genericMarket": "Market"
53
+ },
54
+ "es": {
55
+ "configTitle": "Configuración completa",
56
+ "configText": "Descargue todos los ajustes de ShoppingRoute como archivo JSON o restáurelos desde una copia de seguridad.",
57
+ "downloadConfig": "↓ Descargar copia de seguridad",
58
+ "uploadConfig": "↑ Restaurar copia de seguridad",
59
+ "marketTitle": "Compartir perfil de tienda",
60
+ "marketText": "Descargue una tienda con su recorrido o importe un perfil de tienda desde un archivo JSON.",
61
+ "downloadMarket": "↓ Descargar perfil de tienda",
62
+ "uploadMarket": "↑ Importar perfil de tienda",
63
+ "loading": "Cargando perfiles de tienda …",
64
+ "noMarkets": "No hay perfiles de tienda disponibles",
65
+ "backupDownloaded": "Copia de seguridad descargada.",
66
+ "profileDownloaded": "Perfil de tienda descargado.",
67
+ "configImportStarted": "Copia de seguridad enviada. ShoppingRoute aplicará la configuración y se reiniciará.",
68
+ "profileImportStarted": "Perfil de tienda enviado. ShoppingRoute lo aplicará.",
69
+ "invalidBackup": "El archivo seleccionado no es una copia de seguridad JSON válida.",
70
+ "invalidProfile": "El archivo seleccionado no es un perfil de tienda de ShoppingRoute válido.",
71
+ "noInstance": "No se pudo determinar la instancia de ShoppingRoute.",
72
+ "connectionError": "No se pudo conectar con ioBroker.",
73
+ "exportError": "No se pudo crear la copia de seguridad.",
74
+ "profileError": "No se pudieron cargar los perfiles de tienda.",
75
+ "title": "ShoppingRoute – Copias de seguridad / Compartir",
76
+ "genericMarket": "Tienda"
77
+ },
78
+ "fr": {
79
+ "configTitle": "Configuration complète",
80
+ "configText": "Téléchargez tous les paramètres de ShoppingRoute dans un fichier JSON ou restaurez-les depuis une sauvegarde.",
81
+ "downloadConfig": "↓ Télécharger la sauvegarde",
82
+ "uploadConfig": "↑ Restaurer une sauvegarde",
83
+ "marketTitle": "Partager un profil de magasin",
84
+ "marketText": "Téléchargez un magasin avec son parcours ou importez un profil de magasin depuis un fichier JSON.",
85
+ "downloadMarket": "↓ Télécharger le profil de magasin",
86
+ "uploadMarket": "↑ Importer un profil de magasin",
87
+ "loading": "Chargement des profils de magasin …",
88
+ "noMarkets": "Aucun profil de magasin disponible",
89
+ "backupDownloaded": "Sauvegarde téléchargée.",
90
+ "profileDownloaded": "Profil de magasin téléchargé.",
91
+ "configImportStarted": "Sauvegarde transmise. ShoppingRoute appliquera la configuration puis redémarrera.",
92
+ "profileImportStarted": "Profil de magasin transmis. ShoppingRoute va l’appliquer.",
93
+ "invalidBackup": "Le fichier sélectionné n’est pas une sauvegarde JSON valide.",
94
+ "invalidProfile": "Le fichier sélectionné n’est pas un profil de magasin ShoppingRoute valide.",
95
+ "noInstance": "Impossible de déterminer l’instance ShoppingRoute.",
96
+ "connectionError": "Impossible de se connecter à ioBroker.",
97
+ "exportError": "Impossible de créer la sauvegarde.",
98
+ "profileError": "Impossible de charger les profils de magasin.",
99
+ "title": "ShoppingRoute – Sauvegarde / Partage",
100
+ "genericMarket": "Magasin"
101
+ },
102
+ "it": {
103
+ "configTitle": "Configurazione completa",
104
+ "configText": "Scarica tutte le impostazioni di ShoppingRoute come file JSON o ripristinale da un backup.",
105
+ "downloadConfig": "↓ Scarica backup",
106
+ "uploadConfig": "↑ Ripristina backup",
107
+ "marketTitle": "Condividi profilo del negozio",
108
+ "marketText": "Scarica un negozio con il suo percorso o importa un profilo del negozio da un file JSON.",
109
+ "downloadMarket": "↓ Scarica profilo del negozio",
110
+ "uploadMarket": "↑ Importa profilo del negozio",
111
+ "loading": "Caricamento dei profili dei negozi …",
112
+ "noMarkets": "Nessun profilo del negozio disponibile",
113
+ "backupDownloaded": "Backup scaricato.",
114
+ "profileDownloaded": "Profilo del negozio scaricato.",
115
+ "configImportStarted": "Backup inviato. ShoppingRoute applicherà la configurazione e si riavvierà.",
116
+ "profileImportStarted": "Profilo del negozio inviato. ShoppingRoute lo applicherà.",
117
+ "invalidBackup": "Il file selezionato non è un backup JSON valido.",
118
+ "invalidProfile": "Il file selezionato non è un profilo del negozio ShoppingRoute valido.",
119
+ "noInstance": "Impossibile determinare l’istanza ShoppingRoute.",
120
+ "connectionError": "Impossibile connettersi a ioBroker.",
121
+ "exportError": "Impossibile creare il backup.",
122
+ "profileError": "Impossibile caricare i profili dei negozi.",
123
+ "title": "ShoppingRoute – Backup / Condivisione",
124
+ "genericMarket": "Negozio"
125
+ },
126
+ "nl": {
127
+ "configTitle": "Volledige configuratie",
128
+ "configText": "Download alle ShoppingRoute-instellingen als JSON-bestand of herstel ze vanuit een back-up.",
129
+ "downloadConfig": "↓ Back-up downloaden",
130
+ "uploadConfig": "↑ Back-up herstellen",
131
+ "marketTitle": "Winkelprofiel delen",
132
+ "marketText": "Download een winkel met de bijbehorende looproute of importeer een winkelprofiel uit een JSON-bestand.",
133
+ "downloadMarket": "↓ Winkelprofiel downloaden",
134
+ "uploadMarket": "↑ Winkelprofiel importeren",
135
+ "loading": "Winkelprofielen laden …",
136
+ "noMarkets": "Geen winkelprofielen beschikbaar",
137
+ "backupDownloaded": "Back-up gedownload.",
138
+ "profileDownloaded": "Winkelprofiel gedownload.",
139
+ "configImportStarted": "Back-up verzonden. ShoppingRoute past de configuratie toe en start daarna opnieuw.",
140
+ "profileImportStarted": "Winkelprofiel verzonden. ShoppingRoute zal het toepassen.",
141
+ "invalidBackup": "Het geselecteerde bestand is geen geldige JSON-back-up.",
142
+ "invalidProfile": "Het geselecteerde bestand is geen geldig ShoppingRoute-winkelprofiel.",
143
+ "noInstance": "De ShoppingRoute-instantie kon niet worden bepaald.",
144
+ "connectionError": "Verbinding met ioBroker is mislukt.",
145
+ "exportError": "De back-up kon niet worden gemaakt.",
146
+ "profileError": "De winkelprofielen konden niet worden geladen.",
147
+ "title": "ShoppingRoute – Back-up / Delen",
148
+ "genericMarket": "Winkel"
149
+ },
150
+ "pt": {
151
+ "configTitle": "Configuração completa",
152
+ "configText": "Transfira todas as definições do ShoppingRoute como ficheiro JSON ou restaure-as a partir de uma cópia de segurança.",
153
+ "downloadConfig": "↓ Transferir cópia de segurança",
154
+ "uploadConfig": "↑ Restaurar cópia de segurança",
155
+ "marketTitle": "Partilhar perfil de loja",
156
+ "marketText": "Transfira uma loja com o respetivo percurso ou importe um perfil de loja a partir de um ficheiro JSON.",
157
+ "downloadMarket": "↓ Transferir perfil de loja",
158
+ "uploadMarket": "↑ Importar perfil de loja",
159
+ "loading": "A carregar perfis de loja …",
160
+ "noMarkets": "Não existem perfis de loja disponíveis",
161
+ "backupDownloaded": "Cópia de segurança transferida.",
162
+ "profileDownloaded": "Perfil de loja transferido.",
163
+ "configImportStarted": "Cópia de segurança enviada. O ShoppingRoute aplicará a configuração e reiniciará.",
164
+ "profileImportStarted": "Perfil de loja enviado. O ShoppingRoute irá aplicá-lo.",
165
+ "invalidBackup": "O ficheiro selecionado não é uma cópia de segurança JSON válida.",
166
+ "invalidProfile": "O ficheiro selecionado não é um perfil de loja ShoppingRoute válido.",
167
+ "noInstance": "Não foi possível determinar a instância do ShoppingRoute.",
168
+ "connectionError": "Não foi possível ligar ao ioBroker.",
169
+ "exportError": "Não foi possível criar a cópia de segurança.",
170
+ "profileError": "Não foi possível carregar os perfis de loja.",
171
+ "title": "ShoppingRoute – Cópia de segurança / Partilha",
172
+ "genericMarket": "Loja"
173
+ },
174
+ "pl": {
175
+ "configTitle": "Pełna konfiguracja",
176
+ "configText": "Pobierz wszystkie ustawienia ShoppingRoute jako plik JSON lub przywróć je z kopii zapasowej.",
177
+ "downloadConfig": "↓ Pobierz kopię zapasową",
178
+ "uploadConfig": "↑ Przywróć kopię zapasową",
179
+ "marketTitle": "Udostępnij profil sklepu",
180
+ "marketText": "Pobierz sklep wraz z trasą zakupów lub zaimportuj profil sklepu z pliku JSON.",
181
+ "downloadMarket": "↓ Pobierz profil sklepu",
182
+ "uploadMarket": "↑ Importuj profil sklepu",
183
+ "loading": "Ładowanie profili sklepów …",
184
+ "noMarkets": "Brak dostępnych profili sklepów",
185
+ "backupDownloaded": "Pobrano kopię zapasową.",
186
+ "profileDownloaded": "Pobrano profil sklepu.",
187
+ "configImportStarted": "Przesłano kopię zapasową. ShoppingRoute zastosuje konfigurację i uruchomi się ponownie.",
188
+ "profileImportStarted": "Przesłano profil sklepu. ShoppingRoute go zastosuje.",
189
+ "invalidBackup": "Wybrany plik nie jest prawidłową kopią zapasową JSON.",
190
+ "invalidProfile": "Wybrany plik nie jest prawidłowym profilem sklepu ShoppingRoute.",
191
+ "noInstance": "Nie udało się ustalić instancji ShoppingRoute.",
192
+ "connectionError": "Nie udało się połączyć z ioBroker.",
193
+ "exportError": "Nie udało się utworzyć kopii zapasowej.",
194
+ "profileError": "Nie udało się załadować profili sklepów.",
195
+ "title": "ShoppingRoute – Kopia zapasowa / Udostępnianie",
196
+ "genericMarket": "Sklep"
197
+ },
198
+ "ru": {
199
+ "configTitle": "Полная конфигурация",
200
+ "configText": "Скачайте все настройки ShoppingRoute в виде файла JSON или восстановите их из резервной копии.",
201
+ "downloadConfig": "↓ Скачать резервную копию",
202
+ "uploadConfig": "↑ Восстановить резервную копию",
203
+ "marketTitle": "Поделиться профилем магазина",
204
+ "marketText": "Скачайте профиль магазина с маршрутом или импортируйте профиль из файла JSON.",
205
+ "downloadMarket": "↓ Скачать профиль магазина",
206
+ "uploadMarket": "↑ Импортировать профиль магазина",
207
+ "loading": "Загрузка профилей магазинов …",
208
+ "noMarkets": "Нет доступных профилей магазинов",
209
+ "backupDownloaded": "Резервная копия скачана.",
210
+ "profileDownloaded": "Профиль магазина скачан.",
211
+ "configImportStarted": "Резервная копия передана. ShoppingRoute применит конфигурацию и перезапустится.",
212
+ "profileImportStarted": "Профиль магазина передан. ShoppingRoute применит его.",
213
+ "invalidBackup": "Выбранный файл не является корректной резервной копией JSON.",
214
+ "invalidProfile": "Выбранный файл не является корректным профилем магазина ShoppingRoute.",
215
+ "noInstance": "Не удалось определить экземпляр ShoppingRoute.",
216
+ "connectionError": "Не удалось подключиться к ioBroker.",
217
+ "exportError": "Не удалось создать резервную копию.",
218
+ "profileError": "Не удалось загрузить профили магазинов.",
219
+ "title": "ShoppingRoute – Резервная копия / Обмен",
220
+ "genericMarket": "Магазин"
221
+ },
222
+ "uk": {
223
+ "configTitle": "Повна конфігурація",
224
+ "configText": "Завантажте всі налаштування ShoppingRoute у файлі JSON або відновіть їх із резервної копії.",
225
+ "downloadConfig": "↓ Завантажити резервну копію",
226
+ "uploadConfig": "↑ Відновити резервну копію",
227
+ "marketTitle": "Поділитися профілем магазину",
228
+ "marketText": "Завантажте профіль магазину з маршрутом або імпортуйте профіль із файлу JSON.",
229
+ "downloadMarket": "↓ Завантажити профіль магазину",
230
+ "uploadMarket": "↑ Імпортувати профіль магазину",
231
+ "loading": "Завантаження профілів магазинів …",
232
+ "noMarkets": "Немає доступних профілів магазинів",
233
+ "backupDownloaded": "Резервну копію завантажено.",
234
+ "profileDownloaded": "Профіль магазину завантажено.",
235
+ "configImportStarted": "Резервну копію передано. ShoppingRoute застосує конфігурацію та перезапуститься.",
236
+ "profileImportStarted": "Профіль магазину передано. ShoppingRoute застосує його.",
237
+ "invalidBackup": "Вибраний файл не є коректною резервною копією JSON.",
238
+ "invalidProfile": "Вибраний файл не є коректним профілем магазину ShoppingRoute.",
239
+ "noInstance": "Не вдалося визначити екземпляр ShoppingRoute.",
240
+ "connectionError": "Не вдалося підключитися до ioBroker.",
241
+ "exportError": "Не вдалося створити резервну копію.",
242
+ "profileError": "Не вдалося завантажити профілі магазинів.",
243
+ "title": "ShoppingRoute – Резервна копія / Обмін",
244
+ "genericMarket": "Магазин"
245
+ },
246
+ "zh-cn": {
247
+ "configTitle": "完整配置",
248
+ "configText": "将所有 ShoppingRoute 设置下载为 JSON 文件,或从备份文件恢复。",
249
+ "downloadConfig": "↓ 下载备份",
250
+ "uploadConfig": "↑ 恢复备份",
251
+ "marketTitle": "分享商店配置",
252
+ "marketText": "下载包含购物路线的商店配置,或从 JSON 文件导入商店配置。",
253
+ "downloadMarket": "↓ 下载商店配置",
254
+ "uploadMarket": "↑ 导入商店配置",
255
+ "loading": "正在加载商店配置 …",
256
+ "noMarkets": "没有可用的商店配置",
257
+ "backupDownloaded": "备份已下载。",
258
+ "profileDownloaded": "商店配置已下载。",
259
+ "configImportStarted": "备份已提交。ShoppingRoute 将应用配置并重新启动。",
260
+ "profileImportStarted": "商店配置已提交,ShoppingRoute 将应用该配置。",
261
+ "invalidBackup": "所选文件不是有效的 JSON 备份。",
262
+ "invalidProfile": "所选文件不是有效的 ShoppingRoute 商店配置。",
263
+ "noInstance": "无法确定 ShoppingRoute 实例。",
264
+ "connectionError": "无法连接到 ioBroker。",
265
+ "exportError": "无法创建备份。",
266
+ "profileError": "无法加载商店配置。",
267
+ "title": "ShoppingRoute – 备份 / 分享",
268
+ "genericMarket": "商店"
269
+ }
270
+ };
271
+
272
+ function resolveLanguage(value) {
273
+ const locale = String(value || '').trim().toLowerCase().replace(/_/g, '-');
274
+ if (Object.prototype.hasOwnProperty.call(translations, locale)) return locale;
275
+ const base = locale.split('-')[0];
276
+ if (base === 'zh') return 'zh-cn';
277
+ return Object.prototype.hasOwnProperty.call(translations, base) ? base : 'en';
278
+ }
279
+
280
+ window.ShoppingRouteBackupI18n = { translations, resolveLanguage };
281
+ })();