diff --git a/docs/Changelog.md b/docs/Changelog.md
index 2408865..b200cc1 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file. This project uses [semantic versioning](https://semver.org/).
+## 2.4.0 (2024-09-30)
+ * Behavior change: HBR will now ensure HomeKit accessory names are compliant with [HomeKit's naming guidelines](https://developer.apple.com/design/human-interface-guidelines/homekit#Help-people-choose-useful-names). Invalid characters will be replaced with a space, and multiple spaces will be squashed.
+ * Housekeeping.
+
## 2.3.3 (2024-09-24)
* Improvement: better name resolution.
* Housekeeping.
diff --git a/homebridge-ui/public/index.html b/homebridge-ui/public/index.html
index 1cfc11a..c48bc2f 100644
--- a/homebridge-ui/public/index.html
+++ b/homebridge-ui/public/index.html
@@ -69,6 +69,7 @@
Introduction
Plugins by HJD:
+ - homebridge-comed-hourlypricing: HomeKit integration for ComEd Hourly Pricing customers.
- homebridge-hunter-hydrawise: Hunter Hydrawise integration for HomeKit.
- homebridge-ratgdo: Ratgdo (non-myQ Liftmaster and Chamberlain) garage door and gate opener support for HomeKit
- homebridge-unifi-access: HomeKit integration for the UniFi Access ecosystem
diff --git a/package-lock.json b/package-lock.json
index 0ce4e37..726132b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,29 +1,29 @@
{
"name": "homebridge-ratgdo",
- "version": "2.3.3",
+ "version": "2.4.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "homebridge-ratgdo",
- "version": "2.3.3",
+ "version": "2.4.0",
"license": "ISC",
"dependencies": {
"@adobe/fetch": "^4.1.9",
"@homebridge/plugin-ui-utils": "^1.0.3",
"bonjour-service": "^1.2.1",
"eventsource": "^2.0.2",
- "homebridge-plugin-utils": "^1.8.2"
+ "homebridge-plugin-utils": "^1.9.0"
},
"devDependencies": {
"@stylistic/eslint-plugin": "^2.8.0",
"@types/eventsource": "^1.1.15",
- "@types/node": "^22.6.1",
+ "@types/node": "^22.7.4",
"eslint": "9.11.1",
"homebridge": "^1.8.4",
"shx": "^0.3.4",
"typescript": "^5.6.2",
- "typescript-eslint": "^8.7.0"
+ "typescript-eslint": "^8.8.0"
},
"engines": {
"homebridge": ">=1.8.0 || >=2.0.0",
@@ -411,9 +411,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "22.6.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.6.1.tgz",
- "integrity": "sha512-V48tCfcKb/e6cVUigLAaJDAILdMP0fUW6BidkPK4GpGjXcfbnoHasCZDwz3N3yVt5we2RHm4XTQCpv0KJz9zqw==",
+ "version": "22.7.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz",
+ "integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
@@ -445,17 +445,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.7.0.tgz",
- "integrity": "sha512-RIHOoznhA3CCfSTFiB6kBGLQtB/sox+pJ6jeFu6FxJvqL8qRxq/FfGO/UhsGgQM9oGdXkV4xUgli+dt26biB6A==",
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.0.tgz",
+ "integrity": "sha512-wORFWjU30B2WJ/aXBfOm1LX9v9nyt9D3jsSOxC3cCaTQGCW5k4jNpmjFv3U7p/7s4yvdjHzwtv2Sd2dOyhjS0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.7.0",
- "@typescript-eslint/type-utils": "8.7.0",
- "@typescript-eslint/utils": "8.7.0",
- "@typescript-eslint/visitor-keys": "8.7.0",
+ "@typescript-eslint/scope-manager": "8.8.0",
+ "@typescript-eslint/type-utils": "8.8.0",
+ "@typescript-eslint/utils": "8.8.0",
+ "@typescript-eslint/visitor-keys": "8.8.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@@ -479,16 +479,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.7.0.tgz",
- "integrity": "sha512-lN0btVpj2unxHlNYLI//BQ7nzbMJYBVQX5+pbNXvGYazdlgYonMn4AhhHifQ+J4fGRYA/m1DjaQjx+fDetqBOQ==",
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.0.tgz",
+ "integrity": "sha512-uEFUsgR+tl8GmzmLjRqz+VrDv4eoaMqMXW7ruXfgThaAShO9JTciKpEsB+TvnfFfbg5IpujgMXVV36gOJRLtZg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.7.0",
- "@typescript-eslint/types": "8.7.0",
- "@typescript-eslint/typescript-estree": "8.7.0",
- "@typescript-eslint/visitor-keys": "8.7.0",
+ "@typescript-eslint/scope-manager": "8.8.0",
+ "@typescript-eslint/types": "8.8.0",
+ "@typescript-eslint/typescript-estree": "8.8.0",
+ "@typescript-eslint/visitor-keys": "8.8.0",
"debug": "^4.3.4"
},
"engines": {
@@ -508,14 +508,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.7.0.tgz",
- "integrity": "sha512-87rC0k3ZlDOuz82zzXRtQ7Akv3GKhHs0ti4YcbAJtaomllXoSO8hi7Ix3ccEvCd824dy9aIX+j3d2UMAfCtVpg==",
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.0.tgz",
+ "integrity": "sha512-EL8eaGC6gx3jDd8GwEFEV091210U97J0jeEHrAYvIYosmEGet4wJ+g0SYmLu+oRiAwbSA5AVrt6DxLHfdd+bUg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.7.0",
- "@typescript-eslint/visitor-keys": "8.7.0"
+ "@typescript-eslint/types": "8.8.0",
+ "@typescript-eslint/visitor-keys": "8.8.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -526,14 +526,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.7.0.tgz",
- "integrity": "sha512-tl0N0Mj3hMSkEYhLkjREp54OSb/FI6qyCzfiiclvJvOqre6hsZTGSnHtmFLDU8TIM62G7ygEa1bI08lcuRwEnQ==",
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.0.tgz",
+ "integrity": "sha512-IKwJSS7bCqyCeG4NVGxnOP6lLT9Okc3Zj8hLO96bpMkJab+10HIfJbMouLrlpyOr3yrQ1cA413YPFiGd1mW9/Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.7.0",
- "@typescript-eslint/utils": "8.7.0",
+ "@typescript-eslint/typescript-estree": "8.8.0",
+ "@typescript-eslint/utils": "8.8.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.3.0"
},
@@ -551,9 +551,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.7.0.tgz",
- "integrity": "sha512-LLt4BLHFwSfASHSF2K29SZ+ZCsbQOM+LuarPjRUuHm+Qd09hSe3GCeaQbcCr+Mik+0QFRmep/FyZBO6fJ64U3w==",
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.0.tgz",
+ "integrity": "sha512-QJwc50hRCgBd/k12sTykOJbESe1RrzmX6COk8Y525C9l7oweZ+1lw9JiU56im7Amm8swlz00DRIlxMYLizr2Vw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -565,14 +565,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.7.0.tgz",
- "integrity": "sha512-MC8nmcGHsmfAKxwnluTQpNqceniT8SteVwd2voYlmiSWGOtjvGXdPl17dYu2797GVscK30Z04WRM28CrKS9WOg==",
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.0.tgz",
+ "integrity": "sha512-ZaMJwc/0ckLz5DaAZ+pNLmHv8AMVGtfWxZe/x2JVEkD5LnmhWiQMMcYT7IY7gkdJuzJ9P14fRy28lUrlDSWYdw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "@typescript-eslint/types": "8.7.0",
- "@typescript-eslint/visitor-keys": "8.7.0",
+ "@typescript-eslint/types": "8.8.0",
+ "@typescript-eslint/visitor-keys": "8.8.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -594,16 +594,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.7.0.tgz",
- "integrity": "sha512-ZbdUdwsl2X/s3CiyAu3gOlfQzpbuG3nTWKPoIvAu1pu5r8viiJvv2NPN2AqArL35NCYtw/lrPPfM4gxrMLNLPw==",
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.0.tgz",
+ "integrity": "sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.7.0",
- "@typescript-eslint/types": "8.7.0",
- "@typescript-eslint/typescript-estree": "8.7.0"
+ "@typescript-eslint/scope-manager": "8.8.0",
+ "@typescript-eslint/types": "8.8.0",
+ "@typescript-eslint/typescript-estree": "8.8.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -617,13 +617,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.7.0.tgz",
- "integrity": "sha512-b1tx0orFCCh/THWPQa2ZwWzvOeyzzp36vkJYOpVg0u8UVOIsfVrnuC9FqAw9gRKn+rG2VmWQ/zDJZzkxUnj/XQ==",
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.0.tgz",
+ "integrity": "sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.7.0",
+ "@typescript-eslint/types": "8.8.0",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
@@ -1868,9 +1868,9 @@
}
},
"node_modules/homebridge-plugin-utils": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/homebridge-plugin-utils/-/homebridge-plugin-utils-1.8.2.tgz",
- "integrity": "sha512-2DPA7+97DmqWCivpZqZl3v5k8HwM7n0YXFN5lLHoPQo+2cRFMx4dNnJm6OSAf8KX8pKjErYcfa9NfmkW8tlePg==",
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/homebridge-plugin-utils/-/homebridge-plugin-utils-1.9.0.tgz",
+ "integrity": "sha512-tcH4ZAGc6jIElM28sEsM8UqYHYBWYiZFyg64UglwJXnu2CCbn0JGE90TLxjypViWWLb7JICVzMdj8KfgK4KQtw==",
"license": "ISC",
"dependencies": {
"mqtt": "5.10.1"
@@ -3319,15 +3319,15 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.7.0.tgz",
- "integrity": "sha512-nEHbEYJyHwsuf7c3V3RS7Saq+1+la3i0ieR3qP0yjqWSzVmh8Drp47uOl9LjbPANac4S7EFSqvcYIKXUUwIfIQ==",
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.8.0.tgz",
+ "integrity": "sha512-BjIT/VwJ8+0rVO01ZQ2ZVnjE1svFBiRczcpr1t1Yxt7sT25VSbPfrJtDsQ8uQTy2pilX5nI9gwxhUyLULNentw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.7.0",
- "@typescript-eslint/parser": "8.7.0",
- "@typescript-eslint/utils": "8.7.0"
+ "@typescript-eslint/eslint-plugin": "8.8.0",
+ "@typescript-eslint/parser": "8.8.0",
+ "@typescript-eslint/utils": "8.8.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
diff --git a/package.json b/package.json
index d374ce1..1fa3da2 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "homebridge-ratgdo",
"displayName": "Homebridge Ratgdo",
- "version": "2.3.3",
+ "version": "2.4.0",
"description": "HomeKit integration for LiftMaster and Chamberlain garage door openers, without requiring myQ.",
"license": "ISC",
"repository": {
@@ -33,18 +33,18 @@
"devDependencies": {
"@stylistic/eslint-plugin": "^2.8.0",
"@types/eventsource": "^1.1.15",
- "@types/node": "^22.6.1",
+ "@types/node": "^22.7.4",
"eslint": "9.11.1",
"homebridge": "^1.8.4",
"shx": "^0.3.4",
"typescript": "^5.6.2",
- "typescript-eslint": "^8.7.0"
+ "typescript-eslint": "^8.8.0"
},
"dependencies": {
"@adobe/fetch": "^4.1.9",
"@homebridge/plugin-ui-utils": "^1.0.3",
"bonjour-service": "^1.2.1",
"eventsource": "^2.0.2",
- "homebridge-plugin-utils": "^1.8.2"
+ "homebridge-plugin-utils": "^1.9.0"
}
}
diff --git a/src/ratgdo-device.ts b/src/ratgdo-device.ts
index c1876e9..6eb2d9a 100644
--- a/src/ratgdo-device.ts
+++ b/src/ratgdo-device.ts
@@ -4,7 +4,7 @@
*/
import { API, CharacteristicValue, HAP, PlatformAccessory } from "homebridge";
import { FetchError, fetch } from "@adobe/fetch";
-import { HomebridgePluginLogging, acquireService, validService } from "homebridge-plugin-utils";
+import { HomebridgePluginLogging, acquireService, validService, validateName } from "homebridge-plugin-utils";
import { RATGDO_MOTION_DURATION, RATGDO_OCCUPANCY_DURATION } from "./settings.js";
import { RatgdoDevice, RatgdoReservedNames, RatgdoVariant } from "./ratgdo-types.js";
import { RatgdoOptions } from "./ratgdo-options.js";
@@ -1293,21 +1293,21 @@ export class RatgdoAccessory {
// We use the garage door service as the natural proxy for the name.
let name = this.accessory.getService(this.hap.Service.GarageDoorOpener)?.getCharacteristic(this.hap.Characteristic.ConfiguredName).value as string;
- if(name.length) {
+ if(name?.length) {
return name;
}
name = this.accessory.getService(this.hap.Service.GarageDoorOpener)?.getCharacteristic(this.hap.Characteristic.Name).value as string;
- if(name.length) {
+ if(name?.length) {
return name;
}
name = this.accessory.displayName;
- if(name.length) {
+ if(name?.length) {
return name;
}
@@ -1325,11 +1325,13 @@ export class RatgdoAccessory {
// Utility function to set the current accessory name of this device.
private set accessoryName(name: string) {
+ const cleanedName = validateName(name);
+
// Set all the internally managed names within Homebridge to the new accessory name.
- this.accessory.displayName = name;
- this.accessory._associatedHAPAccessory.displayName = name;
+ this.accessory.displayName = cleanedName;
+ this.accessory._associatedHAPAccessory.displayName = cleanedName;
// Set all the HomeKit-visible names.
- this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.Name, name);
+ this.accessory.getService(this.hap.Service.AccessoryInformation)?.updateCharacteristic(this.hap.Characteristic.Name, cleanedName);
}
}
diff --git a/src/ratgdo-platform.ts b/src/ratgdo-platform.ts
index 55f364a..df8b3aa 100644
--- a/src/ratgdo-platform.ts
+++ b/src/ratgdo-platform.ts
@@ -4,7 +4,7 @@
*/
import { API, APIEvent, DynamicPlatformPlugin, HAP, Logging, PlatformAccessory, PlatformConfig } from "homebridge";
import { Bonjour, Service } from "bonjour-service";
-import { FeatureOptions, MqttClient } from "homebridge-plugin-utils";
+import { FeatureOptions, MqttClient, validateName } from "homebridge-plugin-utils";
import { PLATFORM_NAME, PLUGIN_NAME, RATGDO_AUTODISCOVERY_INTERVAL, RATGDO_AUTODISCOVERY_PROJECT_NAMES, RATGDO_AUTODISCOVERY_TYPES, RATGDO_HEARTBEAT_DURATION,
RATGDO_HEARTBEAT_INTERVAL, RATGDO_MQTT_TOPIC } from "./settings.js";
import { RatgdoOptions, featureOptionCategories, featureOptions } from "./ratgdo-options.js";
@@ -375,7 +375,7 @@ export class RatgdoPlatform implements DynamicPlatformPlugin {
// It's a new device - let's add it to HomeKit.
if(!accessory) {
- accessory = new this.api.platformAccessory(device.name, uuid);
+ accessory = new this.api.platformAccessory(validateName(device.name), uuid);
// Register this accessory with Homebridge and add it to the accessory array so we can track it.
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);