Add support for pairing new devices

This commit is contained in:
Adrian Jagielak 2025-07-28 14:18:42 +02:00
parent 15c65850bf
commit b034197a93
No known key found for this signature in database
GPG Key ID: 0818CF7AF6C62BFB
56 changed files with 1272 additions and 512 deletions

View File

@ -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 hubs 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 hubs 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.
<!--
todo: pairing
todo add info about factory reset hub to restore 30 day trial
-->

View File

@ -3,6 +3,7 @@
## 0.1.8 (28.07.2025)
- Added logo.
- Added support for pairing new devices.
## 0.1.7 (26.07.2025)

View File

@ -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 hubs 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 hubs 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.
<!--
todo: pairing
todo add info about factory reset hub to restore 30 day trial
-->

View File

@ -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'

View File

@ -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",

View File

@ -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",

View File

@ -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')

View File

@ -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
}

View File

@ -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<string, any>;
timeoutMs?: number;
}): Promise<FimpResponse> {
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 });

View File

@ -0,0 +1,14 @@
import { FimpResponse, sendFimpMsg } from './fimp';
export async function pollVinculum(
component: 'device' | 'house' | 'state',
): Promise<FimpResponse> {
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,
});
}

276
futurehome/src/ha/admin.ts Normal file
View File

@ -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

View File

@ -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 isnt 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;
};

View File

@ -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"

View File

@ -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"

View File

@ -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;
}

View File

@ -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"

View File

@ -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.

View File

@ -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;
}

View File

@ -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.

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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;
}

View File

@ -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`.

View File

@ -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;
}

View File

@ -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"

View File

@ -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)

View File

@ -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"

View File

@ -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.
*/

View File

@ -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 isnt 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',

View File

@ -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.<something>.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) {

View File

@ -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<string> {
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<void> {
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));
});
}

View File

@ -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: Паказваць журнал адладкі

View File

@ -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: Паказваць журнал адладкі

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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ą

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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: Показать отладочный журнал

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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: Показувати журнал налагодження

View File

@ -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: Показувати журнал налагодження