From b034197a931946c3cc2fdb331e12a0c5dd37edc0 Mon Sep 17 00:00:00 2001 From: Adrian Jagielak Date: Mon, 28 Jul 2025 14:18:42 +0200 Subject: [PATCH] Add support for pairing new devices --- README.md | 15 +- futurehome/CHANGELOG.md | 1 + futurehome/README.md | 15 +- futurehome/config.yaml | 28 +- futurehome/package-lock.json | 284 +++++++++++++++++- futurehome/package.json | 5 +- .../rootfs/etc/services.d/futurehome/run | 6 +- futurehome/src/admin.ts | 58 ---- futurehome/src/fimp/fimp.ts | 10 +- futurehome/src/fimp/vinculum.ts | 14 + futurehome/src/ha/admin.ts | 276 +++++++++++++++++ futurehome/src/ha/ha_device_config.ts | 86 ++++++ .../ha/mqtt_components/alarm_control_panel.ts | 12 - .../src/ha/mqtt_components/binary_sensor.ts | 6 - futurehome/src/ha/mqtt_components/button.ts | 12 - futurehome/src/ha/mqtt_components/climate.ts | 12 - futurehome/src/ha/mqtt_components/cover.ts | 6 - .../src/ha/mqtt_components/device_tracker.ts | 12 - futurehome/src/ha/mqtt_components/event.ts | 12 - futurehome/src/ha/mqtt_components/fan.ts | 12 - futurehome/src/ha/mqtt_components/light.ts | 12 - futurehome/src/ha/mqtt_components/lock.ts | 12 - futurehome/src/ha/mqtt_components/notify.ts | 12 - futurehome/src/ha/mqtt_components/scene.ts | 12 - futurehome/src/ha/mqtt_components/sensor.ts | 12 - futurehome/src/ha/mqtt_components/siren.ts | 12 - futurehome/src/ha/mqtt_components/switch.ts | 12 - futurehome/src/ha/mqtt_components/vacuum.ts | 12 - .../src/ha/mqtt_components/water_heater.ts | 12 - futurehome/src/ha/publish_device.ts | 90 +----- futurehome/src/index.ts | 72 ++--- futurehome/src/thingsplex/thingsplex.ts | 104 +++++++ futurehome/translations/be.yaml | 21 ++ futurehome/translations/by.yaml | 21 ++ futurehome/translations/cs.yaml | 21 ++ futurehome/translations/cz.yaml | 21 ++ futurehome/translations/da.yaml | 24 +- futurehome/translations/de.yaml | 22 +- futurehome/translations/dk.yaml | 24 +- futurehome/translations/ee.yaml | 21 ++ futurehome/translations/en.yaml | 14 +- futurehome/translations/et.yaml | 21 ++ futurehome/translations/fi.yaml | 26 +- futurehome/translations/is.yaml | 26 +- futurehome/translations/lt.yaml | 21 ++ futurehome/translations/lv.yaml | 21 ++ futurehome/translations/nb.yaml | 24 +- futurehome/translations/nn.yaml | 24 +- futurehome/translations/no.yaml | 24 +- futurehome/translations/pl.yaml | 26 +- futurehome/translations/ru.yaml | 21 ++ futurehome/translations/se.yaml | 22 +- futurehome/translations/sk.yaml | 21 ++ futurehome/translations/sv.yaml | 20 +- futurehome/translations/ua.yaml | 21 ++ futurehome/translations/uk.yaml | 21 ++ 56 files changed, 1272 insertions(+), 512 deletions(-) delete mode 100644 futurehome/src/admin.ts create mode 100644 futurehome/src/fimp/vinculum.ts create mode 100644 futurehome/src/ha/admin.ts create mode 100644 futurehome/src/ha/ha_device_config.ts create mode 100644 futurehome/src/thingsplex/thingsplex.ts create mode 100644 futurehome/translations/be.yaml create mode 100644 futurehome/translations/by.yaml create mode 100644 futurehome/translations/cs.yaml create mode 100644 futurehome/translations/cz.yaml create mode 100644 futurehome/translations/ee.yaml create mode 100644 futurehome/translations/et.yaml create mode 100644 futurehome/translations/lt.yaml create mode 100644 futurehome/translations/lv.yaml create mode 100644 futurehome/translations/ru.yaml create mode 100644 futurehome/translations/sk.yaml create mode 100644 futurehome/translations/ua.yaml create mode 100644 futurehome/translations/uk.yaml diff --git a/README.md b/README.md index 11eeebe..9a70d7d 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,19 @@ This add-on: * Fetches all device metadata from the Futurehome hub and maps them to Home Assistant devices/entities. * Fetches and updates device states and availability in real time. * Supports interaction with devices comparable to the official Futurehome app. +* Supports pairing and unpairing of devices. ## Installation +todo: rewrite the installation instructions + 1. In the Futurehome app, go to Settings > My household > Smarthub and enable Local API. -2. (Optional but highly recommended) Block the hub’s internet (WAN) access in your router settings to prevent future firmware updates. -3. In Home Assistant, enable the **MQTT** integration. -4. [Add this add-on repository to Home Assistant](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fadrianjagielak%2Fhome-assistant-futurehome) and refresh the page. -5. Open the **Add-on Store** and search for "Futurehome". -6. Install, configure, and start the Futurehome add-on. +2. (Optional) In the Futurehome app go to Settings > ??? > ??? and install Thingsplex integration (Needed for support of pairing new devices). +3. (Optional but highly recommended) Block the hub’s internet (WAN) access in your router settings to prevent future firmware updates. +4. In Home Assistant, enable the **MQTT** integration. +5. [Add this add-on repository to Home Assistant](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fadrianjagielak%2Fhome-assistant-futurehome) and refresh the page. +6. Open the **Add-on Store** and search for "Futurehome". +7. Install, configure, and start the Futurehome add-on. # Futurehome Device Services Compatibility Chart @@ -31,7 +35,6 @@ Devices commonly consist of multiple services: for example, a presence sensor mi Some services are more common than others; some are deprecated entirely. diff --git a/futurehome/CHANGELOG.md b/futurehome/CHANGELOG.md index 0709d0a..e007c0c 100644 --- a/futurehome/CHANGELOG.md +++ b/futurehome/CHANGELOG.md @@ -3,6 +3,7 @@ ## 0.1.8 (28.07.2025) - Added logo. +- Added support for pairing new devices. ## 0.1.7 (26.07.2025) diff --git a/futurehome/README.md b/futurehome/README.md index a054cc1..457ef42 100644 --- a/futurehome/README.md +++ b/futurehome/README.md @@ -11,15 +11,19 @@ This add-on: * Fetches all device metadata from the Futurehome hub and maps them to Home Assistant devices/entities. * Fetches and updates device states and availability in real time. * Supports interaction with devices comparable to the official Futurehome app. +* Supports pairing and unpairing of devices. ## Installation +todo: rewrite the installation instructions + 1. In the Futurehome app, go to Settings > My household > Smarthub and enable Local API. -2. (Optional but highly recommended) Block the hub’s internet (WAN) access in your router settings to prevent future firmware updates. -3. In Home Assistant, enable the **MQTT** integration. -4. [Add this add-on repository to Home Assistant](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fadrianjagielak%2Fhome-assistant-futurehome) and refresh the page. -5. Open the **Add-on Store** and search for "Futurehome". -6. Install, configure, and start the Futurehome add-on. +2. (Optional) In the Futurehome app go to Settings > ??? > ??? and install Thingsplex integration (Needed for support of pairing new devices). +3. (Optional but highly recommended) Block the hub’s internet (WAN) access in your router settings to prevent future firmware updates. +4. In Home Assistant, enable the **MQTT** integration. +5. [Add this add-on repository to Home Assistant](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fadrianjagielak%2Fhome-assistant-futurehome) and refresh the page. +6. Open the **Add-on Store** and search for "Futurehome". +7. Install, configure, and start the Futurehome add-on. # Futurehome Device Services Compatibility Chart @@ -30,7 +34,6 @@ Devices commonly consist of multiple services: for example, a presence sensor mi Some services are more common than others; some are deprecated entirely. diff --git a/futurehome/config.yaml b/futurehome/config.yaml index fbacf90..ad67bdd 100644 --- a/futurehome/config.yaml +++ b/futurehome/config.yaml @@ -1,9 +1,9 @@ # https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config name: Futurehome -version: "0.1.8" +version: '0.1.8' slug: futurehome description: Local Futurehome Smarthub integration -url: "https://github.com/adrianjagielak/home-assistant-futurehome" +url: 'https://github.com/adrianjagielak/home-assistant-futurehome' arch: - armhf - armv7 @@ -13,20 +13,24 @@ arch: init: false services: - - "mqtt:need" + - 'mqtt:need' options: - hub_ip: "" - username: "" - password: "" + hub_ip: '' + fh_username: '' + fh_password: '' + tp_username: '' + tp_password: '' demo_mode: false show_debug_log: false schema: - hub_ip: "str?" - username: "str?" - password: "str?" - demo_mode: "bool?" - show_debug_log: "bool?" + hub_ip: 'str?' + fh_username: 'str?' + fh_password: 'str?' + tp_username: 'str?' + tp_password: 'str?' + demo_mode: 'bool?' + show_debug_log: 'bool?' -image: "ghcr.io/adrianjagielak/{arch}-home-assistant-futurehome" +image: 'ghcr.io/adrianjagielak/{arch}-home-assistant-futurehome' diff --git a/futurehome/package-lock.json b/futurehome/package-lock.json index 3ed9a2c..403435e 100644 --- a/futurehome/package-lock.json +++ b/futurehome/package-lock.json @@ -9,12 +9,15 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "axios": "^1.11.0", "mqtt": "^5.13.3", - "uuid": "^11.1.0" + "uuid": "^11.1.0", + "ws": "^8.18.3" }, "devDependencies": { "@eslint/js": "^9.31.0", "@types/node": "^24.0.15", + "@types/ws": "^8.18.1", "@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/parser": "^8.38.0", "eslint": "^9.31.0", @@ -742,6 +745,23 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -835,6 +855,19 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -898,6 +931,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commist": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", @@ -979,6 +1024,29 @@ "dev": true, "license": "MIT" }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -993,6 +1061,51 @@ "dev": true, "license": "MIT" }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1330,6 +1443,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -1347,6 +1480,68 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", @@ -1397,6 +1592,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -1414,6 +1621,45 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/help-me": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", @@ -1665,6 +1911,15 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1689,6 +1944,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", @@ -1945,6 +2221,12 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/futurehome/package.json b/futurehome/package.json index 2bceb70..54bc268 100644 --- a/futurehome/package.json +++ b/futurehome/package.json @@ -9,12 +9,15 @@ "start": "node dist/index.js" }, "dependencies": { + "axios": "^1.11.0", "mqtt": "^5.13.3", - "uuid": "^11.1.0" + "uuid": "^11.1.0", + "ws": "^8.18.3" }, "devDependencies": { "@eslint/js": "^9.31.0", "@types/node": "^24.0.15", + "@types/ws": "^8.18.1", "@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/parser": "^8.38.0", "eslint": "^9.31.0", diff --git a/futurehome/rootfs/etc/services.d/futurehome/run b/futurehome/rootfs/etc/services.d/futurehome/run index 7343af1..6d74cc8 100755 --- a/futurehome/rootfs/etc/services.d/futurehome/run +++ b/futurehome/rootfs/etc/services.d/futurehome/run @@ -6,8 +6,10 @@ set -e export FH_HUB_IP=$(bashio::config 'hub_ip') -export FH_USERNAME=$(bashio::config 'username') -export FH_PASSWORD=$(bashio::config 'password') +export FH_USERNAME=$(bashio::config 'fh_username') +export FH_PASSWORD=$(bashio::config 'fh_password') +export TP_USERNAME=$(bashio::config 'tp_username') +export TP_PASSWORD=$(bashio::config 'tp_password') export DEMO_MODE=$(bashio::config 'demo_mode') export SHOW_DEBUG_LOG=$(bashio::config 'show_debug_log') diff --git a/futurehome/src/admin.ts b/futurehome/src/admin.ts deleted file mode 100644 index 3b552a6..0000000 --- a/futurehome/src/admin.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import { IMqttClient } from './mqtt/interface'; - -export function exposeSmarthubTools( - ha: IMqttClient, - fimp: IMqttClient, - hubAddr = 'pt:j1/mt:cmd/rt:app/rn:zb/ad:1', -) { - const base = 'homeassistant/switch/fh_zb_pairing'; - const device = { - identifiers: ['futurehome_hub'], - name: 'Futurehome Hub', - model: 'Smarthub', - }; - - ha.publish( - `${base}/config`, - JSON.stringify({ - name: 'Zigbee Pairing', - uniq_id: 'fh_zb_pairing', - cmd_t: `${base}/set`, - stat_t: `${base}/state`, - device, - }), - { retain: true, qos: 2 }, - ); - - // // keep last known state locally - // let pairingOn = false; - - ha.subscribe(`${base}/set`); - ha.on('message', (topic, payload) => { - if (topic !== `${base}/set`) return; - const turnOn = payload.toString() === 'ON'; - - // // optimistic update so the UI flips instantly - // pairingOn = turnOn; - ha.publish(`${base}/state`, turnOn ? 'ON' : 'OFF', { - retain: true, - qos: 2, - }); - - // placeholder FIMP message – adjust to real API if different - fimp.publish( - hubAddr, - JSON.stringify({ - type: 'cmd.pairing_mode.set', - service: 'zigbee', - uid: uuidv4(), - val_t: 'str', - val: turnOn ? 'start' : 'stop', - }), - { qos: 1 }, - ); - }); - - // (optional) listen for hub-side confirmation and correct state here -} diff --git a/futurehome/src/fimp/fimp.ts b/futurehome/src/fimp/fimp.ts index fee5aa5..b0ac807 100644 --- a/futurehome/src/fimp/fimp.ts +++ b/futurehome/src/fimp/fimp.ts @@ -21,7 +21,7 @@ export type FimpResponse = { ver?: any; }; -type FimpValueType = +export type FimpValueType = | 'string' | 'int' | 'float' @@ -51,7 +51,7 @@ export async function sendFimpMsg({ cmd: string; val: unknown; val_t: FimpValueType; - props?: any; + props?: Record; timeoutMs?: number; }): Promise { const uid = uuidv4(); @@ -60,9 +60,9 @@ export async function sendFimpMsg({ corid: null, ctime: new Date().toISOString(), props: props, - resp_to: 'pt:j1/mt:rsp/rt:app/rn:ha-futurehome/ad:addon', + resp_to: 'pt:j1/mt:rsp/rt:cloud/rn:remote-client/ad:smarthome-app', serv: service, - src: 'ha-futurehome', + src: 'smarthome-app', tags: [], type: cmd, uid: uid, @@ -165,7 +165,7 @@ service: "${service}", uid: "${uid}", cmd: "${cmd}", val: ${JSON.stringify(val)}, -val_t: "${val_t}" +val_t: "${val_t}"${Object.entries(props).length > 0 ? `\nprops: ${JSON.stringify(props)}` : ''}${timeoutMs != 10000 ? `\ntimeoutMs: ${timeoutMs}` : ''} `); fimp?.publish(topic, message, { qos: 1 }); diff --git a/futurehome/src/fimp/vinculum.ts b/futurehome/src/fimp/vinculum.ts new file mode 100644 index 0000000..63dc232 --- /dev/null +++ b/futurehome/src/fimp/vinculum.ts @@ -0,0 +1,14 @@ +import { FimpResponse, sendFimpMsg } from './fimp'; + +export async function pollVinculum( + component: 'device' | 'house' | 'state', +): Promise { + return await sendFimpMsg({ + address: '/rt:app/rn:vinculum/ad:1', + service: 'vinculum', + cmd: 'cmd.pd7.request', + val: { cmd: 'get', component: null, param: { components: [component] } }, + val_t: 'object', + timeoutMs: 30000, + }); +} diff --git a/futurehome/src/ha/admin.ts b/futurehome/src/ha/admin.ts new file mode 100644 index 0000000..ac95501 --- /dev/null +++ b/futurehome/src/ha/admin.ts @@ -0,0 +1,276 @@ +import { v4 as uuidv4 } from 'uuid'; +import { IMqttClient } from '../mqtt/interface'; +import { CommandHandlers } from './publish_device'; +import { HaDeviceConfig } from './ha_device_config'; +import { ha } from './globals'; +import { log } from '../logger'; +import { abbreviateHaMqttKeys } from './abbreviate_ha_mqtt_keys'; +import { FimpResponse } from '../fimp/fimp'; +import { + connectThingsplexWebSocketAndSend, + loginToThingsplex, +} from '../thingsplex/thingsplex'; +import { delay } from '../utils'; +import { pollVinculum } from '../fimp/vinculum'; + +let initializedState = false; + +export function exposeSmarthubTools(parameters: { + hubId: string; + demoMode: boolean; + hubIp: string; + thingsplexUsername: string; + thingsplexPassword: string; +}): { + commandHandlers: CommandHandlers; +} { + // e.g. "homeassistant/device/futurehome_123456_hub" + const topicPrefix = `homeassistant/device/futurehome_${parameters.hubId}_hub`; + + if (!initializedState) { + ha?.publish( + `${topicPrefix}/inclusion_status/state`, + 'Ready to start inclusion', + { + retain: true, + qos: 2, + }, + ); + initializedState = true; + } + + const configTopic = `${topicPrefix}/config`; + + const deviceId = `futurehome_${parameters.hubId}_hub`; + + const config: HaDeviceConfig = { + device: { + identifiers: deviceId, + name: 'Futurehome Smarthub', + manufacturer: 'Futurehome', + model: 'Smarthub', + serial_number: parameters.hubId, + }, + origin: { + name: 'futurehome', + support_url: + 'https://github.com/adrianjagielak/home-assistant-futurehome', + }, + components: { + [`${deviceId}_zwave_startInclusion`]: { + unique_id: `${deviceId}_zwave_startInclusion`, + platform: 'button', + entity_category: 'diagnostic', + name: 'Start Z-Wave inclusion', + icon: 'mdi:z-wave', + command_topic: `${topicPrefix}/start_inclusion/command`, + payload_press: 'zwave', + availability_topic: `${topicPrefix}/inclusion_status/state`, + availability_template: `{% if value == "Done" or value == "Ready to start inclusion" or value == "Failed trying to start inclusion." or value == "Operation failed. The device can't be included." or value == "Device added successfully!" or value == "" %}online{% else %}offline{% endif %}`, + } as any, + [`${deviceId}_zigbee_startInclusion`]: { + unique_id: `${deviceId}_zigbee_startInclusion`, + platform: 'button', + entity_category: 'diagnostic', + name: 'Start ZigBee inclusion', + icon: 'mdi:zigbee', + command_topic: `${topicPrefix}/start_inclusion/command`, + payload_press: 'zigbee', + availability_topic: `${topicPrefix}/inclusion_status/state`, + availability_template: `{% if value == "Done" or value == "Ready to start inclusion" or value == "Failed trying to start inclusion." or value == "Operation failed. The device can't be included." or value == "Device added successfully!" or value == "" %}online{% else %}offline{% endif %}`, + } as any, + [`${deviceId}_stopInclusion`]: { + unique_id: `${deviceId}_stopInclusion`, + platform: 'button', + entity_category: 'diagnostic', + name: 'Stop inclusion', + icon: 'mdi:cancel', + command_topic: `${topicPrefix}/stop_inclusion/command`, + availability_topic: `${topicPrefix}/inclusion_status/state`, + availability_template: `{% if value == "Done" or value == "Ready to start inclusion" or value == "Failed trying to start inclusion." or value == "Operation failed. The device can't be included." or value == "Device added successfully!" or value == "Starting" or value == "Stopping" or value == "" %}offline{% else %}online{% endif %}`, + } as any, + [`${deviceId}_inclusion_status`]: { + unique_id: `${deviceId}_inclusion_status`, + platform: 'sensor', + entity_category: 'diagnostic', + device_class: 'enum', + name: 'Inclusion status', + state_topic: `${topicPrefix}/inclusion_status/state`, + }, + }, + qos: 2, + }; + + log.debug('Publishing Smarthub tools'); + ha?.publish(configTopic, JSON.stringify(abbreviateHaMqttKeys(config)), { + retain: true, + qos: 2, + }); + + const handlers: CommandHandlers = { + [`${topicPrefix}/start_inclusion/command`]: async (payload) => { + if (parameters.demoMode) { + ha?.publish(`${topicPrefix}/inclusion_status/state`, 'Starting', { + retain: true, + qos: 2, + }); + await delay(2000); + ha?.publish( + `${topicPrefix}/inclusion_status/state`, + 'Looking for device', + { + retain: true, + qos: 2, + }, + ); + await delay(2000); + ha?.publish( + `${topicPrefix}/inclusion_status/state`, + 'Demo mode, inclusion not supported', + { + retain: true, + qos: 2, + }, + ); + await delay(2000); + ha?.publish(`${topicPrefix}/inclusion_status/state`, 'Done', { + retain: true, + qos: 2, + }); + return; + } + + ha?.publish(`${topicPrefix}/inclusion_status/state`, 'Starting', { + retain: true, + qos: 2, + }); + try { + const token = await loginToThingsplex({ + host: parameters.hubIp, + username: parameters.thingsplexUsername, + password: parameters.thingsplexPassword, + }); + await connectThingsplexWebSocketAndSend( + { + host: parameters.hubIp, + token: token, + }, + [ + { + address: + payload == 'zwave' + ? 'pt:j1/mt:cmd/rt:ad/rn:zw/ad:1' + : 'pt:j1/mt:cmd/rt:ad/rn:zigbee/ad:1', + service: payload == 'zwave' ? 'zwave-ad' : 'zigbee', + cmd: 'cmd.thing.inclusion', + val: true, + val_t: 'bool', + }, + ], + ); + } catch (e) { + ha?.publish( + `${topicPrefix}/inclusion_status/state`, + 'Failed trying to start inclusion.', + { + retain: true, + qos: 2, + }, + ); + } + }, + [`${topicPrefix}/stop_inclusion/command`]: async (_payload) => { + ha?.publish(`${topicPrefix}/inclusion_status/state`, 'Stopping', { + retain: true, + qos: 2, + }); + if (parameters.demoMode) { + return; + } + + try { + const token = await loginToThingsplex({ + host: parameters.hubIp, + username: parameters.thingsplexUsername, + password: parameters.thingsplexPassword, + }); + await connectThingsplexWebSocketAndSend( + { + host: parameters.hubIp, + token: token, + }, + [ + { + address: 'pt:j1/mt:cmd/rt:ad/rn:zigbee/ad:1', + service: 'zigbee', + cmd: 'cmd.thing.inclusion', + val: false, + val_t: 'bool', + }, + { + address: 'pt:j1/mt:cmd/rt:ad/rn:zw/ad:1', + service: 'zwave-ad', + cmd: 'cmd.thing.inclusion', + val: false, + val_t: 'bool', + }, + ], + ); + ha?.publish(`${topicPrefix}/inclusion_status/state`, 'Done', { + retain: true, + qos: 2, + }); + } catch (e) { + ha?.publish( + `${topicPrefix}/inclusion_status/state`, + 'Failed trying to stop inclusion.', + { + retain: true, + qos: 2, + }, + ); + } + }, + }; + + return { commandHandlers: handlers }; +} + +export function handleInclusionStatusReport(hubId: string, msg: FimpResponse) { + const topicPrefix = `homeassistant/device/futurehome_${hubId}_hub`; + + let localizedStatus: string; + switch (msg.val) { + case 'ADD_NODE_STARTING': + case 'ADD_NODE_STARTED': + localizedStatus = 'Looking for device'; + break; + case 'ADD_NODE_ADDED': + case 'ADD_NODE_GET_NODE_INFO': + case 'ADD_NODE_PROTOCOL_DONE': + localizedStatus = 'Device added successfully!'; + pollVinculum('device').catch((e) => log.warn('Failed to request devices', e)); + pollVinculum('state').catch((e) => log.warn('Failed to request state', e)); + break; + case 'ADD_NODE_DONE': + localizedStatus = 'Done'; + pollVinculum('device').catch((e) => log.warn('Failed to request devices', e)); + pollVinculum('state').catch((e) => log.warn('Failed to request state', e)); + break; + case 'NET_NODE_INCL_CTRL_OP_FAILED': + localizedStatus = "Operation failed. The device can't be included."; + break; + default: + localizedStatus = msg.val; + log.warn(`Unknown inclusion status: ${msg.val}`); + break; + } + + ha?.publish(`${topicPrefix}/inclusion_status/state`, localizedStatus, { + retain: true, + qos: 2, + }); +} + +// todo exclusion? +// NET_NODE_REMOVE_FAILED", "Device can't be deleted diff --git a/futurehome/src/ha/ha_device_config.ts b/futurehome/src/ha/ha_device_config.ts new file mode 100644 index 0000000..58ec21b --- /dev/null +++ b/futurehome/src/ha/ha_device_config.ts @@ -0,0 +1,86 @@ +import { HaMqttComponent } from './mqtt_components/_component'; + +export type HaDeviceConfig = { + /** + * Information about the device this sensor is a part of to tie it into the [device registry](https://developers.home-assistant.io/docs/device_registry_index/). + * Only works when [`unique_id`](#unique_id) is set. + * At least one of identifiers or connections must be present to identify the device. + */ + device?: { + /** + * A link to the webpage that can manage the configuration of this device. + * Can be either an `http://`, `https://` or an internal `homeassistant://` URL. + */ + configuration_url?: string; + + /** + * A list of connections of the device to the outside world as a list of tuples `[connection_type, connection_identifier]`. + * For example the MAC address of a network interface: + * `"connections": [["mac", "02:5b:26:a8:dc:12"]]`. + */ + connections?: Array<[string, string]>; + + /** + * The hardware version of the device. + */ + hw_version?: string; + + /** + * A list of IDs that uniquely identify the device. + * For example a serial number. + */ + identifiers?: string | string[]; + + /** + * The manufacturer of the device. + */ + manufacturer?: string; + + /** + * The model of the device. + */ + model?: string; + + /** + * The model identifier of the device. + */ + model_id?: string; + + /** + * The name of the device. + */ + name?: string; + + /** + * The serial number of the device. + */ + serial_number?: string; + + /** + * Suggest an area if the device isn’t in one yet. + */ + suggested_area?: string; + + /** + * The firmware version of the device. + */ + sw_version?: string; + + /** + * Identifier of a device that routes messages between this device and Home Assistant. + * Examples of such devices are hubs, or parent devices of a sub-device. + * This is used to show device topology in Home Assistant. + */ + via_device?: string; + }; + origin: { + name: 'futurehome'; + support_url: 'https://github.com/adrianjagielak/home-assistant-futurehome'; + }; + components: { + [key: string]: HaMqttComponent; + }; + state_topic?: string; + availability_topic?: string; + qos: number; +}; diff --git a/futurehome/src/ha/mqtt_components/alarm_control_panel.ts b/futurehome/src/ha/mqtt_components/alarm_control_panel.ts index fe1e9c1..c98fb30 100644 --- a/futurehome/src/ha/mqtt_components/alarm_control_panel.ts +++ b/futurehome/src/ha/mqtt_components/alarm_control_panel.ts @@ -114,24 +114,12 @@ export interface AlarmControlPanelComponent extends BaseComponent { */ payload_arm_custom_bypass?: string; - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - /** * The payload to disarm your Alarm Panel. * Default: "DISARM" */ payload_disarm?: string; - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; - /** * The payload to trigger the alarm on your Alarm Panel. * Default: "TRIGGER" diff --git a/futurehome/src/ha/mqtt_components/binary_sensor.ts b/futurehome/src/ha/mqtt_components/binary_sensor.ts index edccf07..75c47f5 100644 --- a/futurehome/src/ha/mqtt_components/binary_sensor.ts +++ b/futurehome/src/ha/mqtt_components/binary_sensor.ts @@ -86,12 +86,6 @@ export interface BinarySensorComponent extends BaseComponent { */ off_delay?: number; - /** - * The string that represents the `online` state. - * Default: "online" - */ - payload_available?: string; - /** * The string that represents the `offline` state. * Default: "offline" diff --git a/futurehome/src/ha/mqtt_components/button.ts b/futurehome/src/ha/mqtt_components/button.ts index c3b0983..42ed3f0 100644 --- a/futurehome/src/ha/mqtt_components/button.ts +++ b/futurehome/src/ha/mqtt_components/button.ts @@ -51,16 +51,4 @@ export interface ButtonComponent extends BaseComponent { * Usage example can be found in [MQTT sensor](https://www.home-assistant.io/integrations/sensor.mqtt/#json-attributes-topic-configuration) documentation. */ json_attributes_topic?: string; - - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; } diff --git a/futurehome/src/ha/mqtt_components/climate.ts b/futurehome/src/ha/mqtt_components/climate.ts index 90e1fc5..05097dc 100644 --- a/futurehome/src/ha/mqtt_components/climate.ts +++ b/futurehome/src/ha/mqtt_components/climate.ts @@ -157,18 +157,6 @@ export interface ClimateComponent extends BaseComponent { */ optimistic?: boolean; - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; - /** * The payload sent to turn off the device. * Default: "OFF" diff --git a/futurehome/src/ha/mqtt_components/cover.ts b/futurehome/src/ha/mqtt_components/cover.ts index bec4c20..78cf03a 100644 --- a/futurehome/src/ha/mqtt_components/cover.ts +++ b/futurehome/src/ha/mqtt_components/cover.ts @@ -47,12 +47,6 @@ export interface CoverComponent extends BaseComponent { */ optimistic?: boolean; - /** - * The payload that represents the online state. - * Default: "online" - */ - payload_available?: string; - /** * The command payload that closes the cover. * Set to `null` to disable the close command. diff --git a/futurehome/src/ha/mqtt_components/device_tracker.ts b/futurehome/src/ha/mqtt_components/device_tracker.ts index ff88e89..1b3aa5d 100644 --- a/futurehome/src/ha/mqtt_components/device_tracker.ts +++ b/futurehome/src/ha/mqtt_components/device_tracker.ts @@ -83,16 +83,4 @@ export interface DeviceTrackerComponent extends BaseComponent { * Default: '"None"' */ payload_reset?: string; - - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; } diff --git a/futurehome/src/ha/mqtt_components/event.ts b/futurehome/src/ha/mqtt_components/event.ts index 7c0bfb3..bb82504 100644 --- a/futurehome/src/ha/mqtt_components/event.ts +++ b/futurehome/src/ha/mqtt_components/event.ts @@ -56,18 +56,6 @@ export interface EventComponent extends BaseComponent { */ name?: string; - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; - /** * Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) * to extract the value and render it to a valid JSON event payload. diff --git a/futurehome/src/ha/mqtt_components/fan.ts b/futurehome/src/ha/mqtt_components/fan.ts index b798ae9..674bba4 100644 --- a/futurehome/src/ha/mqtt_components/fan.ts +++ b/futurehome/src/ha/mqtt_components/fan.ts @@ -154,18 +154,6 @@ export interface FanComponent extends BaseComponent { */ json_attributes_topic?: string; - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; - /** * The payload that represents the stop state. * Default: "OFF" diff --git a/futurehome/src/ha/mqtt_components/light.ts b/futurehome/src/ha/mqtt_components/light.ts index 7b552c9..f612e5b 100644 --- a/futurehome/src/ha/mqtt_components/light.ts +++ b/futurehome/src/ha/mqtt_components/light.ts @@ -342,18 +342,6 @@ export interface LightComponent extends BaseComponent { */ min_mireds?: number; - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; - /** * The payload that represents the off state. * Default: "OFF" diff --git a/futurehome/src/ha/mqtt_components/lock.ts b/futurehome/src/ha/mqtt_components/lock.ts index 2d93932..d51030a 100644 --- a/futurehome/src/ha/mqtt_components/lock.ts +++ b/futurehome/src/ha/mqtt_components/lock.ts @@ -61,24 +61,12 @@ export interface LockComponent extends BaseComponent { */ optimistic?: boolean; - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - /** * The payload sent to the lock to lock it. * Default: "LOCK" */ payload_lock?: string; - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; - /** * The payload sent to the lock to unlock it. * Default: "UNLOCK" diff --git a/futurehome/src/ha/mqtt_components/notify.ts b/futurehome/src/ha/mqtt_components/notify.ts index 6a75538..d6f1128 100644 --- a/futurehome/src/ha/mqtt_components/notify.ts +++ b/futurehome/src/ha/mqtt_components/notify.ts @@ -40,16 +40,4 @@ export interface NotifyComponent extends BaseComponent { * Usage example can be found in [MQTT sensor](https://www.home-assistant.io/integrations/sensor.mqtt/#json-attributes-topic-configuration) documentation. */ json_attributes_topic?: string; - - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; } diff --git a/futurehome/src/ha/mqtt_components/scene.ts b/futurehome/src/ha/mqtt_components/scene.ts index 4c1ef40..ace9bbe 100644 --- a/futurehome/src/ha/mqtt_components/scene.ts +++ b/futurehome/src/ha/mqtt_components/scene.ts @@ -25,18 +25,6 @@ export interface SceneComponent extends BaseComponent { */ payload_on?: string; - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; - /** * Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) * to extract the JSON dictionary from messages received on the `json_attributes_topic`. diff --git a/futurehome/src/ha/mqtt_components/sensor.ts b/futurehome/src/ha/mqtt_components/sensor.ts index d22c215..8c6095c 100644 --- a/futurehome/src/ha/mqtt_components/sensor.ts +++ b/futurehome/src/ha/mqtt_components/sensor.ts @@ -103,16 +103,4 @@ export interface SensorComponent extends BaseComponent { * Available variables: `entity_id`. The `entity_id` can be used to reference the entity's attributes. */ last_reset_value_template?: string; - - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; } diff --git a/futurehome/src/ha/mqtt_components/siren.ts b/futurehome/src/ha/mqtt_components/siren.ts index edebcb3..ade8b63 100644 --- a/futurehome/src/ha/mqtt_components/siren.ts +++ b/futurehome/src/ha/mqtt_components/siren.ts @@ -50,18 +50,6 @@ export interface SirenComponent extends BaseComponent { */ optimistic?: boolean; - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; - /** * The payload that represents `off` state. If specified, will be used for both comparing to the value in the `state_topic` (see `value_template` and `state_off` for details) and sending as `off` command to the `command_topic`. * Default: "OFF" diff --git a/futurehome/src/ha/mqtt_components/switch.ts b/futurehome/src/ha/mqtt_components/switch.ts index e6ee3fe..a93fc7d 100644 --- a/futurehome/src/ha/mqtt_components/switch.ts +++ b/futurehome/src/ha/mqtt_components/switch.ts @@ -52,18 +52,6 @@ export interface SwitchComponent extends BaseComponent { */ optimistic?: boolean; - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; - /** * The payload that represents `off` state. * If specified, will be used for both comparing to the value in the `state_topic` (see `value_template` and `state_off` for details) diff --git a/futurehome/src/ha/mqtt_components/vacuum.ts b/futurehome/src/ha/mqtt_components/vacuum.ts index 74344ab..0a42ca7 100644 --- a/futurehome/src/ha/mqtt_components/vacuum.ts +++ b/futurehome/src/ha/mqtt_components/vacuum.ts @@ -39,12 +39,6 @@ export interface VacuumComponent extends BaseComponent { */ json_attributes_topic?: string; - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - /** * The payload to send to the `command_topic` to begin a spot cleaning cycle. * Default: "clean_spot" @@ -57,12 +51,6 @@ export interface VacuumComponent extends BaseComponent { */ payload_locate?: string; - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; - /** * The payload to send to the `command_topic` to pause the vacuum. * Default: "pause" diff --git a/futurehome/src/ha/mqtt_components/water_heater.ts b/futurehome/src/ha/mqtt_components/water_heater.ts index e51b1b5..ea073f9 100644 --- a/futurehome/src/ha/mqtt_components/water_heater.ts +++ b/futurehome/src/ha/mqtt_components/water_heater.ts @@ -15,18 +15,6 @@ export interface WaterHeaterComponent extends BaseComponent { */ platform: 'water_heater'; - /** - * The payload that represents the available state. - * Default: "online" - */ - payload_available?: string; - - /** - * The payload that represents the unavailable state. - * Default: "offline" - */ - payload_not_available?: string; - /** * The MQTT topic to publish commands to change the water heater operation mode. */ diff --git a/futurehome/src/ha/publish_device.ts b/futurehome/src/ha/publish_device.ts index 52e8f64..0b9b503 100644 --- a/futurehome/src/ha/publish_device.ts +++ b/futurehome/src/ha/publish_device.ts @@ -32,93 +32,9 @@ import { user_code__components } from '../services/user_code'; import { water_heater__components } from '../services/water_heater'; import { abbreviateHaMqttKeys } from './abbreviate_ha_mqtt_keys'; import { ha } from './globals'; +import { HaDeviceConfig } from './ha_device_config'; import { HaMqttComponent } from './mqtt_components/_component'; -type HaDeviceConfig = { - /** - * Information about the device this sensor is a part of to tie it into the [device registry](https://developers.home-assistant.io/docs/device_registry_index/). - * Only works when [`unique_id`](#unique_id) is set. - * At least one of identifiers or connections must be present to identify the device. - */ - device?: { - /** - * A link to the webpage that can manage the configuration of this device. - * Can be either an `http://`, `https://` or an internal `homeassistant://` URL. - */ - configuration_url?: string; - - /** - * A list of connections of the device to the outside world as a list of tuples `[connection_type, connection_identifier]`. - * For example the MAC address of a network interface: - * `"connections": [["mac", "02:5b:26:a8:dc:12"]]`. - */ - connections?: Array<[string, string]>; - - /** - * The hardware version of the device. - */ - hw_version?: string; - - /** - * A list of IDs that uniquely identify the device. - * For example a serial number. - */ - identifiers?: string | string[]; - - /** - * The manufacturer of the device. - */ - manufacturer?: string; - - /** - * The model of the device. - */ - model?: string; - - /** - * The model identifier of the device. - */ - model_id?: string; - - /** - * The name of the device. - */ - name?: string; - - /** - * The serial number of the device. - */ - serial_number?: string; - - /** - * Suggest an area if the device isn’t in one yet. - */ - suggested_area?: string; - - /** - * The firmware version of the device. - */ - sw_version?: string; - - /** - * Identifier of a device that routes messages between this device and Home Assistant. - * Examples of such devices are hubs, or parent devices of a sub-device. - * This is used to show device topology in Home Assistant. - */ - via_device?: string; - }; - origin: { - name: 'futurehome'; - support_url: 'https://github.com/adrianjagielak/home-assistant-futurehome'; - }; - components: { - [key: string]: HaMqttComponent; - }; - state_topic: string; - availability_topic: string; - qos: number; -}; - export type ServiceComponentsCreationResult = { components: { [key: string]: HaMqttComponent }; commandHandlers?: CommandHandlers; @@ -348,7 +264,7 @@ export function haPublishDevice(parameters: { const availabilityTopic = `${topicPrefix}/availability`; const config: HaDeviceConfig = { device: { - identifiers: parameters.vinculumDeviceData.id.toString(), + identifiers: `futurehome_${parameters.hubId}_${parameters.vinculumDeviceData.id}`, name: parameters.vinculumDeviceData?.client?.name ?? parameters.vinculumDeviceData?.modelAlias ?? @@ -366,7 +282,7 @@ export function haPublishDevice(parameters: { serial_number: parameters.deviceInclusionReport?.product_hash ?? undefined, hw_version: parameters.deviceInclusionReport?.hw_ver ?? undefined, - via_device: 'todo_hub_id', + via_device: `futurehome_${parameters.hubId}_hub`, }, origin: { name: 'futurehome', diff --git a/futurehome/src/index.ts b/futurehome/src/index.ts index 50199ea..ac911a8 100644 --- a/futurehome/src/index.ts +++ b/futurehome/src/index.ts @@ -7,11 +7,15 @@ import { haUpdateState, haUpdateStateValueReport } from './ha/update_state'; import { VinculumPd7Device } from './fimp/vinculum_pd7_device'; import { haUpdateAvailability } from './ha/update_availability'; import { delay } from './utils'; +import { exposeSmarthubTools, handleInclusionStatusReport } from './ha/admin'; +import { pollVinculum } from './fimp/vinculum'; (async () => { const hubIp = process.env.FH_HUB_IP || 'futurehome-smarthub.local'; - const hubUsername = process.env.FH_USERNAME || ''; - const hubPassword = process.env.FH_PASSWORD || ''; + const localApiUsername = process.env.FH_USERNAME || ''; + const localApiPassword = process.env.FH_PASSWORD || ''; + const thingsplexUsername = process.env.TP_USERNAME || ''; + const thingsplexPassword = process.env.TP_PASSWORD || ''; const demoMode = (process.env.DEMO_MODE || '').toLowerCase().includes('true'); const showDebugLog = (process.env.SHOW_DEBUG_LOG || '') .toLowerCase() @@ -35,7 +39,7 @@ import { delay } from './utils'; setHa(ha); log.info('Connected to HA broker'); - if (!demoMode && (!hubUsername || !hubPassword)) { + if (!demoMode && (!localApiUsername || !localApiPassword)) { log.info( 'Empty username or password in non-demo mode. Removing all Futurehome devices from Home Assistant...', ); @@ -57,32 +61,18 @@ import { delay } from './utils'; log.info('Connecting to Futurehome hub...'); const fimp = await connectHub({ hubIp, - username: hubUsername, - password: hubPassword, + username: localApiUsername, + password: localApiPassword, demo: demoMode, }); fimp.subscribe('#'); setFimp(fimp); log.info('Connected to Futurehome hub'); - const house = await sendFimpMsg({ - address: '/rt:app/rn:vinculum/ad:1', - service: 'vinculum', - cmd: 'cmd.pd7.request', - val: { cmd: 'get', component: null, param: { components: ['house'] } }, - val_t: 'object', - timeoutMs: 30000, - }); + const house = await pollVinculum('house'); const hubId = house.val.param.house.hubId; - const devices = await sendFimpMsg({ - address: '/rt:app/rn:vinculum/ad:1', - service: 'vinculum', - cmd: 'cmd.pd7.request', - val: { cmd: 'get', component: null, param: { components: ['device'] } }, - val_t: 'object', - timeoutMs: 30000, - }); + const devices = await pollVinculum('device'); const haConfig = retainedMessages.filter((msg) => msg.topic.endsWith('/config'), @@ -181,15 +171,24 @@ import { delay } from './utils'; log.error('Failed publishing device', device, e); } } + if (demoMode || (thingsplexUsername && thingsplexPassword)) { + Object.assign( + commandHandlers, + exposeSmarthubTools({ + hubId, + demoMode, + hubIp, + thingsplexUsername, + thingsplexPassword, + }).commandHandlers, + ); + } setHaCommandHandlers(commandHandlers); }; vinculumDevicesToHa(devices); let knownDeviceIds = new Set(devices.val.param.device.map((d: any) => d?.id)); - // todo - // exposeSmarthubTools(); - fimp.on('message', async (topic, buf) => { try { const msg: FimpResponse = JSON.parse(buf.toString()); @@ -252,6 +251,11 @@ import { delay } from './utils'; break; } + case 'evt.thing.inclusion_status_report': { + handleInclusionStatusReport(hubId, msg); + break; + } + default: { // Handle any event that matches the pattern: evt..report if (/^evt\..+\.report$/.test(msg.type ?? '')) { @@ -271,14 +275,7 @@ import { delay } from './utils'; const pollState = () => { log.debug('Refreshing Vinculum state after 30 seconds...'); - sendFimpMsg({ - address: '/rt:app/rn:vinculum/ad:1', - service: 'vinculum', - cmd: 'cmd.pd7.request', - val: { cmd: 'get', component: null, param: { components: ['state'] } }, - val_t: 'object', - timeoutMs: 30000, - }).catch((e) => log.warn('Failed to request state', e)); + pollVinculum('state').catch((e) => log.warn('Failed to request state', e)); }; // Request initial state pollState(); @@ -290,14 +287,9 @@ import { delay } from './utils'; const pollDevices = () => { log.debug('Refreshing Vinculum devices after 30 minutes...'); - sendFimpMsg({ - address: '/rt:app/rn:vinculum/ad:1', - service: 'vinculum', - cmd: 'cmd.pd7.request', - val: { cmd: 'get', component: null, param: { components: ['device'] } }, - val_t: 'object', - timeoutMs: 30000, - }).catch((e) => log.warn('Failed to request state', e)); + pollVinculum('device').catch((e) => + log.warn('Failed to request devices', e), + ); }; // Poll devices every 30 minutes (1800000 ms) if (!demoMode) { diff --git a/futurehome/src/thingsplex/thingsplex.ts b/futurehome/src/thingsplex/thingsplex.ts new file mode 100644 index 0000000..942050a --- /dev/null +++ b/futurehome/src/thingsplex/thingsplex.ts @@ -0,0 +1,104 @@ +// src/futurehomeClient.ts +import axios from 'axios'; +import { WebSocket } from 'ws'; +import { URLSearchParams } from 'url'; +import { FimpValueType } from '../fimp/fimp'; +import { v4 as uuidv4 } from 'uuid'; + +/** + * Logs in to the Thingsplex and extracts the tplex token. + * @param username - The login username + * @param password - The login password + * @returns The tplex token if login is successful + */ +export async function loginToThingsplex(parameters: { + host: string; + username: string; + password: string; +}): Promise { + const url = `http://${parameters.host}:8081/fimp/login`; + const payload = new URLSearchParams({ + username: parameters.username, + password: parameters.password, + }).toString(); + + try { + const response = await axios.post(url, payload, { + maxRedirects: 0, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + validateStatus: (status) => status === 301, + }); + + const setCookie = response.headers['set-cookie']; + if (!setCookie || setCookie.length === 0) { + throw new Error('Set-Cookie header missing in login response'); + } + + const cookie = setCookie.find((c) => c.startsWith('tplex=')); + if (!cookie) { + throw new Error('tplex cookie not found in Set-Cookie header'); + } + + const match = cookie.match(/tplex=([^;]+)/); + if (!match) { + throw new Error('Unable to extract tplex token from cookie'); + } + + return match[1]; + } catch (err) { + throw new Error(`Login failed: ${(err as Error).message}`); + } +} + +/** + * Connects to the Thingsplex websocket with the tplex token and sends the messages. + * @param token - The tplex token from login + */ +export function connectThingsplexWebSocketAndSend( + parameters: { + host: string; + token: string; + }, + messages: { + address: string; + service: string; + cmd: string; + val: unknown; + val_t: FimpValueType; + props?: any; + }[], +): Promise { + return new Promise((resolve, reject) => { + const ws = new WebSocket(`ws://${parameters.host}:8081/ws-bridge`, { + headers: { + Cookie: `tplex=${parameters.token}`, + }, + }); + + ws.on('open', () => { + for (const msg of messages) { + const message = { + serv: msg.service, + type: msg.cmd, + val_t: msg.val_t, + val: msg.val, + props: msg.props, + tags: null, + resp_to: 'pt:j1/mt:rsp/rt:app/rn:tplex-ui/ad:1', + src: 'tplex-ui', + ver: '1', + uid: uuidv4(), + topic: msg.address, + }; + + ws.send(JSON.stringify(message)); + } + ws.close(); // Close immediately after sending + }); + + ws.on('close', () => resolve()); + ws.on('error', (err) => reject(err)); + }); +} diff --git a/futurehome/translations/be.yaml b/futurehome/translations/be.yaml new file mode 100644 index 0000000..9a0d990 --- /dev/null +++ b/futurehome/translations/be.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: IP хаба (неабавязкова) + description: Пакіньце пустым для аўтаматычнага выяўлення. + fh_username: + name: Лагін лакальнага API + description: Ваш лагін для лакальнага API/MQTT у Smarthub. + fh_password: + name: Пароль лакальнага API + description: Ваш пароль для лакальнага API/MQTT у Smarthub. + tp_username: + name: Лагін Thingsplex (неабавязкова) + description: Ваш лагін для Thingsplex. + tp_password: + name: Пароль Thingsplex (неабавязкова) + description: Ваш пароль для Thingsplex. + demo_mode: + name: Дэманстрацыйны рэжым + description: Выкарыстоўвайце запісаны стан з рэальнага Futurehome Smarthub для мадэлявання прылад. + show_debug_log: + name: Паказваць журнал адладкі diff --git a/futurehome/translations/by.yaml b/futurehome/translations/by.yaml new file mode 100644 index 0000000..9a0d990 --- /dev/null +++ b/futurehome/translations/by.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: IP хаба (неабавязкова) + description: Пакіньце пустым для аўтаматычнага выяўлення. + fh_username: + name: Лагін лакальнага API + description: Ваш лагін для лакальнага API/MQTT у Smarthub. + fh_password: + name: Пароль лакальнага API + description: Ваш пароль для лакальнага API/MQTT у Smarthub. + tp_username: + name: Лагін Thingsplex (неабавязкова) + description: Ваш лагін для Thingsplex. + tp_password: + name: Пароль Thingsplex (неабавязкова) + description: Ваш пароль для Thingsplex. + demo_mode: + name: Дэманстрацыйны рэжым + description: Выкарыстоўвайце запісаны стан з рэальнага Futurehome Smarthub для мадэлявання прылад. + show_debug_log: + name: Паказваць журнал адладкі diff --git a/futurehome/translations/cs.yaml b/futurehome/translations/cs.yaml new file mode 100644 index 0000000..3994f59 --- /dev/null +++ b/futurehome/translations/cs.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: IP adresa hubu (volitelné) + description: Nechte prázdné pro automatické vyhledání. + fh_username: + name: Uživatelské jméno pro lokální API + description: Vaše uživatelské jméno pro lokální API/MQTT Smarthubu. + fh_password: + name: Heslo pro lokální API + description: Vaše heslo pro lokální API/MQTT Smarthubu. + tp_username: + name: Uživatelské jméno Thingsplex (volitelné) + description: Vaše uživatelské jméno pro Thingsplex. + tp_password: + name: Heslo Thingsplex (volitelné) + description: Vaše heslo pro Thingsplex. + demo_mode: + name: Demo režim + description: Použijte uložený stav ze skutečného Futurehome Smarthubu pro simulaci zařízení. + show_debug_log: + name: Zobrazit ladicí protokol diff --git a/futurehome/translations/cz.yaml b/futurehome/translations/cz.yaml new file mode 100644 index 0000000..3994f59 --- /dev/null +++ b/futurehome/translations/cz.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: IP adresa hubu (volitelné) + description: Nechte prázdné pro automatické vyhledání. + fh_username: + name: Uživatelské jméno pro lokální API + description: Vaše uživatelské jméno pro lokální API/MQTT Smarthubu. + fh_password: + name: Heslo pro lokální API + description: Vaše heslo pro lokální API/MQTT Smarthubu. + tp_username: + name: Uživatelské jméno Thingsplex (volitelné) + description: Vaše uživatelské jméno pro Thingsplex. + tp_password: + name: Heslo Thingsplex (volitelné) + description: Vaše heslo pro Thingsplex. + demo_mode: + name: Demo režim + description: Použijte uložený stav ze skutečného Futurehome Smarthubu pro simulaci zařízení. + show_debug_log: + name: Zobrazit ladicí protokol diff --git a/futurehome/translations/da.yaml b/futurehome/translations/da.yaml index b4ed52d..be8f13f 100644 --- a/futurehome/translations/da.yaml +++ b/futurehome/translations/da.yaml @@ -1,15 +1,21 @@ configuration: hub_ip: - name: Hub IP (valgfrit) - description: Lad stå tom for automatisk opdagelse. - username: - name: Brugernavn - description: Dit brugernavn til lokal API/MQTT-adgang til Smarthub. - password: - name: Adgangskode - description: Din adgangskode til lokal API/MQTT-adgang til Smarthub. + name: Hub IP (valgfri) + description: Lad feltet være tomt for automatisk opdagelse. + fh_username: + name: Lokal API-brugernavn + description: Dit Smarthub lokale API/MQTT-brugernavn. + fh_password: + name: Lokal API-adgangskode + description: Din Smarthub lokale API/MQTT-adgangskode. + tp_username: + name: Thingsplex-brugernavn (valgfri) + description: Dit Thingsplex-brugernavn. + tp_password: + name: Thingsplex-adgangskode (valgfri) + description: Din Thingsplex-adgangskode. demo_mode: name: Demotilstand - description: Brug en optaget tilstand fra en ægte Futurehome Smarthub til at simulere enheder. + description: Brug en optaget prøvetilstand fra en rigtig Futurehome Smarthub til at simulere enheder. show_debug_log: name: Vis fejllog diff --git a/futurehome/translations/de.yaml b/futurehome/translations/de.yaml index 6775488..d811df9 100644 --- a/futurehome/translations/de.yaml +++ b/futurehome/translations/de.yaml @@ -2,14 +2,20 @@ configuration: hub_ip: name: Hub-IP (optional) description: Leer lassen für automatische Erkennung. - username: - name: Benutzername - description: Dein Benutzername für den lokalen API/MQTT-Zugang zum Smarthub. - password: - name: Passwort - description: Dein Passwort für den lokalen API/MQTT-Zugang zum Smarthub. + fh_username: + name: Lokaler API-Benutzername + description: Dein Smarthub lokaler API-/MQTT-Benutzername. + fh_password: + name: Lokales API-Passwort + description: Dein Smarthub lokales API-/MQTT-Passwort. + tp_username: + name: Thingsplex-Benutzername (optional) + description: Dein Thingsplex-Benutzername. + tp_password: + name: Thingsplex-Passwort (optional) + description: Dein Thingsplex-Passwort. demo_mode: - name: Demomodus - description: Verwende einen aufgezeichneten Zustand von einem echten Futurehome Smarthub, um Geräte zu simulieren. + name: Demo-Modus + description: Verwende einen aufgezeichneten Beispielzustand eines echten Futurehome Smarthubs, um Geräte zu simulieren. show_debug_log: name: Debug-Log anzeigen diff --git a/futurehome/translations/dk.yaml b/futurehome/translations/dk.yaml index b4ed52d..be8f13f 100644 --- a/futurehome/translations/dk.yaml +++ b/futurehome/translations/dk.yaml @@ -1,15 +1,21 @@ configuration: hub_ip: - name: Hub IP (valgfrit) - description: Lad stå tom for automatisk opdagelse. - username: - name: Brugernavn - description: Dit brugernavn til lokal API/MQTT-adgang til Smarthub. - password: - name: Adgangskode - description: Din adgangskode til lokal API/MQTT-adgang til Smarthub. + name: Hub IP (valgfri) + description: Lad feltet være tomt for automatisk opdagelse. + fh_username: + name: Lokal API-brugernavn + description: Dit Smarthub lokale API/MQTT-brugernavn. + fh_password: + name: Lokal API-adgangskode + description: Din Smarthub lokale API/MQTT-adgangskode. + tp_username: + name: Thingsplex-brugernavn (valgfri) + description: Dit Thingsplex-brugernavn. + tp_password: + name: Thingsplex-adgangskode (valgfri) + description: Din Thingsplex-adgangskode. demo_mode: name: Demotilstand - description: Brug en optaget tilstand fra en ægte Futurehome Smarthub til at simulere enheder. + description: Brug en optaget prøvetilstand fra en rigtig Futurehome Smarthub til at simulere enheder. show_debug_log: name: Vis fejllog diff --git a/futurehome/translations/ee.yaml b/futurehome/translations/ee.yaml new file mode 100644 index 0000000..55515bf --- /dev/null +++ b/futurehome/translations/ee.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: Hubi IP (valikuline) + description: Jäta tühjaks automaatseks tuvastamiseks. + fh_username: + name: Kohaliku API kasutajanimi + description: Sinu Smarthubi kohaliku API/MQTT kasutajanimi. + fh_password: + name: Kohaliku API parool + description: Sinu Smarthubi kohaliku API/MQTT parool. + tp_username: + name: Thingsplexi kasutajanimi (valikuline) + description: Sinu Thingsplexi kasutajanimi. + tp_password: + name: Thingsplexi parool (valikuline) + description: Sinu Thingsplexi parool. + demo_mode: + name: Demorežiim + description: Kasuta päris Futurehome Smarthubist salvestatud näidisseisu seadmete simuleerimiseks. + show_debug_log: + name: Näita silumislogi diff --git a/futurehome/translations/en.yaml b/futurehome/translations/en.yaml index 4eef3d5..b1f5116 100644 --- a/futurehome/translations/en.yaml +++ b/futurehome/translations/en.yaml @@ -2,12 +2,18 @@ configuration: hub_ip: name: Hub IP (Optional) description: Leave blank for automatic discovery. - username: - name: Username + fh_username: + name: Local API Username description: Your Smarthub local API/MQTT username. - password: - name: Password + fh_password: + name: Local API Password description: Your Smarthub local API/MQTT password. + tp_username: + name: Thingsplex Username (Optional) + description: Your Thingsplex username. + tp_password: + name: Thingsplex Password (Optional) + description: Your Thingsplex password. demo_mode: name: Demo Mode description: Use a sample recorded state from a real Futurehome Smarthub to simulate devices. diff --git a/futurehome/translations/et.yaml b/futurehome/translations/et.yaml new file mode 100644 index 0000000..55515bf --- /dev/null +++ b/futurehome/translations/et.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: Hubi IP (valikuline) + description: Jäta tühjaks automaatseks tuvastamiseks. + fh_username: + name: Kohaliku API kasutajanimi + description: Sinu Smarthubi kohaliku API/MQTT kasutajanimi. + fh_password: + name: Kohaliku API parool + description: Sinu Smarthubi kohaliku API/MQTT parool. + tp_username: + name: Thingsplexi kasutajanimi (valikuline) + description: Sinu Thingsplexi kasutajanimi. + tp_password: + name: Thingsplexi parool (valikuline) + description: Sinu Thingsplexi parool. + demo_mode: + name: Demorežiim + description: Kasuta päris Futurehome Smarthubist salvestatud näidisseisu seadmete simuleerimiseks. + show_debug_log: + name: Näita silumislogi diff --git a/futurehome/translations/fi.yaml b/futurehome/translations/fi.yaml index 797852c..4adea34 100644 --- a/futurehome/translations/fi.yaml +++ b/futurehome/translations/fi.yaml @@ -1,15 +1,21 @@ configuration: hub_ip: - name: Keskittimen IP (valinnainen) - description: Jätä tyhjäksi automaattista tunnistusta varten. - username: - name: Käyttäjätunnus - description: Paikallisen Smarthub API/MQTT -yhteyden käyttäjätunnus. - password: - name: Salasana - description: Paikallisen Smarthub API/MQTT -yhteyden salasana. + name: Hubin IP-osoite (valinnainen) + description: Jätä tyhjäksi automaattista etsintää varten. + fh_username: + name: Paikallinen API-käyttäjätunnus + description: Smarthubin paikallinen API/MQTT-käyttäjätunnuksesi. + fh_password: + name: Paikallinen API-salasana + description: Smarthubin paikallinen API/MQTT-salasanasi. + tp_username: + name: Thingsplex-käyttäjätunnus (valinnainen) + description: Thingsplex-käyttäjätunnuksesi. + tp_password: + name: Thingsplex-salasana (valinnainen) + description: Thingsplex-salasanasi. demo_mode: name: Demotila - description: Käytä tallennettua esimerkkitilaa oikeasta Futurehome Smarthubista laitteiden simuloimiseen. + description: Käytä tallennettua tilanäytettä oikeasta Futurehome Smarthubista laitteiden simuloimiseen. show_debug_log: - name: Näytä vianmääritysloki + name: Näytä virheloki diff --git a/futurehome/translations/is.yaml b/futurehome/translations/is.yaml index cd87d29..d0b8ffa 100644 --- a/futurehome/translations/is.yaml +++ b/futurehome/translations/is.yaml @@ -1,15 +1,21 @@ configuration: hub_ip: - name: Miðlari IP (valkvæmt) - description: Láttu autt til að uppgötva sjálfkrafa. - username: - name: Notandanafn - description: Notandanafn þitt fyrir staðbundið API/MQTT aðgang að Smarthub. - password: - name: Lykilorð - description: Lykilorðið þitt fyrir staðbundið API/MQTT aðgang að Smarthub. + name: IP-tala miðlara (valkvætt) + description: Skildu eftir autt til að uppgötva sjálfkrafa. + fh_username: + name: Notandanafn fyrir staðbundið API + description: Notandanafn fyrir staðbundið API/MQTT í Smarthub. + fh_password: + name: Lykilorð fyrir staðbundið API + description: Lykilorð fyrir staðbundið API/MQTT í Smarthub. + tp_username: + name: Thingsplex notandanafn (valkvætt) + description: Notandanafn þitt fyrir Thingsplex. + tp_password: + name: Thingsplex lykilorð (valkvætt) + description: Lykilorðið þitt fyrir Thingsplex. demo_mode: name: Sýnishamur - description: Notaðu skráða stöðu úr raunverulegum Futurehome Smarthub til að líkja eftir tækjum. + description: Notaðu upptaka af raunverulegu ástandi úr Futurehome Smarthub til að herma eftir tækjum. show_debug_log: - name: Sýna aflúsunarskrá + name: Sýna villuskýrslu diff --git a/futurehome/translations/lt.yaml b/futurehome/translations/lt.yaml new file mode 100644 index 0000000..f358a58 --- /dev/null +++ b/futurehome/translations/lt.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: Hub IP (pasirinktinai) + description: Palikite tuščią automatiniam aptikimui. + fh_username: + name: Vietinio API naudotojo vardas + description: Jūsų Smarthub vietinio API/MQTT naudotojo vardas. + fh_password: + name: Vietinio API slaptažodis + description: Jūsų Smarthub vietinio API/MQTT slaptažodis. + tp_username: + name: Thingsplex naudotojo vardas (pasirinktinai) + description: Jūsų Thingsplex naudotojo vardas. + tp_password: + name: Thingsplex slaptažodis (pasirinktinai) + description: Jūsų Thingsplex slaptažodis. + demo_mode: + name: Demonstracinis režimas + description: Naudokite įrašytą būsena iš tikro Futurehome Smarthub, kad imituotumėte įrenginius. + show_debug_log: + name: Rodyti derinimo žurnalą diff --git a/futurehome/translations/lv.yaml b/futurehome/translations/lv.yaml new file mode 100644 index 0000000..2eb08c2 --- /dev/null +++ b/futurehome/translations/lv.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: Hub IP (pēc izvēles) + description: Atstāj tukšu automātiskai noteikšanai. + fh_username: + name: Vietējā API lietotājvārds + description: Tavs Smarthub vietējais API/MQTT lietotājvārds. + fh_password: + name: Vietējā API parole + description: Tava Smarthub vietējā API/MQTT parole. + tp_username: + name: Thingsplex lietotājvārds (pēc izvēles) + description: Tavs Thingsplex lietotājvārds. + tp_password: + name: Thingsplex parole (pēc izvēles) + description: Tava Thingsplex parole. + demo_mode: + name: Demonstrācijas režīms + description: Izmanto ierakstītu stāvokli no īsta Futurehome Smarthub, lai simulētu ierīces. + show_debug_log: + name: Rādīt atkļūdošanas žurnālu diff --git a/futurehome/translations/nb.yaml b/futurehome/translations/nb.yaml index d6774bd..802ef1a 100644 --- a/futurehome/translations/nb.yaml +++ b/futurehome/translations/nb.yaml @@ -1,15 +1,21 @@ configuration: hub_ip: - name: Hub IP (valgfritt) + name: Hub-IP (valgfritt) description: La stå tomt for automatisk oppdagelse. - username: - name: Brukernavn - description: Ditt brukernavn for lokal API/MQTT-tilgang til Smarthub. - password: - name: Passord - description: Ditt passord for lokal API/MQTT-tilgang til Smarthub. + fh_username: + name: Lokalt API-brukernavn + description: Ditt lokale API-/MQTT-brukernavn for Smarthub. + fh_password: + name: Lokalt API-passord + description: Ditt lokale API-/MQTT-passord for Smarthub. + tp_username: + name: Thingsplex-brukernavn (valgfritt) + description: Ditt brukernavn for Thingsplex. + tp_password: + name: Thingsplex-passord (valgfritt) + description: Ditt passord for Thingsplex. demo_mode: name: Demomodus - description: Bruk et forhåndsopptatt eksempel fra en ekte Futurehome Smarthub for å simulere enheter. + description: Bruk en forhåndsopptatt tilstand fra en ekte Futurehome Smarthub for å simulere enheter. show_debug_log: - name: Vis feillogg + name: Vis feilsøkingslogg diff --git a/futurehome/translations/nn.yaml b/futurehome/translations/nn.yaml index a72c39d..bccea47 100644 --- a/futurehome/translations/nn.yaml +++ b/futurehome/translations/nn.yaml @@ -1,15 +1,21 @@ configuration: hub_ip: - name: Hub IP (valfritt) + name: Hub-IP (valfritt) description: La stå tomt for automatisk oppdaging. - username: - name: Brukarnamn - description: Brukarnamnet ditt for lokal API/MQTT-tilgang til Smarthub. - password: - name: Passord - description: Passordet ditt for lokal API/MQTT-tilgang til Smarthub. + fh_username: + name: Lokalt API-brukarnamn + description: Ditt lokale API-/MQTT-brukarnamn for Smarthub. + fh_password: + name: Lokalt API-passord + description: Ditt lokale API-/MQTT-passord for Smarthub. + tp_username: + name: Thingsplex-brukarnamn (valfritt) + description: Ditt brukarnamn for Thingsplex. + tp_password: + name: Thingsplex-passord (valfritt) + description: Ditt passord for Thingsplex. demo_mode: name: Demomodus - description: Bruk eit førehandsopptak frå ein ekte Futurehome Smarthub for å simulere einingar. + description: Bruk ein førehandsopptak frå ein ekte Futurehome Smarthub for å simulere einingar. show_debug_log: - name: Vis feillogg + name: Vis feilsøkingslogg diff --git a/futurehome/translations/no.yaml b/futurehome/translations/no.yaml index d6774bd..802ef1a 100644 --- a/futurehome/translations/no.yaml +++ b/futurehome/translations/no.yaml @@ -1,15 +1,21 @@ configuration: hub_ip: - name: Hub IP (valgfritt) + name: Hub-IP (valgfritt) description: La stå tomt for automatisk oppdagelse. - username: - name: Brukernavn - description: Ditt brukernavn for lokal API/MQTT-tilgang til Smarthub. - password: - name: Passord - description: Ditt passord for lokal API/MQTT-tilgang til Smarthub. + fh_username: + name: Lokalt API-brukernavn + description: Ditt lokale API-/MQTT-brukernavn for Smarthub. + fh_password: + name: Lokalt API-passord + description: Ditt lokale API-/MQTT-passord for Smarthub. + tp_username: + name: Thingsplex-brukernavn (valgfritt) + description: Ditt brukernavn for Thingsplex. + tp_password: + name: Thingsplex-passord (valgfritt) + description: Ditt passord for Thingsplex. demo_mode: name: Demomodus - description: Bruk et forhåndsopptatt eksempel fra en ekte Futurehome Smarthub for å simulere enheter. + description: Bruk en forhåndsopptatt tilstand fra en ekte Futurehome Smarthub for å simulere enheter. show_debug_log: - name: Vis feillogg + name: Vis feilsøkingslogg diff --git a/futurehome/translations/pl.yaml b/futurehome/translations/pl.yaml index c70ea58..e46fbeb 100644 --- a/futurehome/translations/pl.yaml +++ b/futurehome/translations/pl.yaml @@ -1,15 +1,21 @@ configuration: hub_ip: - name: IP huba (opcjonalne) - description: Pozostaw puste, aby wykryć automatycznie. - username: - name: Nazwa użytkownika - description: Twoja nazwa użytkownika do lokalnego API/MQTT Smarthuba. - password: - name: Hasło - description: Twoje hasło do lokalnego API/MQTT Smarthuba. + name: IP centralki (opcjonalne) + description: Pozostaw puste, aby włączyć automatyczne wykrywanie. + fh_username: + name: Nazwa użytkownika lokalnego API + description: Twoja nazwa użytkownika lokalnego API/MQTT Smarthub. + fh_password: + name: Hasło do lokalnego API + description: Twoje hasło do lokalnego API/MQTT Smarthub. + tp_username: + name: Nazwa użytkownika Thingsplex (opcjonalne) + description: Twoja nazwa użytkownika Thingsplex. + tp_password: + name: Hasło do Thingsplex (opcjonalne) + description: Twoje hasło do Thingsplex. demo_mode: - name: Tryb demo - description: Użyj przykładowego zapisanego stanu z prawdziwego Futurehome Smarthub, aby symulować urządzenia. + name: Tryb demonstracyjny + description: Użyj zapisanego stanu z prawdziwego Futurehome Smarthub, aby symulować urządzenia. show_debug_log: name: Pokaż dziennik debugowania diff --git a/futurehome/translations/ru.yaml b/futurehome/translations/ru.yaml new file mode 100644 index 0000000..799403e --- /dev/null +++ b/futurehome/translations/ru.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: IP хаба (необязательно) + description: Оставьте пустым для автоматического обнаружения. + fh_username: + name: Имя пользователя локального API + description: Ваше имя пользователя локального API/MQTT для Smarthub. + fh_password: + name: Пароль локального API + description: Ваш пароль локального API/MQTT для Smarthub. + tp_username: + name: Имя пользователя Thingsplex (необязательно) + description: Ваше имя пользователя Thingsplex. + tp_password: + name: Пароль Thingsplex (необязательно) + description: Ваш пароль Thingsplex. + demo_mode: + name: Демонстрационный режим + description: Используйте сохранённое состояние от настоящего Futurehome Smarthub для имитации устройств. + show_debug_log: + name: Показать отладочный журнал diff --git a/futurehome/translations/se.yaml b/futurehome/translations/se.yaml index 5d67d5e..6ebfbcb 100644 --- a/futurehome/translations/se.yaml +++ b/futurehome/translations/se.yaml @@ -1,15 +1,21 @@ configuration: hub_ip: - name: Hub IP (valfritt) + name: Hubbens IP (valfritt) description: Lämna tomt för automatisk upptäckt. - username: - name: Användarnamn - description: Ditt användarnamn för lokal API/MQTT-åtkomst till Smarthub. - password: - name: Lösenord - description: Ditt lösenord för lokal API/MQTT-åtkomst till Smarthub. + fh_username: + name: Lokalt API-användarnamn + description: Ditt lokala API-/MQTT-användarnamn för Smarthub. + fh_password: + name: Lösenord för lokalt API + description: Ditt lokala API-/MQTT-lösenord för Smarthub. + tp_username: + name: Thingsplex-användarnamn (valfritt) + description: Ditt användarnamn för Thingsplex. + tp_password: + name: Thingsplex-lösenord (valfritt) + description: Ditt lösenord för Thingsplex. demo_mode: name: Demoläge description: Använd ett inspelat exempel från en riktig Futurehome Smarthub för att simulera enheter. show_debug_log: - name: Čájet divrrodagaslogu + name: Visa felsökningslogg diff --git a/futurehome/translations/sk.yaml b/futurehome/translations/sk.yaml new file mode 100644 index 0000000..f62db61 --- /dev/null +++ b/futurehome/translations/sk.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: IP adresa hubu (voliteľné) + description: Nechajte prázdne pre automatické vyhľadanie. + fh_username: + name: Používateľské meno lokálneho API + description: Vaše používateľské meno pre lokálne API/MQTT Smarthubu. + fh_password: + name: Heslo lokálneho API + description: Vaše heslo pre lokálne API/MQTT Smarthubu. + tp_username: + name: Používateľské meno Thingsplex (voliteľné) + description: Vaše používateľské meno pre Thingsplex. + tp_password: + name: Heslo Thingsplex (voliteľné) + description: Vaše heslo pre Thingsplex. + demo_mode: + name: Demo režim + description: Použite uložený stav zo skutočného Futurehome Smarthubu na simuláciu zariadení. + show_debug_log: + name: Zobraziť ladicí protokol diff --git a/futurehome/translations/sv.yaml b/futurehome/translations/sv.yaml index d3e236c..6ebfbcb 100644 --- a/futurehome/translations/sv.yaml +++ b/futurehome/translations/sv.yaml @@ -1,13 +1,19 @@ configuration: hub_ip: - name: Hub IP (valfritt) + name: Hubbens IP (valfritt) description: Lämna tomt för automatisk upptäckt. - username: - name: Användarnamn - description: Ditt användarnamn för lokal API/MQTT-åtkomst till Smarthub. - password: - name: Lösenord - description: Ditt lösenord för lokal API/MQTT-åtkomst till Smarthub. + fh_username: + name: Lokalt API-användarnamn + description: Ditt lokala API-/MQTT-användarnamn för Smarthub. + fh_password: + name: Lösenord för lokalt API + description: Ditt lokala API-/MQTT-lösenord för Smarthub. + tp_username: + name: Thingsplex-användarnamn (valfritt) + description: Ditt användarnamn för Thingsplex. + tp_password: + name: Thingsplex-lösenord (valfritt) + description: Ditt lösenord för Thingsplex. demo_mode: name: Demoläge description: Använd ett inspelat exempel från en riktig Futurehome Smarthub för att simulera enheter. diff --git a/futurehome/translations/ua.yaml b/futurehome/translations/ua.yaml new file mode 100644 index 0000000..9b60a5e --- /dev/null +++ b/futurehome/translations/ua.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: IP хабу (необов’язково) + description: Залиште порожнім для автоматичного виявлення. + fh_username: + name: Ім’я користувача локального API + description: Ваше ім’я користувача локального API/MQTT для Smarthub. + fh_password: + name: Пароль локального API + description: Ваш пароль локального API/MQTT для Smarthub. + tp_username: + name: Ім’я користувача Thingsplex (необов’язково) + description: Ваше ім’я користувача для Thingsplex. + tp_password: + name: Пароль Thingsplex (необов’язково) + description: Ваш пароль для Thingsplex. + demo_mode: + name: Демонстраційний режим + description: Використовуйте записаний стан із реального Futurehome Smarthub для імітації пристроїв. + show_debug_log: + name: Показувати журнал налагодження diff --git a/futurehome/translations/uk.yaml b/futurehome/translations/uk.yaml new file mode 100644 index 0000000..9b60a5e --- /dev/null +++ b/futurehome/translations/uk.yaml @@ -0,0 +1,21 @@ +configuration: + hub_ip: + name: IP хабу (необов’язково) + description: Залиште порожнім для автоматичного виявлення. + fh_username: + name: Ім’я користувача локального API + description: Ваше ім’я користувача локального API/MQTT для Smarthub. + fh_password: + name: Пароль локального API + description: Ваш пароль локального API/MQTT для Smarthub. + tp_username: + name: Ім’я користувача Thingsplex (необов’язково) + description: Ваше ім’я користувача для Thingsplex. + tp_password: + name: Пароль Thingsplex (необов’язково) + description: Ваш пароль для Thingsplex. + demo_mode: + name: Демонстраційний режим + description: Використовуйте записаний стан із реального Futurehome Smarthub для імітації пристроїв. + show_debug_log: + name: Показувати журнал налагодження