From 6ea5f25bb00b90530abedce621dcd635ff233e2f Mon Sep 17 00:00:00 2001 From: n30nex Date: Fri, 4 Sep 2026 20:04:19 -0400 Subject: [PATCH 1/4] Fix region setup safety and simplify bilingual site journeys --- .github/ISSUE_TEMPLATE/community_idea.yml | 6 +- README.md | 73 ++++++ docs/analyzer/broker-reference.fr.md | 9 +- docs/analyzer/broker-reference.md | 9 +- docs/analyzer/builds/mctomqtt.fr.md | 6 +- docs/analyzer/builds/mctomqtt.md | 4 +- docs/analyzer/builds/mqtt-firmware.fr.md | 24 +- docs/analyzer/builds/mqtt-firmware.md | 26 ++- docs/analyzer/builds/pymc.fr.md | 6 +- docs/analyzer/builds/pymc.md | 6 +- docs/analyzer/data-collection-access.fr.md | 2 +- .../scripts/add-meshcore-ca-broker.sh | 39 ++-- .../add-meshcore-ca-packetcapture-broker.ps1 | 48 ++-- docs/analyzer/verify.fr.md | 2 +- docs/analyzer/verify.md | 2 +- .../javascripts/analyzer-command-builder.js | 17 +- docs/assets/javascripts/radio-profiles.js | 50 ++++ .../assets/javascripts/repeater-hash-check.js | 2 +- docs/assets/radio-profiles.json | 27 +++ .../regions/modules/configurator-support.js | 6 +- docs/assets/regions/regions.css | 84 +++---- docs/assets/regions/regions.js | 214 ++++++++++++++---- docs/config/editor/issue.js | 3 +- docs/config/index.fr.md | 7 +- docs/config/index.md | 7 +- docs/config/map.fr.md | 7 +- docs/config/map.md | 7 +- docs/config/standard.fr.md | 16 +- docs/config/standard.md | 16 +- docs/hardware/recommended-antenna.fr.md | 6 +- docs/hardware/recommended-antenna.md | 6 +- docs/hardware/recommended-companions.fr.md | 4 +- docs/hardware/recommended-companions.md | 4 +- docs/hardware/repeater-mounting-options.fr.md | 8 +- docs/hardware/repeater-mounting-options.md | 2 +- .../repeater-solar-1w-diy-build.fr.md | 14 +- .../repeater-solar-300mw-diy-build.fr.md | 2 +- docs/javascripts/community-submission.js | 24 +- docs/javascripts/submission-form.js | 17 ++ docs/meshcore/flash-companion.fr.md | 6 +- docs/meshcore/flash-companion.md | 6 +- docs/meshcore/flash-repeater.fr.md | 8 +- docs/meshcore/flash-repeater.md | 10 +- docs/meshcore/flash-room-server.fr.md | 4 +- docs/meshcore/flash-room-server.md | 4 +- docs/meshcore/general-faq.fr.md | 12 +- docs/meshcore/general-faq.md | 11 +- docs/meshcore/general-howto.fr.md | 11 + docs/meshcore/general-howto.md | 10 + docs/meshcore/generate-repeater-id.fr.md | 2 +- docs/meshcore/generate-repeater-id.md | 2 +- docs/privacy.fr.md | 7 + docs/privacy.md | 6 + docs/provinces/alberta.fr.md | 36 +-- docs/provinces/alberta.md | 50 ++-- docs/provinces/british-columbia.fr.md | 8 +- docs/provinces/british-columbia.md | 12 +- docs/provinces/index.fr.md | 48 ++-- docs/provinces/index.md | 48 ++-- docs/provinces/new-brunswick.fr.md | 4 +- docs/provinces/new-brunswick.md | 4 +- docs/provinces/nova-scotia.fr.md | 4 +- docs/provinces/nova-scotia.md | 6 +- docs/provinces/ontario.fr.md | 12 +- docs/provinces/ontario.md | 18 +- docs/provinces/quebec.fr.md | 24 +- docs/provinces/quebec.md | 34 +-- docs/provinces/saskatchewan.fr.md | 8 +- docs/provinces/saskatchewan.md | 10 +- docs/resources/glossary.fr.md | 5 +- docs/resources/glossary.md | 2 +- docs/start/companion.fr.md | 33 +-- docs/start/companion.md | 32 +-- docs/start/observer.fr.md | 33 +-- docs/start/observer.md | 34 +-- docs/start/repeater.fr.md | 34 +-- docs/start/repeater.md | 33 +-- docs/start/room-server.fr.md | 28 ++- docs/start/room-server.md | 28 ++- docs/submit-idea.fr.md | 47 ++-- docs/submit-idea.md | 47 ++-- maintenance/site-audit-2026-09-04.md | 63 ++++++ mkdocs.yml | 1 - overrides/partials/page-feedback.html | 6 +- scripts/add-meshcore-ca-broker.sh | 39 ++-- scripts/postprocess-site.mjs | 19 ++ scripts/run-lighthouse.mjs | 13 +- scripts/validate-communities.py | 30 ++- tests/browser/audit-regressions.spec.mjs | 156 +++++++++++++ tests/content/analyzer-journey.test.mjs | 9 +- tests/content/broker-helpers.test.mjs | 58 +++++ tests/content/community-directory.test.mjs | 2 +- tests/content/p0-content-safety.test.mjs | 8 +- tests/content/radio-profiles.test.mjs | 46 ++++ tests/content/site-postprocess.test.mjs | 14 +- tests/content/test_validate_communities.py | 2 +- tests/editor/community-submission.test.mjs | 19 +- tests/editor/issue.test.mjs | 7 + tools/region-proposal-gateway/README.md | 25 +- tools/region-proposal-gateway/gateway.py | 28 ++- .../tests/test_gateway.py | 14 +- 101 files changed, 1516 insertions(+), 636 deletions(-) create mode 100644 README.md create mode 100644 docs/assets/javascripts/radio-profiles.js create mode 100644 docs/assets/radio-profiles.json create mode 100644 maintenance/site-audit-2026-09-04.md create mode 100644 tests/browser/audit-regressions.spec.mjs create mode 100644 tests/content/broker-helpers.test.mjs create mode 100644 tests/content/radio-profiles.test.mjs diff --git a/.github/ISSUE_TEMPLATE/community_idea.yml b/.github/ISSUE_TEMPLATE/community_idea.yml index 0ec0865d..484c6911 100644 --- a/.github/ISSUE_TEMPLATE/community_idea.yml +++ b/.github/ISSUE_TEMPLATE/community_idea.yml @@ -24,7 +24,7 @@ body: - Feature or project idea - Other community feedback validations: - required: true + required: false - type: dropdown id: experience @@ -37,7 +37,7 @@ body: - Repeater, room server, or observer operator - Developer or documentation contributor validations: - required: true + required: false - type: input id: summary @@ -71,7 +71,7 @@ body: description: What should change? placeholder: Add... validations: - required: true + required: false - type: textarea id: context diff --git a/README.md b/README.md new file mode 100644 index 00000000..ef7f6cc9 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# MeshCore Canada + +Community documentation and network tools for [meshcore.ca](https://meshcore.ca/), +in English and French. Built with MkDocs Material and small browser scripts. + +## Work locally + +Use Python 3.13 and Node.js 22.19 or newer. From the repository root: + +```sh +python -m pip install -r requirements-docs.txt +npm ci +npm run docs:build +python -m http.server 4173 --bind 127.0.0.1 --directory .tmp/site +``` + +Open `http://127.0.0.1:4173/`. Rebuild after editing. Build output belongs in +`.tmp/`, not in a commit. `npm run docs:build:preview` also checks subpath hosting. + +## Where to make changes + +- Pages: `docs/`. Update each English `.md` and French `.fr.md` pair; retain old + heading anchors when changing titles. +- Navigation, theme, and shared templates: `mkdocs.yml` and `overrides/`. +- Community listings: `data/communities.json` and `data/communities.fr.json`. + Run `python scripts/validate-communities.py --write` after editing the source. + It generates the directory pages and `docs/assets/radio-profiles.json`. +- Region tools: `docs/assets/regions/` and `docs/config/editor/`. + Follow the boundary proposal workflow; do not hand-edit generated geography. +- Broker settings: `docs/analyzer/observer-config.json`. The build generates the + broker reference table from this file, including its no-JavaScript version. +- Anonymous submissions: `tools/region-proposal-gateway/`. The site and gateway + deploy separately; check the gateway README before changing their contract. + +Never commit credentials, private keys, precise private locations, or test +submissions containing personal information. Human maintainers review changes +before publication; an automated test is not hardware or policy approval. + +## Check a change + +```sh +python scripts/validate-content.py +python scripts/validate-communities.py +python scripts/validate_community_submission.py +python -m unittest discover -s tests/content -p "test_*.py" +npm run test:content +npm run test:editor +npm run check:links +npx playwright install chromium firefox webkit +npm run test:browser +npm run audit:lighthouse +``` + +The broker-helper tests need Bash and PowerShell (`pwsh`); on Windows they use +Git Bash. They run in temporary directories, do not install software, and never +connect to a real broker. Browser submission tests intercept requests locally. + +For region, gateway, or automation changes, also run: + +```sh +python -m pip install -r scripts/requirements-regions.txt +python -m pip install -r tools/region-proposal-gateway/requirements.txt +node scripts/validate-regions.cjs +python scripts/verify-region-geometry.py +python scripts/verify-region-geometry.py --partition docs/assets/regions/canada-region-partition-digital.geojson +python -m unittest discover -s tools/region-proposal-gateway/tests +python -m unittest discover -s tests/automation +``` + +The quality workflow runs these checks on pull requests. Publishing is a separate +reviewed workflow; opening a PR does not authorize deploying it or changing brokers. +See [the September audit follow-up](maintenance/site-audit-2026-09-04.md) for the +current fixes, test coverage, and confirmations still needed from maintainers. diff --git a/docs/analyzer/broker-reference.fr.md b/docs/analyzer/broker-reference.fr.md index 5b395226..fe8aa8dc 100644 --- a/docs/analyzer/broker-reference.fr.md +++ b/docs/analyzer/broker-reference.fr.md @@ -15,8 +15,6 @@ estimated_time: 8 minutes destructive: false page_styles: - assets/styles/analyzer.css?v=20260722-2 -page_scripts: - - assets/javascripts/analyzer-broker-reference.js?v=20260722-2 --- # Paramètres de connexion des observateurs @@ -45,10 +43,13 @@ Ces valeurs proviennent de la [configuration commune des observateurs](observer- -

Chargement des paramètres officiels de connexion…

-Si le tableau ne s’affiche pas, ouvrez [observer-config.json](observer-config.json). +## Accès en lecture seule + +La [liste des comptes MQTT en lecture seule](data-collection-access.md#read-only-mqtt-accounts) indique les abonnés autorisés, dont QuinteMesh. Pour demander un accès, contactez un administrateur ci-dessous. + +Les réglages JWT de cette page concernent les **observateurs qui publient des paquets**. Les abonnés en lecture seule utilisent les identifiants et les instructions fournis par un administrateur. N’utilisez pas la clé privée d’un observateur pour cet accès. ## Administrateurs des courtiers diff --git a/docs/analyzer/broker-reference.md b/docs/analyzer/broker-reference.md index 195a60d7..c77b5be9 100644 --- a/docs/analyzer/broker-reference.md +++ b/docs/analyzer/broker-reference.md @@ -15,8 +15,6 @@ estimated_time: 8 minutes destructive: false page_styles: - assets/styles/analyzer.css?v=20260722-2 -page_scripts: - - assets/javascripts/analyzer-broker-reference.js?v=20260722-2 --- # Observer connection reference @@ -44,10 +42,13 @@ These values come from the shared [observer configuration](observer-config.json) -

Loading official connection settings…

-If the table does not load, open [observer-config.json](observer-config.json). +## Read-only access + +See the [read-only MQTT account list](data-collection-access.md#read-only-mqtt-accounts) for approved subscribers, including QuinteMesh. Ask an administrator below to request access. + +The JWT settings on this page are for **observers publishing packets**. Read-only subscribers use the credentials and connection instructions supplied by an administrator. Do not use an observer’s private key for subscriber access. ## Broker administrators diff --git a/docs/analyzer/builds/mctomqtt.fr.md b/docs/analyzer/builds/mctomqtt.fr.md index beac6c0b..491a3a58 100644 --- a/docs/analyzer/builds/mctomqtt.fr.md +++ b/docs/analyzer/builds/mctomqtt.fr.md @@ -63,8 +63,8 @@ Sur un hôte à liaison série, l’outil : Pour la capture d’un compagnon, il met à jour `~/.meshcore-packet-capture/.env.local`, crée une sauvegarde horodatée, -configure les emplacements 1 et 2, désactive les emplacements 3 à 6 et peut -redémarrer le service de capture. +met à jour les emplacements MeshCore Canada existants ou utilise des emplacements +vides. Les autres connexions sont conservées. Il peut redémarrer le service de capture. Les options d’installation téléchargent et exécutent des programmes d’installation distincts provenant des projets d’origine. Ne les utilisez pas @@ -128,7 +128,7 @@ Après avoir examiné la version actuellement publiée de l’outil, vous pouvez exécuter directement ce même fichier : ```bash -bash <(curl -fsSL https://meshcore.ca/analyzer/scripts/add-meshcore-ca-broker.sh) --device serial-host --iata YOW +bash add-meshcore-ca-broker.sh --device serial-host --iata YOW ``` La méthode de téléchargement présentée plus haut facilite l’examen et la diff --git a/docs/analyzer/builds/mctomqtt.md b/docs/analyzer/builds/mctomqtt.md index e6c5aff4..54c0bf0a 100644 --- a/docs/analyzer/builds/mctomqtt.md +++ b/docs/analyzer/builds/mctomqtt.md @@ -59,7 +59,7 @@ On a serial host, the helper: - adds the primary and backup MeshCore Canada addresses and location code; and - restarts `mctomqtt` unless `--no-restart` is used. -For companion capture, it updates `~/.meshcore-packet-capture/.env.local`, makes a timestamped backup, configures slots 1 and 2, disables slots 3–6, and may restart the capture service. +For companion capture, it updates `~/.meshcore-packet-capture/.env.local`, makes a timestamped backup, updates existing Canada broker slots or uses empty slots, and may restart the capture service. Install flags download and run separate upstream installers. Do not use them until you have reviewed the named upstream installer too. @@ -114,7 +114,7 @@ The service should stay active without repeated TLS or authentication errors. After reviewing the current published helper, you can run that same file directly: ```bash -bash <(curl -fsSL https://meshcore.ca/analyzer/scripts/add-meshcore-ca-broker.sh) --device serial-host --iata YOW +bash add-meshcore-ca-broker.sh --device serial-host --iata YOW ``` The downloaded-file method above is easier to review and recover. diff --git a/docs/analyzer/builds/mqtt-firmware.fr.md b/docs/analyzer/builds/mqtt-firmware.fr.md index ad8bd4fa..54410865 100644 --- a/docs/analyzer/builds/mqtt-firmware.fr.md +++ b/docs/analyzer/builds/mqtt-firmware.fr.md @@ -15,7 +15,8 @@ destructive: true page_styles: - assets/styles/analyzer.css?v=20260722-2 page_scripts: - - assets/javascripts/analyzer-command-builder.js?v=20260722-2 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/javascripts/analyzer-command-builder.js?v=20260904-1 --- # Configurer un observateur MQTT autonome @@ -100,6 +101,10 @@ propre à la communauté, la configuration canadienne de départ est : ### 3. Générer les commandes +Ces commandes remplacent les emplacements MQTT **1 et 2**. S’ils servent déjà à un +autre réseau, ajoutez le Canada dans des emplacements libres avec **Configure via USB**. +Les emplacements 3 à 6 restent inchangés. La répétition est désactivée par défaut. + L’interface en ligne de commande n’a pas de règle générale documentée pour les guillemets. Le générateur rejette les espaces, les guillemets, les barres obliques inverses, les caractères de contrôle et les autres valeurs Wi-Fi @@ -146,11 +151,14 @@ représenter de façon sûre. + +

Choisissez le profil de votre communauté. L’emplacement seul ne détermine pas les réglages radio.

+

Chargement des suggestions d’emplacements canadiens…

Le SSID et le mot de passe restent uniquement dans cette page. Ils sont effacés lorsque vous la quittez et masqués dans l’aperçu jusqu’à ce que vous choisissiez de les afficher.

@@ -175,23 +183,17 @@ sensibles : ```text set name YOW-Repeater-01 -set radio 910.525,62.5,7,5 -set path.hash.mode 2 set mqtt.iata YOW set wifi.powersave none set mqtt1.preset meshcore-ca-1 set mqtt2.preset meshcore-ca-2 -set mqtt3.preset none -set mqtt4.preset none -set mqtt5.preset none -set mqtt6.preset none set mqtt.status on set mqtt.packets on set mqtt.raw off set mqtt.rx on set mqtt.tx advert set bridge.enabled on -set repeat on +set repeat off advert ``` @@ -233,7 +235,7 @@ La carte est correctement configurée lorsque : - le code d’emplacement est exact; - les préréglages sont `meshcore-ca-1` et `meshcore-ca-2`; - la publication des paquets et le mode pont sont activés; -- le mode de hachage des chemins est `2`. +- le profil radio et la taille d’identifiant correspondent à vos choix. ## Vérifier dans CoreScope { #check-in-corescope } diff --git a/docs/analyzer/builds/mqtt-firmware.md b/docs/analyzer/builds/mqtt-firmware.md index c0145bed..f86cae55 100644 --- a/docs/analyzer/builds/mqtt-firmware.md +++ b/docs/analyzer/builds/mqtt-firmware.md @@ -15,7 +15,8 @@ destructive: true page_styles: - assets/styles/analyzer.css?v=20260722-2 page_scripts: - - assets/javascripts/analyzer-command-builder.js?v=20260722-2 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/javascripts/analyzer-command-builder.js?v=20260904-1 --- # Build a standalone MQTT observer @@ -88,6 +89,10 @@ Use the local mesh settings. When no community override exists, the Canadian onb ### 3. Build the commands +These commands replace MQTT slots **1 and 2**. Check those slots first; if they +serve another network, use **Configure via USB** to add Canada in unused slots. +Slots 3–6 are left unchanged. Repeating is off unless you choose to enable it. + The CLI has no documented general quoting contract. The builder rejects spaces, quotes, backslashes, control characters, and other ambiguous Wi-Fi values. Use **Configure via USB** for a network it cannot represent safely.
@@ -130,11 +135,14 @@ The CLI has no documented general quoting contract. The builder rejects spaces,
+ +

Choose your community’s profile explicitly. Location alone does not select a radio network.

+

Loading Canadian location suggestions…

SSID and password stay in this page only. They are cleared when you leave and hidden from the preview until you reveal them.

@@ -152,27 +160,23 @@ Check the non-sensitive summary and redacted preview. Reveal and copy commands o ### Enter commands by hand +These commands preserve radio settings and presets 3–6, but replace presets 1 and 2. On an existing observer, check those slots first. Choose radio values in the builder above; changes take effect after reboot. Observing does not require repeating. Enable repeating only after coordinating with nearby operators. + If you prefer manual entry, set the non-sensitive values first: ```text set name YOW-Repeater-01 -set radio 910.525,62.5,7,5 -set path.hash.mode 2 set mqtt.iata YOW set wifi.powersave none set mqtt1.preset meshcore-ca-1 set mqtt2.preset meshcore-ca-2 -set mqtt3.preset none -set mqtt4.preset none -set mqtt5.preset none -set mqtt6.preset none set mqtt.status on set mqtt.packets on set mqtt.raw off set mqtt.rx on set mqtt.tx advert set bridge.enabled on -set repeat on +set repeat off advert ``` @@ -210,7 +214,7 @@ The board is configured correctly when: - the location code is correct; - presets are `meshcore-ca-1` and `meshcore-ca-2`; - packet publishing and bridge mode are on; and -- path hash mode is `2`. +- the radio profile and path hash mode match your explicit choices. ## Verify in CoreScope diff --git a/docs/analyzer/builds/pymc.fr.md b/docs/analyzer/builds/pymc.fr.md index 3b95fab2..4fa17482 100644 --- a/docs/analyzer/builds/pymc.fr.md +++ b/docs/analyzer/builds/pymc.fr.md @@ -50,7 +50,9 @@ Notez l’état actuel du service : ```bash sudo systemctl status pymc-repeater --no-pager -sudo cp -- /etc/pymc_repeater/config.yaml /etc/pymc_repeater/config.yaml.pre-meshcore-ca +backup="/etc/pymc_repeater/config.yaml.pre-meshcore-ca.$(date -u +%Y%m%dT%H%M%S).$" +sudo cp --no-clobber -- /etc/pymc_repeater/config.yaml "$backup" +printf 'Backup: %s\n' "$backup" ``` ## Ce qui sera modifié @@ -140,7 +142,7 @@ bon état ne confirme pas que les paquets se sont rendus à CoreScope. Restaurez exactement la sauvegarde créée avant la modification : ```bash -sudo cp -- /etc/pymc_repeater/config.yaml.pre-meshcore-ca /etc/pymc_repeater/config.yaml +sudo cp -- "${backup:?Set backup to the saved backup path first}" /etc/pymc_repeater/config.yaml sudo systemctl restart pymc-repeater sudo systemctl status pymc-repeater --no-pager ``` diff --git a/docs/analyzer/builds/pymc.md b/docs/analyzer/builds/pymc.md index e027fc52..fcfec7e4 100644 --- a/docs/analyzer/builds/pymc.md +++ b/docs/analyzer/builds/pymc.md @@ -45,7 +45,9 @@ Record the current service state: ```bash sudo systemctl status pymc-repeater --no-pager -sudo cp -- /etc/pymc_repeater/config.yaml /etc/pymc_repeater/config.yaml.pre-meshcore-ca +backup="/etc/pymc_repeater/config.yaml.pre-meshcore-ca.$(date -u +%Y%m%dT%H%M%S).$" +sudo cp --no-clobber -- /etc/pymc_repeater/config.yaml "$backup" +printf 'Backup: %s\n' "$backup" ``` ## What this changes @@ -128,7 +130,7 @@ Finish with [Check your observer](../verify.md). A healthy systemd service is no Restore the exact backup made before editing: ```bash -sudo cp -- /etc/pymc_repeater/config.yaml.pre-meshcore-ca /etc/pymc_repeater/config.yaml +sudo cp -- "${backup:?Set backup to the saved backup path first}" /etc/pymc_repeater/config.yaml sudo systemctl restart pymc-repeater sudo systemctl status pymc-repeater --no-pager ``` diff --git a/docs/analyzer/data-collection-access.fr.md b/docs/analyzer/data-collection-access.fr.md index 692679e9..e55d57eb 100644 --- a/docs/analyzer/data-collection-access.fr.md +++ b/docs/analyzer/data-collection-access.fr.md @@ -70,7 +70,7 @@ MeshCore Canada n’offre pas d’abonnement direct général au courtier. L’a direct est limité à CoreScope, aux administrateurs des réseaux maillés locaux et aux personnes autorisées par les administrateurs de l’infrastructure. -## Comptes MQTT en lecture seule +## Comptes MQTT en lecture seule { #read-only-mqtt-accounts } Cet inventaire public répertorie les services qui disposent d’un accès en lecture seule aux courtiers MQTT de MeshCore Canada. Il indique le service et diff --git a/docs/analyzer/scripts/add-meshcore-ca-broker.sh b/docs/analyzer/scripts/add-meshcore-ca-broker.sh index 619ab147..f1996894 100644 --- a/docs/analyzer/scripts/add-meshcore-ca-broker.sh +++ b/docs/analyzer/scripts/add-meshcore-ca-broker.sh @@ -30,8 +30,9 @@ Supported installs: - Companion radio via meshcore-packet-capture (.env.local) Usage: - MESHCORE_CA_IATA=YOW bash <(curl -fsSL https://meshcore.ca/analyzer/scripts/add-meshcore-ca-broker.sh) - bash <(curl -fsSL https://meshcore.ca/analyzer/scripts/add-meshcore-ca-broker.sh) --iata YOW --device serial-host + curl -fsSLo add-meshcore-ca-broker.sh https://meshcore.ca/analyzer/scripts/add-meshcore-ca-broker.sh + less add-meshcore-ca-broker.sh + bash ./add-meshcore-ca-broker.sh --iata YOW --device serial-host Options: --iata CODE Real 3-letter IATA airport code. @@ -537,15 +538,28 @@ set_packetcapture_slot() { upsert_env "$file" "PACKETCAPTURE_MQTT${slot}_TOPIC_PACKETS" 'meshcore/{IATA}/{PUBLIC_KEY}/packets' } -disable_packetcapture_slot() { - local file="$1" - local slot="$2" - upsert_env "$file" "PACKETCAPTURE_MQTT${slot}_ENABLED" "false" - upsert_env "$file" "PACKETCAPTURE_MQTT${slot}_SERVER" "" +choose_packetcapture_slots() { + local file="$1" n server slot1="" slot2="" free=() + for n in 1 2 3 4 5 6; do + server="$(env_value "$file" "PACKETCAPTURE_MQTT${n}_SERVER" | tr -d '\r')" + server="${server#\"}"; server="${server%\"}" + server="${server#\'}"; server="${server%\'}" + if [ "$server" = "$BROKER1_HOST" ] && [ -z "$slot1" ]; then slot1="$n" + elif [ "$server" = "$BROKER2_HOST" ] && [ -z "$slot2" ]; then slot2="$n" + elif [ -z "$server" ]; then free+=("$n") + fi + done + if [ -z "$slot1" ] && [ "${#free[@]}" -gt 0 ]; then slot1="${free[0]}"; free=("${free[@]:1}"); fi + if [ -z "$slot2" ] && [ "${#free[@]}" -gt 0 ]; then slot2="${free[0]}"; fi + if [ -z "$slot1" ] || [ -z "$slot2" ]; then + echo "Not enough empty broker slots. Keep your existing connections, or free two slots and run again. No settings changed." >&2 + return 1 + fi + printf '%s %s\n' "$slot1" "$slot2" } patch_env_capture() { - local env_file current_iata backup n + local env_file current_iata backup slots slot1 slot2 env_file="$(packetcapture_env_path)" install_packetcapture mkdir -p "$(dirname "$env_file")" @@ -556,16 +570,15 @@ patch_env_capture() { fi require_iata + slots="$(choose_packetcapture_slots "$env_file")" || return 1 + read -r slot1 slot2 <<< "$slots" backup="$(backup_path "$env_file")" cp -p "$env_file" "$backup" chmod 0600 "$backup" upsert_env "$env_file" "PACKETCAPTURE_IATA" "$IATA" - set_packetcapture_slot "$env_file" 1 "$BROKER1_HOST" - set_packetcapture_slot "$env_file" 2 "$BROKER2_HOST" - for n in 3 4 5 6; do - disable_packetcapture_slot "$env_file" "$n" - done + set_packetcapture_slot "$env_file" "$slot1" "$BROKER1_HOST" + set_packetcapture_slot "$env_file" "$slot2" "$BROKER2_HOST" chmod 0600 "$env_file" say "Patched: $env_file" say "Backup written: $backup" diff --git a/docs/analyzer/scripts/add-meshcore-ca-packetcapture-broker.ps1 b/docs/analyzer/scripts/add-meshcore-ca-packetcapture-broker.ps1 index 9d747ea6..f0982250 100644 --- a/docs/analyzer/scripts/add-meshcore-ca-packetcapture-broker.ps1 +++ b/docs/analyzer/scripts/add-meshcore-ca-packetcapture-broker.ps1 @@ -114,7 +114,7 @@ function Set-EnvValue { $lines = Get-Content $Path } $updated = $false - $out = foreach ($line in $lines) { + $out = @(foreach ($line in $lines) { if ($line -match "^${Key}=") { if (-not $updated) { $updated = $true @@ -124,7 +124,7 @@ function Set-EnvValue { else { $line } - } + }) if (-not $updated) { $out += "${Key}=${Value}" } @@ -135,16 +135,16 @@ function Set-PacketCaptureSlot { param( [string]$Path, [int]$Slot, - [string]$Host + [string]$BrokerHost ) Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_ENABLED" -Value "true" - Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_SERVER" -Value $Host + Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_SERVER" -Value $BrokerHost Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_PORT" -Value $BrokerPort Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_TRANSPORT" -Value "websockets" Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_USE_TLS" -Value "true" Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_TLS_VERIFY" -Value "true" Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_USE_AUTH_TOKEN" -Value "true" - Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_TOKEN_AUDIENCE" -Value $Host + Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_TOKEN_AUDIENCE" -Value $BrokerHost Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_KEEPALIVE" -Value "120" Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_QOS" -Value "0" Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_RETAIN" -Value "true" @@ -152,13 +152,29 @@ function Set-PacketCaptureSlot { Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_TOPIC_PACKETS" -Value "meshcore/{IATA}/{PUBLIC_KEY}/packets" } -function Disable-PacketCaptureSlot { - param( - [string]$Path, - [int]$Slot - ) - Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_ENABLED" -Value "false" - Set-EnvValue -Path $Path -Key "PACKETCAPTURE_MQTT${Slot}_SERVER" -Value "" +function Get-PacketCaptureSlots { + param([string]$Path) + $servers = @{} + foreach ($line in Get-Content -LiteralPath $Path) { + if ($line -match '^PACKETCAPTURE_MQTT([1-6])_SERVER=(.*)$') { + $servers[[int]$Matches[1]] = $Matches[2].Trim().Trim('"', "'") + } + } + $chosen = @($null, $null) + for ($index = 0; $index -lt 2; $index++) { + $broker = @($Broker1Host, $Broker2Host)[$index] + $slot = 1..6 | Where-Object { $servers[$_] -eq $broker -and $_ -notin $chosen } | Select-Object -First 1 + $chosen[$index] = $slot + } + for ($index = 0; $index -lt 2; $index++) { + if (-not $chosen[$index]) { + $chosen[$index] = 1..6 | Where-Object { [string]::IsNullOrWhiteSpace($servers[$_]) -and $_ -notin $chosen } | Select-Object -First 1 + } + } + if (-not $chosen[0] -or -not $chosen[1]) { + throw 'Not enough empty broker slots. Free slots before trying again. No settings changed.' + } + return $chosen } function Ensure-PacketCaptureInstall { @@ -206,15 +222,13 @@ if (-not (Test-Path $envPath)) { New-Item -ItemType File -Path $envPath -Force | Out-Null } +$slots = @(Get-PacketCaptureSlots -Path $envPath) $backupPath = New-BackupPath -Path $envPath Copy-Item -Path $envPath -Destination $backupPath -Force Set-EnvValue -Path $envPath -Key "PACKETCAPTURE_IATA" -Value $Iata -Set-PacketCaptureSlot -Path $envPath -Slot 1 -Host $Broker1Host -Set-PacketCaptureSlot -Path $envPath -Slot 2 -Host $Broker2Host -foreach ($slot in 3..6) { - Disable-PacketCaptureSlot -Path $envPath -Slot $slot -} +Set-PacketCaptureSlot -Path $envPath -Slot $slots[0] -BrokerHost $Broker1Host +Set-PacketCaptureSlot -Path $envPath -Slot $slots[1] -BrokerHost $Broker2Host Write-Host "" Write-Info "Patched: $envPath" diff --git a/docs/analyzer/verify.fr.md b/docs/analyzer/verify.fr.md index 259c5434..c15b9c72 100644 --- a/docs/analyzer/verify.fr.md +++ b/docs/analyzer/verify.fr.md @@ -18,7 +18,7 @@ page_styles: # Vérifier votre observateur -Une connexion au courtier confirme seulement l’accès Internet. Votre +Une connexion ne confirme pas que les paquets arrivent à CoreScope. Votre observateur fonctionne lorsqu’un véritable paquet radio se rend jusqu’à CoreScope. diff --git a/docs/analyzer/verify.md b/docs/analyzer/verify.md index 3ebd73b6..91aadbc2 100644 --- a/docs/analyzer/verify.md +++ b/docs/analyzer/verify.md @@ -18,7 +18,7 @@ page_styles: # Check your observer -A broker connection proves only internet access. Your observer is working when +Connected does not mean packets have reached CoreScope. Your observer is working when a real radio packet reaches CoreScope. ## Follow a packet through four stages diff --git a/docs/assets/javascripts/analyzer-command-builder.js b/docs/assets/javascripts/analyzer-command-builder.js index ca0fabf0..8f54966f 100644 --- a/docs/assets/javascripts/analyzer-command-builder.js +++ b/docs/assets/javascripts/analyzer-command-builder.js @@ -78,7 +78,9 @@ number: document.getElementById("observer-number"), ssid: document.getElementById("observer-ssid"), password: document.getElementById("observer-password"), - repeat: document.getElementById("observer-repeat") + repeat: document.getElementById("observer-repeat"), + radio: document.getElementById("observer-radio"), + hash: document.getElementById("observer-hash") }; var output = document.getElementById("observer-command-output"); var status = document.getElementById("observer-copy-status"); @@ -99,6 +101,8 @@ } root.dataset.ready = "true"; + var radioProfiles = window.MeshCoreRadioProfiles; + if (radioProfiles) radioProfiles.populate(fields.radio); var exactCommands = []; var commandsRevealed = false; @@ -126,6 +130,8 @@ [tr("Board", "Carte"), fields.board.options[fields.board.selectedIndex].text], [tr("Location", "Emplacement"), iata || tr("Not set", "Non défini")], [tr("Node name", "Nom du nœud"), nodeName || tr("Not set", "Non défini")], + [tr("Radio network", "Réseau radio"), radioProfiles ? radioProfiles.label(fields.radio.value) : tr("Keep current settings", "Conserver les réglages actuels")], + [tr("Advert ID size", "Taille de l’identifiant d’annonce"), fields.hash.options[fields.hash.selectedIndex].text], [ tr("Mesh traffic", "Trafic du réseau maillé"), repeat === "on" @@ -218,18 +224,13 @@ exactCommands = [ "set name " + nodeName, - "set radio 910.525,62.5,7,5", - "set path.hash.mode 2", + ].concat(radioProfiles ? radioProfiles.commands(fields.radio.value, fields.hash.value) : [], [ "set mqtt.iata " + iata, "set wifi.ssid " + ssid, "set wifi.pwd " + password, "set wifi.powersave none", "set mqtt1.preset meshcore-ca-1", "set mqtt2.preset meshcore-ca-2", - "set mqtt3.preset none", - "set mqtt4.preset none", - "set mqtt5.preset none", - "set mqtt6.preset none", "set mqtt.status on", "set mqtt.packets on", "set mqtt.raw off", @@ -239,7 +240,7 @@ "set repeat " + repeat, "advert", "reboot" - ]; + ]); revealCommandsButton.disabled = false; showCurrentCommands(); } diff --git a/docs/assets/javascripts/radio-profiles.js b/docs/assets/javascripts/radio-profiles.js new file mode 100644 index 00000000..9b728779 --- /dev/null +++ b/docs/assets/javascripts/radio-profiles.js @@ -0,0 +1,50 @@ +(function () { + "use strict"; + var source = new URL("../radio-profiles.json", document.currentScript.src); + var french = /^fr(?:-|$)/i.test(document.documentElement.lang); + var profiles = []; + var loading; + + function commands(id, hashMode) { + var result = []; + if (id && id !== "keep") { + var profile = profiles.find(function (item) { return item.id === id; }); + if (!profile) throw new Error("Unknown radio profile"); + var radio = profile.radio; + result.push("set radio " + [radio.frequency_mhz, radio.bandwidth_khz, radio.spreading_factor, radio.coding_rate].join(",")); + } + if (["0", "1", "2"].indexOf(hashMode) !== -1) result.push("set path.hash.mode " + hashMode); + return result; + } + + function populate(select) { + if (!loading) loading = fetch(source).then(function (response) { + if (!response.ok) throw new Error("Radio profiles unavailable"); + return response.json(); + }).then(function (data) { profiles = data; }); + return loading.then(function () { + profiles.forEach(function (profile) { + var option = document.createElement("option"); + option.value = profile.id; + option.textContent = (french ? profile.name_fr : profile.name) + " · " + + profile.radio.frequency_mhz + " MHz"; + select.appendChild(option); + }); + }).catch(function () { + // Keep-current remains usable; never substitute a default when data fails. + var notice = document.createElement("p"); + notice.setAttribute("role", "status"); + notice.textContent = french + ? "Profils radio indisponibles. Les réglages actuels seront conservés." + : "Radio profiles unavailable. Current settings will be kept."; + select.after(notice); + }); + } + + function label(id) { + var profile = profiles.find(function (item) { return item.id === id; }); + return profile ? (french ? profile.name_fr : profile.name) + " · " + profile.radio.frequency_mhz + " MHz" + : (french ? "Conserver les réglages actuels" : "Keep current settings"); + } + window.MeshCoreRadioProfiles = { commands: commands, populate: populate, label: label }; +}()); diff --git a/docs/assets/javascripts/repeater-hash-check.js b/docs/assets/javascripts/repeater-hash-check.js index 03beec0b..7c315846 100644 --- a/docs/assets/javascripts/repeater-hash-check.js +++ b/docs/assets/javascripts/repeater-hash-check.js @@ -315,7 +315,7 @@ var rows = summaries.map(function (summary) { var selected = summary.bytes === selectedBytes; return '' + - '' + summary.bytes + ' ' + + '' + escapeHtml(summary.bytes === 1 ? message(language, "oneByte") : message(language, summary.bytes === 2 ? "twoBytes" : "threeBytes")) + (selected ? ' ' + escapeHtml(message(language, "selected")) + '' : '') + '' + '' + escapeHtml(summary.prefix) + '' + diff --git a/docs/assets/radio-profiles.json b/docs/assets/radio-profiles.json new file mode 100644 index 00000000..70b39d23 --- /dev/null +++ b/docs/assets/radio-profiles.json @@ -0,0 +1,27 @@ +[ + { + "id": "canada", + "name": "Canada default", + "name_fr": "Réglages canadiens par défaut", + "radio": { + "frequency_mhz": 910.525, + "bandwidth_khz": 62.5, + "spreading_factor": 7, + "coding_rate": 5 + }, + "route": "provinces/#canada-baseline" + }, + { + "id": "bc-mesh", + "name": "BC Mesh", + "name_fr": "BC Mesh", + "radio": { + "frequency_mhz": 910.425, + "bandwidth_khz": 62.5, + "spreading_factor": 7, + "coding_rate": 5 + }, + "route": "provinces/british-columbia/#community-bc-mesh", + "reviewed": "2026-08-29" + } +] diff --git a/docs/assets/regions/modules/configurator-support.js b/docs/assets/regions/modules/configurator-support.js index 897b63ec..6a3b4e34 100644 --- a/docs/assets/regions/modules/configurator-support.js +++ b/docs/assets/regions/modules/configurator-support.js @@ -25,12 +25,14 @@ var paths = Array.isArray(input.paths) ? input.paths.map(normalizeText).filter(Boolean) : []; var commands = Array.isArray(input.commands) ? input.commands.map(normalizeText).filter(Boolean) : []; var lines = [ - "MeshCore Canada repeater commissioning summary", + "MeshCore Canada repeater setup summary", "Generated: " + normalizeText(input.generatedAt), "Location label: " + (normalizeText(input.locationLabel) || "Not recorded"), "Home region: " + normalizeText(input.homeRegion), "Firmware: " + normalizeText(input.firmware), "Region budget: " + normalizeText(input.budget), + "Radio network: " + normalizeText(input.radio || "Keep current settings"), + "Advert ID size: " + normalizeText(input.hashMode || "Keep current settings"), "", "Forwarding paths:" ]; @@ -43,6 +45,8 @@ "1. Run region before saving and compare every path above.", "2. Run region save only after the paths and flood permissions are correct.", "3. Run region again after saving.", + "4. If radio settings changed, reboot and run get radio to confirm them.", + "5. If the advert ID size changed, run get path.hash.mode to confirm it.", "", "This summary omits exact coordinates, passwords, private keys, and device identifiers." ); diff --git a/docs/assets/regions/regions.css b/docs/assets/regions/regions.css index 8b05c312..05e1db32 100644 --- a/docs/assets/regions/regions.css +++ b/docs/assets/regions/regions.css @@ -6,15 +6,13 @@ body:has([data-mcc-regions]) { background: var(--md-default-bg-color, #ffffff); } -/* Region tools use one deliberate dark workbench in either site palette. This - keeps dense maps, command output, and status colours readable after a theme - switch while the header, navigation, and surrounding site still follow it. */ +/* Use the site's palette for the workbench as well as its surrounding navigation. */ body:has([data-mcc-regions]) .md-main, body:has([data-mcc-regions]) .md-main__inner, body:has([data-mcc-regions]) .md-content, body:has([data-mcc-regions]) .md-content__inner { - color: #dce9e7; - background: #0c151c; + color: var(--md-default-fg-color); + background: var(--md-default-bg-color); } body:has([data-mcc-regions="map"]) .md-main__inner { @@ -24,7 +22,7 @@ body:has([data-mcc-regions="map"]) .md-main__inner { body:has([data-mcc-regions]) .md-typeset h1, body:has([data-mcc-regions]) .md-typeset h2, body:has([data-mcc-regions]) .md-typeset h3 { - color: #ffffff; + color: var(--md-default-fg-color); } body:has([data-mcc-regions]) .md-main .md-typeset a { @@ -49,7 +47,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { border: 1px solid rgba(133, 179, 174, 0.24); border-left: 0.22rem solid #2f7d68; border-radius: 0.35rem; - color: #eaf3f1; + color: var(--mc-color-text); background: rgba(88, 184, 137, 0.1); } @@ -65,9 +63,9 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { padding: 0.85rem 0.95rem; border: 1px solid rgba(133, 179, 174, 0.24); border-radius: 0.35rem; - color: #eaf3f1; + color: var(--mc-color-text); text-decoration: none; - background: #101d26; + background: var(--mcc-surface); } .mcc-region-action:hover { @@ -92,17 +90,22 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { --mcc-blue: #303fa1; --mcc-gold: #f4bf5a; --mcc-red: #ff7373; - --mcc-border: rgba(159, 216, 189, 0.22); + --mcc-border: var(--mc-color-border); --mcc-soft: rgba(66, 135, 255, 0.15); - --mcc-panel: #0c151c; - --mcc-surface: #101d26; - --mcc-surface-2: #172630; - --mcc-text: #dce9e7; - --mcc-muted: #a7b9bb; + --mcc-panel: var(--md-default-bg-color); + --mcc-surface: var(--mc-color-surface); + --mcc-surface-2: var(--mc-color-surface-raised); + --mcc-text: var(--md-default-fg-color); + --mcc-muted: var(--mc-color-text-muted); + --mcc-action-text: #fff; margin: 1rem 0 2rem; color: var(--mcc-text); } +[data-md-color-scheme="slate"] [data-mcc-regions] { + --mcc-action-text: #101827; +} + /* Match the first configurator step before its progressive enhancement mounts. */ [data-mcc-regions="config"] { display: block; @@ -137,14 +140,14 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { padding: 0.35rem 0 0.25rem; border: 0; border-radius: 0; - color: #ffffff; + color: var(--mcc-text); background: transparent; } .mcc-console-header h1, .mcc-console-header h2 { margin: 0; - color: #ffffff; + color: var(--mcc-text); font-size: clamp(1.25rem, 2.1vw, 1.65rem); letter-spacing: 0; } @@ -286,6 +289,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-button:hover, .mcc-button:focus { + color: var(--mcc-action-text); background: var(--mcc-green-dark); border-color: var(--mcc-green-dark); } @@ -303,7 +307,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-button-secondary:hover, .mcc-button-secondary:focus { - color: #ffffff; + color: var(--mcc-text); background: var(--mcc-soft); border-color: var(--mcc-green); } @@ -462,7 +466,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { } .mcc-status-warning { - color: #f6dda8; + color: var(--mc-color-warning); background: color-mix(in srgb, var(--mcc-gold), transparent 82%); } @@ -487,7 +491,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { border: 1px solid var(--mcc-border); border-radius: 0.35rem; color: var(--mcc-text); - background: #09131a; + background: var(--mcc-surface); text-align: left; cursor: pointer; } @@ -574,7 +578,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { } .mcc-empty-state strong { - color: #ffffff; + color: var(--mcc-text); font-size: 1rem; } @@ -706,7 +710,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { min-width: 0; margin: 0.12rem 0 0; overflow: hidden; - color: #eaf3f1; + color: var(--mc-color-text); font-size: 0.78rem; text-overflow: ellipsis; white-space: nowrap; @@ -928,7 +932,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { z-index: 1001; padding: 0.45rem 0.65rem; border-radius: 0.3rem; - color: #ffffff; + color: var(--mcc-action-text); background: var(--mcc-green-dark); transform: translateY(calc(-100% - 1rem)); } @@ -990,7 +994,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-region-crumb:hover, .mcc-region-crumb:focus-visible { - color: #ffffff; + color: var(--mcc-text); text-decoration: underline; } @@ -1044,7 +1048,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { padding: 0 0 0.2rem; border: 0; border-radius: 0; - color: #ffffff; + color: var(--mcc-text); background: transparent; } @@ -1066,7 +1070,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-panel-header h1, .mcc-panel-header h2 { margin: 0; - color: #ffffff; + color: var(--mcc-text); font-size: 1.28rem; letter-spacing: 0; } @@ -1100,7 +1104,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-map-canvas .leaflet-bar a, .mcc-map-canvas .leaflet-bar a:hover { border-color: rgba(133, 179, 174, 0.24); - color: #eaf3f1; + color: var(--mc-color-text); background: rgba(11, 20, 27, 0.92); } @@ -1145,7 +1149,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { padding: 0.9rem; border: 1px solid rgba(159, 216, 189, 0.24); border-radius: 0.45rem; - color: #eaf3f1; + color: var(--mc-color-text); background: rgba(7, 16, 22, 0.92); box-shadow: 0 18px 42px rgba(0, 0, 0, 0.34); } @@ -1238,7 +1242,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { } .mcc-detail-node code { - color: #ffffff; + color: var(--mcc-text); font-size: 0.82rem; } @@ -1364,7 +1368,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { } .mcc-visible-legend-head strong { - color: #eaf3f1; + color: var(--mc-color-text); font-size: 0.76rem; } @@ -1416,7 +1420,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-visible-region strong { display: block; overflow: hidden; - color: #eaf3f1; + color: var(--mc-color-text); font-size: 0.72rem; text-overflow: ellipsis; white-space: nowrap; @@ -1507,7 +1511,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { padding: 0.62rem 0.7rem; border: 1px solid rgba(159, 216, 189, 0.18); border-radius: 0.35rem; - color: #eaf3f1; + color: var(--mc-color-text); background: var(--mcc-surface); text-decoration: none; } @@ -1515,7 +1519,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-action-button:hover, .mcc-action-button:focus { border-color: rgba(159, 216, 189, 0.42); - color: #ffffff; + color: var(--mcc-text); background: rgba(88, 184, 137, 0.11); text-decoration: none; } @@ -1572,7 +1576,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { .mcc-stat strong { display: block; margin-top: 0.18rem; - color: #ffffff; + color: var(--mcc-text); font-size: 1.45rem; line-height: 1; } @@ -1593,7 +1597,7 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { display: flex; gap: 0.36rem; align-items: center; - color: #ffffff; + color: var(--mcc-text); font-size: 0.86rem; } @@ -1717,8 +1721,8 @@ body:has([data-mcc-regions]) .md-main .md-typeset a { } .mcc-region-table td { - color: #eaf3f1; - background: #101d26; + color: var(--mc-color-text); + background: var(--mcc-surface); font-size: 0.76rem; } @@ -1951,7 +1955,7 @@ ol.mcc-wizard-progress > li { } .mcc-finish-path strong { - color: #ffffff; + color: var(--mcc-text); } .mcc-finish-path small { @@ -2011,7 +2015,7 @@ ol.mcc-guide-steps > li::before { .mcc-guide-steps h4 { margin: 0; - color: #ffffff; + color: var(--mcc-text); font-size: 0.92rem; } @@ -2056,7 +2060,7 @@ ol.mcc-guide-steps > li::before { .mcc-connect-method h5 { margin: 0; - color: #ffffff; + color: var(--mcc-text); font-size: 0.84rem; } diff --git a/docs/assets/regions/regions.js b/docs/assets/regions/regions.js index 6230a5c3..5bb1d597 100644 --- a/docs/assets/regions/regions.js +++ b/docs/assets/regions/regions.js @@ -13,12 +13,24 @@ var activeMaps = []; var LUCIDE_SRC = new URL("vendor/lucide.js", assetBase).href; var configuratorSupport = window.MeshCoreRegionConfiguratorSupport || {}; + var radioProfiles = window.MeshCoreRadioProfiles; var REQUEST_TIMEOUT_MS = 12000; var frenchRuntime = /^fr(?:-|$)/i.test( document.documentElement ? document.documentElement.lang || "" : "" ); var FRENCH_RUNTIME_TEXT = { + "Radio network": "Réseau radio", + "Advert ID size": "Taille de l’identifiant d’annonce", + "Keep current settings": "Conserver les réglages actuels", + "Choose a profile only after checking with your community. A region does not select a radio network.": "Confirmez le profil auprès de votre communauté. Une région ne détermine pas les réglages radio.", + "Radio changes take effect after reboot.": "Les changements radio prennent effet après le redémarrage.", + "Choose the region you mean:": "Choisissez la région recherchée :", + "This saved location is invalid or no longer available. Choose a region again.": "Cet emplacement est invalide ou n’est plus disponible. Choisissez une région à nouveau.", + "Setup summary": "Résumé de configuration", + "3 bytes": "3 octets", + "2 bytes": "2 octets", + "1 byte": "1 octet", "Unable to load MeshCore Canada region data": "Impossible de charger les données régionales de MeshCore Canada", "Unable to load the Canadian map layer": "Impossible de charger la couche cartographique canadienne", "Unable to load the Canadian location layer": "Impossible de charger la couche canadienne de localisation", @@ -271,12 +283,15 @@ "Open standard and change process": "Ouvrir la norme et le processus de modification", "Try again": "Réessayer", "Loading release evidence…": "Chargement des preuves de publication…", - "Select this leaf to see its full path.": "Sélectionnez cette région terminale pour voir son chemin complet.", + "Select this region to see its full path.": "Sélectionnez cette région pour voir son chemin complet.", "Province or territory": "Province ou territoire", "resolves to": "correspond à", "Aliases": "Alias", "None recorded": "Aucun", - "Planned paths": "Chemins prévus", + "Repeater paths": "Chemins du répéteur", + "Region details": "Détails de la région", + "Find a community": "Trouver une communauté", + "These are routing regions, not radio coverage boundaries.": "Ces régions servent au routage; elles ne représentent pas la portée radio.", "Configure this region": "Configurer cette région", "Copy link": "Copier le lien", "This location is outside Canada.": "Cet emplacement se trouve à l’extérieur du Canada.", @@ -990,9 +1005,38 @@ if (state.selectedExternalPaths && state.selectedExternalPaths.length) { params.set("external", state.selectedExternalPaths.join(",")); } + if (state.firmware) params.set("firmware", state.firmware); + if (state.radioProfile) params.set("radio", state.radioProfile); + if (state.hashMode) params.set("hash", state.hashMode); + if (state.deviceRole) params.set("role", state.deviceRole); + if (state.wizardStep) params.set("step", state.wizardStep); + if (state.finishPath) params.set("instructions", state.finishPath); return regionPageHref("map") + (params.toString() ? "?" + params.toString() : ""); } + function keepLanguageSelection(state) { + document.querySelectorAll(".md-select__link[hreflang]").forEach(function (link) { + // Transfer only the tool's public selections, never arbitrary URL fields or secrets. + link.addEventListener("click", function () { + var target = new URL(link.href); + target.search = new URL(mapHrefForState(state)).search; + link.href = target.href; + }); + }); + } + + function initialLocation(data, params) { + var tag = params.get("tag"); + var seed = tag && seedForTag(data, tag); + if (tag && (!seed || statusFor(data, tag).state === "retired")) return null; + var point = params.has("lat") || params.has("lon") + ? configuratorSupport.parseCoordinates(params.get("lat"), params.get("lon")) + : seed; + if (!point) return null; + return { lat: point.lat, lon: point.lon, tag: tag || null, countryCode: "ca", + name: String(params.get("name") || (tag && labelFor(data, tag)) || "").slice(0, 160) }; + } + function configHrefForState(state) { var mapHref = new URL(mapHrefForState(state)); return regionPageHref("config") + mapHref.search; @@ -1199,11 +1243,8 @@ }); } - function buildCommands(data, tags, firmware, includeBaseline, parentOverrides) { - var lines = []; - if (includeBaseline) { - lines = lines.concat(data.meta.baselineCommands || []); - } + function buildCommands(data, tags, firmware, settings, parentOverrides) { + var lines = radioProfiles ? radioProfiles.commands(settings.radioProfile, settings.hashMode) : []; if (firmware === "1.16") { var regionDefLine = "region def " + regionDefTokens(data, tags, parentOverrides).join(" "); @@ -1342,12 +1383,14 @@ if (!matches.length) return null; var best = matches[0]; var tied = matches.filter(function (item) { return item.score === best.score; }); + if (tied.length > 1 && best.score > 1) return null; if (tied.length > 1) { return { ambiguous: true, choices: tied.map(function (item) { var province = provinceTagFor(data, item.seed.tag); - return labelFor(data, item.seed.tag) + (province ? ", " + province.toUpperCase() : ""); + return { tag: item.seed.tag, lat: item.seed.lat, lon: item.seed.lon, countryCode: "ca", + name: labelFor(data, item.seed.tag) + (province ? ", " + province.toUpperCase() : "") }; }) }; } @@ -1424,10 +1467,24 @@ }); } + function showLocationChoices(target, choices, choose) { + if (!choices) return; + choices.forEach(function (geo) { + var button = document.createElement("button"); + button.type = "button"; + button.className = "mcc-button mcc-button-secondary"; + button.textContent = geo.name + " (" + geo.tag + ")"; + button.addEventListener("click", function () { choose(geo); }); + target.appendChild(button); + }); + } + function geocode(data, query, signal) { var localMatch = localGeocode(data, query); if (localMatch && localMatch.ambiguous) { - return Promise.reject(new Error("That name matches more than one region (" + localMatch.choices.join("; ") + "). Add a province or postal code.")); + var error = new Error("Choose the region you mean:"); + error.choices = localMatch.choices; + return Promise.reject(error); } if (localMatch && localMatch.exactLocalMatch) return Promise.resolve(localMatch); var postal = parseCanadianPostalCode(query); @@ -1626,7 +1683,7 @@ return; } var firmware = state.firmware || data.meta.defaultFirmware || "1.16"; - var commands = buildCommands(data, rec.tags, firmware, state.includeBaseline, rec.parentOverrides); + var commands = buildCommands(data, rec.tags, firmware, state, rec.parentOverrides); var technicalCommands = commands.concat(["region", "region save", "region"]); var titleTag = state.resolution.primary.seed.tag; var statusNotes = rec.notes.map(function (note) { @@ -1638,7 +1695,8 @@ var firmwareLabel = firmware === "1.16" ? "v1.16+" : firmware === "1.15" ? "v1.15.x" : "v1.14.x"; var guided = state.finishPath === "guided"; var verificationCommands = ["region"]; - if (state.includeBaseline) verificationCommands.push("get radio"); + if (state.radioProfile !== "keep") verificationCommands.push("get radio"); + if (state.hashMode !== "keep") verificationCommands.push("get path.hash.mode"); var expectedPaths = rec.paths.map(function (path) { return labelledPath(data, path); }); var expectedPathMarkup = '
' + expectedPaths.map(function (path) { return "" + esc(path) + ""; @@ -1687,7 +1745,7 @@ '
' + '

Save:

' + '
' + - '

' + (state.includeBaseline + '

' + (state.radioProfile !== "keep" ? 'Restart the device, reconnect, then run these final checks:' : 'Run this once more to confirm the saved region:') + '

' + '
' + verificationCommands.map(function (line) { @@ -1736,10 +1794,12 @@ (!guided ? '' : '') + '
' + scopeNotice + + '

' + esc(radioProfiles ? radioProfiles.label(state.radioProfile) : "Keep current settings") + + (state.radioProfile !== "keep" ? ' — Radio changes take effect after reboot.' : '') + '

' + technicalDetails + resultBody + '
' + - '

Commissioning record

Download or print a summary without exact coordinates, credentials, or device identifiers.

' + + '

Setup summary

Download or print a summary without exact coordinates, credentials, or device identifiers.

' + '
' + '
' + '
' + @@ -1795,7 +1855,7 @@ if (!rec) return null; if (rec.budget.tagCount > 32 || rec.budget.responseBytes > 172) return null; var firmware = state.firmware || data.meta.defaultFirmware || "1.16"; - return buildCommands(data, rec.tags, firmware, state.includeBaseline, rec.parentOverrides) + return buildCommands(data, rec.tags, firmware, state, rec.parentOverrides) .concat(["region", "region save", "region"]); } @@ -1811,6 +1871,8 @@ homeRegion: labelledPath(data, ancestryFor(data, homeTag)), firmware: state.firmware === "1.16" ? "v1.16+" : "v" + state.firmware + ".x", budget: rec.budget.tagCount + " / 32 tags, " + rec.budget.responseBytes + " / 172 bytes", + radio: radioProfiles ? radioProfiles.label(state.radioProfile) : "Keep current settings", + hashMode: state.hashMode === "keep" ? "Keep current settings" : (state.hashMode === "0" ? "1 byte" : String(Number(state.hashMode) + 1) + " bytes"), paths: rec.paths.map(function (path) { return labelledPath(data, path); }), commands: commands }); @@ -2035,12 +2097,13 @@ '' + '
' + '
' + + '' + + '' + + '

Choose a profile only after checking with your community. A region does not select a radio network.

' + + '' + + '' + '
' + - 'Radio and firmware options' + - '
' + - '' + - '' + - '
' + + 'Firmware version' + '

Firmware version

' + '
' + '' + @@ -2077,7 +2140,8 @@ deviceRole: "repeater", type: "residential", firmware: data.meta.defaultFirmware || "1.16", - includeBaseline: true, + radioProfile: "keep", + hashMode: "keep", selectedMetros: [], selectedExternalPaths: [], canGenerate: false, @@ -2141,6 +2205,8 @@ '
Node
' + esc(deviceLabels[state.deviceRole] || deviceLabels.repeater) + '
' + '
Place
' + esc(state.name || labelFor(data, state.resolution.primary.seed.tag)) + '
' + '
Home region
' + esc(labelledPath(data, ancestryFor(data, state.resolution.primary.seed.tag))) + '
' + + '
Radio network
' + esc(radioProfiles ? radioProfiles.label(state.radioProfile) : "Keep current settings") + '
' + + '
Advert ID size
' + (state.hashMode === "keep" ? 'Keep current settings' : state.hashMode === "0" ? '1 byte' : esc(Number(state.hashMode) + 1) + ' bytes') + '
' + '
Budget
' + esc(rec.budget.tagCount) + ' / 32 tags · ' + esc(rec.budget.responseBytes) + ' / 172 bytes
' + '' + '

Forwarding paths

    ' + paths + '
' + @@ -2179,6 +2245,7 @@ heading.focus({ preventScroll: true }); } } + updateMapLinks(); refreshIcons(el); } @@ -2190,6 +2257,7 @@ button.setAttribute("aria-pressed", active ? "true" : "false"); }); renderResult(data, els.result, state); + updateMapLinks(); } function advanceStep() { @@ -2333,6 +2401,7 @@ if (err && err.name === "AbortError") return; state.canGenerate = false; setStatus(els.status, esc(err.message || "Location lookup failed"), "error"); + showLocationChoices(els.status, err.choices, useGeo); refreshTool(data, els, state, updateMapLinks); }).finally(function () { if (activeGeocodeController !== thisController) return; @@ -2419,14 +2488,13 @@ input.addEventListener("change", function () { state.deviceRole = input.value; if (state.deviceRole === "advanced" && els.technicalSettings) els.technicalSettings.open = true; + updateMapLinks(); }); }); - el.querySelectorAll("input[name='mcc-device-path']").forEach(function (input) { - input.addEventListener("change", function () { - state.includeBaseline = input.value === "new"; - renderResult(data, els.result, state); - }); - }); + var profileSelect = el.querySelector("#mcc-radio-profile"); + var hashSelect = el.querySelector("#mcc-hash-mode"); + profileSelect.addEventListener("change", function () { state.radioProfile = profileSelect.value; updateMapLinks(); }); + hashSelect.addEventListener("change", function () { state.hashMode = hashSelect.value; updateMapLinks(); }); el.querySelectorAll("input[name='mcc-type']").forEach(function (input) { input.addEventListener("change", function () { state.type = input.value; @@ -2440,13 +2508,52 @@ input.addEventListener("change", function () { state.firmware = input.value; renderResult(data, els.result, state); + updateMapLinks(); }); }); renderConfigRegionBrowser(state.browseTag); updateMapLinks(); var initialParams = new URLSearchParams(window.location.search); + keepLanguageSelection(state); + var profileReady = radioProfiles ? radioProfiles.populate(profileSelect) : Promise.resolve(); + profileReady.then(function () { + if (Array.from(profileSelect.options).some(function (option) { return option.value === initialParams.get("radio"); })) { + state.radioProfile = profileSelect.value = initialParams.get("radio"); + updateMapLinks(); + if (state.wizardStep === 4) showStep(4); + } + }); + [["firmware", "mcc-firmware", ["1.14", "1.15", "1.16"]], + ["deviceRole", "mcc-device-role", ["repeater", "room", "advanced"]]].forEach(function (entry) { + var value = initialParams.get(entry[0] === "deviceRole" ? "role" : entry[0]); + if (entry[2].indexOf(value) !== -1) { + state[entry[0]] = value; + el.querySelector("input[name='" + entry[1] + "'][value='" + value + "']").checked = true; + } + }); + if (["0", "1", "2"].indexOf(initialParams.get("hash")) !== -1) state.hashMode = hashSelect.value = initialParams.get("hash"); + if (initialParams.get("instructions") === "technical") selectFinishPath("technical"); + var location = initialLocation(data, initialParams); var initialQuery = (initialParams.get("place") || "").trim(); - if (initialQuery) { + if (location) { + els.input.value = location.name; + useGeo(location).then(function () { + if (!state.canGenerate) return; + if (initialParams.get("type") === "large") { + state.type = "high-site"; + el.querySelector("input[name='mcc-type'][value='high-site']").checked = true; + state.selectedMetros = String(initialParams.get("regions") || "").split(",").filter(function (tag) { return Boolean(seedForTag(data, tag)); }); + state.selectedExternalPaths = selectedExternalRegionPaths(data, String(initialParams.get("external") || "").split(",")).map(function (record) { return record.id; }); + } + state.maxStep = 4; + refreshTool(data, els, state, updateMapLinks); + showStep(/^[1-4]$/.test(initialParams.get("step")) ? Number(initialParams.get("step")) : 3); + }); + } else if (initialParams.has("tag") || initialParams.has("lat") || initialParams.has("lon")) { + state.maxStep = 2; + showStep(2); + setStatus(els.status, "This saved location is invalid or no longer available. Choose a region again.", "warning"); + } else if (initialQuery) { state.maxStep = 2; els.input.value = initialQuery; showStep(2); @@ -2502,11 +2609,11 @@ '
' + '
' + '' + - '
' + - '

Browse by province or territory

' + + '
' + + 'Browse by province or territory' + '
' + '
' + - '
' + + '' + '' + @@ -2558,7 +2665,8 @@ jurisdictionTag: null, type: requestedLargeCoverage ? "high-site" : "residential", firmware: data.meta.defaultFirmware || "1.16", - includeBaseline: true, + radioProfile: "keep", + hashMode: ["0", "1", "2"].indexOf(mapParams.get("hash")) !== -1 ? mapParams.get("hash") : "keep", selectedMetros: requestedLargeCoverage ? requestedCanadianRegions : [], selectedExternalPaths: requestedLargeCoverage ? requestedExternalPaths : [], canGenerate: false, @@ -2681,7 +2789,7 @@ '' + esc(labelFor(data, child)) + '' + (nested ? leafCount + ' subregions' : child.toUpperCase() + ' · region') + ''; - }).join("") : '

Select this leaf to see its full path.

'; + }).join("") : '

Select this region to see its full path.

'; if (!map || !browseLayer) return; browseLayer.clearLayers(); if (tag !== (data.meta.rootTag || "can") && data.partitionByTag) { @@ -2728,15 +2836,22 @@ return normalizeLocationSearch(alias) !== normalizeLocationSearch(tag) && normalizeLocationSearch(alias) !== normalizeLocationSearch(labelFor(data, tag)); }); + aliases = aliases.filter(function (alias, index) { + return aliases.findIndex(function (item) { return normalizeLocationSearch(item) === normalizeLocationSearch(alias); }) === index; + }); + var communityUrl = new URL("../provinces/", regionPageHref("config")); + communityUrl.searchParams.set("community", labelFor(data, provinceTagFor(data, tag))); els.resultSection.hidden = false; els.textResult.innerHTML = '

' + esc(state.name) + ' resolves to ' + esc(labelFor(data, tag)) + ' (' + esc(tag) + ').

' + '

' + esc(labelledPath(data, ancestryFor(data, tag))) + '

' + - '
Province or territory
' + esc(labelFor(data, provinceTagFor(data, tag))) + '
' + + '

These are routing regions, not radio coverage boundaries.

' + + '

Find a community

' + + '
Region details
Province or territory
' + esc(labelFor(data, provinceTagFor(data, tag))) + '
' + '
Status
' + esc(statusLabel(statusFor(data, tag).state || "draft")) + '
' + '
Aliases
' + esc(aliases.length ? aliases.join(", ") : "None recorded") + '
' + - '
Planned paths
' + esc(rec ? rec.paths.length : 1) + '
' + + '
Repeater paths
' + esc(rec ? rec.paths.length : 1) + '
' + '
Configure this region' + '
'; var copyLink = els.textResult.querySelector("[data-action='copy-region-link']"); @@ -2812,6 +2927,7 @@ .catch(function (error) { if (error && error.name === "AbortError") return; setStatus(els.status, esc(error.message || "Location lookup failed"), "error"); + showLocationChoices(els.status, error.choices, function (geo) { return useGeo(geo, true, geo.tag); }); }).finally(function () { if (activeGeocodeController !== thisController) return; els.locate.disabled = false; @@ -2941,18 +3057,24 @@ renderRegionTable(els.table, data, function (tag) { chooseRegionNode(tag); }); window.setTimeout(loadInteractiveMap, 0); - var initialLat = Number(mapParams.get("lat")); - var initialLon = Number(mapParams.get("lon")); - var hasInitialLocation = mapParams.has("lat") && mapParams.has("lon") && - Number.isFinite(initialLat) && Number.isFinite(initialLon) && - initialLat >= -90 && initialLat <= 90 && initialLon >= -180 && initialLon <= 180; - if (hasInitialLocation) { - var requestedTag = slug(mapParams.get("tag")); - if (!data.hierarchy[requestedTag] || !seedForTag(data, requestedTag)) requestedTag = null; - var initialName = String(mapParams.get("name") || "").slice(0, 160) || - initialLat.toFixed(4) + ", " + initialLon.toFixed(4); - els.input.value = initialName; - useGeo({ lat: initialLat, lon: initialLon, name: initialName, countryCode: "ca", tag: requestedTag }, false, requestedTag); + keepLanguageSelection(state); + // Load the same validated profile list used by the configurator before carrying a choice back. + if (radioProfiles) radioProfiles.populate(document.createElement("select")).then(function () { + try { + radioProfiles.commands(mapParams.get("radio"), "keep"); + state.radioProfile = mapParams.get("radio") || "keep"; + } catch (_) { state.radioProfile = "keep"; } + renderTextResult(); + }); + if (["1.14", "1.15", "1.16"].indexOf(mapParams.get("firmware")) !== -1) state.firmware = mapParams.get("firmware"); + if (["repeater", "room", "advanced"].indexOf(mapParams.get("role")) !== -1) state.deviceRole = mapParams.get("role"); + if (["guided", "technical"].indexOf(mapParams.get("instructions")) !== -1) state.finishPath = mapParams.get("instructions"); + var initialGeo = initialLocation(data, mapParams); + if (initialGeo) { + els.input.value = initialGeo.name; + useGeo(initialGeo, false, initialGeo.tag); + } else if (mapParams.has("tag") || mapParams.has("lat") || mapParams.has("lon")) { + setStatus(els.status, "This saved location is invalid or no longer available. Choose a region again.", "warning"); } setMapMode(mapParams.get("view") === "audit" ? "audit" : "explore"); } diff --git a/docs/config/editor/issue.js b/docs/config/editor/issue.js index ebbc024f..36992ed6 100644 --- a/docs/config/editor/issue.js +++ b/docs/config/editor/issue.js @@ -159,7 +159,8 @@ export function validateSubmissionConfig(value, endpoint = DEFAULT_SUBMISSION_EN version: SUBMISSION_CONTRACT_VERSION, endpoint: cleanEndpoint(endpoint), turnstileSiteKey: siteKey, - turnstileAction: action + turnstileAction: action, + ...(value.communityIdeaOptionalDetails === true ? { communityIdeaOptionalDetails: true } : {}) }); } diff --git a/docs/config/index.fr.md b/docs/config/index.fr.md index f6c0ac9e..13c0c3cd 100644 --- a/docs/config/index.fr.md +++ b/docs/config/index.fr.md @@ -14,10 +14,11 @@ tested_with: difficulty: intermediate estimated_time: 5-10 minutes page_styles: - - assets/regions/regions.css?v=20260820-1 + - assets/regions/regions.css?v=20260904-1 page_scripts: - - assets/regions/modules/configurator-support.js?v=20260722-2 - - assets/regions/regions.js?v=20260820-1 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/regions/modules/configurator-support.js?v=20260904-1 + - assets/regions/regions.js?v=20260904-1 hide: - navigation - toc diff --git a/docs/config/index.md b/docs/config/index.md index 2a5a9745..c7ee9003 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -14,10 +14,11 @@ tested_with: difficulty: intermediate estimated_time: 5-10 minutes page_styles: - - assets/regions/regions.css?v=20260820-1 + - assets/regions/regions.css?v=20260904-1 page_scripts: - - assets/regions/modules/configurator-support.js?v=20260722-2 - - assets/regions/regions.js?v=20260820-1 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/regions/modules/configurator-support.js?v=20260904-1 + - assets/regions/regions.js?v=20260904-1 hide: - navigation - toc diff --git a/docs/config/map.fr.md b/docs/config/map.fr.md index e63b50c7..4e2fa778 100644 --- a/docs/config/map.fr.md +++ b/docs/config/map.fr.md @@ -15,10 +15,11 @@ tested_with: region_catalog: national-partition-2026-07-19 difficulty: beginner page_styles: - - assets/regions/regions.css?v=20260820-1 + - assets/regions/regions.css?v=20260904-1 page_scripts: - - assets/regions/modules/configurator-support.js?v=20260722-2 - - assets/regions/regions.js?v=20260820-1 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/regions/modules/configurator-support.js?v=20260904-1 + - assets/regions/regions.js?v=20260904-1 hide: - toc --- diff --git a/docs/config/map.md b/docs/config/map.md index df02ec96..cee67591 100644 --- a/docs/config/map.md +++ b/docs/config/map.md @@ -15,10 +15,11 @@ tested_with: region_catalog: national-partition-2026-07-19 difficulty: beginner page_styles: - - assets/regions/regions.css?v=20260820-1 + - assets/regions/regions.css?v=20260904-1 page_scripts: - - assets/regions/modules/configurator-support.js?v=20260722-2 - - assets/regions/regions.js?v=20260820-1 + - assets/javascripts/radio-profiles.js?v=20260904-1 + - assets/regions/modules/configurator-support.js?v=20260904-1 + - assets/regions/regions.js?v=20260904-1 hide: - toc --- diff --git a/docs/config/standard.fr.md b/docs/config/standard.fr.md index c71f9027..b7ec1d86 100644 --- a/docs/config/standard.fr.md +++ b/docs/config/standard.fr.md @@ -6,7 +6,7 @@ audience: - region-maintainer task: understand-region-standard scope: canada-baseline -status: verified +status: draft owner: region-maintainers last_reviewed: 2026-07-19 review_by: 2026-10-19 @@ -17,6 +17,18 @@ difficulty: advanced # Définition des régions et autorité de MeshCore Canada +## Pour configurer votre appareil + +1. [Trouvez votre région](map.md). +2. Confirmez les réglages radio auprès de votre [communauté](../provinces/index.md). +3. Sur un répéteur, [préparez les chemins régionaux](index.md), vérifiez-les, puis enregistrez-les. + +Une région organise le routage; elle ne garantit pas la portée radio. Ottawa–Gatineau et Lloydminster utilisent plusieurs chemins pour leur zone partagée. + +Le reste de cette page décrit les règles proposées et les contrôles destinés aux responsables du registre. Les modifications de limites nécessitent un examen communautaire. + +## Référence du registre + Cette norme définit les régions MeshCore du Canada. Elle explique comment les emplacements et les limites sont attribués, qui approuve les changements et comment les conflits entre sources sont résolus. @@ -29,7 +41,7 @@ comment les conflits entre sources sont résolus. | Entrée sémantique actuelle | Canada MeshCore Region Strategy v1.1.1 | | Entrée actuelle pour les limites communautaires | Instantané MeshMapper Canada, 2026-07-12 | | Preuve opérationnelle actuelle | Instantané canadien de densité radio protégeant la vie privée, 2026-07-15 UTC; instantané agrégé des routes Canada–États-Unis, 2026-07-18 | -| Adoption | Devient normative après approbation et fusion par MeshCore Canada | +| Adoption | Examen communautaire; publier cette page ne signifie pas que tous les réseaux l’ont adoptée | Statistique Canada emploie en français les termes **aire de diffusion (AD)**, **subdivision de recensement (SDR)**, **division de recensement (DR)** et diff --git a/docs/config/standard.md b/docs/config/standard.md index 7a6745e5..5d774bfd 100644 --- a/docs/config/standard.md +++ b/docs/config/standard.md @@ -6,7 +6,7 @@ audience: - region-maintainer task: understand-region-standard scope: canada-baseline -status: verified +status: draft owner: region-maintainers last_reviewed: 2026-07-19 review_by: 2026-10-19 @@ -17,6 +17,18 @@ difficulty: advanced # MeshCore Canada region definition and authority +## Setting up a device? + +1. [Find your region](map.md). +2. Confirm the radio settings with your [community](../provinces/index.md). +3. For a repeater, [prepare its region paths](index.md), check them, then save. + +A region organizes routing; it does not guarantee radio coverage. Ottawa–Gatineau and Lloydminster use several paths for their shared area. + +The rest of this page documents proposed registry rules and maintainer checks. Boundary changes still require community review. + +## Registry reference + This standard defines Canada's MeshCore regions. It explains how locations and boundaries are assigned, who approves changes, and how source conflicts are resolved. @@ -29,7 +41,7 @@ resolved. | Current semantic input | Canada MeshCore Region Strategy v1.1.1 | | Current community boundary input | MeshMapper Canada snapshot, 2026-07-12 | | Current operational evidence | Privacy-safe Canadian radio-density snapshot, 2026-07-15 UTC; aggregate Canada–U.S. route snapshot, 2026-07-18 | -| Adoption | Becomes normative when approved and merged by MeshCore Canada | +| Adoption | Community review; publishing this page does not establish network-wide adoption | !!! important "What is authoritative today?" This page and the generated national partition are proposed for review. The candidate assigns every digital DA exactly once and the public map renders only dissolved leaf regions. No raw source polygon or approximate circle is a boundary. The partition becomes operationally authoritative only after community review and every release check in this standard passes. diff --git a/docs/hardware/recommended-antenna.fr.md b/docs/hardware/recommended-antenna.fr.md index 5dfef261..2daa7cb1 100644 --- a/docs/hardware/recommended-antenna.fr.md +++ b/docs/hardware/recommended-antenna.fr.md @@ -56,9 +56,9 @@ Ce sont des antennes SMA, plus compactes, qui ont pourtant constamment offert un | Produit | Connecteur | Coût (CAD) | Lien | |---|---|---|---| -| Gizont 167CM 915MHz SMA M | SMA | 12 $ | [Space Hedgehog (boutique locale)](https://space-hedgehog.com/products/gizont-915mhz-antenna?variant=51602989711416) | -| Gizont 167CM 915MHz SMA M | SMA | 10,53 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | -| Gizont 167CM 915MHz RP-SMA M | RP-SMA | 10,53 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | +| Gizont 915 MHz SMA M | SMA | 12 $ | [Space Hedgehog (boutique locale)](https://space-hedgehog.com/products/gizont-915mhz-antenna?variant=51602989711416) | +| Gizont 915 MHz SMA M | SMA | 10,53 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | +| Gizont 915 MHz RP-SMA M | RP-SMA | 10,53 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | | LINX ANT-916-CW-HW-SMA | SMA | 14,65 $ | [DigiKey](https://www.digikey.ca/en/products/detail/te-connectivity-linx/ANT-916-CW-HW-SMA/2694126?s=N4IgTCBcDaIDIEkByANABAQSQFQLQE4BGANlwGEB1XACSoGUBZDEAXQF8g) | | Taoglas TI.09.A.0111 | SMA | 17,47 $ | [DigiKey](https://www.digikey.ca/en/products/detail/taoglas-limited/TI-09-A-0111/2332695?s=N4IgTCBcDaICoEMD2BzANggzgAjgSQDoAGATgIEFiBGGkAXQF8g) | | Seeed Studio LoRa Antenna Kit | SMA | 6,79 $ | [Seeed Studio](https://www.seeedstudio.com/LoRa-Antenna-Kit-for-reTerminal-DM-p-5714.html) | diff --git a/docs/hardware/recommended-antenna.md b/docs/hardware/recommended-antenna.md index 7cb821c0..db36afe1 100644 --- a/docs/hardware/recommended-antenna.md +++ b/docs/hardware/recommended-antenna.md @@ -56,9 +56,9 @@ These are SMA antennas and are more compact, yet they've consistently shown exce | Product | Connector | Cost (CAD) | Link | |---|---|---|---| -| Gizont 167CM 915MHz SMA M | SMA | $12 | [Space Hedgehog (local store)](https://space-hedgehog.com/products/gizont-915mhz-antenna?variant=51602989711416) | -| Gizont 167CM 915MHz SMA M | SMA | $10.53 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | -| Gizont 167CM 915MHz RP-SMA M | RP-SMA | $10.53 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | +| Gizont 915 MHz SMA M | SMA | $12 | [Space Hedgehog (local store)](https://space-hedgehog.com/products/gizont-915mhz-antenna?variant=51602989711416) | +| Gizont 915 MHz SMA M | SMA | $10.53 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | +| Gizont 915 MHz RP-SMA M | RP-SMA | $10.53 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) | | LINX ANT-916-CW-HW-SMA | SMA | $14.65 | [DigiKey](https://www.digikey.ca/en/products/detail/te-connectivity-linx/ANT-916-CW-HW-SMA/2694126?s=N4IgTCBcDaIDIEkByANABAQSQFQLQE4BGANlwGEB1XACSoGUBZDEAXQF8g) | | Taoglas TI.09.A.0111 | SMA | $17.47 | [DigiKey](https://www.digikey.ca/en/products/detail/taoglas-limited/TI-09-A-0111/2332695?s=N4IgTCBcDaICoEMD2BzANggzgAjgSQDoAGATgIEFiBGGkAXQF8g) | | Seeed Studio LoRa Antenna Kit | SMA | $6.79 | [Seeed Studio](https://www.seeedstudio.com/LoRa-Antenna-Kit-for-reTerminal-DM-p-5714.html) | diff --git a/docs/hardware/recommended-companions.fr.md b/docs/hardware/recommended-companions.fr.md index 5cbac16e..4a7dd1e1 100644 --- a/docs/hardware/recommended-companions.fr.md +++ b/docs/hardware/recommended-companions.fr.md @@ -81,11 +81,11 @@ Il s’agit d’un rôle de **compagnon** et il exige un téléphone intelligent | **Carte LoRa** | Heltec T114 (ensemble avec écran) | 45,99 $ | [AliExpress](https://www.aliexpress.com/item/1005007916299029.html) | | **Câble IPEX à SMA à angle droit** | SMA-KW 2PCS 8cm | 4,67 $ | [AliExpress](https://www.aliexpress.com/item/1005009270132403.html) | | **Pile** | MakerFocus LiPo 3,7 V 3000 mAh (paquet de 4), connecteur Micro JST 1.5 avec circuit de protection | 34,34 $ | [MakerFocus](https://www.makerfocus.com/products/makerfocus-3-7v-3000mah-lithium-rechargeable-battery-1s-3c-lipo-battery-pack-of-4?variant=44823607541998) | -| **Antenne** | Gizont 167CM 915MHz SMA M | 10,68 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) (assurez-vous de choisir la bonne antenne à l’ouverture du lien) | +| **Antenne** | Gizont 915 MHz SMA M | 10,68 $ | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) (assurez-vous de choisir la bonne antenne à l’ouverture du lien) | -*Coût total approximatif :* **95,68 $ CAD** +*Total de cet exemple d’achat :* **95,68 $ CAD**, dont un lot de quatre piles et deux câbles. Ce n’est pas le coût par appareil. *Les prix datent du 2 septembre 2026. Consultez les pages liées pour vérifier les prix, l’expédition, les droits de douane et la disponibilité.* diff --git a/docs/hardware/recommended-companions.md b/docs/hardware/recommended-companions.md index e212c5ef..4893b417 100644 --- a/docs/hardware/recommended-companions.md +++ b/docs/hardware/recommended-companions.md @@ -81,11 +81,11 @@ This is a **companion node** role and requires a smartphone. The MeshCore app co | **LoRa board** | Heltec T114 (bundle with screen) | $45.99 | [AliExpress](https://www.aliexpress.com/item/1005007916299029.html) | | **Right-angle IPEX to SMA pigtail cable** | SMA-KW 2PCS 8cm | $4.67 | [AliExpress](https://www.aliexpress.com/item/1005009270132403.html) | | **Battery** | MakerFocus 3.7V 3000mAh LiPo (pack of 4), Micro JST 1.5 connection with protection board | $34.34 | [MakerFocus](https://www.makerfocus.com/products/makerfocus-3-7v-3000mah-lithium-rechargeable-battery-1s-3c-lipo-battery-pack-of-4?variant=44823607541998) | -| **Antenna** | Gizont 167CM 915MHz SMA M | $10.68 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) (make sure you select the right antenna when opening the link) | +| **Antenna** | Gizont 915 MHz SMA M | $10.68 | [AliExpress](https://www.aliexpress.com/item/1005004607615001.html) (make sure you select the right antenna when opening the link) | -*Approximate total cost:* **$95.68 CAD** +*Example shopping total:* **$95.68 CAD**, including a four-battery pack and two pigtails. This is not the per-device cost. *Prices are dated September 2, 2026. Check the linked pages for current prices, shipping, duties, and availability.* diff --git a/docs/hardware/repeater-mounting-options.fr.md b/docs/hardware/repeater-mounting-options.fr.md index 38cf9890..4af68583 100644 --- a/docs/hardware/repeater-mounting-options.fr.md +++ b/docs/hardware/repeater-mounting-options.fr.md @@ -138,11 +138,9 @@ sous tension en hauteur.
-Aucun intervalle d’inspection pancanadien n’a été approuvé. Avant le -déploiement, consignez un intervalle propre au site et les conditions qui -déclenchent une inspection supplémentaire, comme des conditions -météorologiques extrêmes, des travaux à proximité, une infiltration d’eau ou -un changement du comportement radio. +Fixez un calendrier d’inspection adapté au site. Inspectez aussi l’installation +après une tempête, des travaux à proximité, une infiltration d’eau ou un changement +du comportement radio. Pour chaque inspection, consignez la date, le nom de la personne qui l’a effectuée, les photographies, l’état de la quincaillerie et des câbles, l’état diff --git a/docs/hardware/repeater-mounting-options.md b/docs/hardware/repeater-mounting-options.md index 96d4d9d1..b433677f 100644 --- a/docs/hardware/repeater-mounting-options.md +++ b/docs/hardware/repeater-mounting-options.md @@ -110,7 +110,7 @@ If anything moves, leaks, loosens, corrodes, changes electrically, or fails its
-No Canada-wide inspection interval has been approved. Before deployment, record a site-specific interval and the conditions that trigger an extra inspection, such as severe weather, nearby work, water entry, or changed radio behaviour. +Set an inspection schedule for the site. Inspect again after severe weather, nearby work, water entry, or changed radio behaviour. Record each inspection date, inspector, photographs, hardware/cable condition, enclosure condition, radio verification, corrective work, and next due date. diff --git a/docs/hardware/repeater-solar-1w-diy-build.fr.md b/docs/hardware/repeater-solar-1w-diy-build.fr.md index b7f93ee0..bea7a777 100644 --- a/docs/hardware/repeater-solar-1w-diy-build.fr.md +++ b/docs/hardware/repeater-solar-1w-diy-build.fr.md @@ -39,7 +39,7 @@ facultative, ses mesures et ses limites climatiques n’ont pas été reproduite
Radio
GOME Ikoka Stick 0.4.0
Alimentation
Waveshare Solar Power Manager
-
Micrologiciel
Ni épinglé ni reproduit
+
Micrologiciel
Version non confirmée; fonctionnement non vérifié
Entretien
À définir pendant l’examen du site
@@ -52,7 +52,7 @@ de trafic. Utilisez le [modèle de 300 mW à l’état d’ébauche](repeater-solar-300mw-diy-build.md) ou un système préassemblé examiné, sauf si une personne responsable du réseau -et une personne responsable de l’examen du matériel et des RF conviennent que +et une personne qualifiée en électronique et en RF conviennent que cette expérience répond à un besoin mesuré. ## Avant de commencer @@ -63,8 +63,8 @@ cette expérience répond à un besoin mesuré.
  • Le besoin du réseau local et les effets sur les autres régions sont documentés.
  • Le matériel Ikoka exact et la cible du micrologiciel du répéteur sont confirmés et récupérables par USB.
  • -
  • Une personne responsable de l’examen du matériel et de l’électricité approuve la pile, le chargeur, le panneau, le câblage, la protection, la télémétrie facultative et le plan thermique du boîtier.
  • -
  • Une personne responsable de l’examen RF approuve la radio, le filtre, la ligne de transmission RF, l’antenne, les connecteurs et le site.
  • +
  • Une personne qualifiée en électronique approuve la pile, le chargeur, le panneau, le câblage, la protection, la télémétrie facultative et le plan thermique du boîtier.
  • +
  • Une personne qualifiée en RF approuve la radio, le filtre, la ligne de transmission RF, l’antenne, les connecteurs et le site.
  • Un examen de la structure et du site couvre la masse finale, la surface du panneau, le montage, le vent, la glace, la neige, le passage des câbles et l’accès.
  • La construction restera sous surveillance sur l’établi jusqu’à l’approbation de son dossier d’essai.
@@ -152,7 +152,7 @@ maintien de la pile et la qualité d’impression avant l’utilisation.

Étape 4 — Examiner la modification du démarrage du gestionnaire d’alimentation

Ne pontez pas les pastilles du bouton de démarrage du gestionnaire d’alimentation. Cette modification n’a pas été reproduite ni examinée.

-

Avant d’envisager tout changement, exigez qu’une personne responsable de l’examen du matériel approuve le schéma exact, les modes de défaillance, les répercussions sur la garantie, la méthode de soudage, l’essai de démarrage et le retour en arrière.

+

Avant d’envisager tout changement, exigez qu’une personne qualifiée en électronique approuve le schéma exact, les modes de défaillance, les répercussions sur la garantie, la méthode de soudage, l’essai de démarrage et le retour en arrière.

Pont de fil non vérifié sur les pastilles du bouton de démarrage du gestionnaire solaire
Modification non vérifiée. Ne la reproduisez pas à partir de cette photographie.
@@ -212,7 +212,7 @@ devinés.
  • Consignez les pièces exactes, les révisions, les documents sources, le schéma, les photographies, la polarité et les valeurs mesurées.
  • Utilisez des méthodes supervisées de première mise sous tension et de limitation du courant adaptées au matériel examiné.
  • Confirmez que l’appareil démarre après l’application de chaque source d’alimentation prévue et après une coupure d’alimentation contrôlée.
  • -
  • Confirmez que l’identité, le micrologiciel et les réglages de radio, de région, de parcours, d’annonce et d’accès du répéteur résistent au redémarrage.
  • +
  • Confirmez que l’identité, le micrologiciel et les réglages de radio, de région, de parcours, d’annonce et d’accès du répéteur sont conservés après le redémarrage.
  • Confirmez qu’un compagnon à proximité reçoit une annonce et que l’essai d’acheminement local prévu réussit.
  • Mesurez le comportement RF et électrique avec la méthode approuvée par la personne responsable de l’examen; ne déduisez pas la puissance de sortie à partir d’un seul réglage du micrologiciel.
  • Effectuez sous surveillance un essai de recharge et de charge qui couvre les conditions examinées sans dépasser les limites des composants.
  • @@ -221,7 +221,7 @@ devinés. Ne déployez pas l’appareil avant qu’une personne responsable de l’examen du -matériel et de l’électricité, une personne responsable de l’examen RF et le +matériel et de l’électricité, une personne qualifiée en RF et le propriétaire du site aient approuvé le dossier d’essai complet. ## Récupération et retour en arrière diff --git a/docs/hardware/repeater-solar-300mw-diy-build.fr.md b/docs/hardware/repeater-solar-300mw-diy-build.fr.md index eb61317f..c445ed2d 100644 --- a/docs/hardware/repeater-solar-300mw-diy-build.fr.md +++ b/docs/hardware/repeater-solar-300mw-diy-build.fr.md @@ -201,7 +201,7 @@ chargeur, l’appareil de protection, la carte radio et le panneau exacts. Ne montez pas l’appareil avant qu’il ait réussi toutes les vérifications et -qu’une personne responsable de l’examen du matériel ait approuvé le dossier du +qu’une personne qualifiée en électronique ait approuvé le dossier du site. ## Récupération et retour en arrière diff --git a/docs/javascripts/community-submission.js b/docs/javascripts/community-submission.js index 065d25d3..7499d0bf 100644 --- a/docs/javascripts/community-submission.js +++ b/docs/javascripts/community-submission.js @@ -54,12 +54,12 @@ function boundedText(value, maximum, label, required = false, multiline = false) } export function buildCommunityIdea(data) { - const category = boundedText(data.category, 80, "Contribution type", true); - const experience = boundedText(data.experience, 80, "MeshCore experience", true); + const category = boundedText(data.category || "Other community feedback", 80, "Contribution type"); + const experience = boundedText(data.experience, 80, "MeshCore experience"); if (!COMMUNITY_CATEGORIES.includes(category)) { throw new Error("Choose a valid contribution type."); } - if (!MESHCORE_EXPERIENCE_LEVELS.includes(experience)) { + if (experience && !MESHCORE_EXPERIENCE_LEVELS.includes(experience)) { throw new Error("Choose a valid MeshCore experience level."); } if (data.publicAcknowledged !== true) { @@ -72,7 +72,7 @@ export function buildCommunityIdea(data) { experience, summary: boundedText(data.summary, 100, "Short title", true), need: boundedText(data.need, 2000, "What is difficult now", true, true), - idea: boundedText(data.idea, 2000, "What would help", true, true), + idea: boundedText(data.idea, 2000, "What would help", false, true), publicAcknowledged: true }; const optional = { @@ -83,9 +83,19 @@ export function buildCommunityIdea(data) { Object.entries(optional).forEach(([key, value]) => { if (value) proposal[key] = value; }); + if (data.sourcePage) proposal.sourcePage = normalizeSourcePage(data.sourcePage); return proposal; } +export function normalizeSourcePage(value) { + const url = new URL(value); + if (url.origin !== "https://meshcore.ca" || url.username || url.password || url.search || + !/^\/[a-zA-Z0-9/_.-]*$/.test(url.pathname) || !/^(?:#[a-zA-Z0-9_-]+)?$/.test(url.hash) || url.href.length > 400) { + throw new Error("Choose a valid source page."); + } + return url.href; +} + function section(heading, value, fallback = "Not provided") { return `## ${heading}\n\n${value || fallback}`; } @@ -99,6 +109,7 @@ export function buildSubmissionText(proposal) { section("Problem", proposal.need), section("Suggested change", proposal.idea), section("Additional context", proposal.context), + ...(proposal.sourcePage ? [section("Source page", proposal.sourcePage)] : []), section( "Public contact", proposal.followUp, @@ -135,6 +146,7 @@ export function buildFrenchSubmissionText(proposal) { frenchSection("Problème", proposal.need), frenchSection("Changement proposé", proposal.idea), frenchSection("Autres précisions", proposal.context), + ...(proposal.sourcePage ? [frenchSection("Page concernée", proposal.sourcePage)] : []), frenchSection( "Contact public", proposal.followUp, @@ -148,9 +160,7 @@ export function buildManualGithubLink( proposal, sourcePage = COMMUNITY_SOURCE_PAGE, ) { - if (![COMMUNITY_SOURCE_PAGE, COMMUNITY_FRENCH_SOURCE_PAGE].includes(sourcePage)) { - throw new Error("Choose a valid source page."); - } + sourcePage = normalizeSourcePage(proposal.sourcePage || sourcePage); const params = new URLSearchParams({ template: "community_idea.yml", title: `[Community idea] ${proposal.summary}`, diff --git a/docs/javascripts/submission-form.js b/docs/javascripts/submission-form.js index a4f151b4..80f81dc6 100644 --- a/docs/javascripts/submission-form.js +++ b/docs/javascripts/submission-form.js @@ -312,6 +312,7 @@ idea: value("submission-idea"), context: value("submission-context"), followUp: value("submission-follow-up"), + sourcePage: form.elements.source_page.value, publicAcknowledged: document.getElementById("submission-public").checked }; } @@ -422,6 +423,9 @@ config = await loaded.transport.fetchSubmissionConfig({ endpoint: loaded.community.COMMUNITY_SUBMISSION_ENDPOINT }); + // The gateway must support the shorter form before it can accept its payload. + // Older deployments still offer the copy/GitHub path, without a failed POST. + if (!config.communityIdeaOptionalDetails) throw new Error("Submission service update required"); turnstile = await loaded.transport.loadTurnstile(); if (widgetId === null) { setAntiSpamStatus("Complete the check."); @@ -577,6 +581,19 @@ elements.antiSpamRetry.addEventListener("click", initialiseSubmission); initialiseCharacterCounters(); restoreDraft(); + loadModules().then(function (loaded) { + const params = new URLSearchParams(window.location.search); + try { + if (params.has("source_page")) form.elements.source_page.value = loaded.community.normalizeSourcePage(params.get("source_page")); + } catch (_) { /* Ignore malformed context; the form remains usable. */ } + const community = params.get("community"); + if (community && /^[a-z0-9-]{1,80}$/.test(community) && !document.getElementById("submission-context").value) { + document.getElementById("submission-context").value = "Community listing: " + community; + document.getElementById("submission-category").value = "Regional community information"; + } else if (params.has("source_page") && !document.getElementById("submission-category").value) { + document.getElementById("submission-category").value = "Documentation correction"; + } + }).catch(function () { /* GitHub fallback remains available. */ }); updateDraftControls(); updateActions(); })(); diff --git a/docs/meshcore/flash-companion.fr.md b/docs/meshcore/flash-companion.fr.md index c0dd026b..fdec5835 100644 --- a/docs/meshcore/flash-companion.fr.md +++ b/docs/meshcore/flash-companion.fr.md @@ -69,9 +69,9 @@ officiel. Il faut un navigateur compatible avec 2. Sélectionnez le modèle exact de la carte. 3. Sélectionnez **Companion Radio (Bluetooth)**. 4. Sélectionnez la version du micrologiciel destinée à cette carte. -5. Cliquez sur **Enter DFU Mode**. Le programme devrait indiquer qu’il a trouvé un appareil dans le mode attendu. +5. Suivez les étapes de connexion propres à votre carte. Utilisez **Enter DFU Mode** seulement si cette cible le demande. Le programme devrait indiquer qu’il a trouvé un appareil dans le mode attendu. 6. Vérifiez de nouveau le matériel et le micrologiciel sélectionnés. -7. Cliquez sur **Erase Flash**, puis attendez le message confirmant la réussite de l’effacement. +7. Pour une première installation ou une réinitialisation, sauvegardez d’abord. Cliquez sur **Erase Flash** seulement si les instructions de la carte le demandent. Pour une mise à jour, laissez l’effacement désactivé sauf indication contraire dans les notes de version. 8. Cliquez sur **Flash** et attendez le message de fin avant de débrancher l’appareil. ## Récupération en cas d’échec @@ -105,7 +105,7 @@ sauf si votre méthode d’essai locale exige un autre réglage. 1. Confirmez que l’application se reconnecte et affiche le nom de l’appareil et les réglages radio attendus. 2. Envoyez un message d’essai dans le canal **Public**. -3. Une réponse comme **Heard X Repeats** indique qu’au moins un répéteur a signalé l’avoir reçu. Un simple résultat **Sent** ne prouve pas que les réglages sont incorrects; rendez-vous dans une zone où la couverture est connue ou demandez à la communauté locale de vous aider à faire l’essai. +3. Une réponse comme **Heard X Repeats** indique qu’votre compagnon a entendu une retransmission du message. Un simple résultat **Sent** ne prouve pas que les réglages sont incorrects; rendez-vous dans une zone où la couverture est connue ou demandez à la communauté locale de vous aider à faire l’essai. La configuration n’est terminée que si les réglages enregistrés résistent à un redémarrage et qu’un essai local réussit. diff --git a/docs/meshcore/flash-companion.md b/docs/meshcore/flash-companion.md index 9c2cebb0..636f5993 100644 --- a/docs/meshcore/flash-companion.md +++ b/docs/meshcore/flash-companion.md @@ -62,9 +62,9 @@ Use the official [MeshCore Web Flasher](https://meshcore.io/flasher). The flashe 2. Select the exact hardware model. 3. Select **Companion Radio (Bluetooth)**. 4. Select the firmware version intended for that board. -5. Click **Enter DFU Mode**. The flasher should report that it found a device in the expected mode. +5. Follow the flasher’s connection steps for your board. Use **Enter DFU Mode** only when that target asks for it. The flasher should report that it found a device in the expected mode. 6. Recheck the hardware and firmware selections. -7. Click **Erase Flash**, then wait for a successful erase message. +7. For a first installation or deliberate reset, back up first. Click **Erase Flash** only if the board’s instructions require it. For an update, leave erase off unless its release notes require a reset. 8. Click **Flash** and wait for the completion message before disconnecting the device. ## Recovery if flashing fails @@ -89,7 +89,7 @@ The optional **Message Settings → Auto Reset Path** preference affects how the 1. Confirm the app reconnects and shows the expected device name and radio settings. 2. Send a test message in the **Public** channel. -3. A response such as **Heard X Repeats** indicates that at least one repeater reported hearing it. A plain **Sent** result is not proof that the settings are wrong; move to a known coverage area or ask the local community to help test. +3. A response such as **Heard X Repeats** indicates that your companion heard a rebroadcast of the message. A plain **Sent** result is not proof that the settings are wrong; move to a known coverage area or ask the local community to help test. Do not consider the setup complete until the saved settings survive a reboot and a local test succeeds. diff --git a/docs/meshcore/flash-repeater.fr.md b/docs/meshcore/flash-repeater.fr.md index d752fb26..acb3b1cd 100644 --- a/docs/meshcore/flash-repeater.fr.md +++ b/docs/meshcore/flash-repeater.fr.md @@ -21,7 +21,7 @@ page_styles: - assets/styles/devices-builds.css?v=20260728-1 - assets/styles/repeater-hash-check.css?v=20260820-1 page_scripts: - - assets/javascripts/repeater-hash-check.js?v=20260820-1 + - assets/javascripts/repeater-hash-check.js?v=20260904-1 --- # Programmer, configurer et tester un répéteur sur l’établi @@ -89,7 +89,7 @@ guide ou d’une capture d’écran. 3. Activez son mode de chargeur d’amorçage UF2. Sur une RAK4631, il faut normalement appuyer deux fois sur le bouton près du port USB; pour les autres cartes, utilisez la méthode de réinitialisation documentée. 4. Confirmez qu’un lecteur USB apparaît et examinez `INFO.TXT` pour vérifier l’identité de la carte. 5. Copiez le fichier UF2 correspondant sur ce lecteur. Le lecteur peut se déconnecter pendant le redémarrage de la carte. -6. Revenez au mode de chargeur d’amorçage et confirmez que `INFO.TXT` indique la version `0.9.2` avant de continuer. +6. Revenez au mode de chargeur d’amorçage et confirmez que `INFO.TXT` indique la carte et la version du chargeur d’amorçage correspondant à la version choisie avant de continuer. Si l’identité de la carte ou la version attendue ne correspond pas, arrêtez et récupérez l’appareil par USB avant de programmer MeshCore. @@ -103,9 +103,9 @@ officiel dans un navigateur compatible avec 1. Branchez le répéteur par USB. 2. Sélectionnez le modèle exact du matériel. 3. Sélectionnez **Repeater** comme rôle et choisissez la version destinée à cette carte. -4. Cliquez sur **Enter DFU Mode** et attendez que le programme trouve la carte. +4. Suivez les étapes de connexion propres à votre carte. Utilisez **Enter DFU Mode** seulement si cette cible le demande. 5. Vérifiez de nouveau la carte et le rôle sélectionnés. -6. Cliquez sur **Erase Flash** et attendez le message confirmant la réussite de l’effacement. +6. Pour une première installation ou une réinitialisation, sauvegardez d’abord. Cliquez sur **Erase Flash** seulement si les instructions de la carte le demandent. Pour une mise à jour, laissez l’effacement désactivé sauf indication contraire dans les notes de version. 7. Cliquez sur **Flash** et attendez la fin de l’opération avant de débrancher l’appareil. Si la programmation échoue après l’effacement, n’effacez pas l’appareil à diff --git a/docs/meshcore/flash-repeater.md b/docs/meshcore/flash-repeater.md index fbac57fe..245c270a 100644 --- a/docs/meshcore/flash-repeater.md +++ b/docs/meshcore/flash-repeater.md @@ -21,7 +21,7 @@ page_styles: - assets/styles/devices-builds.css?v=20260728-1 - assets/styles/repeater-hash-check.css?v=20260820-1 page_scripts: - - assets/javascripts/repeater-hash-check.js?v=20260820-1 + - assets/javascripts/repeater-hash-check.js?v=20260904-1 --- # Flash, configure, and bench-test a repeater @@ -54,7 +54,7 @@ Never post the private key or passwords in an issue, screenshot, log, or chat. I ## What flashing changes -An erase replaces firmware and can delete identity and settings. The setup also writes radio, advert, path-hash, access, location, and region values that affect the shared network. +Flashing replaces firmware. Erasing can also delete identity and settings. The setup also writes radio, advert, path-hash, access, location, and region values that affect the shared network. ## Recovery plan @@ -74,7 +74,7 @@ hardware. Do not use a filename copied from an older guide or screenshot. 3. Enter its UF2 bootloader mode. On a RAK4631 this is normally done by double-pressing the button beside USB; other boards use their documented reset method. 4. Confirm a USB drive appears and inspect `INFO.TXT` so the board identity is what you expect. 5. Copy the matching UF2 file to that drive. The drive may disconnect as the board reboots. -6. Re-enter bootloader mode and confirm `INFO.TXT` reports bootloader version `0.9.2` before continuing. +6. Re-enter bootloader mode and confirm `INFO.TXT` reports the board and bootloader version from the release you selected before continuing. If the board identity or expected version does not match, stop and recover over USB before flashing MeshCore. @@ -85,9 +85,9 @@ Use the official [MeshCore Web Flasher](https://meshcore.io/flasher) in a browse 1. Connect the repeater by USB. 2. Select the exact hardware model. 3. Select **Repeater** as the role and choose the intended version for that board. -4. Click **Enter DFU Mode** and wait for the flasher to find the board. +4. Follow the flasher’s connection steps for your board. Use **Enter DFU Mode** only when that target asks for it. 5. Recheck the selected board and role. -6. Click **Erase Flash** and wait for a successful erase message. +6. For a first installation or deliberate reset, back up first. Click **Erase Flash** only if the board’s instructions require it. For an update, leave erase off unless its release notes require a reset. 7. Click **Flash** and wait for completion before disconnecting. If flashing fails after erase, do not repeatedly erase. Refresh the page, return the board to DFU mode, verify the target again, and retry **Flash**. Use the board's USB recovery process if it no longer appears. diff --git a/docs/meshcore/flash-room-server.fr.md b/docs/meshcore/flash-room-server.fr.md index d6bf66a0..5cd88565 100644 --- a/docs/meshcore/flash-room-server.fr.md +++ b/docs/meshcore/flash-room-server.fr.md @@ -67,9 +67,9 @@ n’utilisez pas les fichiers d’une autre carte. 1. Ouvrez le [programme de mise à jour Web MeshCore](https://meshcore.io/flasher) officiel. 2. Sélectionnez le modèle exact de l’appareil. 3. Sélectionnez **Room Server** et la version destinée à cette carte. -4. Cliquez sur **Enter DFU Mode** et attendez que l’appareil attendu apparaisse. +4. Suivez les étapes de connexion propres à votre carte. Utilisez **Enter DFU Mode** seulement si cette cible le demande. 5. Vérifiez de nouveau le matériel et le rôle sélectionnés. -6. Cliquez sur **Erase Flash** et attendez le message confirmant la réussite de l’effacement. +6. Pour une première installation ou une réinitialisation, sauvegardez d’abord. Cliquez sur **Erase Flash** seulement si les instructions de la carte le demandent. Pour une mise à jour, laissez l’effacement désactivé sauf indication contraire dans les notes de version. 7. Cliquez sur **Flash** et attendez la fin de l’opération avant de débrancher l’appareil. Si la programmation échoue après l’effacement, laissez l’appareil branché, diff --git a/docs/meshcore/flash-room-server.md b/docs/meshcore/flash-room-server.md index e186e13b..d4babb94 100644 --- a/docs/meshcore/flash-room-server.md +++ b/docs/meshcore/flash-room-server.md @@ -54,9 +54,9 @@ Keep the exact board's USB recovery method, backed-up identity/settings, a known 1. Open the official [MeshCore Web Flasher](https://meshcore.io/flasher). 2. Select the exact device model. 3. Select **Room Server** and the intended version for that board. -4. Click **Enter DFU Mode** and wait for the expected device to appear. +4. Follow the flasher’s connection steps for your board. Use **Enter DFU Mode** only when that target asks for it. 5. Recheck the hardware and role selections. -6. Click **Erase Flash** and wait for a successful erase message. +6. For a first installation or deliberate reset, back up first. Click **Erase Flash** only if the board’s instructions require it. For an update, leave erase off unless its release notes require a reset. 7. Click **Flash** and wait for completion before disconnecting. If flashing fails after erase, leave the device connected, refresh the flasher, re-enter DFU mode, confirm the board and role, and retry **Flash**. Use the board's documented USB recovery process if it is no longer detected. diff --git a/docs/meshcore/general-faq.fr.md b/docs/meshcore/general-faq.fr.md index 645ad23a..22878599 100644 --- a/docs/meshcore/general-faq.fr.md +++ b/docs/meshcore/general-faq.fr.md @@ -34,8 +34,8 @@ source actuelle. ### Quels réglages radio dois-je utiliser au Canada? -Pour un répéteur, utilisez le [configurateur de répéteur](../config/index.md). Il -fournit les réglages actuels pour l’emplacement choisi. +Utilisez le profil radio de votre communauté. Le [configurateur de répéteur](../config/index.md) +trouve les chemins régionaux, mais conserve les réglages radio tant que vous ne choisissez pas de profil. Quel que soit le rôle de l’appareil, consultez d’abord le [répertoire des communautés](../provinces/index.md). Lorsqu’une communauté @@ -43,10 +43,10 @@ publie des réglages locaux différents, suivez-les. ### Qu’est-ce que le mode de hachage des parcours? -Il détermine la taille des identifiants utilisés dans les parcours d’annonce. -La norme canadienne des régions et le configurateur fournissent le réglage -actuel d’un répéteur. Utilisez leur résultat plutôt que de copier une ancienne -commande provenant d’une discussion ou d’une capture d’écran. +Il détermine la taille des identifiants dans les parcours d’annonce : 1, 2 ou 3 octets. +MeshCore Canada recommande 3 octets pour réduire les collisions. Les versions 1.14 +et suivantes peuvent relayer des parcours de tailles différentes; les nœuds voisins +n’ont pas tous besoin d’utiliser la même taille d’identifiant. [Lire la norme des régions](../config/standard.md). diff --git a/docs/meshcore/general-faq.md b/docs/meshcore/general-faq.md index 1c9c919e..9f1e1e0c 100644 --- a/docs/meshcore/general-faq.md +++ b/docs/meshcore/general-faq.md @@ -34,18 +34,17 @@ of repeating values that may change. ### Which radio settings should I use in Canada? -Use the [repeater configurator](../config/index.md) for a repeater. It returns -the current settings for the location you select. +Use your community’s radio profile. The [repeater configurator](../config/index.md) +finds region paths, but keeps your radio settings unless you choose a profile. For any role, first check the [community directory](../provinces/index.md). Follow a published local override when one exists. ### What is path hash mode? -It controls the size of the identifiers used in advert paths. The Canadian -region standard and configurator provide the current setting for a repeater. -Use their output instead of copying an old command from a discussion or -screenshot. +It sets the size of the identifiers in this node’s advert paths: 1, 2, or 3 bytes. +MeshCore Canada recommends 3 bytes to reduce collisions. Firmware 1.14 and newer +can forward mixed sizes; neighbouring nodes do not all need the same ID size. [Read the region standard](../config/standard.md). diff --git a/docs/meshcore/general-howto.fr.md b/docs/meshcore/general-howto.fr.md index e2aad6c1..7b625e77 100644 --- a/docs/meshcore/general-howto.fr.md +++ b/docs/meshcore/general-howto.fr.md @@ -20,6 +20,7 @@ destructive: false Choisissez une tâche. Les captures montrent une version de l’application mobile MeshCore; les libellés et leur emplacement peuvent différer. +- [Envoyer votre premier message](#send-your-first-message) - [Partager votre lien de contact](#share-your-contact-link) - [Importer un lien de contact](#import-a-contact-link) - [Tracer un parcours](#trace-a-path) @@ -36,6 +37,16 @@ MeshCore; les libellés et leur emplacement peuvent différer.
    +## Envoyer votre premier message { #send-your-first-message } + +1. Connectez l’application à votre compagnon et confirmez les réglages radio locaux. +2. Ouvrez le canal de votre communauté ou choisissez un contact importé. +3. Envoyez une courte salutation et demandez une réponse. + +Une réponse confirme la communication dans les deux sens. L’indicateur d’envoi +ne suffit pas à confirmer la réception. Sans réponse, vérifiez avec une personne +à proximité avant d’envoyer d’autres essais. + ## Partager votre lien de contact { #share-your-contact-link } Utilisez cette procédure lorsqu’une autre personne veut vous ajouter comme diff --git a/docs/meshcore/general-howto.md b/docs/meshcore/general-howto.md index d3d5aa30..23a7d3b4 100644 --- a/docs/meshcore/general-howto.md +++ b/docs/meshcore/general-howto.md @@ -20,6 +20,7 @@ destructive: false Choose a task. The screenshots show one version of the MeshCore mobile app, so labels and positions may differ. +- [Send your first message](#send-your-first-message) - [Share your contact link](#share-your-contact-link) - [Import a contact link](#import-a-contact-link) - [Trace a path](#trace-a-path) @@ -36,6 +37,15 @@ labels and positions may differ.
    +## Send your first message + +1. Connect the app to your companion and confirm the local radio settings. +2. Open the channel used by your community, or choose an imported contact. +3. Send a short greeting and ask for a reply. + +A reply confirms two-way communication. A sent indicator alone does not confirm +delivery. If nobody replies, check with a nearby operator before sending more tests. + ## Share your contact link Use this when another person needs to add you as a contact. diff --git a/docs/meshcore/generate-repeater-id.fr.md b/docs/meshcore/generate-repeater-id.fr.md index 0071e789..8bd51bba 100644 --- a/docs/meshcore/generate-repeater-id.fr.md +++ b/docs/meshcore/generate-repeater-id.fr.md @@ -22,7 +22,7 @@ page_styles: - assets/styles/devices-builds.css?v=20260728-1 - assets/styles/repeater-hash-check.css?v=20260820-1 page_scripts: - - assets/javascripts/repeater-hash-check.js?v=20260820-1 + - assets/javascripts/repeater-hash-check.js?v=20260904-1 --- # Changer l’identifiant d’un répéteur en mode 1 octet diff --git a/docs/meshcore/generate-repeater-id.md b/docs/meshcore/generate-repeater-id.md index 217bb453..08a47dd4 100644 --- a/docs/meshcore/generate-repeater-id.md +++ b/docs/meshcore/generate-repeater-id.md @@ -22,7 +22,7 @@ page_styles: - assets/styles/devices-builds.css?v=20260728-1 - assets/styles/repeater-hash-check.css?v=20260820-1 page_scripts: - - assets/javascripts/repeater-hash-check.js?v=20260820-1 + - assets/javascripts/repeater-hash-check.js?v=20260904-1 --- # Change a repeater ID in 1-byte mode diff --git a/docs/privacy.fr.md b/docs/privacy.fr.md index 944d0b20..3a552fbd 100644 --- a/docs/privacy.fr.md +++ b/docs/privacy.fr.md @@ -43,6 +43,13 @@ limitation du débit. Aucune période de conservation n’a encore été publié ## Données enregistrées sur votre appareil +Changer de langue ou passer entre la carte et le configurateur conserve votre +sélection dans l’URL de destination. Elle peut contenir les coordonnées saisies, +un lieu, des chemins régionaux et un profil radio. Ces URL peuvent apparaître dans +l’historique du navigateur et les journaux du serveur. Ne partagez pas un lien qui +contient un emplacement privé. Les résumés téléchargés omettent les coordonnées +exactes et les identifiants de connexion. + Les listes de configuration, les brouillons d’idées et le dernier code de région Beacon choisi dans l’outil de vérification sont enregistrés uniquement dans votre navigateur lorsque vous utilisez ces fonctions. Les clés publiques diff --git a/docs/privacy.md b/docs/privacy.md index b18db609..f9647749 100644 --- a/docs/privacy.md +++ b/docs/privacy.md @@ -42,6 +42,12 @@ period has not yet been published. ## Saved on your device +Switching language or moving between the region map and configurator carries your +selection in the destination URL. This can include entered coordinates, a location +label, region paths, and radio-profile choices. These URLs may appear in browser +history and server access logs. Do not share a link containing a private location. +Downloaded setup summaries omit exact coordinates and credentials. + Setup checklists, idea drafts, and the last Beacon region code selected in the repeater-ID checker are saved in your browser only when you use those features. Repeater public keys, passwords, private keys, anti-spam tokens, and location diff --git a/docs/provinces/alberta.fr.md b/docs/provinces/alberta.fr.md index 0f445769..05cf45d9 100644 --- a/docs/provinces/alberta.fr.md +++ b/docs/provinces/alberta.fr.md @@ -39,7 +39,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -56,7 +56,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -68,7 +68,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -82,7 +82,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -93,7 +93,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -104,7 +104,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Certains liens restent à vérifier

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -116,7 +116,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -130,7 +130,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -142,7 +142,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -155,7 +155,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -167,7 +167,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -180,7 +180,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -192,7 +192,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -209,7 +209,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Certains liens restent à vérifier

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -220,7 +220,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -231,7 +231,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Certains liens restent à vérifier

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -242,7 +242,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -253,7 +253,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    diff --git a/docs/provinces/alberta.md b/docs/provinces/alberta.md index 48a696b4..fd540cf6 100644 --- a/docs/provinces/alberta.md +++ b/docs/provinces/alberta.md @@ -39,7 +39,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -54,9 +54,9 @@ card lists different local settings.
  • Website: Monitoring tools (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -68,7 +68,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -80,9 +80,9 @@ card lists different local settings.
  • Website: WAeV live map (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -93,7 +93,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -104,7 +104,7 @@ card lists different local settings.

    Contact check: Some links still need review

    -

    Update this listing

    +

    Update this listing

    @@ -116,7 +116,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -128,9 +128,9 @@ card lists different local settings.
  • Website: Recommended Calgary RX channels (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -142,7 +142,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -153,9 +153,9 @@ card lists different local settings.
  • MeshMapper: Edmonton network map (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -167,7 +167,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -178,9 +178,9 @@ card lists different local settings.
  • MeshMapper: Edmonton network map (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -192,7 +192,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -209,7 +209,7 @@ card lists different local settings.

    Contact check: Some links still need review

    -

    Update this listing

    +

    Update this listing

    @@ -220,7 +220,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -231,7 +231,7 @@ card lists different local settings.

    Contact check: Some links still need review

    -

    Update this listing

    +

    Update this listing

    @@ -242,7 +242,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -251,9 +251,9 @@ card lists different local settings.
  • Website: MeshMonitoring (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -261,7 +261,7 @@ card lists different local settings.

    Telegram: Alberta topic in MeshCore Canada (external)

    -

    Contact check: Verified on 2026-08-29

    +

    Contact check: Links checked on 2026-08-29

    ## Add or update a listing diff --git a/docs/provinces/british-columbia.fr.md b/docs/provinces/british-columbia.fr.md index fab08e78..744294ce 100644 --- a/docs/provinces/british-columbia.fr.md +++ b/docs/provinces/british-columbia.fr.md @@ -43,7 +43,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Préréglage radio : Custom; Valeurs radio brutes : 910.425 MHz / 62.5 kHz / SF7 / CR5
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29
    @@ -57,7 +57,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    @@ -68,7 +68,7 @@ sauf si une fiche indique des réglages locaux différents.
    Réglages
    Réglages par défaut du Canada
    -
    Dernière vérification
    +
    Fiche révisée
    2026-08-29

    Coordonnées

    @@ -78,7 +78,7 @@ sauf si une fiche indique des réglages locaux différents.

    Vérification : Effectuée le 2026-08-29

    -

    Mettre cette fiche à jour

    +

    Mettre cette fiche à jour

    diff --git a/docs/provinces/british-columbia.md b/docs/provinces/british-columbia.md index 1ef7f6d0..b5a68166 100644 --- a/docs/provinces/british-columbia.md +++ b/docs/provinces/british-columbia.md @@ -42,7 +42,7 @@ card lists different local settings.
    Settings
    Radio preset: Custom; Raw radio values: 910.425 MHz / 62.5 kHz / SF7 / CR5
    -
    Last verified
    +
    Listing reviewed
    2026-08-29
    @@ -54,9 +54,9 @@ card lists different local settings.
  • Website: Ridgeline network map and tools (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    @@ -67,7 +67,7 @@ card lists different local settings.
    Settings
    Uses the Canada defaults
    -
    Last verified
    +
    Listing reviewed
    2026-08-29

    Contacts

    @@ -75,9 +75,9 @@ card lists different local settings.
  • Website: Salish Mesh website (external)
  • -Contact check: Verified on 2026-08-29 +Contact check: Links checked on 2026-08-29

    -

    Update this listing

    +

    Update this listing

    diff --git a/docs/provinces/index.fr.md b/docs/provinces/index.fr.md index 9092deb4..d2cd6660 100644 --- a/docs/provinces/index.fr.md +++ b/docs/provinces/index.fr.md @@ -78,7 +78,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

    Lower Mainland et île de Vancouver

    Province ou territoire : Colombie-Britannique

    Réglages : Réglages locaux différents — Préréglage radio : Custom; Valeurs radio brutes : 910.425 MHz / 62.5 kHz / SF7 / CR5

    -

    Dernière vérification : 2026-08-29

    +

    Fiche révisée : 2026-08-29

    @@ -92,7 +92,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

    Mer des Salish et environs

    Province ou territoire : Colombie-Britannique

    Réglages : Réglages par défaut du Canada

    -

    Dernière vérification : 2026-08-29

    +

    Fiche révisée : 2026-08-29

    @@ -107,7 +107,7 @@ complète fonctionne sans carte, autorisation de localisation ni compte GitHub.

    Développement du réseau maillé LoRa hors réseau de l’Alberta, exploité par la communauté

    Province ou territoire : Alberta

    Réglages : Réglages par défaut du Canada

    -

    Dernière vérification : 2026-08-29

    +

    Fiche révisée : 2026-08-29