mirror of
https://github.com/agessaman/MeshCore.git
synced 2026-07-21 06:51:04 +00:00
Add Mesh America provider catalogs
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
# Mesh America provider catalogs
|
||||
|
||||
Generated provider catalogs for the Keymind Cascade MeshCore release assets.
|
||||
|
||||
Send Mesh America these catalog URLs after this directory is committed and pushed to `keymindCascade`:
|
||||
|
||||
```text
|
||||
Provider name: Keymind Cascade
|
||||
Catalog URL: https://raw.githubusercontent.com/mikecarper/MeshCore/keymindCascade/mesh-america/keymind-cascade-v1.16.0-provider.json
|
||||
```
|
||||
|
||||
```text
|
||||
Provider name: Keymind Cascade Logging
|
||||
Catalog URL: https://raw.githubusercontent.com/mikecarper/MeshCore/keymindCascade/mesh-america/keymind-cascade-logging-v1.16.0-provider.json
|
||||
```
|
||||
|
||||
Regenerate both catalogs from the release asset folders with:
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File mesh-america\generate-mesh-america-catalogs.ps1
|
||||
```
|
||||
@@ -0,0 +1,275 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$Repo = 'mikecarper/MeshCore'
|
||||
$OutputDir = $PSScriptRoot
|
||||
$Root = Split-Path -Parent $PSScriptRoot
|
||||
|
||||
$Catalogs = @(
|
||||
@{
|
||||
Name = 'non-logging'
|
||||
SourceDir = Join-Path $Root '.releases\non-logging'
|
||||
Tag = 'v1.16.0-halo-keymind-dev-28ee5d2c'
|
||||
Output = Join-Path $OutputDir 'keymind-cascade-v1.16.0-provider.json'
|
||||
Description = 'Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning and Cascade defaults. This is the standard build without extra logging.'
|
||||
},
|
||||
@{
|
||||
Name = 'logging'
|
||||
SourceDir = Join-Path $Root '.releases\logging'
|
||||
Tag = 'logging-v1.16.0-halo-keymind-dev-28ee5d2c'
|
||||
Output = Join-Path $OutputDir 'keymind-cascade-logging-v1.16.0-provider.json'
|
||||
Description = 'Keymind Cascade MeshCore v1.16.0 dev firmware with Halo/Keymind retry tuning, Cascade defaults, and extra logging enabled for diagnostics.'
|
||||
}
|
||||
)
|
||||
|
||||
$RoleDefinitions = [ordered]@{
|
||||
companionSerial = [ordered]@{ name = 'Companion Serial' }
|
||||
companionWifi = [ordered]@{ name = 'Companion WiFi' }
|
||||
repeaterBridgeEspnow = [ordered]@{ name = 'Repeater Bridge ESP-NOW' }
|
||||
repeaterBridgeRs232 = [ordered]@{ name = 'Repeater Bridge RS232' }
|
||||
sensor = [ordered]@{ name = 'Sensor' }
|
||||
terminalChat = [ordered]@{ name = 'Terminal Chat' }
|
||||
}
|
||||
|
||||
$RolePatterns = @(
|
||||
@{ Suffix = 'logging_repeater_bridge_espnow'; Role = 'repeaterBridgeEspnow'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = 'Logging' },
|
||||
@{ Suffix = 'logging_repeater'; Role = 'repeater'; Title = 'Repeater'; SubTitle = 'Logging' },
|
||||
@{ Suffix = 'companion_radio_ble_femoff'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'FEM off' },
|
||||
@{ Suffix = 'companion_radio_ble_femon'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'FEM on' },
|
||||
@{ Suffix = 'companion_radio_ble_ps_femoff'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'Power saving, FEM off' },
|
||||
@{ Suffix = 'companion_radio_ble_ps_femon'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'Power saving, FEM on' },
|
||||
@{ Suffix = 'companion_radio_ble_ps'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = 'Power saving' },
|
||||
@{ Suffix = 'companion_radio_ble_'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = $null },
|
||||
@{ Suffix = 'companion_radio_ble'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = $null },
|
||||
@{ Suffix = 'companion_ble'; Role = 'companionBle'; Title = 'Companion BLE'; SubTitle = $null },
|
||||
@{ Suffix = 'companion_radio_usb_'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null },
|
||||
@{ Suffix = 'companion_radio_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null },
|
||||
@{ Suffix = 'companion_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null },
|
||||
@{ Suffix = 'comp_radio_usb'; Role = 'companionUsb'; Title = 'Companion USB'; SubTitle = $null },
|
||||
@{ Suffix = 'companion_radio_serial'; Role = 'companionSerial'; Title = 'Companion Serial'; SubTitle = $null },
|
||||
@{ Suffix = 'companion_radio_wifi'; Role = 'companionWifi'; Title = 'Companion WiFi'; SubTitle = $null },
|
||||
@{ Suffix = 'repeater_bridge_rs232_serial1'; Role = 'repeaterBridgeRs232'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 1' },
|
||||
@{ Suffix = 'repeater_bridge_rs232_serial2'; Role = 'repeaterBridgeRs232'; Title = 'Repeater Bridge RS232'; SubTitle = 'Serial 2' },
|
||||
@{ Suffix = 'repeater_bridge_rs232'; Role = 'repeaterBridgeRs232'; Title = 'Repeater Bridge RS232'; SubTitle = $null },
|
||||
@{ Suffix = 'repeater_bridge_espnow_'; Role = 'repeaterBridgeEspnow'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = $null },
|
||||
@{ Suffix = 'repeater_bridge_espnow'; Role = 'repeaterBridgeEspnow'; Title = 'Repeater Bridge ESP-NOW'; SubTitle = $null },
|
||||
@{ Suffix = 'Repeater'; Role = 'repeater'; Title = 'Repeater'; SubTitle = $null },
|
||||
@{ Suffix = 'repeater_'; Role = 'repeater'; Title = 'Repeater'; SubTitle = $null },
|
||||
@{ Suffix = 'repeatr'; Role = 'repeater'; Title = 'Repeater'; SubTitle = $null },
|
||||
@{ Suffix = 'repeater'; Role = 'repeater'; Title = 'Repeater'; SubTitle = $null },
|
||||
@{ Suffix = 'room_server_'; Role = 'roomServer'; Title = 'Room Server'; SubTitle = $null },
|
||||
@{ Suffix = 'room_server'; Role = 'roomServer'; Title = 'Room Server'; SubTitle = $null },
|
||||
@{ Suffix = 'room_svr'; Role = 'roomServer'; Title = 'Room Server'; SubTitle = $null },
|
||||
@{ Suffix = 'terminal_chat'; Role = 'terminalChat'; Title = 'Terminal Chat'; SubTitle = $null },
|
||||
@{ Suffix = 'sensor'; Role = 'sensor'; Title = 'Sensor'; SubTitle = $null }
|
||||
)
|
||||
|
||||
function ConvertTo-DeviceName {
|
||||
param([string]$Prefix)
|
||||
$name = $Prefix.Trim('_')
|
||||
$name = $name -replace '_', ' '
|
||||
$name = $name -replace '\s+', ' '
|
||||
return $name.Trim()
|
||||
}
|
||||
|
||||
function Get-RoleInfo {
|
||||
param([string]$DeviceRole)
|
||||
|
||||
foreach ($pattern in $RolePatterns) {
|
||||
$suffix = $pattern.Suffix
|
||||
if ($DeviceRole.EndsWith("_$suffix", [StringComparison]::OrdinalIgnoreCase) -or
|
||||
$DeviceRole.EndsWith($suffix, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
$prefixLength = $DeviceRole.Length - $suffix.Length
|
||||
if ($prefixLength -gt 0 -and $DeviceRole[$prefixLength - 1] -eq '_') {
|
||||
$prefixLength--
|
||||
}
|
||||
|
||||
$devicePrefix = $DeviceRole.Substring(0, $prefixLength).Trim('_')
|
||||
if (-not $devicePrefix) {
|
||||
throw "Unable to parse device name from '$DeviceRole'"
|
||||
}
|
||||
|
||||
return [ordered]@{
|
||||
DeviceKey = $devicePrefix
|
||||
DeviceName = ConvertTo-DeviceName $devicePrefix
|
||||
Role = $pattern.Role
|
||||
Title = $pattern.Title
|
||||
SubTitle = $pattern.SubTitle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw "Unable to parse role from '$DeviceRole'"
|
||||
}
|
||||
|
||||
function Get-FileSortRank {
|
||||
param([string]$Type, [string]$Name)
|
||||
|
||||
switch ($Type) {
|
||||
'flash-wipe' { return 10 }
|
||||
'flash-update' { return 20 }
|
||||
'flash' { return 30 }
|
||||
default {
|
||||
if ($Name.EndsWith('.uf2', [StringComparison]::OrdinalIgnoreCase)) { return 40 }
|
||||
if ($Name.EndsWith('.hex', [StringComparison]::OrdinalIgnoreCase)) { return 50 }
|
||||
return 60
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Get-DeviceType {
|
||||
param([array]$Files)
|
||||
|
||||
$extensions = @($Files | ForEach-Object { $_.Extension.ToLowerInvariant() } | Sort-Object -Unique)
|
||||
$hasMergedBin = @($Files | Where-Object {
|
||||
$_.Extension -ieq '.bin' -and $_.BaseName.EndsWith('-merged', [StringComparison]::OrdinalIgnoreCase)
|
||||
}).Count -gt 0
|
||||
|
||||
if ($hasMergedBin) { return 'esp32' }
|
||||
if ($extensions -contains '.zip') { return 'nrf52' }
|
||||
return 'noflash'
|
||||
}
|
||||
|
||||
function Get-FileType {
|
||||
param(
|
||||
[System.IO.FileInfo]$File,
|
||||
[string]$DeviceType
|
||||
)
|
||||
|
||||
$extension = $File.Extension.ToLowerInvariant()
|
||||
$isMerged = $File.BaseName.EndsWith('-merged', [StringComparison]::OrdinalIgnoreCase)
|
||||
|
||||
if ($DeviceType -eq 'esp32' -and $extension -eq '.bin' -and $isMerged) { return 'flash-wipe' }
|
||||
if ($DeviceType -eq 'esp32' -and $extension -eq '.bin') { return 'flash-update' }
|
||||
if ($DeviceType -eq 'nrf52' -and $extension -eq '.zip') { return 'flash' }
|
||||
return 'download'
|
||||
}
|
||||
|
||||
function Get-FileTitle {
|
||||
param([string]$Type, [string]$Name)
|
||||
|
||||
switch ($Type) {
|
||||
'flash-wipe' { return 'Full install (bootloader + firmware)' }
|
||||
'flash-update' { return 'Update (app only)' }
|
||||
'flash' { return 'Serial DFU package' }
|
||||
default {
|
||||
if ($Name.EndsWith('.uf2', [StringComparison]::OrdinalIgnoreCase)) { return 'UF2 download' }
|
||||
if ($Name.EndsWith('.hex', [StringComparison]::OrdinalIgnoreCase)) { return 'HEX download' }
|
||||
return 'Download'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Get-DownloadUrl {
|
||||
param(
|
||||
[string]$Tag,
|
||||
[string]$Name
|
||||
)
|
||||
|
||||
$escapedName = [Uri]::EscapeDataString($Name).Replace('%2F', '/')
|
||||
return "https://github.com/$Repo/releases/download/$Tag/$escapedName"
|
||||
}
|
||||
|
||||
function New-Catalog {
|
||||
param([hashtable]$Definition)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Definition.SourceDir)) {
|
||||
throw "Source directory not found: $($Definition.SourceDir)"
|
||||
}
|
||||
|
||||
$sourceFiles = @(Get-ChildItem -LiteralPath $Definition.SourceDir -File | Sort-Object Name)
|
||||
if (-not $sourceFiles.Count) {
|
||||
throw "No release files found in $($Definition.SourceDir)"
|
||||
}
|
||||
|
||||
$parsedFiles = foreach ($file in $sourceFiles) {
|
||||
$stem = $file.BaseName
|
||||
if ($stem.EndsWith('-merged', [StringComparison]::OrdinalIgnoreCase)) {
|
||||
$stem = $stem.Substring(0, $stem.Length - '-merged'.Length)
|
||||
}
|
||||
|
||||
$suffix = "-$($Definition.Tag)"
|
||||
if (-not $stem.EndsWith($suffix, [StringComparison]::Ordinal)) {
|
||||
throw "File '$($file.Name)' does not end with expected tag '$($Definition.Tag)'"
|
||||
}
|
||||
|
||||
$deviceRole = $stem.Substring(0, $stem.Length - $suffix.Length)
|
||||
$roleInfo = Get-RoleInfo $deviceRole
|
||||
|
||||
[pscustomobject]@{
|
||||
File = $file
|
||||
DeviceKey = $roleInfo.DeviceKey
|
||||
DeviceName = $roleInfo.DeviceName
|
||||
Role = $roleInfo.Role
|
||||
Title = $roleInfo.Title
|
||||
SubTitle = $roleInfo.SubTitle
|
||||
}
|
||||
}
|
||||
|
||||
$devices = New-Object System.Collections.ArrayList
|
||||
foreach ($deviceGroup in @($parsedFiles | Group-Object DeviceKey | Sort-Object Name)) {
|
||||
$deviceFiles = @($deviceGroup.Group | ForEach-Object { $_.File })
|
||||
$deviceType = Get-DeviceType $deviceFiles
|
||||
$firmware = New-Object System.Collections.ArrayList
|
||||
|
||||
foreach ($roleGroup in @($deviceGroup.Group | Group-Object Role,Title,SubTitle | Sort-Object Name)) {
|
||||
$first = $roleGroup.Group[0]
|
||||
$files = @(
|
||||
$roleGroup.Group |
|
||||
Sort-Object @{ Expression = { Get-FileSortRank (Get-FileType $_.File $deviceType) $_.File.Name } }, @{ Expression = { $_.File.Name } } |
|
||||
ForEach-Object {
|
||||
$type = Get-FileType $_.File $deviceType
|
||||
[ordered]@{
|
||||
type = $type
|
||||
name = $_.File.Name
|
||||
url = Get-DownloadUrl $Definition.Tag $_.File.Name
|
||||
title = Get-FileTitle $type $_.File.Name
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
$firmwareEntry = [ordered]@{
|
||||
role = $first.Role
|
||||
title = $first.Title
|
||||
}
|
||||
|
||||
if ($first.SubTitle) {
|
||||
$firmwareEntry.subTitle = $first.SubTitle
|
||||
}
|
||||
|
||||
$firmwareEntry.version = [ordered]@{
|
||||
$Definition.Tag = [ordered]@{
|
||||
notes = $Definition.Description
|
||||
files = $files
|
||||
}
|
||||
}
|
||||
|
||||
[void]$firmware.Add($firmwareEntry)
|
||||
}
|
||||
|
||||
[void]$devices.Add([ordered]@{
|
||||
maker = 'keymindCascade'
|
||||
class = 'keymindCascade'
|
||||
name = $deviceGroup.Group[0].DeviceName
|
||||
type = $deviceType
|
||||
firmware = @($firmware)
|
||||
})
|
||||
}
|
||||
|
||||
return [ordered]@{
|
||||
description = $Definition.Description
|
||||
maker = [ordered]@{
|
||||
keymindCascade = [ordered]@{ name = 'Keymind Cascade' }
|
||||
}
|
||||
role = $RoleDefinitions
|
||||
device = @($devices)
|
||||
}
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
|
||||
|
||||
foreach ($catalogDef in $Catalogs) {
|
||||
$catalog = New-Catalog $catalogDef
|
||||
$json = $catalog | ConvertTo-Json -Depth 100
|
||||
[System.IO.File]::WriteAllText($catalogDef.Output, $json + [Environment]::NewLine, [System.Text.UTF8Encoding]::new($false))
|
||||
Write-Output ("Wrote {0}" -f $catalogDef.Output)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user