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 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. * Fetches and updates device states and availability in real time.
* Supports interaction with devices comparable to the official Futurehome app. * Supports interaction with devices comparable to the official Futurehome app.
* Supports pairing and unpairing of devices.
## Installation ## Installation
todo: rewrite the installation instructions
1. In the Futurehome app, go to Settings > My household > Smarthub and enable Local API. 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. 2. (Optional) In the Futurehome app go to Settings > ??? > ??? and install Thingsplex integration (Needed for support of pairing new devices).
3. In Home Assistant, enable the **MQTT** integration. 3. (Optional but highly recommended) Block the hubs internet (WAN) access in your router settings to prevent future firmware updates.
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. 4. In Home Assistant, enable the **MQTT** integration.
5. Open the **Add-on Store** and search for "Futurehome". 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. Install, configure, and start the Futurehome add-on. 6. Open the **Add-on Store** and search for "Futurehome".
7. Install, configure, and start the Futurehome add-on.
# Futurehome Device Services Compatibility Chart # 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. 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 todo add info about factory reset hub to restore 30 day trial
--> -->

View File

@ -3,6 +3,7 @@
## 0.1.8 (28.07.2025) ## 0.1.8 (28.07.2025)
- Added logo. - Added logo.
- Added support for pairing new devices.
## 0.1.7 (26.07.2025) ## 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 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. * Fetches and updates device states and availability in real time.
* Supports interaction with devices comparable to the official Futurehome app. * Supports interaction with devices comparable to the official Futurehome app.
* Supports pairing and unpairing of devices.
## Installation ## Installation
todo: rewrite the installation instructions
1. In the Futurehome app, go to Settings > My household > Smarthub and enable Local API. 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. 2. (Optional) In the Futurehome app go to Settings > ??? > ??? and install Thingsplex integration (Needed for support of pairing new devices).
3. In Home Assistant, enable the **MQTT** integration. 3. (Optional but highly recommended) Block the hubs internet (WAN) access in your router settings to prevent future firmware updates.
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. 4. In Home Assistant, enable the **MQTT** integration.
5. Open the **Add-on Store** and search for "Futurehome". 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. Install, configure, and start the Futurehome add-on. 6. Open the **Add-on Store** and search for "Futurehome".
7. Install, configure, and start the Futurehome add-on.
# Futurehome Device Services Compatibility Chart # 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. 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 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 # https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config
name: Futurehome name: Futurehome
version: "0.1.8" version: '0.1.8'
slug: futurehome slug: futurehome
description: Local Futurehome Smarthub integration description: Local Futurehome Smarthub integration
url: "https://github.com/adrianjagielak/home-assistant-futurehome" url: 'https://github.com/adrianjagielak/home-assistant-futurehome'
arch: arch:
- armhf - armhf
- armv7 - armv7
@ -13,20 +13,24 @@ arch:
init: false init: false
services: services:
- "mqtt:need" - 'mqtt:need'
options: options:
hub_ip: "" hub_ip: ''
username: "" fh_username: ''
password: "" fh_password: ''
tp_username: ''
tp_password: ''
demo_mode: false demo_mode: false
show_debug_log: false show_debug_log: false
schema: schema:
hub_ip: "str?" hub_ip: 'str?'
username: "str?" fh_username: 'str?'
password: "str?" fh_password: 'str?'
demo_mode: "bool?" tp_username: 'str?'
show_debug_log: "bool?" 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", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"axios": "^1.11.0",
"mqtt": "^5.13.3", "mqtt": "^5.13.3",
"uuid": "^11.1.0" "uuid": "^11.1.0",
"ws": "^8.18.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.31.0", "@eslint/js": "^9.31.0",
"@types/node": "^24.0.15", "@types/node": "^24.0.15",
"@types/ws": "^8.18.1",
"@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.38.0", "@typescript-eslint/parser": "^8.38.0",
"eslint": "^9.31.0", "eslint": "^9.31.0",
@ -742,6 +745,23 @@
"dev": true, "dev": true,
"license": "Python-2.0" "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": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -835,6 +855,19 @@
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"license": "MIT" "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": { "node_modules/callsites": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@ -898,6 +931,18 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/commist": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz",
@ -979,6 +1024,29 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/eastasianwidth": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@ -993,6 +1061,51 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/escape-string-regexp": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@ -1330,6 +1443,26 @@
"dev": true, "dev": true,
"license": "ISC" "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": { "node_modules/foreground-child": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
@ -1347,6 +1480,68 @@
"url": "https://github.com/sponsors/isaacs" "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": { "node_modules/glob": {
"version": "11.0.3", "version": "11.0.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz",
@ -1397,6 +1592,18 @@
"url": "https://github.com/sponsors/sindresorhus" "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": { "node_modules/graphemer": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
@ -1414,6 +1621,45 @@
"node": ">=8" "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": { "node_modules/help-me": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
@ -1665,6 +1911,15 @@
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC" "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": { "node_modules/merge2": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -1689,6 +1944,27 @@
"node": ">=8.6" "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": { "node_modules/minimatch": {
"version": "10.0.3", "version": "10.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",
@ -1945,6 +2221,12 @@
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"license": "MIT" "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": { "node_modules/punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",

View File

@ -9,12 +9,15 @@
"start": "node dist/index.js" "start": "node dist/index.js"
}, },
"dependencies": { "dependencies": {
"axios": "^1.11.0",
"mqtt": "^5.13.3", "mqtt": "^5.13.3",
"uuid": "^11.1.0" "uuid": "^11.1.0",
"ws": "^8.18.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.31.0", "@eslint/js": "^9.31.0",
"@types/node": "^24.0.15", "@types/node": "^24.0.15",
"@types/ws": "^8.18.1",
"@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.38.0", "@typescript-eslint/parser": "^8.38.0",
"eslint": "^9.31.0", "eslint": "^9.31.0",

View File

@ -6,8 +6,10 @@
set -e set -e
export FH_HUB_IP=$(bashio::config 'hub_ip') export FH_HUB_IP=$(bashio::config 'hub_ip')
export FH_USERNAME=$(bashio::config 'username') export FH_USERNAME=$(bashio::config 'fh_username')
export FH_PASSWORD=$(bashio::config 'password') 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 DEMO_MODE=$(bashio::config 'demo_mode')
export SHOW_DEBUG_LOG=$(bashio::config 'show_debug_log') 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; ver?: any;
}; };
type FimpValueType = export type FimpValueType =
| 'string' | 'string'
| 'int' | 'int'
| 'float' | 'float'
@ -51,7 +51,7 @@ export async function sendFimpMsg({
cmd: string; cmd: string;
val: unknown; val: unknown;
val_t: FimpValueType; val_t: FimpValueType;
props?: any; props?: Record<string, any>;
timeoutMs?: number; timeoutMs?: number;
}): Promise<FimpResponse> { }): Promise<FimpResponse> {
const uid = uuidv4(); const uid = uuidv4();
@ -60,9 +60,9 @@ export async function sendFimpMsg({
corid: null, corid: null,
ctime: new Date().toISOString(), ctime: new Date().toISOString(),
props: props, 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, serv: service,
src: 'ha-futurehome', src: 'smarthome-app',
tags: [], tags: [],
type: cmd, type: cmd,
uid: uid, uid: uid,
@ -165,7 +165,7 @@ service: "${service}",
uid: "${uid}", uid: "${uid}",
cmd: "${cmd}", cmd: "${cmd}",
val: ${JSON.stringify(val)}, 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 }); 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; payload_arm_custom_bypass?: string;
/**
* The payload that represents the available state.
* Default: "online"
*/
payload_available?: string;
/** /**
* The payload to disarm your Alarm Panel. * The payload to disarm your Alarm Panel.
* Default: "DISARM" * Default: "DISARM"
*/ */
payload_disarm?: string; 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. * The payload to trigger the alarm on your Alarm Panel.
* Default: "TRIGGER" * Default: "TRIGGER"

View File

@ -86,12 +86,6 @@ export interface BinarySensorComponent extends BaseComponent {
*/ */
off_delay?: number; off_delay?: number;
/**
* The string that represents the `online` state.
* Default: "online"
*/
payload_available?: string;
/** /**
* The string that represents the `offline` state. * The string that represents the `offline` state.
* Default: "offline" * 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. * 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; 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; 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. * The payload sent to turn off the device.
* Default: "OFF" * Default: "OFF"

View File

@ -47,12 +47,6 @@ export interface CoverComponent extends BaseComponent {
*/ */
optimistic?: boolean; optimistic?: boolean;
/**
* The payload that represents the online state.
* Default: "online"
*/
payload_available?: string;
/** /**
* The command payload that closes the cover. * The command payload that closes the cover.
* Set to `null` to disable the close command. * Set to `null` to disable the close command.

View File

@ -83,16 +83,4 @@ export interface DeviceTrackerComponent extends BaseComponent {
* Default: '"None"' * Default: '"None"'
*/ */
payload_reset?: string; 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; 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) * 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. * 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; 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. * The payload that represents the stop state.
* Default: "OFF" * Default: "OFF"

View File

@ -342,18 +342,6 @@ export interface LightComponent extends BaseComponent {
*/ */
min_mireds?: number; 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. * The payload that represents the off state.
* Default: "OFF" * Default: "OFF"

View File

@ -61,24 +61,12 @@ export interface LockComponent extends BaseComponent {
*/ */
optimistic?: boolean; optimistic?: boolean;
/**
* The payload that represents the available state.
* Default: "online"
*/
payload_available?: string;
/** /**
* The payload sent to the lock to lock it. * The payload sent to the lock to lock it.
* Default: "LOCK" * Default: "LOCK"
*/ */
payload_lock?: string; 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. * The payload sent to the lock to unlock it.
* Default: "UNLOCK" * 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. * 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; 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; 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) * 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`. * 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. * Available variables: `entity_id`. The `entity_id` can be used to reference the entity's attributes.
*/ */
last_reset_value_template?: string; 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; 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`. * 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" * Default: "OFF"

View File

@ -52,18 +52,6 @@ export interface SwitchComponent extends BaseComponent {
*/ */
optimistic?: boolean; 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. * 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) * 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; 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. * The payload to send to the `command_topic` to begin a spot cleaning cycle.
* Default: "clean_spot" * Default: "clean_spot"
@ -57,12 +51,6 @@ export interface VacuumComponent extends BaseComponent {
*/ */
payload_locate?: string; 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. * The payload to send to the `command_topic` to pause the vacuum.
* Default: "pause" * Default: "pause"

View File

@ -15,18 +15,6 @@ export interface WaterHeaterComponent extends BaseComponent {
*/ */
platform: 'water_heater'; 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. * 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 { water_heater__components } from '../services/water_heater';
import { abbreviateHaMqttKeys } from './abbreviate_ha_mqtt_keys'; import { abbreviateHaMqttKeys } from './abbreviate_ha_mqtt_keys';
import { ha } from './globals'; import { ha } from './globals';
import { HaDeviceConfig } from './ha_device_config';
import { HaMqttComponent } from './mqtt_components/_component'; 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 = { export type ServiceComponentsCreationResult = {
components: { [key: string]: HaMqttComponent }; components: { [key: string]: HaMqttComponent };
commandHandlers?: CommandHandlers; commandHandlers?: CommandHandlers;
@ -348,7 +264,7 @@ export function haPublishDevice(parameters: {
const availabilityTopic = `${topicPrefix}/availability`; const availabilityTopic = `${topicPrefix}/availability`;
const config: HaDeviceConfig = { const config: HaDeviceConfig = {
device: { device: {
identifiers: parameters.vinculumDeviceData.id.toString(), identifiers: `futurehome_${parameters.hubId}_${parameters.vinculumDeviceData.id}`,
name: name:
parameters.vinculumDeviceData?.client?.name ?? parameters.vinculumDeviceData?.client?.name ??
parameters.vinculumDeviceData?.modelAlias ?? parameters.vinculumDeviceData?.modelAlias ??
@ -366,7 +282,7 @@ export function haPublishDevice(parameters: {
serial_number: serial_number:
parameters.deviceInclusionReport?.product_hash ?? undefined, parameters.deviceInclusionReport?.product_hash ?? undefined,
hw_version: parameters.deviceInclusionReport?.hw_ver ?? undefined, hw_version: parameters.deviceInclusionReport?.hw_ver ?? undefined,
via_device: 'todo_hub_id', via_device: `futurehome_${parameters.hubId}_hub`,
}, },
origin: { origin: {
name: 'futurehome', name: 'futurehome',

View File

@ -7,11 +7,15 @@ import { haUpdateState, haUpdateStateValueReport } from './ha/update_state';
import { VinculumPd7Device } from './fimp/vinculum_pd7_device'; import { VinculumPd7Device } from './fimp/vinculum_pd7_device';
import { haUpdateAvailability } from './ha/update_availability'; import { haUpdateAvailability } from './ha/update_availability';
import { delay } from './utils'; import { delay } from './utils';
import { exposeSmarthubTools, handleInclusionStatusReport } from './ha/admin';
import { pollVinculum } from './fimp/vinculum';
(async () => { (async () => {
const hubIp = process.env.FH_HUB_IP || 'futurehome-smarthub.local'; const hubIp = process.env.FH_HUB_IP || 'futurehome-smarthub.local';
const hubUsername = process.env.FH_USERNAME || ''; const localApiUsername = process.env.FH_USERNAME || '';
const hubPassword = process.env.FH_PASSWORD || ''; 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 demoMode = (process.env.DEMO_MODE || '').toLowerCase().includes('true');
const showDebugLog = (process.env.SHOW_DEBUG_LOG || '') const showDebugLog = (process.env.SHOW_DEBUG_LOG || '')
.toLowerCase() .toLowerCase()
@ -35,7 +39,7 @@ import { delay } from './utils';
setHa(ha); setHa(ha);
log.info('Connected to HA broker'); log.info('Connected to HA broker');
if (!demoMode && (!hubUsername || !hubPassword)) { if (!demoMode && (!localApiUsername || !localApiPassword)) {
log.info( log.info(
'Empty username or password in non-demo mode. Removing all Futurehome devices from Home Assistant...', '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...'); log.info('Connecting to Futurehome hub...');
const fimp = await connectHub({ const fimp = await connectHub({
hubIp, hubIp,
username: hubUsername, username: localApiUsername,
password: hubPassword, password: localApiPassword,
demo: demoMode, demo: demoMode,
}); });
fimp.subscribe('#'); fimp.subscribe('#');
setFimp(fimp); setFimp(fimp);
log.info('Connected to Futurehome hub'); log.info('Connected to Futurehome hub');
const house = await sendFimpMsg({ const house = await pollVinculum('house');
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 hubId = house.val.param.house.hubId; const hubId = house.val.param.house.hubId;
const devices = await sendFimpMsg({ const devices = await pollVinculum('device');
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 haConfig = retainedMessages.filter((msg) => const haConfig = retainedMessages.filter((msg) =>
msg.topic.endsWith('/config'), msg.topic.endsWith('/config'),
@ -181,15 +171,24 @@ import { delay } from './utils';
log.error('Failed publishing device', device, e); log.error('Failed publishing device', device, e);
} }
} }
if (demoMode || (thingsplexUsername && thingsplexPassword)) {
Object.assign(
commandHandlers,
exposeSmarthubTools({
hubId,
demoMode,
hubIp,
thingsplexUsername,
thingsplexPassword,
}).commandHandlers,
);
}
setHaCommandHandlers(commandHandlers); setHaCommandHandlers(commandHandlers);
}; };
vinculumDevicesToHa(devices); vinculumDevicesToHa(devices);
let knownDeviceIds = new Set(devices.val.param.device.map((d: any) => d?.id)); let knownDeviceIds = new Set(devices.val.param.device.map((d: any) => d?.id));
// todo
// exposeSmarthubTools();
fimp.on('message', async (topic, buf) => { fimp.on('message', async (topic, buf) => {
try { try {
const msg: FimpResponse = JSON.parse(buf.toString()); const msg: FimpResponse = JSON.parse(buf.toString());
@ -252,6 +251,11 @@ import { delay } from './utils';
break; break;
} }
case 'evt.thing.inclusion_status_report': {
handleInclusionStatusReport(hubId, msg);
break;
}
default: { default: {
// Handle any event that matches the pattern: evt.<something>.report // Handle any event that matches the pattern: evt.<something>.report
if (/^evt\..+\.report$/.test(msg.type ?? '')) { if (/^evt\..+\.report$/.test(msg.type ?? '')) {
@ -271,14 +275,7 @@ import { delay } from './utils';
const pollState = () => { const pollState = () => {
log.debug('Refreshing Vinculum state after 30 seconds...'); log.debug('Refreshing Vinculum state after 30 seconds...');
sendFimpMsg({ pollVinculum('state').catch((e) => log.warn('Failed to request state', e));
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));
}; };
// Request initial state // Request initial state
pollState(); pollState();
@ -290,14 +287,9 @@ import { delay } from './utils';
const pollDevices = () => { const pollDevices = () => {
log.debug('Refreshing Vinculum devices after 30 minutes...'); log.debug('Refreshing Vinculum devices after 30 minutes...');
sendFimpMsg({ pollVinculum('device').catch((e) =>
address: '/rt:app/rn:vinculum/ad:1', log.warn('Failed to request devices', e),
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));
}; };
// Poll devices every 30 minutes (1800000 ms) // Poll devices every 30 minutes (1800000 ms)
if (!demoMode) { 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: configuration:
hub_ip: hub_ip:
name: Hub IP (valgfrit) name: Hub IP (valgfri)
description: Lad stå tom for automatisk opdagelse. description: Lad feltet være tomt for automatisk opdagelse.
username: fh_username:
name: Brugernavn name: Lokal API-brugernavn
description: Dit brugernavn til lokal API/MQTT-adgang til Smarthub. description: Dit Smarthub lokale API/MQTT-brugernavn.
password: fh_password:
name: Adgangskode name: Lokal API-adgangskode
description: Din adgangskode til lokal API/MQTT-adgang til Smarthub. 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: demo_mode:
name: Demotilstand 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: show_debug_log:
name: Vis fejllog name: Vis fejllog

View File

@ -2,14 +2,20 @@ configuration:
hub_ip: hub_ip:
name: Hub-IP (optional) name: Hub-IP (optional)
description: Leer lassen für automatische Erkennung. description: Leer lassen für automatische Erkennung.
username: fh_username:
name: Benutzername name: Lokaler API-Benutzername
description: Dein Benutzername für den lokalen API/MQTT-Zugang zum Smarthub. description: Dein Smarthub lokaler API-/MQTT-Benutzername.
password: fh_password:
name: Passwort name: Lokales API-Passwort
description: Dein Passwort für den lokalen API/MQTT-Zugang zum Smarthub. 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: demo_mode:
name: Demomodus name: Demo-Modus
description: Verwende einen aufgezeichneten Zustand von einem echten Futurehome Smarthub, um Geräte zu simulieren. description: Verwende einen aufgezeichneten Beispielzustand eines echten Futurehome Smarthubs, um Geräte zu simulieren.
show_debug_log: show_debug_log:
name: Debug-Log anzeigen name: Debug-Log anzeigen

View File

@ -1,15 +1,21 @@
configuration: configuration:
hub_ip: hub_ip:
name: Hub IP (valgfrit) name: Hub IP (valgfri)
description: Lad stå tom for automatisk opdagelse. description: Lad feltet være tomt for automatisk opdagelse.
username: fh_username:
name: Brugernavn name: Lokal API-brugernavn
description: Dit brugernavn til lokal API/MQTT-adgang til Smarthub. description: Dit Smarthub lokale API/MQTT-brugernavn.
password: fh_password:
name: Adgangskode name: Lokal API-adgangskode
description: Din adgangskode til lokal API/MQTT-adgang til Smarthub. 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: demo_mode:
name: Demotilstand 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: show_debug_log:
name: Vis fejllog 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: hub_ip:
name: Hub IP (Optional) name: Hub IP (Optional)
description: Leave blank for automatic discovery. description: Leave blank for automatic discovery.
username: fh_username:
name: Username name: Local API Username
description: Your Smarthub local API/MQTT username. description: Your Smarthub local API/MQTT username.
password: fh_password:
name: Password name: Local API Password
description: Your Smarthub local API/MQTT 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: demo_mode:
name: Demo Mode name: Demo Mode
description: Use a sample recorded state from a real Futurehome Smarthub to simulate devices. 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: configuration:
hub_ip: hub_ip:
name: Keskittimen IP (valinnainen) name: Hubin IP-osoite (valinnainen)
description: Jätä tyhjäksi automaattista tunnistusta varten. description: Jätä tyhjäksi automaattista etsintää varten.
username: fh_username:
name: Käyttäjätunnus name: Paikallinen API-käyttäjätunnus
description: Paikallisen Smarthub API/MQTT -yhteyden käyttäjätunnus. description: Smarthubin paikallinen API/MQTT-käyttäjätunnuksesi.
password: fh_password:
name: Salasana name: Paikallinen API-salasana
description: Paikallisen Smarthub API/MQTT -yhteyden 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: demo_mode:
name: Demotila 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: show_debug_log:
name: Näytä vianmääritysloki name: Näytä virheloki

View File

@ -1,15 +1,21 @@
configuration: configuration:
hub_ip: hub_ip:
name: Miðlari IP (valkvæmt) name: IP-tala miðlara (valkvætt)
description: Láttu autt til að uppgötva sjálfkrafa. description: Skildu eftir autt til að uppgötva sjálfkrafa.
username: fh_username:
name: Notandanafn name: Notandanafn fyrir staðbundið API
description: Notandanafn þitt fyrir staðbundið API/MQTT aðgang að Smarthub. description: Notandanafn fyrir staðbundið API/MQTT í Smarthub.
password: fh_password:
name: Lykilorð name: Lykilorð fyrir staðbundið API
description: Lykilorðið þitt fyrir staðbundið API/MQTT aðgang að Smarthub. 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: demo_mode:
name: Sýnishamur 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: 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: configuration:
hub_ip: hub_ip:
name: Hub IP (valgfritt) name: Hub-IP (valgfritt)
description: La stå tomt for automatisk oppdagelse. description: La stå tomt for automatisk oppdagelse.
username: fh_username:
name: Brukernavn name: Lokalt API-brukernavn
description: Ditt brukernavn for lokal API/MQTT-tilgang til Smarthub. description: Ditt lokale API-/MQTT-brukernavn for Smarthub.
password: fh_password:
name: Passord name: Lokalt API-passord
description: Ditt passord for lokal API/MQTT-tilgang til Smarthub. 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: demo_mode:
name: Demomodus 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: show_debug_log:
name: Vis feillogg name: Vis feilsøkingslogg

View File

@ -1,15 +1,21 @@
configuration: configuration:
hub_ip: hub_ip:
name: Hub IP (valfritt) name: Hub-IP (valfritt)
description: La stå tomt for automatisk oppdaging. description: La stå tomt for automatisk oppdaging.
username: fh_username:
name: Brukarnamn name: Lokalt API-brukarnamn
description: Brukarnamnet ditt for lokal API/MQTT-tilgang til Smarthub. description: Ditt lokale API-/MQTT-brukarnamn for Smarthub.
password: fh_password:
name: Passord name: Lokalt API-passord
description: Passordet ditt for lokal API/MQTT-tilgang til Smarthub. 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: demo_mode:
name: Demomodus 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: show_debug_log:
name: Vis feillogg name: Vis feilsøkingslogg

View File

@ -1,15 +1,21 @@
configuration: configuration:
hub_ip: hub_ip:
name: Hub IP (valgfritt) name: Hub-IP (valgfritt)
description: La stå tomt for automatisk oppdagelse. description: La stå tomt for automatisk oppdagelse.
username: fh_username:
name: Brukernavn name: Lokalt API-brukernavn
description: Ditt brukernavn for lokal API/MQTT-tilgang til Smarthub. description: Ditt lokale API-/MQTT-brukernavn for Smarthub.
password: fh_password:
name: Passord name: Lokalt API-passord
description: Ditt passord for lokal API/MQTT-tilgang til Smarthub. 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: demo_mode:
name: Demomodus 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: show_debug_log:
name: Vis feillogg name: Vis feilsøkingslogg

View File

@ -1,15 +1,21 @@
configuration: configuration:
hub_ip: hub_ip:
name: IP huba (opcjonalne) name: IP centralki (opcjonalne)
description: Pozostaw puste, aby wykryć automatycznie. description: Pozostaw puste, aby włączyć automatyczne wykrywanie.
username: fh_username:
name: Nazwa użytkownika name: Nazwa użytkownika lokalnego API
description: Twoja nazwa użytkownika do lokalnego API/MQTT Smarthuba. description: Twoja nazwa użytkownika lokalnego API/MQTT Smarthub.
password: fh_password:
name: Hasło name: Hasło do lokalnego API
description: Twoje hasło do lokalnego API/MQTT Smarthuba. 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: demo_mode:
name: Tryb demo name: Tryb demonstracyjny
description: Użyj przykładowego zapisanego stanu z prawdziwego Futurehome Smarthub, aby symulować urządzenia. description: Użyj zapisanego stanu z prawdziwego Futurehome Smarthub, aby symulować urządzenia.
show_debug_log: show_debug_log:
name: Pokaż dziennik debugowania 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: configuration:
hub_ip: hub_ip:
name: Hub IP (valfritt) name: Hubbens IP (valfritt)
description: Lämna tomt för automatisk upptäckt. description: Lämna tomt för automatisk upptäckt.
username: fh_username:
name: Användarnamn name: Lokalt API-användarnamn
description: Ditt användarnamn för lokal API/MQTT-åtkomst till Smarthub. description: Ditt lokala API-/MQTT-användarnamn för Smarthub.
password: fh_password:
name: Lösenord name: Lösenord för lokalt API
description: Ditt lösenord för lokal API/MQTT-åtkomst till Smarthub. 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: demo_mode:
name: Demoläge name: Demoläge
description: Använd ett inspelat exempel från en riktig Futurehome Smarthub för att simulera enheter. description: Använd ett inspelat exempel från en riktig Futurehome Smarthub för att simulera enheter.
show_debug_log: 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: configuration:
hub_ip: hub_ip:
name: Hub IP (valfritt) name: Hubbens IP (valfritt)
description: Lämna tomt för automatisk upptäckt. description: Lämna tomt för automatisk upptäckt.
username: fh_username:
name: Användarnamn name: Lokalt API-användarnamn
description: Ditt användarnamn för lokal API/MQTT-åtkomst till Smarthub. description: Ditt lokala API-/MQTT-användarnamn för Smarthub.
password: fh_password:
name: Lösenord name: Lösenord för lokalt API
description: Ditt lösenord för lokal API/MQTT-åtkomst till Smarthub. 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: demo_mode:
name: Demoläge name: Demoläge
description: Använd ett inspelat exempel från en riktig Futurehome Smarthub för att simulera enheter. 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: Показувати журнал налагодження