├── env.d.ts ├── .vscode └── extensions.json ├── dist ├── favicon.ico ├── assets │ ├── xknx_logo_inverted-1da82253.png │ └── index-04e56ed8.css └── index.html ├── public ├── favicon.ico └── index.html ├── src ├── assets │ ├── xknx_logo_inverted.png │ ├── main.css │ └── base.css ├── main.ts ├── views │ ├── HomeView.vue │ └── AboutView.vue ├── router │ └── index.ts ├── App.vue └── components │ └── ConfigurationImporter.vue ├── .prettierrc.json ├── tsconfig.vitest.json ├── tsconfig.node.json ├── tsconfig.json ├── tsconfig.app.json ├── .eslintrc.cjs ├── index.html ├── .gitignore ├── vite.config.ts ├── vitest.config.ts ├── package.json └── README.md /env.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JensVanhooydonck/xknx-configuration-importer/HEAD/dist/favicon.ico -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JensVanhooydonck/xknx-configuration-importer/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/xknx_logo_inverted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JensVanhooydonck/xknx-configuration-importer/HEAD/src/assets/xknx_logo_inverted.png -------------------------------------------------------------------------------- /dist/assets/xknx_logo_inverted-1da82253.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JensVanhooydonck/xknx-configuration-importer/HEAD/dist/assets/xknx_logo_inverted-1da82253.png -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "printWidth": 100, 7 | "trailingComma": "none" 8 | } -------------------------------------------------------------------------------- /tsconfig.vitest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "exclude": [], 4 | "compilerOptions": { 5 | "composite": true, 6 | "lib": [], 7 | "types": ["node", "jsdom"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.node.json", 3 | "include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "playwright.config.*"], 4 | "compilerOptions": { 5 | "composite": true, 6 | "experimentalDecorators": true, 7 | "types": ["node"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createPinia } from 'pinia' 3 | 4 | import App from './App.vue' 5 | import router from './router' 6 | 7 | import './assets/main.css' 8 | 9 | const app = createApp(App) 10 | 11 | app.use(createPinia()) 12 | app.use(router) 13 | 14 | app.mount('#app') 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "compilerOptions": { 4 | "experimentalDecorators": true 5 | }, 6 | "references": [ 7 | { 8 | "path": "./tsconfig.node.json" 9 | }, 10 | { 11 | "path": "./tsconfig.app.json" 12 | }, 13 | { 14 | "path": "./tsconfig.vitest.json" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.web.json", 3 | "include": ["../env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "experimentalDecorators": true, 8 | "baseUrl": ".", 9 | "paths": { 10 | "@/*": ["./src/*"] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require('@rushstack/eslint-patch/modern-module-resolution') 3 | 4 | module.exports = { 5 | root: true, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-typescript', 10 | '@vue/eslint-config-prettier/skip-formatting' 11 | ], 12 | parserOptions: { 13 | ecmaVersion: 'latest' 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | xknx configuration importer 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/views/HomeView.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | 3 | import { defineConfig } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | import vueJsx from '@vitejs/plugin-vue-jsx' 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | plugins: [vue(), vueJsx()], 10 | base: '/xknx-configuration-importer/', 11 | resolve: { 12 | alias: { 13 | '@': fileURLToPath(new URL('./src', import.meta.url)) 14 | } 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url' 2 | import { mergeConfig } from 'vite' 3 | import { configDefaults, defineConfig } from 'vitest/config' 4 | import viteConfig from './vite.config' 5 | 6 | export default mergeConfig( 7 | viteConfig, 8 | defineConfig({ 9 | test: { 10 | environment: 'jsdom', 11 | exclude: [...configDefaults.exclude, 'e2e/*'], 12 | root: fileURLToPath(new URL('./', import.meta.url)) 13 | } 14 | }) 15 | ) 16 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | import HomeView from '../views/HomeView.vue' 3 | import AboutView from '../views/AboutView.vue' 4 | 5 | const router = createRouter({ 6 | history: createWebHistory('/xknx-configuration-importer/'), 7 | routes: [ 8 | { 9 | path: '/', 10 | name: 'home', 11 | component: HomeView 12 | }, 13 | { 14 | path: '/about', 15 | name: 'about', 16 | component: AboutView 17 | } 18 | ] 19 | }) 20 | 21 | export default router 22 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | xknx configuration importer 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/views/AboutView.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/assets/main.css: -------------------------------------------------------------------------------- 1 | @import './base.css'; 2 | 3 | #app { 4 | max-width: 1280px; 5 | margin: 0 auto; 6 | padding: 2rem; 7 | 8 | font-weight: normal; 9 | } 10 | 11 | a { 12 | text-decoration: none; 13 | color: rgb(209, 111, 0); 14 | transition: 0.4s; 15 | } 16 | 17 | @media (hover: hover) { 18 | a:hover { 19 | background-color: hsla(34, 100%, 37%, 0.2); 20 | } 21 | } 22 | 23 | @media (min-width: 1024px) { 24 | body { 25 | display: flex; 26 | place-items: center; 27 | } 28 | 29 | #app { 30 | display: grid; 31 | grid-template-columns: 1fr 1fr; 32 | padding: 0 2rem; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | xknx-configuration-importer 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xknx-configuration-importer", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "run-p type-check build-only", 8 | "preview": "vite preview", 9 | "test:unit": "vitest", 10 | "build-only": "vite build", 11 | "type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false", 12 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", 13 | "format": "prettier --write src/" 14 | }, 15 | "dependencies": { 16 | "fast-xml-parser": "^4.1.3", 17 | "js-yaml": "^4.1.0", 18 | "jszip": "^3.10.1", 19 | "pinia": "^2.0.32", 20 | "sass": "^1.61.0", 21 | "vue": "^3.2.47", 22 | "vue-facing-decorator": "^2.1.19", 23 | "vue-router": "^4.1.6" 24 | }, 25 | "devDependencies": { 26 | "@rushstack/eslint-patch": "^1.2.0", 27 | "@types/js-yaml": "^4.0.5", 28 | "@types/jsdom": "^21.1.0", 29 | "@types/node": "^18.14.2", 30 | "@vitejs/plugin-vue": "^4.0.0", 31 | "@vitejs/plugin-vue-jsx": "^3.0.0", 32 | "@vue/eslint-config-prettier": "^7.1.0", 33 | "@vue/eslint-config-typescript": "^11.0.2", 34 | "@vue/test-utils": "^2.3.0", 35 | "@vue/tsconfig": "^0.1.3", 36 | "eslint": "^8.34.0", 37 | "eslint-plugin-vue": "^9.9.0", 38 | "jsdom": "^21.1.0", 39 | "npm-run-all": "^4.1.5", 40 | "prettier": "^2.8.4", 41 | "typescript": "~4.8.4", 42 | "vite": "^4.1.4", 43 | "vitest": "^0.29.1", 44 | "vue-tsc": "^1.2.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 19 | 20 | 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xknx-configuration-importer 2 | 3 | This project is created to convert a knxproj file created by ETS5 or ETS6 to a xKNX-yaml file. This uses the so called `Functions` in ETS to automaticly convert a group of groupaddresses to the correct group in xKNX. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). 8 | 9 | ## Type Support for `.vue` Imports in TS 10 | 11 | TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types. 12 | 13 | If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: 14 | 15 | 1. Disable the built-in TypeScript Extension 16 | 1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette 17 | 2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` 18 | 2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. 19 | 20 | ## Customize configuration 21 | 22 | See [Vite Configuration Reference](https://vitejs.dev/config/). 23 | 24 | ## Project Setup 25 | 26 | ```sh 27 | npm install 28 | ``` 29 | 30 | ### Compile and Hot-Reload for Development 31 | 32 | ```sh 33 | npm run dev 34 | ``` 35 | 36 | ### Type-Check, Compile and Minify for Production 37 | 38 | ```sh 39 | npm run build 40 | ``` 41 | 42 | ### Run Unit Tests with [Vitest](https://vitest.dev/) 43 | 44 | ```sh 45 | npm run test:unit 46 | ``` 47 | 48 | ### Lint with [ESLint](https://eslint.org/) 49 | 50 | ```sh 51 | npm run lint 52 | ``` 53 | -------------------------------------------------------------------------------- /src/assets/base.css: -------------------------------------------------------------------------------- 1 | /* color palette from */ 2 | :root { 3 | --vt-c-white: #ffffff; 4 | --vt-c-white-soft: #f8f8f8; 5 | --vt-c-white-mute: #f2f2f2; 6 | 7 | --vt-c-black: #181818; 8 | --vt-c-black-soft: #222222; 9 | --vt-c-black-mute: #282828; 10 | 11 | --vt-c-indigo: #2c3e50; 12 | 13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); 14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); 15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); 16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); 17 | 18 | --vt-c-text-light-1: var(--vt-c-indigo); 19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66); 20 | --vt-c-text-dark-1: var(--vt-c-white); 21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); 22 | } 23 | 24 | /* semantic color variables for this project */ 25 | :root { 26 | --color-background: var(--vt-c-white); 27 | --color-background-soft: var(--vt-c-white-soft); 28 | --color-background-mute: var(--vt-c-white-mute); 29 | 30 | --color-border: var(--vt-c-divider-light-2); 31 | --color-border-hover: var(--vt-c-divider-light-1); 32 | 33 | --color-heading: var(--vt-c-text-light-1); 34 | --color-text: var(--vt-c-text-light-1); 35 | 36 | --section-gap: 160px; 37 | } 38 | 39 | @media (prefers-color-scheme: dark) { 40 | :root { 41 | --color-background: var(--vt-c-black); 42 | --color-background-soft: var(--vt-c-black-soft); 43 | --color-background-mute: var(--vt-c-black-mute); 44 | 45 | --color-border: var(--vt-c-divider-dark-2); 46 | --color-border-hover: var(--vt-c-divider-dark-1); 47 | 48 | --color-heading: var(--vt-c-text-dark-1); 49 | --color-text: var(--vt-c-text-dark-2); 50 | } 51 | } 52 | 53 | *, 54 | *::before, 55 | *::after { 56 | box-sizing: border-box; 57 | margin: 0; 58 | position: relative; 59 | font-weight: normal; 60 | } 61 | 62 | body { 63 | min-height: 100vh; 64 | color: var(--color-text); 65 | background: var(--color-background); 66 | transition: color 0.5s, background-color 0.5s; 67 | line-height: 1.6; 68 | font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, 69 | Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 70 | font-size: 15px; 71 | text-rendering: optimizeLegibility; 72 | -webkit-font-smoothing: antialiased; 73 | -moz-osx-font-smoothing: grayscale; 74 | } 75 | -------------------------------------------------------------------------------- /dist/assets/index-04e56ed8.css: -------------------------------------------------------------------------------- 1 | header[data-v-8cf9f3d9]{line-height:1.5;max-height:100vh}.logo[data-v-8cf9f3d9]{display:block;margin:0 auto 2rem}nav[data-v-8cf9f3d9]{width:100%;font-size:12px;text-align:center;margin-top:2rem}nav a.router-link-exact-active[data-v-8cf9f3d9]{color:var(--color-text)}nav a.router-link-exact-active[data-v-8cf9f3d9]:hover{background-color:transparent}nav a[data-v-8cf9f3d9]{display:inline-block;padding:0 1rem;border-left:1px solid var(--color-border)}nav a[data-v-8cf9f3d9]:first-of-type{border:0}@media (min-width: 1024px){header[data-v-8cf9f3d9]{display:flex;place-items:center;padding-right:calc(var(--section-gap) / 2)}.logo[data-v-8cf9f3d9]{margin:0 2rem 0 0}header .wrapper[data-v-8cf9f3d9]{display:flex;place-items:flex-start;flex-wrap:wrap}nav[data-v-8cf9f3d9]{text-align:left;margin-left:-1rem;font-size:1rem;padding:1rem 0;margin-top:1rem}}main[data-v-31b2d0d7]{padding:25px}.container[data-v-31b2d0d7]{display:flex;flex-direction:column;align-items:center}.container .info[data-v-31b2d0d7]{padding:10px}.container .info span[data-v-31b2d0d7]{color:orange;font-style:italic}.container .process[data-v-31b2d0d7],.container .buttons[data-v-31b2d0d7]{padding:10px}.container .process button[data-v-31b2d0d7],.container .process input[data-v-31b2d0d7],.container .buttons button[data-v-31b2d0d7],.container .buttons input[data-v-31b2d0d7]{border:1px solid transparent;padding:5px 10px;color:#d16f00;background:unset;transition:.4s;cursor:pointer}.container .process button[data-v-31b2d0d7]:hover,.container .process input[data-v-31b2d0d7]:hover,.container .buttons button[data-v-31b2d0d7]:hover,.container .buttons input[data-v-31b2d0d7]:hover{border:1px solid orange;color:#fff;background-color:#d16f0080;transition:.4s}.container .buttons[data-v-31b2d0d7]{width:100%;display:flex;flex-direction:row;justify-content:space-around}main[data-v-98c0d28b]{padding:25px}span[data-v-98c0d28b]{color:orange;font-style:italic}:root{--vt-c-white: #ffffff;--vt-c-white-soft: #f8f8f8;--vt-c-white-mute: #f2f2f2;--vt-c-black: #181818;--vt-c-black-soft: #222222;--vt-c-black-mute: #282828;--vt-c-indigo: #2c3e50;--vt-c-divider-light-1: rgba(60, 60, 60, .29);--vt-c-divider-light-2: rgba(60, 60, 60, .12);--vt-c-divider-dark-1: rgba(84, 84, 84, .65);--vt-c-divider-dark-2: rgba(84, 84, 84, .48);--vt-c-text-light-1: var(--vt-c-indigo);--vt-c-text-light-2: rgba(60, 60, 60, .66);--vt-c-text-dark-1: var(--vt-c-white);--vt-c-text-dark-2: rgba(235, 235, 235, .64)}:root{--color-background: var(--vt-c-white);--color-background-soft: var(--vt-c-white-soft);--color-background-mute: var(--vt-c-white-mute);--color-border: var(--vt-c-divider-light-2);--color-border-hover: var(--vt-c-divider-light-1);--color-heading: var(--vt-c-text-light-1);--color-text: var(--vt-c-text-light-1);--section-gap: 160px}@media (prefers-color-scheme: dark){:root{--color-background: var(--vt-c-black);--color-background-soft: var(--vt-c-black-soft);--color-background-mute: var(--vt-c-black-mute);--color-border: var(--vt-c-divider-dark-2);--color-border-hover: var(--vt-c-divider-dark-1);--color-heading: var(--vt-c-text-dark-1);--color-text: var(--vt-c-text-dark-2)}}*,*:before,*:after{box-sizing:border-box;margin:0;position:relative;font-weight:400}body{min-height:100vh;color:var(--color-text);background:var(--color-background);transition:color .5s,background-color .5s;line-height:1.6;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:15px;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#app{max-width:1280px;margin:0 auto;padding:2rem;font-weight:400}a{text-decoration:none;color:#d16f00;transition:.4s}@media (hover: hover){a:hover{background-color:#bd6b0033}}@media (min-width: 1024px){body{display:flex;place-items:center}#app{display:grid;grid-template-columns:1fr 1fr;padding:0 2rem}} 2 | -------------------------------------------------------------------------------- /src/components/ConfigurationImporter.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 339 | 340 | 341 | --------------------------------------------------------------------------------