[DOCS] Migrate documentation from VuePress 1.x to VitePress (#2299)

- Replace VuePress 1.9.10 with VitePress 1.6.4
- Convert config from CommonJS to ESM TypeScript (config.mts)
- Convert shared Theengs nav config to ESM (commonConfig.mts)
- Rewrite BoardEnvironmentTable in Vue 3 Composition API
- Rewrite FlashEnvironmentSelector in Vue 3 Composition API
- Enable built-in local search and dark mode
- Add medium-zoom for image zoom
- Use VitePress CSS variables for dark mode compatibility
- Update CI script paths from .vuepress to .vitepress
- Update .gitignore for VitePress build artifacts
- Rename README.md files to index.md (VitePress convention)
- Fix markdown compatibility (angle-bracket escaping, image paths)
- Drop PWA plugin (not needed)

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-03-30 07:00:20 -05:00
1 parent 600d48dc48
commit 8273617c17
33 files changed
+3650 -27914

No files matched your search

+8 -6
View File
@@ -24,11 +24,11 @@ managed_components
.github/*instructions.md
.github/workflows/_docs
# CI/CD Generated outputs
docs/.vuepress/dist/
docs/.vuepress/public/boards-info.json
docs/.vuepress/public/commonConfig.js
docs/.vuepress/public/firmware_build
docs/.vitepress/dist/
docs/.vitepress/cache/
docs/.vitepress/public/boards-info.json
docs/.vitepress/public/commonConfig.js
docs/.vitepress/public/firmware_build
# Generated site/firmware/reports files
generated/
@@ -41,8 +41,10 @@ scripts/latest_version.json
scripts/latest_version_dev.json
# Generated configuration files
docs/.vuepress/meta.json
docs/.vitepress/meta.json
# Generated full documentation pages
docs/prerequisites/board-full.md
docs/upload/web-install-full.md
.superpowers/
docs/superpowers/
+41
View File
@@ -0,0 +1,41 @@
// docs/.vitepress/commonConfig.mts
// Adapter that loads the shared Theengs commonConfig.js (CommonJS) downloaded
// by CI from https://www.theengs.io/commonConfig.js, and re-exports the nav
// and head arrays in a format VitePress can consume.
//
// This keeps theengs.io as the single source of truth for nav/head until all
// Theengs docs sites are migrated to VitePress.
import { createRequire } from 'module'
import { existsSync } from 'fs'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'
const __dirname = dirname(fileURLToPath(import.meta.url))
const require = createRequire(import.meta.url)
const commonConfigPath = resolve(__dirname, 'public/commonConfig.js')
let sharedConfig: any = null
if (existsSync(commonConfigPath)) {
sharedConfig = require(commonConfigPath)
} else {
console.warn(
'commonConfig.js not found in .vitepress/public/. ' +
'Using fallback nav/head. Run CI or download from https://www.theengs.io/commonConfig.js'
)
}
// Re-export nav from shared config, or use a minimal fallback
export const commonNav = sharedConfig?.themeConfig?.nav ?? [
{ text: 'Home', link: 'https://www.theengs.io' },
{ text: 'Community', link: 'https://community.openmqttgateway.com' },
{ text: 'Shop', link: 'https://shop.theengs.io/' }
]
// Re-export head from shared config, or use a minimal fallback
export const commonHead = sharedConfig?.head ?? [
['meta', { name: 'viewport', content: 'width=device-width, initial-scale=1' }],
['meta', { name: 'theme-color', content: '#3eaf7c' }]
]
@@ -79,7 +79,7 @@
@click="openSelector(board.environment)">
<div class="board-card__image">
<span v-if="popularEnvironments.indexOf(board.environment) !== -1" class="popular-badge">Popular</span>
<span v-if="popularEnvironments.includes(board.environment)" class="popular-badge">Popular</span>
<img
:src="getBoardImageUrl(board)"
:alt="board.environment"
@@ -112,7 +112,7 @@
<svg :class="{rotated: expandedModules[board.environment]}" width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 6 15 12 9 18"/></svg>
</span>
</div>
<transition name="expand-fade">
<Transition name="expand-fade">
<div class="libraries-badges"
:style="expandedModules[board.environment] ? '' : 'max-height: 2.2em; overflow: hidden;'">
<span
@@ -122,7 +122,7 @@
{{ mod }}
</span>
</div>
</transition>
</Transition>
</div>
@@ -139,7 +139,7 @@
</span>
</div>
<transition name="expand-fade">
<Transition name="expand-fade">
<div class="libraries-badges"
:style="expandedLibraries[board.environment] ? '' : 'max-height: 2.2em; overflow: hidden;'">
<span
@@ -149,7 +149,7 @@
{{ lib }}
</span>
</div>
</transition>
</Transition>
</div>
@@ -163,190 +163,202 @@
</div>
</template>
<script>
export default {
name: 'BoardEnvironmentTable',
props: {
boardsUrl: {
type: String,
default: '/boards-info.json'
},
selectorPath: {
type: String,
default: '/upload/board-selector.html'
}
},
data() {
return {
boards: [],
loading: false,
error: null,
expandedModules: {},
expandedLibraries: {},
searchQuery: '',
activeMcuFilter: null,
activeGatewayFilter: null,
popularEnvironments: [
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
interface Board {
environment: string
description?: string
microcontroller?: string
modules?: string[]
libraries?: string[]
customImg?: string
CustomImg?: string
}
interface GatewayFilter {
key: string
label: string
}
const props = withDefaults(defineProps<{
boardsUrl?: string
selectorPath?: string
}>(), {
boardsUrl: '/boards-info.json',
selectorPath: '/upload/board-selector.html'
})
const boards = ref<Board[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
const expandedModules = reactive<Record<string, boolean>>({})
const expandedLibraries = reactive<Record<string, boolean>>({})
const searchQuery = ref('')
const activeMcuFilter = ref<string | null>(null)
const activeGatewayFilter = ref<string | null>(null)
const popularEnvironments = [
'esp32dev-ble',
'theengs-bridge-v11',
'lilygo-rtl_433',
'lilygo-rtl_433-fsk',
'esp32dev-ir'
]
};
},
computed: {
resolvedBoardsUrl() {
return this.buildUrl(this.boardsUrl);
},
resolvedSelectorUrl() {
return this.buildUrl(this.selectorPath);
},
mcuFilters() {
var families = {};
this.boards.forEach(function(b) {
var family = this.getMcuFamily(b.microcontroller);
if (family) families[family] = true;
}.bind(this));
return Object.keys(families).sort();
},
gatewayFilters() {
var gateways = {
]
function buildUrl(path: string): string {
const base = import.meta.env.BASE_URL || '/'
const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base
const cleanPath = path.startsWith('/') ? path : `/${path}`
return `${cleanBase}${cleanPath}`
}
const resolvedBoardsUrl = computed(() => buildUrl(props.boardsUrl))
const resolvedSelectorUrl = computed(() => buildUrl(props.selectorPath))
function getMcuFamily(mcu: string | undefined): string | null {
if (!mcu) return null
const m = mcu.toLowerCase()
if (m.includes('esp32-s3') || m.includes('esp32s3') || m.includes('atoms3') || m.includes('lilygo-t3-s3')) return 'ESP32-S3'
if (m.includes('esp32-c3') || m.includes('esp32c3') || m.includes('lolin_c3') || m.includes('airm2m')) return 'ESP32-C3'
if (m.includes('esp32') || m.includes('m5st') || m.includes('heltec') || m.includes('ttgo') || m.includes('lolin32') || m.includes('pico32') || m.includes('tinypico') || m.includes('feather')) return 'ESP32'
if (m.includes('esp8') || m.includes('nodemcu')) return 'ESP8266'
return 'Other'
}
const mcuFilters = computed<string[]>(() => {
const families: Record<string, boolean> = {}
boards.value.forEach(b => {
const family = getMcuFamily(b.microcontroller)
if (family) families[family] = true
})
return Object.keys(families).sort()
})
const gatewayFilters = computed<GatewayFilter[]>(() => {
const gateways: Record<string, string> = {
BT: 'BLE',
RF: 'RF',
IR: 'IR',
LORA: 'LoRa',
RTL_433: 'RTL_433',
Pilight: 'Pilight'
};
var result = [];
var self = this;
Object.keys(gateways).forEach(function(key) {
var hasBoards = self.boards.some(function(b) {
return Array.isArray(b.modules) && b.modules.some(function(m) {
return m.toLowerCase().indexOf(key.toLowerCase()) !== -1;
});
});
if (hasBoards) {
result.push({ key: key, label: gateways[key] });
}
});
return result;
},
hasActiveFilters() {
return this.searchQuery || this.activeMcuFilter || this.activeGatewayFilter;
},
filteredBoards() {
var self = this;
return this.boards.filter(function(board) {
if (self.searchQuery) {
var q = self.searchQuery.toLowerCase();
var haystack = [
const result: GatewayFilter[] = []
Object.keys(gateways).forEach(key => {
const hasBoards = boards.value.some(b =>
Array.isArray(b.modules) && b.modules.some(m =>
m.toLowerCase().includes(key.toLowerCase())
)
)
if (hasBoards) {
result.push({ key, label: gateways[key] })
}
})
return result
})
const hasActiveFilters = computed(() =>
searchQuery.value || activeMcuFilter.value || activeGatewayFilter.value
)
const filteredBoards = computed<Board[]>(() =>
boards.value.filter(board => {
if (searchQuery.value) {
const q = searchQuery.value.toLowerCase()
const haystack = [
board.environment,
board.description || '',
board.microcontroller || '',
(board.modules || []).join(' ')
].join(' ').toLowerCase();
if (haystack.indexOf(q) === -1) return false;
].join(' ').toLowerCase()
if (!haystack.includes(q)) return false
}
if (self.activeMcuFilter) {
var family = self.getMcuFamily(board.microcontroller);
if (family !== self.activeMcuFilter) return false;
if (activeMcuFilter.value) {
const family = getMcuFamily(board.microcontroller)
if (family !== activeMcuFilter.value) return false
}
if (self.activeGatewayFilter) {
var key = self.activeGatewayFilter.toLowerCase();
if (!Array.isArray(board.modules) || !board.modules.some(function(m) {
return m.toLowerCase().indexOf(key) !== -1;
})) return false;
if (activeGatewayFilter.value) {
const key = activeGatewayFilter.value.toLowerCase()
if (!Array.isArray(board.modules) || !board.modules.some(m =>
m.toLowerCase().includes(key)
)) return false
}
return true;
});
}
},
methods: {
getMcuFamily(mcu) {
if (!mcu) return null;
var m = mcu.toLowerCase();
if (m.indexOf('esp32-s3') !== -1 || m.indexOf('esp32s3') !== -1 || m.indexOf('atoms3') !== -1 || m.indexOf('lilygo-t3-s3') !== -1) return 'ESP32-S3';
if (m.indexOf('esp32-c3') !== -1 || m.indexOf('esp32c3') !== -1 || m.indexOf('lolin_c3') !== -1 || m.indexOf('airm2m') !== -1) return 'ESP32-C3';
if (m.indexOf('esp32') !== -1 || m.indexOf('m5st') !== -1 || m.indexOf('heltec') !== -1 || m.indexOf('ttgo') !== -1 || m.indexOf('lolin32') !== -1 || m.indexOf('pico32') !== -1 || m.indexOf('tinypico') !== -1 || m.indexOf('feather') !== -1) return 'ESP32';
if (m.indexOf('esp8') !== -1 || m.indexOf('nodemcu') !== -1) return 'ESP8266';
return 'Other';
},
toggleMcuFilter(mcu) {
this.activeMcuFilter = this.activeMcuFilter === mcu ? null : mcu;
},
toggleGatewayFilter(gw) {
this.activeGatewayFilter = this.activeGatewayFilter === gw ? null : gw;
},
clearFilters() {
this.searchQuery = '';
this.activeMcuFilter = null;
this.activeGatewayFilter = null;
},
buildUrl(path) {
const base = this.$site?.base || '/';
const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;
const cleanPath = path.startsWith('/') ? path : `/${path}`;
return `${cleanBase}${cleanPath}`;
},
async loadBoards() {
this.loading = true;
this.error = null;
return true
})
)
function toggleMcuFilter(mcu: string) {
activeMcuFilter.value = activeMcuFilter.value === mcu ? null : mcu
}
function toggleGatewayFilter(gw: string) {
activeGatewayFilter.value = activeGatewayFilter.value === gw ? null : gw
}
function clearFilters() {
searchQuery.value = ''
activeMcuFilter.value = null
activeGatewayFilter.value = null
}
async function loadBoards() {
loading.value = true
error.value = null
try {
const response = await fetch(this.resolvedBoardsUrl);
const response = await fetch(resolvedBoardsUrl.value)
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const data = await response.json();
const data = await response.json()
if (!Array.isArray(data)) {
throw new Error('boards-info.json must be an array');
throw new Error('boards-info.json must be an array')
}
var self = this;
this.boards = data
.filter(board => board && typeof board.environment === 'string')
.sort(function(a, b) {
var aPopular = self.popularEnvironments.indexOf(a.environment) !== -1;
var bPopular = self.popularEnvironments.indexOf(b.environment) !== -1;
if (aPopular && !bPopular) return -1;
if (!aPopular && bPopular) return 1;
return a.environment.localeCompare(b.environment);
});
} catch (err) {
console.error('Failed to load boards-info:', err);
this.error = err.message || 'Unable to load board information';
boards.value = data
.filter((board: Board) => board && typeof board.environment === 'string')
.sort((a: Board, b: Board) => {
const aPopular = popularEnvironments.includes(a.environment)
const bPopular = popularEnvironments.includes(b.environment)
if (aPopular && !bPopular) return -1
if (!aPopular && bPopular) return 1
return a.environment.localeCompare(b.environment)
})
} catch (err: unknown) {
console.error('Failed to load boards-info:', err)
error.value = err instanceof Error ? err.message : 'Unable to load board information'
} finally {
this.loading = false;
loading.value = false
}
},
toggleModules(env) {
this.$set(this.expandedModules, env, !this.expandedModules[env]);
},
toggleLibraries(env) {
this.$set(this.expandedLibraries, env, !this.expandedLibraries[env]);
},
openSelector(environment) {
if (!environment) return;
const url = `${this.resolvedSelectorUrl}?env=${encodeURIComponent(environment)}`;
window.location.href = url;
},
getBoardImageUrl(board) {
const customImg = board.customImg || board.CustomImg;
}
function toggleModules(env: string) {
expandedModules[env] = !expandedModules[env]
}
function toggleLibraries(env: string) {
expandedLibraries[env] = !expandedLibraries[env]
}
function openSelector(environment: string) {
if (!environment) return
const url = `${resolvedSelectorUrl.value}?env=${encodeURIComponent(environment)}`
window.location.href = url
}
function getBoardImageUrl(board: Board): string {
const customImg = board.customImg || board.CustomImg
if (customImg) {
// If it's an absolute URL, use it as-is
if (customImg.startsWith('http')) {
return customImg;
return customImg
}
// Otherwise, build the URL with site base
return this.buildUrl(customImg);
return buildUrl(customImg)
}
return this.buildUrl('/img/microcontroller.gif');
}
},
mounted() {
this.loadBoards();
}
};
return buildUrl('/img/microcontroller.gif')
}
onMounted(() => {
loadBoards()
})
</script>
<style scoped>
@@ -371,29 +383,29 @@ export default {
.search-icon {
position: absolute;
left: 12px;
color: var(--text-color-secondary, #666);
color: var(--vp-c-text-2);
pointer-events: none;
}
.search-input {
width: 100%;
padding: 0.6rem 2.2rem 0.6rem 2.4rem;
border: 1px solid var(--border-color, #eaecef);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
font-size: 0.9rem;
background: #ffffff;
color: var(--text-color, #2c3e50);
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
transition: border-color 0.2s;
}
.search-input:focus {
outline: none;
border-color: var(--accent-color, #3eaf7c);
border-color: var(--vp-c-brand-1);
box-shadow: 0 0 0 3px rgba(62, 175, 124, 0.15);
}
.search-input::placeholder {
color: var(--text-color-secondary, #999);
color: var(--vp-c-text-3);
}
.search-clear {
@@ -403,14 +415,14 @@ export default {
border: none;
cursor: pointer;
padding: 4px;
color: var(--text-color-secondary, #666);
color: var(--vp-c-text-2);
border-radius: 50%;
display: flex;
align-items: center;
}
.search-clear:hover {
background: rgba(0, 0, 0, 0.05);
background: var(--vp-c-bg-soft);
}
.filter-groups {
@@ -428,7 +440,7 @@ export default {
.filter-group__label {
font-size: 0.8rem;
font-weight: 600;
color: var(--text-color-secondary, #666);
color: var(--vp-c-text-2);
white-space: nowrap;
}
@@ -439,9 +451,9 @@ export default {
}
.filter-chip {
background: var(--code-bg-color, #f6f8fa);
color: var(--text-color, #2c3e50);
border: 1px solid var(--border-color, #eaecef);
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
border: 1px solid var(--vp-c-divider);
padding: 0.25rem 0.7rem;
border-radius: 16px;
font-size: 0.78rem;
@@ -451,14 +463,14 @@ export default {
}
.filter-chip:hover {
border-color: var(--accent-color, #3eaf7c);
color: var(--accent-color, #3eaf7c);
border-color: var(--vp-c-brand-1);
color: var(--vp-c-brand-1);
}
.filter-chip--active {
background: var(--accent-color, #3eaf7c);
background: var(--vp-c-brand-1);
color: #ffffff;
border-color: var(--accent-color, #3eaf7c);
border-color: var(--vp-c-brand-1);
}
.filter-chip--active:hover {
@@ -471,13 +483,13 @@ export default {
align-items: center;
gap: 0.75rem;
font-size: 0.85rem;
color: var(--text-color-secondary, #666);
color: var(--vp-c-text-2);
}
.filter-clear {
background: none;
border: none;
color: var(--accent-color, #3eaf7c);
color: var(--vp-c-brand-1);
cursor: pointer;
font-size: 0.85rem;
font-weight: 500;
@@ -491,7 +503,7 @@ export default {
.no-results {
text-align: center;
padding: 3rem 1rem;
color: var(--text-color-secondary, #666);
color: var(--vp-c-text-2);
}
.no-results p {
@@ -501,15 +513,15 @@ export default {
/* Grid Layout */
.boards-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 1.5rem;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin-top: 1.5rem;
}
/* Card Design */
.board-card {
background: #ffffff;
border: 1px solid var(--border-color, #eaecef);
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
overflow: hidden;
cursor: pointer;
@@ -522,18 +534,18 @@ export default {
.board-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
border-color: var(--accent-color, #3eaf7c);
border-color: var(--vp-c-brand-1);
}
/* Image Section */
.board-card__image {
background: var(--code-bg-color, #f6f8fa);
background: var(--vp-c-bg-soft);
padding: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
min-height: 120px;
border-bottom: 1px solid var(--border-color, #eaecef);
border-bottom: 1px solid var(--vp-c-divider);
position: relative;
}
@@ -585,22 +597,22 @@ export default {
.board-card__title code {
background: transparent;
color: var(--accent-color, #3eaf7c);
color: var(--vp-c-brand-1);
font-size: 0.9em;
padding: 0;
font-weight: 600;
}
.board-card__chip {
background: var(--code-bg-color, #f6f8fa);
color: var(--text-color-secondary, #666);
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-2);
padding: 0.3rem 0.8rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
white-space: nowrap;
flex-shrink: 0;
border: 1px solid var(--border-color, #eaecef);
border: 1px solid var(--vp-c-divider);
}
/* Description */
@@ -608,7 +620,7 @@ export default {
margin: 0;
font-size: 0.9rem;
line-height: 1.5;
color: var(--text-color-secondary, #666);
color: var(--vp-c-text-2);
}
@@ -616,7 +628,7 @@ export default {
.board-card__libraries {
margin-top: 0.5rem;
padding-top: 0.25rem;
border-top: 1px solid var(--border-color, #eaecef);
border-top: 1px solid var(--vp-c-divider);
}
.modules-label-row {
@@ -628,7 +640,7 @@ export default {
.libraries-label {
font-size: 0.8rem;
font-weight: 600;
color: var(--text-color-secondary, #666);
color: var(--vp-c-text-2);
display: inline-block;
}
@@ -640,24 +652,24 @@ export default {
.lib-badge {
display: inline-block;
background: var(--code-bg-color, #f6f8fa);
color: var(--text-color, #2c3e50);
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
padding: 0.2rem 0.6rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 500;
border: 1px solid var(--border-color, #eaecef);
border: 1px solid var(--vp-c-divider);
}
/* Action Footer */
.board-card__action {
padding-top: 0.75rem;
margin-top: 0.5rem;
border-top: 1px solid var(--border-color, #eaecef);
border-top: 1px solid var(--vp-c-divider);
}
.action-text {
color: var(--accent-color, #3eaf7c);
color: var(--vp-c-brand-1);
font-size: 0.85rem;
font-weight: 600;
display: flex;
@@ -676,7 +688,7 @@ export default {
height: 16px;
min-width: 16px;
border: 2px solid #cfd8dc;
border-top-color: var(--accent-color, #3eaf7c);
border-top-color: var(--vp-c-brand-1);
border-radius: 50%;
animation: spin 0.8s linear infinite;
vertical-align: middle;
@@ -702,7 +714,6 @@ export default {
.boards-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
.board-card__image {
@@ -730,11 +741,11 @@ export default {
.expand-fade-enter-active, .expand-fade-leave-active {
transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s;
}
.expand-fade-enter, .expand-fade-leave-to {
.expand-fade-enter-from, .expand-fade-leave-to {
opacity: 0;
max-height: 0;
}
.expand-fade-enter-to, .expand-fade-leave {
.expand-fade-enter-to, .expand-fade-leave-from {
opacity: 1;
max-height: 500px;
}
@@ -745,7 +756,7 @@ export default {
justify-content: center;
background: none;
border: none;
color: var(--accent-color, #3eaf7c);
color: var(--vp-c-brand-1);
cursor: pointer;
margin-top: 0.3em;
margin-left: 0.2em;
@@ -15,7 +15,7 @@
</div>
<!-- Board Details (when selected) -->
<transition name="fade">
<Transition name="fade">
<div v-if="currentEnvironment && !loading" class="board-details">
<!-- Hero Section - Clean and Focused -->
@@ -117,10 +117,6 @@
</div>
</div>
<!-- Partitions -->
<div v-if="currentEnvironment.partitions" class="tech-section">
<h4 class="tech-section__title">Partitions</h4>
@@ -141,160 +137,160 @@
</details>
</div>
</transition>
<!-- Borad Detail not selected message -->
<transition name="fade">
</Transition>
<!-- Board Detail not selected message -->
<Transition name="fade">
<div v-if="!currentEnvironment && !loading && !error" class="custom-block info">
<p class="custom-block-title">Need to select one environment...</p>
</div>
</transition>
</Transition>
</div>
</template>
<script>
export default {
name: 'FlashEnvironmentSelector',
props: {
boardsUrl: {
type: String,
default: '/boards-info.json'
},
firmwareBaseUrl: {
type: String,
default: '/firmware_build/'
}
},
data() {
return {
environments: [],
selectedEnvironment: '',
loading: true,
error: null,
imageError: false
};
},
computed: {
currentEnvironment() {
return this.environments.find(env => env.environment === this.selectedEnvironment);
},
resolvedBoardsUrl() {
return this.buildUrl(this.boardsUrl);
},
resolvedFirmwareBaseUrl() {
return this.buildUrl(this.firmwareBaseUrl);
},
manifestUrl() {
if (!this.selectedEnvironment) return null;
return `${this.resolvedFirmwareBaseUrl}${this.selectedEnvironment}.manifest.json`;
},
boardImageUrl() {
if (!this.currentEnvironment) return null;
const customImg = this.currentEnvironment.customImg || this.currentEnvironment.CustomImg;
if (customImg && !this.imageError) {
// If it's an absolute URL, use it as-is
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
interface Environment {
environment: string
description?: string
microcontroller?: string
modules?: string[]
libraries?: string[]
hardware?: string
partitions?: string
options?: string | string[]
customImg?: string
CustomImg?: string
}
const props = withDefaults(defineProps<{
boardsUrl?: string
firmwareBaseUrl?: string
}>(), {
boardsUrl: '/boards-info.json',
firmwareBaseUrl: '/firmware_build/'
})
const environments = ref<Environment[]>([])
const selectedEnvironment = ref<string>('')
const loading = ref<boolean>(true)
const error = ref<string | null>(null)
const imageError = ref<boolean>(false)
const currentEnvironment = computed<Environment | undefined>(() => {
return environments.value.find(env => env.environment === selectedEnvironment.value)
})
const resolvedBoardsUrl = computed<string>(() => buildUrl(props.boardsUrl))
const resolvedFirmwareBaseUrl = computed<string>(() => buildUrl(props.firmwareBaseUrl))
const manifestUrl = computed<string | null>(() => {
if (!selectedEnvironment.value) return null
return `${resolvedFirmwareBaseUrl.value}${selectedEnvironment.value}.manifest.json`
})
const boardImageUrl = computed<string | null>(() => {
if (!currentEnvironment.value) return null
const customImg = currentEnvironment.value.customImg || currentEnvironment.value.CustomImg
if (customImg && !imageError.value) {
if (customImg.startsWith('http')) {
return customImg;
return customImg
}
// Otherwise, build the URL with site base
return this.buildUrl(customImg);
return buildUrl(customImg)
}
return this.buildUrl('/img/microcontroller.gif');
}
},
watch: {
selectedEnvironment(newValue) {
this.imageError = false;
this.error = null;
}
},
mounted() {
const script = document.createElement('script');
script.type="module";
script.src="https://unpkg.com/esp-web-tools@10/dist/web/install-button.js?module";
document.head.appendChild(script);
this.loadEnvironments();
},
methods: {
buildUrl(path) {
// Concatenate this.$site.base with the provided path
const base = this.$site?.base || '/';
// Remove trailing slash from base and leading slash from path to avoid double slashes
const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base;
const cleanPath = path.startsWith('/') ? path : `/${path}`;
return `${cleanBase}${cleanPath}`;
},
getQueryParam(param) {
// Extract query parameter from URL
const params = new URLSearchParams(window.location.search);
return params.get(param);
},
async loadEnvironments() {
this.loading = true;
this.error = null;
return buildUrl('/img/microcontroller.gif')
})
watch(selectedEnvironment, () => {
imageError.value = false
error.value = null
})
onMounted(() => {
const script = document.createElement('script')
script.type = 'module'
script.src = 'https://unpkg.com/esp-web-tools@10/dist/web/install-button.js?module'
document.head.appendChild(script)
loadEnvironments()
})
function buildUrl(path: string): string {
const base = import.meta.env.BASE_URL || '/'
const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base
const cleanPath = path.startsWith('/') ? path : `/${path}`
return `${cleanBase}${cleanPath}`
}
function getQueryParam(param: string): string | null {
const params = new URLSearchParams(window.location.search)
return params.get(param)
}
async function loadEnvironments(): Promise<void> {
loading.value = true
error.value = null
try {
const response = await fetch(this.resolvedBoardsUrl);
const response = await fetch(resolvedBoardsUrl.value)
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const data = await response.json();
const data = await response.json()
if (!Array.isArray(data)) {
throw new Error('Invalid data format: expected an array');
throw new Error('Invalid data format: expected an array')
}
const validEnvironments = data.filter(env =>
const validEnvironments = data.filter((env: unknown) =>
env &&
typeof env === 'object' &&
env.environment &&
typeof env.environment === 'string'
);
(env as Environment).environment &&
typeof (env as Environment).environment === 'string'
) as Environment[]
if (validEnvironments.length === 0) {
throw new Error('No valid environments found in data');
throw new Error('No valid environments found in data')
}
this.environments = validEnvironments;
environments.value = validEnvironments
// Check for env parameter in query string and auto-select
const envParam = this.getQueryParam('env');
const envParam = getQueryParam('env')
if (envParam) {
const matchingEnv = validEnvironments.find(env => env.environment === envParam);
const matchingEnv = validEnvironments.find(env => env.environment === envParam)
if (matchingEnv) {
this.selectedEnvironment = envParam;
selectedEnvironment.value = envParam
} else {
console.warn(`Environment '${envParam}' not found in available environments`);
console.warn(`Environment '${envParam}' not found in available environments`)
}
}
} catch (error) {
console.error('Failed to load environments:', error);
this.error = error.message || 'Failed to load environments';
} catch (err) {
console.error('Failed to load environments:', err)
error.value = (err instanceof Error ? err.message : null) || 'Failed to load environments'
} finally {
this.loading = false;
loading.value = false
}
},
}
formatOptions(optionsText) {
if (!optionsText) return '';
let optionsTextMap=[];
if (typeof optionsText === 'string'){
function formatOptions(optionsText: string | string[]): string {
if (!optionsText) return ''
let optionsTextMap: string[] = []
if (typeof optionsText === 'string') {
optionsTextMap = optionsText.split('\n')
}else if (Array.isArray(optionsText)){
optionsTextMap = optionsText;
} else if (Array.isArray(optionsText)) {
optionsTextMap = optionsText
}
return optionsTextMap.map(line => line.trim())
.filter(line => line.length > 0)
.join('\n');
},
.join('\n')
}
handleImageError(event) {
this.imageError = true;
}
}
};
function handleImageError(): void {
imageError.value = true
}
</script>
<style scoped>
@@ -305,8 +301,6 @@ export default {
padding: 0 1rem;
}
/* Board Details */
cursor: pointer;
/* Board Details Container */
.board-details {
margin-top: 2rem;
@@ -317,12 +311,12 @@ export default {
display: grid;
grid-template-columns: 1fr auto;
gap: 24px;
background: #ffffff;
background: var(--vp-c-bg);
border-radius: 12px;
padding: 32px;
margin-bottom: 16px;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
border: 1px solid rgba(0, 0, 0, 0.08);
box-shadow: 0 1px 2px 0 var(--vp-c-bg-soft);
border: 1px solid var(--vp-c-divider);
}
.board-hero__content {
@@ -335,7 +329,7 @@ export default {
margin: 0 0 8px 0;
font-size: 1.75rem;
font-weight: 400;
color: var(--accent-color, #3eaf7c);
color: var(--vp-c-brand-1);
font-family: 'Monaco', 'Courier New', monospace;
line-height: 1.2;
letter-spacing: -0.01em;
@@ -354,7 +348,7 @@ export default {
text-transform: uppercase;
font-weight: 500;
letter-spacing: 0.1em;
color: rgba(0, 0, 0, 0.6);
color: var(--vp-c-text-2);
}
.switcher-select-wrapper {
@@ -365,8 +359,8 @@ export default {
.switcher-select {
appearance: none;
background: rgba(0, 0, 0, 0.08);
color: rgba(0, 0, 0, 0.87);
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
border: none;
padding: 6px 32px 6px 12px;
border-radius: 16px;
@@ -375,16 +369,17 @@ export default {
font-family: 'Monaco', 'Courier New', monospace;
cursor: pointer;
transition: background 0.2s cubic-bezier(0.4, 0, 0.2, 1);
height: 32px;
min-width: 180px;
min-height: 32px;
min-width: 280px;
width: auto;
}
.switcher-select:hover {
background: rgba(0, 0, 0, 0.12);
background: var(--vp-c-bg-soft);
}
.switcher-select:focus {
outline: 2px solid var(--accent-color, #3eaf7c);
outline: 2px solid var(--vp-c-brand-1);
outline-offset: 2px;
}
@@ -408,13 +403,13 @@ export default {
text-transform: uppercase;
font-weight: 500;
letter-spacing: 0.1em;
color: rgba(0, 0, 0, 0.6);
color: var(--vp-c-text-2);
}
.meta-value {
font-size: 0.875rem;
font-weight: 500;
color: rgba(0, 0, 0, 0.87);
color: var(--vp-c-text-1);
font-family: 'Monaco', 'Courier New', monospace;
}
@@ -422,7 +417,7 @@ export default {
margin: 0;
font-size: 1rem;
line-height: 1.5;
color: rgba(0, 0, 0, 0.87);
color: var(--vp-c-text-1);
max-width: 600px;
}
@@ -471,19 +466,19 @@ export default {
}
.connect-button--disabled {
background: rgba(0, 0, 0, 0.12);
color: rgba(0, 0, 0, 0.38);
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-3);
cursor: not-allowed;
box-shadow: none;
}
/* Technical Details - Material Surface */
.tech-details {
background: #ffffff;
background: var(--vp-c-bg);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
border: 1px solid rgba(0, 0, 0, 0.08);
box-shadow: 0 1px 2px 0 var(--vp-c-bg-soft);
border: 1px solid var(--vp-c-divider);
}
.tech-details__summary {
@@ -493,7 +488,7 @@ export default {
padding: 16px 24px;
cursor: pointer;
user-select: none;
background: rgba(0, 0, 0, 0.02);
background: var(--vp-c-bg-alt);
transition: background 0.2s cubic-bezier(0.4, 0, 0.2, 1);
list-style: none;
}
@@ -503,7 +498,7 @@ export default {
}
.tech-details__summary:hover {
background: rgba(0, 0, 0, 0.04);
background: var(--vp-c-bg-soft);
}
.tech-details__icon {
@@ -514,7 +509,7 @@ export default {
.tech-details__title {
font-size: 1rem;
font-weight: 500;
color: rgba(0, 0, 0, 0.87);
color: var(--vp-c-text-1);
flex: 1;
letter-spacing: 0.01em;
}
@@ -556,13 +551,13 @@ export default {
text-transform: uppercase;
font-weight: 500;
letter-spacing: 0.1em;
color: rgba(0, 0, 0, 0.6);
color: var(--vp-c-text-2);
}
.tech-section__content {
font-size: 0.875rem;
line-height: 1.5;
color: rgba(0, 0, 0, 0.87);
color: var(--vp-c-text-1);
}
/* Chips - Material Design */
@@ -573,8 +568,8 @@ export default {
}
.chip {
background: rgba(0, 0, 0, 0.08);
color: rgba(0, 0, 0, 0.87);
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
padding: 6px 12px;
border-radius: 8px;
font-size: 0.875rem;
@@ -587,27 +582,27 @@ export default {
/* Code Styles - Material Surface */
.code-inline {
background: rgba(0, 0, 0, 0.05);
background: var(--vp-c-bg-soft);
padding: 2px 6px;
border-radius: 4px;
font-size: 0.875rem;
font-family: 'Monaco', 'Courier New', monospace;
color: rgba(0, 0, 0, 0.87);
color: var(--vp-c-text-1);
}
.code-block {
background: rgba(0, 0, 0, 0.03);
background: var(--vp-c-bg-soft);
padding: 16px;
border-radius: 8px;
font-size: 0.8125rem;
line-height: 1.5;
overflow-x: auto;
margin: 0;
border: 1px solid rgba(0, 0, 0, 0.08);
border: 1px solid var(--vp-c-divider);
white-space: pre-wrap;
word-break: break-word;
font-family: 'Monaco', 'Courier New', monospace;
color: rgba(0, 0, 0, 0.87);
color: var(--vp-c-text-1);
}
/* Loading Spinner */
@@ -616,7 +611,7 @@ export default {
width: 18px;
height: 18px;
border: 2px solid rgba(62, 175, 124, 0.3);
border-top-color: var(--accent-color, #3eaf7c);
border-top-color: var(--vp-c-brand-1);
border-radius: 50%;
animation: spin 0.8s linear infinite;
vertical-align: middle;
+138
View File
@@ -0,0 +1,138 @@
// docs/.vitepress/config.mts
import { defineConfig } from 'vitepress'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'
import { commonNav, commonHead } from './commonConfig.mts'
const __dirname = dirname(fileURLToPath(import.meta.url))
// Load metadata with override support
import defaults from './defaults.json'
let meta = { ...defaults }
try {
const overrides = await import('./meta.json')
meta = { ...meta, ...overrides.default }
} catch {
console.warn('meta.json not found or not valid. Using default configuration.')
}
export default defineConfig({
title: `${meta.title} - ${meta.version}`,
description: 'One gateway, many technologies: MQTT gateway for ESP8266 or ESP32 with bidirectional 433mhz/315mhz/868mhz, Infrared communications, BLE, LoRa, beacons detection, mi flora / mi jia / LYWSD02/ Mi Scale compatibility, SMS & LORA.',
base: meta.url_prefix as '/' | `/${string}/`,
outDir: resolve(__dirname, '../../', meta.dest),
head: commonHead,
ignoreDeadLinks: [
/localhost/,
/LoraTemperature/
],
lastUpdated: true,
markdown: {
lineNumbers: true
},
themeConfig: {
siteTitle: 'OpenMQTTGateway',
nav: commonNav,
socialLinks: [
{ icon: 'github', link: `https://github.com/${meta.theme_config_repo}` }
],
editLink: {
pattern: `https://github.com/${meta.theme_config_repo}/edit/development/docs/:path`
},
sidebar: [
{ text: '0 - What is it for', link: '/' },
{
text: '1 - Prerequisites',
collapsed: true,
items: [
{ text: 'Devices', link: '/prerequisites/devices' },
{ text: 'Board', link: '/prerequisites/board' },
{ text: 'Parts', link: '/prerequisites/parts' },
{ text: 'Broker', link: '/prerequisites/broker' },
{ text: 'Controller', link: '/prerequisites/controller' }
]
},
{
text: '2 - Set it up',
collapsed: true,
items: [
{ text: 'RF', link: '/setitup/rf' },
{ text: 'BLE', link: '/setitup/ble' },
{ text: 'IR', link: '/setitup/ir' },
{ text: 'LoRa', link: '/setitup/lora' },
{ text: 'GSM', link: '/setitup/gsm' },
{ text: 'Serial', link: '/setitup/serial' },
{ text: 'Sensors', link: '/setitup/sensors' },
{ text: 'Actuators', link: '/setitup/actuators' }
]
},
{
text: '3 - Upload',
collapsed: true,
link: '/upload/',
items: [
{ text: '(Option 1) Upload from the web', link: '/upload/web-install' },
{ text: 'Binaries', link: '/upload/binaries' },
{ text: 'Builds', link: '/upload/builds' },
{ text: 'Gitpod', link: '/upload/gitpod' },
{ text: 'Portal', link: '/upload/portal' },
{ text: 'Advanced Configuration', link: '/upload/advanced-configuration' },
{ text: 'Troubleshoot', link: '/upload/troubleshoot' }
]
},
{
text: '4 - Use',
collapsed: true,
items: [
{ text: 'RF', link: '/use/rf' },
{ text: 'BLE', link: '/use/ble' },
{ text: 'IR', link: '/use/ir' },
{ text: 'LoRa', link: '/use/lora' },
{ text: 'GSM', link: '/use/gsm' },
{ text: 'Serial', link: '/use/serial' },
{ text: 'RFM69', link: '/use/rfm69' },
{ text: 'Sensors', link: '/use/sensors' },
{ text: 'Actuators', link: '/use/actuators' },
{ text: 'Boards', link: '/use/boards' },
{ text: 'Displays', link: '/use/displays' },
{ text: 'Gateway', link: '/use/gateway' },
{ text: 'WebUI', link: '/use/webui' }
]
},
{
text: '5 - Integrate',
collapsed: true,
items: [
{ text: 'OpenHAB 2', link: '/integrate/openhab2' },
{ text: 'OpenHAB 3', link: '/integrate/openhab3' },
{ text: 'Home Assistant', link: '/integrate/home_assistant' },
{ text: 'Node-RED', link: '/integrate/node_red' },
{ text: 'AWS IoT', link: '/integrate/aws_iot' },
{ text: 'Jeedom', link: '/integrate/jeedom' }
]
},
{
text: '6 - Participate',
collapsed: true,
link: '/participate/',
items: [
{ text: 'Quick Start', link: '/participate/quick_start' },
{ text: 'Support', link: '/participate/support' },
{ text: 'Development', link: '/participate/development' },
{ text: 'Adding Protocols', link: '/participate/adding-protocols' },
{ text: 'Community', link: '/participate/community' },
{ text: 'License', link: meta.url_license_file }
]
}
],
search: {
provider: 'local'
}
},
sitemap: {
hostname: meta.hostname
},
vite: {
publicDir: '.vitepress/public'
}
})
File renamed without changes.
File renamed without changes.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 603 B

After

Width:  |  Height:  |  Size: 603 B

File renamed without changes.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 324 KiB

After

Width:  |  Height:  |  Size: 324 KiB

File renamed without changes.
+32
View File
@@ -0,0 +1,32 @@
/* docs/.vitepress/theme/custom.css */
/* Prevent long site title from overlapping sidebar/nav */
.VPNavBarTitle .title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Widen the entire content column on pages without aside (board/flash pages) */
.VPDoc:not(.has-aside) .content-container,
.VPDoc:not(.has-aside) .container {
max-width: 100% !important;
}
.VPDoc:not(.has-aside) .vp-doc {
max-width: 100% !important;
}
/* Gray backdrop when ESP Web Tools install dialog is open */
ewt-install-dialog {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
}
+34
View File
@@ -0,0 +1,34 @@
// docs/.vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import mediumZoom from 'medium-zoom'
import { onMounted, watch, nextTick } from 'vue'
import { useRoute } from 'vitepress'
import './custom.css'
import BoardEnvironmentTable from '../components/BoardEnvironmentTable.vue'
import FlashEnvironmentSelector from '../components/FlashEnvironmentSelector.vue'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('BoardEnvironmentTable', BoardEnvironmentTable)
app.component('FlashEnvironmentSelector', FlashEnvironmentSelector)
},
setup() {
const route = useRoute()
const initZoom = () => {
mediumZoom('.vp-doc img:not(.no-zoom)', {
background: 'var(--vp-c-bg)'
})
}
onMounted(() => {
initZoom()
})
watch(
() => route.path,
() => nextTick(() => initZoom())
)
}
}
-135
View File
@@ -1,135 +0,0 @@
let meta = require('./defaults.json');
try {
meta_overload = require('./meta.json');
meta = { ...meta, ...meta_overload };
} catch (e) {
console.warn('meta.json not found or not valid. Using default configuration.');
}
const fs = require('fs');
const path = require('path');
const commonConfigPath = path.resolve(__dirname, 'public/commonConfig.js');
if (!fs.existsSync(commonConfigPath)) {
throw new Error(`commonConfig.js not found in ${commonConfigPath}.\nPlease download from https://www.theengs.io/commonConfig.js or create this file before you build the documentation.`);
}
const commonConfig = require('./public/commonConfig');
module.exports = {
...commonConfig,
title: `${meta.title} - ${meta.version}`,
base: meta.url_prefix,
dest: meta.dest, // default is generated/site
description: 'One gateway, many technologies: MQTT gateway for ESP8266 or ESP32 with bidirectional 433mhz/315mhz/868mhz, Infrared communications, BLE, LoRa, beacons detection, mi flora / mi jia / LYWSD02/ Mi Scale compatibility, SMS & LORA.',
head: [...commonConfig.head],
themeConfig: {
...commonConfig.themeConfig,
repo: meta.theme_config_repo,
docsDir: 'docs',
mode: meta.mode,
sidebar: [
['/', '0 - What is it for 🏠'],
{
title: '1 - Prerequisites🧭', // required
//collapsable: true, // optional, defaults to true
sidebarDepth: 1, // optional, defaults to 1
children: [
'prerequisites/devices',
'prerequisites/board',
'prerequisites/parts',
'prerequisites/broker',
'prerequisites/controller']
},
{
title: '2 - Set it up 🔨', // required
sidebarDepth: 1, // optional, defaults to 1
children: [
'setitup/rf',
'setitup/ble',
'setitup/ir',
'setitup/lora',
'setitup/gsm',
'setitup/serial',
'setitup/sensors',
'setitup/actuators'
]
},
{
title: '3 - Upload ➡️', // required
path: '/upload/',
sidebarDepth: 1, // optional, defaults to 1
children: [
['upload/web-install', "(Option 1) Upload from the web"],
'upload/binaries',
'upload/builds',
'upload/gitpod',
'upload/portal',
'upload/advanced-configuration',
'upload/troubleshoot'
]
},
{
title: '4 - Use ✈️', // required
sidebarDepth: 1, // optional, defaults to 1
children: [
'use/rf',
'use/ble',
'use/ir',
'use/lora',
'use/gsm',
'use/serial',
'use/rfm69',
'use/sensors',
'use/actuators',
'use/boards',
'use/displays',
'use/gateway',
'use/webui'
]
},
{
title: '5 - Integrate 🎉', // required
sidebarDepth: 1, // optional, defaults to 1
children: [
'integrate/openhab2',
'integrate/openhab3',
'integrate/home_assistant',
'integrate/node_red',
'integrate/aws_iot',
'integrate/jeedom'
]
},
{
title: '6 - Participate 💻', // required
path: '/participate/',
sidebarDepth: 1, // optional, defaults to 1
children: [
'participate/quick_start',
'participate/support',
'participate/development',
'participate/adding-protocols',
'participate/community',
[meta.url_license_file, 'License']
]
}
]
},
plugins: {
'sitemap': {
hostname: meta.hostname,
urls: [
'https://decoder.theengs.io/devices/devices.html',
meta.url_community_forum,
'https://shop.theengs.io/',
'https://shop.theengs.io/products/theengs-plug-smart-plug-ble-gateway-and-energy-consumption',
'https://shop.theengs.io/products/theengs-bridge-esp32-ble-mqtt-gateway-with-ethernet-and-external-antenna',
'https://app.theengs.io/',
'https://gateway.theengs.io/',
'https://decoder.theengs.io/',
'https://parser.theengs.io/',
'https://www.theengs.io/'
],
},
}
}
-13
View File
@@ -1,13 +0,0 @@
div.table-generated-page
main
div.content__default,
footer.page-edit,
div.page-nav
// Display full with the API pages
max-width: 65%
.sidebar
z-index 6
.navbar
z-index 6
File renamed without changes.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

+12 -11
View File
@@ -1,17 +1,18 @@
<ClientOnly>
<span v-if="$site.themeConfig.mode === 'dev'">
<script setup>
import { useData } from 'vitepress'
const { site } = useData()
const isDev = site.value.title?.includes('edge') || site.value.title?.includes('dev')
</script>
::: warning Development Version
This is the edge version of the documentation. It is under active development and may contain bugs, incomplete features, or breaking changes. Use it at your own risk.
:::
</span>
</ClientOnly>
<div v-if="isDev" class="warning custom-block">
<p class="custom-block-title">Development Version</p>
<p>This is the edge version of the documentation. It is under active development and may contain bugs, incomplete features, or breaking changes. Use it at your own risk.</p>
</div>
OpenMQTTGateway aims to unify various technologies and protocols into a single firmware. This reduces the need for multiple physical bridges and streamlines diverse technologies under the widely-used [MQTT](http://mqtt.org/) protocol.
<div style="text-align: center;">
<img src="img/OpenMQTTGateway.png" alt="Overview of the protocols and compatible controllers" style="max-width: 100%; height: auto;">
<img src="./img/OpenMQTTGateway.png" alt="Overview of the protocols and compatible controllers" style="max-width: 100%; height: auto;">
</div>
# What is an MQTT gateway or bridge ?
@@ -83,7 +84,7 @@ Support open-source development through sponsorship and gain exclusive access to
<div style="text-align: center;">
<a href="https://shop.theengs.io/products/theengs-bridge-esp32-ble-mqtt-gateway-with-ethernet-and-external-antenna" target="_blank" rel="noopener noreferrer">
<img src="img/Theengs-Bridge-ble-gateway.png" alt="Theengs bridge view" style="max-width: 100%; height: auto;">
<img src="./img/Theengs-Bridge-ble-gateway.png" alt="Theengs bridge view" style="max-width: 100%; height: auto;">
</a>
</div>
@@ -99,7 +100,7 @@ Support open-source development through sponsorship and gain exclusive access to
<div style="text-align: center;">
<a href="https://shop.theengs.io/products/theengs-plug-smart-plug-ble-gateway-and-energy-consumption" target="_blank" rel="noopener noreferrer">
<img src="img/Theengs-Plug-OpenMQTTGateway.png" alt="Theengs plug view" style="max-width: 100%; height: auto;">
<img src="./img/Theengs-Plug-OpenMQTTGateway.png" alt="Theengs plug view" style="max-width: 100%; height: auto;">
</a>
</div>
+1 -1
View File
@@ -37,7 +37,7 @@ OMG will use the auto discovery functionality of home assistant to create gatewa
The Bluetooth and the RTL_433 gateway will automatically create devices and entities, the RF gateway will create DeviceTrigger.
The OpenMQTTGateway will also be available as a device to monitor its parameters and control it. The sensors (DHT for example) and actuators (relays) are attached to the gateway.
On first and subsequent startups, auto discovery will start. If you want to prevent this from happening, be sure to manually turn off auto discovery, either by using the UI in Home Assistant, or by publishing to the home/<gatewayname>/commands/MQTTtoSYS/config topic.
On first and subsequent startups, auto discovery will start. If you want to prevent this from happening, be sure to manually turn off auto discovery, either by using the UI in Home Assistant, or by publishing to the `home/<gatewayname>/commands/MQTTtoSYS/config` topic.
30 minutes after its activation the auto discovery will be automatically deactivated, you can reactivate it from the gateway controls.
Some devices may require a button push or motion/contact event to trigger a message and generate the auto discovery.
:::
File renamed without changes.
+27 -27
View File
@@ -76,11 +76,11 @@ Install these tools first. They are useful for **all** types of work:
On Windows you can download:
- Git from the official site: <https://git-scm.com/downloads>
- Visual Studio Code from <https://code.visualstudio.com/>
- Git from the official site: https://git-scm.com/downloads
- Visual Studio Code from https://code.visualstudio.com/
- Git Bash comes with "Git for Windows".
On Linux you can install these with your package manager (for example apt on Ubuntu). For more help you can read the Git book: <https://git-scm.com/book/en/v2/Getting-Started-Installing-Git>.
On Linux you can install these with your package manager (for example apt on Ubuntu). For more help you can read the Git book: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git.
**Check (common tools):** open a terminal (Git Bash, WSL, or Linux shell) and run:
@@ -117,9 +117,9 @@ Install these extra tools:
- Python 3.10 or newer
- PlatformIO CLI (pip package)
PlatformIO is the main build system for this project. We will use it from VS Code and from the terminal. If you want to read more, the official docs are here: <https://docs.platformio.org/>.
PlatformIO is the main build system for this project. We will use it from VS Code and from the terminal. If you want to read more, the official docs are here: https://docs.platformio.org/.
On Windows you can download Python from: <https://www.python.org/downloads/>
On Windows you can download Python from: https://www.python.org/downloads/
On Linux you can install Python with your package manager.
@@ -150,9 +150,9 @@ Install:
- Node.js 18 or newer
- npm (usually comes with Node.js)
On Windows you can get Node.js from: <https://nodejs.org/en/download>
On Windows you can get Node.js from: https://nodejs.org/en/download
On Linux you can install Node.js with your package manager or by following the docs: <https://nodejs.org/en/download/package-manager>.
On Linux you can install Node.js with your package manager or by following the docs: https://nodejs.org/en/download/package-manager.
**Check (docs tools):**
@@ -164,7 +164,7 @@ npm --version
If one of these commands fails, reinstall Node.js, close the terminal, and try again.
### 2.4 PlatformIO extension in Visual Studio Code
Now add PlatformIO inside VS Code. You can also see pictures of these steps in many ESP32 + PlatformIO tutorials, for example on the PlatformIO site: <https://platformio.org/install/ide?install=vscode>.
Now add PlatformIO inside VS Code. You can also see pictures of these steps in many ESP32 + PlatformIO tutorials, for example on the PlatformIO site: https://platformio.org/install/ide?install=vscode.
Steps (based on common PlatformIO + VS Code ESP32 guides, adapted for this project):
@@ -216,7 +216,7 @@ cd OpenMQTTGateway
you should see main, lib, scripts, docs and other files.
If the clone fails, check your internet connection or a possible proxy. Then try again. If you are new to Git, the GitHub "Hello World" guide can also help: <https://docs.github.com/en/get-started/start-your-journey/hello-world>.
If the clone fails, check your internet connection or a possible proxy. Then try again. If you are new to Git, the GitHub "Hello World" guide can also help: https://docs.github.com/en/get-started/start-your-journey/hello-world.
@@ -224,7 +224,7 @@ If the clone fails, check your internet connection or a possible proxy. Then try
This section explains how to build and flash the firmware using Visual Studio Code with the PlatformIO extension.
If you know the classic Arduino IDE, you can think of PlatformIO as a more powerful alternative. It manages many boards, libraries, and environments for you. The official introduction is here: <https://docs.platformio.org/en/latest/what-is-platformio.html>.
If you know the classic Arduino IDE, you can think of PlatformIO as a more powerful alternative. It manages many boards, libraries, and environments for you. The official introduction is here: https://docs.platformio.org/en/latest/what-is-platformio.html.
You do **not** create a new PlatformIO project. This repository is already a PlatformIO project. You just open it.
@@ -268,7 +268,7 @@ In OpenMQTTGateway the structure is a bit different, because it is a large proje
**Check:** open platformio.ini in VS Code and scroll. You should see several [env:...] sections. This means PlatformIO understands the project.
If you want to know more about this file, see the PlatformIO docs about configuration: <https://docs.platformio.org/en/latest/projectconf/index.html>.
If you want to know more about this file, see the PlatformIO docs about configuration: https://docs.platformio.org/en/latest/projectconf/index.html.
### 4.3 Choose a PlatformIO environment
@@ -350,7 +350,7 @@ The Serial Monitor lets you see log messages from the firmware.
If the text is unreadable, check that the baud rate matches the value in platformio.ini (monitor_speed). In this project the default is usually 115200.
If you want to learn more about Serial Monitor problems, the PlatformIO docs have a short page: <https://docs.platformio.org/en/latest/core/userguide/device/cmd_monitor.html>.
If you want to learn more about Serial Monitor problems, the PlatformIO docs have a short page: https://docs.platformio.org/en/latest/core/userguide/device/cmd_monitor.html.
### 4.7 Run PlatformIO tests (optional)
@@ -387,7 +387,7 @@ This script calls other scripts:
The scripts are written for bash. On Windows always use **Git Bash** or **WSL** for them, not plain PowerShell or cmd.
If you are new to bash, a gentle introduction is here: <https://ubuntu.com/tutorials/command-line-for-beginners>.
If you are new to bash, a gentle introduction is here: https://ubuntu.com/tutorials/command-line-for-beginners.
### 5.1 Check that you are in the right place
@@ -493,14 +493,14 @@ Example direct call to ci_prepare_artifacts.sh:
This script:
- Copies firmware.bin, partitions.bin, bootloader.bin and other files from .pio/build/<env> into the output folder.
- Copies firmware.bin, partitions.bin, bootloader.bin and other files from `.pio/build/<env>` into the output folder.
- Renames them with the environment name, for example esp32dev-all-test-firmware.bin.
- Creates archives for the libraries used by that environment.
**Check:**
- The folder generated/toDeploy (or your custom output) exists.
- It contains files like <env>-firmware.bin and one or more *-libraries.tgz archives.
- It contains files like `<env>-firmware.bin` and one or more `*-libraries.tgz` archives.
If the script says the build directory is missing, run a firmware build first (section 5.3 or 5.4).
@@ -790,18 +790,18 @@ If any word in this guide is not clear, you can search it on the web or ask in t
Here is a list of useful links related to tools used in this project:
- **OpenMQTTGateway project**: <https://github.com/1technophile/OpenMQTTGateway>
- **PlatformIO main site**: <https://platformio.org/>
- **PlatformIO installation for VS Code**: <https://platformio.org/install/ide?install=vscode>
- **PlatformIO documentation**: <https://docs.platformio.org/>
- **Visual Studio Code documentation**: <https://code.visualstudio.com/docs>
- **Git official site**: <https://git-scm.com/>
- **Pro Git book (free)**: <https://git-scm.com/book/en/v2>
- **GitHub getting started**: <https://docs.github.com/en/get-started>
- **Node.js documentation**: <https://nodejs.org/en/docs>
- **npm documentation**: <https://docs.npmjs.com/>
- **VuePress documentation** (v1, similar to what this project uses): <https://v1.vuepress.vuejs.org/>
- **MQTT introduction (HiveMQ)**: <https://www.hivemq.com/mqtt-essentials/>
- **OpenMQTTGateway project**: https://github.com/1technophile/OpenMQTTGateway
- **PlatformIO main site**: https://platformio.org/
- **PlatformIO installation for VS Code**: https://platformio.org/install/ide?install=vscode
- **PlatformIO documentation**: https://docs.platformio.org/
- **Visual Studio Code documentation**: https://code.visualstudio.com/docs
- **Git official site**: https://git-scm.com/
- **Pro Git book (free)**: https://git-scm.com/book/en/v2
- **GitHub getting started**: https://docs.github.com/en/get-started
- **Node.js documentation**: https://nodejs.org/en/docs
- **npm documentation**: https://docs.npmjs.com/
- **VuePress documentation** (v1, similar to what this project uses): https://v1.vuepress.vuejs.org/
- **MQTT introduction (HiveMQ)**: https://www.hivemq.com/mqtt-essentials/
You do **not** need to read all of them now. Keep this list as a bookmark and come back when you are curious or stuck.
+1 -1
View File
@@ -1,5 +1,5 @@
---
pageClass: table-generated-page
aside: false
---
# Boards
+1 -5
View File
@@ -8,9 +8,7 @@ Added to that is an overview of devices supported by OpenMQTTGateway:
OpenMQTTGateway can support a wide range of 433mhz/315mhz devices, all the ones with SC5262 / SC5272, HX2262 / HX2272, PT2262 / PT2272, EV1527, RT1527, FP1527, HS1527 chipsets are supported by the RF gateway. Added to that RF2 support Kaku and Pilight an [huge list](https://wiki.pilight.org/devices).
Note that for the moment RF, RF2 and Pilight can not be activated on the same boards together.
![boards](../img/OpenMQTTGateway_devices_rf1.png ':size=250%')
![boards](../img/OpenMQTTGateway_devices_rf2.png ':size=250%')
![boards](../img/OpenMQTTGateway_devices_rf3.png ':size=250%')
## For BLE devices
OpenMQTTGateway is able to scan all the BLE devices that advertise their data so as to do presence detection.
@@ -23,8 +21,6 @@ OpenMQTTGateway currently supports the decoding of [more than 100 Bluetooth devi
- (3) See https://github.com/pvvx/ATC_MiThermometer
:::
![devices](../img/OpenMQTTGateway_devices_ble.png ':size=250%')
## For infrared IR devices
The list of supported devices for ESP is [here](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/SupportedProtocols.md), and [here](https://github.com/1technophile/OpenMQTTGateway/blob/6f73160d1421bebf2c1bbc9b8017978ff5b16520/main/config_IR.h#L123) for Arduino boards, as there is also the possibility of using raw and global cache (ESP) sending possibilities of this gateway is huge!
-7
View File
@@ -1,10 +1,3 @@
---
pageClass: table-generated-page
---
<FlashEnvironmentSelector
boardsUrl="/boards-info.json"
firmwareBaseUrl="firmware_build/" />
File renamed without changes.
+1 -2
View File
@@ -1,5 +1,5 @@
---
pageClass: table-generated-page
aside: false
---
## Select your firmware
@@ -32,4 +32,3 @@ Support open-source development through sponsorship and gain exclusive access to
<iframe src="https://github.com/sponsors/theengs/button" title="Sponsor Theengs" height="32" width="228" style="border: 0; border-radius: 6px;"></iframe>
</div>
+5 -5
View File
@@ -23,9 +23,9 @@ const {
// Base directories
const ROOT_DIR = path.join(__dirname, '..');
const DOCS_DIR = path.join(ROOT_DIR, 'docs');
const VUEPRESS_DIR = path.join(DOCS_DIR, '.vuepress');
const PUBLIC_DIR = path.join(VUEPRESS_DIR, 'public');
const COMPONENTS_DIR = path.join(VUEPRESS_DIR, 'components');
const VITEPRESS_DIR = path.join(DOCS_DIR, '.vitepress');
const PUBLIC_DIR = path.join(VITEPRESS_DIR, 'public');
const COMPONENTS_DIR = path.join(VITEPRESS_DIR, 'components');
const ARTIFACTS_DIR = path.join(ROOT_DIR, 'generated', 'artifacts');
// Feature-specific directories
@@ -34,8 +34,8 @@ const FIRMWARE_BUILD_DIR = path.join(PUBLIC_DIR, 'firmware_build');
const BOARDS_INFO_FILE = path.join(PUBLIC_DIR, 'boards-info.json');
// Configuration files
const DEFAULTS_CONFIG_PATH = path.join(VUEPRESS_DIR, 'defaults.json');
const META_CONFIG_PATH = path.join(VUEPRESS_DIR, 'meta.json');
const DEFAULTS_CONFIG_PATH = path.join(VITEPRESS_DIR, 'defaults.json');
const META_CONFIG_PATH = path.join(VITEPRESS_DIR, 'meta.json');
+2973 -27332
View File
File diff suppressed because it is too large. Load diff
+11 -10
View File
@@ -2,19 +2,20 @@
"scripts": {
"gen:ssl": "node scripts/ensure_ssl_certs.js",
"gen:metadata": "node docsgen/gen_wu.js",
"gen:site:only": "vuepress build docs",
"docs:dev": "npm run gen:metadata && vuepress dev docs",
"docs:build": "npm run gen:metadata && npm run gen:site:only",
"site:preview": " npm run gen:ssl && node scripts/preview_site.js",
"site:preview:full": "npm run docs:build && npm run gen:ssl && node scripts/preview_site.js"
},
"devDependencies": {
"vuepress": "^1.9.10"
"gen:commonconfig": "curl -sSf -o docs/.vitepress/public/commonConfig.js https://www.theengs.io/commonConfig.js",
"docs:dev": "npm run gen:commonconfig && npm run gen:metadata && vitepress dev docs",
"docs:build": "npm run gen:commonconfig && npm run gen:metadata && vitepress build docs",
"docs:preview": "vitepress preview docs",
"site:preview": " npm run gen:ssl && node scripts/preview_site.js"
},
"dependencies": {
"ini": "^4.1.1",
"markdown-table": "^3.0.3",
"mime-types": "^2.1.35",
"vuepress-plugin-sitemap": "^2.3.1"
"mime-types": "^2.1.35"
},
"devDependencies": {
"medium-zoom": "^1.1.0",
"vitepress": "^1.6.4",
"vue": "^3.5.31"
}
}
+4 -4
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# CI/CD Site/Documentation Builder
# This script builds and deploys VuePress documentation with version management.
# This script builds and deploys VitePress documentation with version management.
# Usage: ./scripts/ci_site.sh [OPTIONS] - Specify options for the script execution.
set -euo pipefail
@@ -91,7 +91,7 @@ download_common_config() {
log_info "Downloading common configuration..."
local config_url="https://www.theengs.io/commonConfig.js"
local config_dest="${DOCS_DIR}/.vuepress/public/commonConfig.js"
local config_dest="${DOCS_DIR}/.vitepress/public/commonConfig.js"
local curl_opts="-sSf"
# Optionally disable TLS verification if explicitly requested
@@ -121,7 +121,7 @@ create_configuration_files() {
## Create a meta.json file on config folder
local meta_file="${DOCS_DIR}/.vuepress/meta.json"
local meta_file="${DOCS_DIR}/.vitepress/meta.json"
cat > "$meta_file" <<EOF
{
"title": "Theengs OpenMQTTGateway",
@@ -184,7 +184,7 @@ usage() {
cat << EOF
Usage: $0 [OPTIONS]
Build and deploy OpenMQTTGateway documentation with VuePress.
Build and deploy OpenMQTTGateway documentation with VitePress.
Generates site structure and meta.json file.
OPTIONS: