From 49abb59a6f33c3eebf41ebb4095ac22fbfdc6496 Mon Sep 17 00:00:00 2001 From: Jacky Liang Date: Mon, 19 Aug 2024 16:56:05 -0400 Subject: [PATCH] New features and bug fixes ### UPDATED - Plugin will now check if itself is outdated by comparing between "semver" versions and not through an equals comparison. - Sensor name minimum is now set to the length of "0" to accomodate for the new settings panel form. - Swapped the placements for ADT Sensor Zone and Type, where the sensor zone will now come first. - Authentication API will now properly check for an error message during login in the "getVerificationMethods()" method. ### FIXED - Potential issue with `isMaintenancePeriod()` where the end time may have also included the next day. ### ADDED - New settings panel (classic settings panel will continue to be accessible via the "Plugin" tab > "Classic View" button) --- README.md | 10 +- config.schema.json | 32 +- package.json | 4 +- src/config-ui/vite/public/style.css | 820 ++++++++++++++++++ .../vite/src/components/fingerprint-table.tsx | 216 +++++ .../vite/src/pages/settings-classic.tsx | 53 ++ .../vite/src/pages/settings-fingerprint.tsx | 26 + .../vite/src/pages/settings-general.tsx | 200 +++++ .../vite/src/pages/settings-login.tsx | 124 +++ .../vite/src/pages/settings-plugin.tsx | 162 ++++ .../vite/src/pages/settings-sensors.tsx | 295 +++++++ src/config-ui/vite/src/pages/settings.tsx | 280 ++---- .../vite/src/pages/setup-complete.tsx | 4 +- .../vite/src/pages/setup-sensors.tsx | 2 +- .../vite/src/pages/setup-welcome.tsx | 2 +- src/config-ui/vite/src/router.tsx | 7 +- .../styles/components/fingerprint-table.ts | 14 + .../vite/src/styles/pages/setup-complete.ts | 4 +- .../vite/src/styles/pages/setup-welcome.ts | 4 +- src/lib/auth.ts | 18 +- src/lib/platform.ts | 4 +- src/lib/schema.ts | 4 +- src/lib/utility.ts | 9 +- src/types/config-ui.d.ts | 164 +++- src/types/constant.d.ts | 9 + src/types/shared.d.ts | 8 +- tsconfig.json | 1 + 27 files changed, 2230 insertions(+), 246 deletions(-) create mode 100644 src/config-ui/vite/src/components/fingerprint-table.tsx create mode 100644 src/config-ui/vite/src/pages/settings-classic.tsx create mode 100644 src/config-ui/vite/src/pages/settings-fingerprint.tsx create mode 100644 src/config-ui/vite/src/pages/settings-general.tsx create mode 100644 src/config-ui/vite/src/pages/settings-login.tsx create mode 100644 src/config-ui/vite/src/pages/settings-plugin.tsx create mode 100644 src/config-ui/vite/src/pages/settings-sensors.tsx create mode 100644 src/config-ui/vite/src/styles/components/fingerprint-table.ts diff --git a/README.md b/README.md index 2cd862e..fab8136 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ Here is an example of how the `config.json` file for this plugin should be confi { "name": "Family Room Couch Window 1", "adtName": "Family Room Window (99)", - "adtType": "doorWindow", - "adtZone": 99 + "adtZone": 99, + "adtType": "doorWindow" } ] }, @@ -136,12 +136,12 @@ All sensors are now organized within an array of objects, with each object conta - For display purposes (offers clarity in the event of an unforeseen reset). - __ADT Name__ (`adtName`) - Must match the name shown under the "Name" column in the "System" tab when logged into the portal. -- __ADT Type__ (`adtType`) - - Must match the type shown under the "Device Type" column in the "System" tab when logged into the portal. - - For example, if the type is "Door/Window Sensor", the value should be `doorWindow`. Read the [Supported Devices](#supported-devices) section for more information. - __ADT Zone__ (`adtZone`) - Must match the zone shown under the "Zone" column in the "System" tab when logged into the portal. - For compatibility reasons, only devices with zones 1 through 99 are supported. +- __ADT Type__ (`adtType`) + - Must match the type shown under the "Device Type" column in the "System" tab when logged into the portal. + - For example, if the type is "Door/Window Sensor", the value should be `doorWindow`. Read the [Supported Devices](#supported-devices) section for more information. If you do not wish to add sensors, simply assign an empty array (e.g. `[]`). However, it is advisable to include all supported sensors, as having none does not optimize plugin performance. diff --git a/config.schema.json b/config.schema.json index 19d8a66..a5b7b54 100644 --- a/config.schema.json +++ b/config.schema.json @@ -150,7 +150,7 @@ "required": false, "description": "Optional. Provide a display name for this sensor to differentiate it from the names assigned by ADT technicians during installation.", "placeholder": "e.g. Family Room Couch Window 1", - "minLength": 1, + "minLength": 0, "maxLength": 50 }, "adtName": { @@ -162,6 +162,15 @@ "minLength": 1, "maxLength": 100 }, + "adtZone": { + "title": "ADT Sensor Zone", + "type": "number", + "required": true, + "description": "Specify the exact zone associated with the sensor you want to add. Double-check the zone to ensure the correct sensor is added.", + "placeholder": "e.g. 99", + "minimum": 1, + "maximum": 99 + }, "adtType": { "title": "ADT Sensor Type", "type": "string", @@ -223,15 +232,6 @@ ] } ] - }, - "adtZone": { - "title": "ADT Sensor Zone", - "type": "number", - "required": true, - "description": "Specify the exact zone associated with the sensor you want to add. Double-check the zone to ensure the correct sensor is added.", - "placeholder": "e.g. 99", - "minimum": 1, - "maximum": 99 } } }, @@ -301,10 +301,6 @@ { "key": "fingerprint", "type": "password" - }, - { - "type": "help", - "helpvalue": "
In a future update, you will be able to utilize an upgraded settings panel that allows you to see the details of your fingerprint. Fingerprints generated are randomized.
" } ] }, @@ -328,13 +324,13 @@ "key": "sensors[].adtName", "type": "text" }, - { - "key": "sensors[].adtType", - "type": "select" - }, { "key": "sensors[].adtZone", "type": "number" + }, + { + "key": "sensors[].adtType", + "type": "select" } ] } diff --git a/package.json b/package.json index 42fd797..9670075 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "homebridge-adt-pulse", "displayName": "Homebridge ADT Pulse", - "version": "3.3.5", + "version": "3.4.0", "description": "Homebridge security system platform for ADT Pulse", "main": "./build/index.js", "exports": "./build/index.js", @@ -79,6 +79,7 @@ "react": "18.3.1", "react-dom": "18.3.1", "react-hook-form": "7.52.1", + "semver": "7.6.3", "serialize-error": "11.0.3", "tough-cookie": "4.1.4", "ua-parser-js": "2.0.0-beta.3", @@ -91,6 +92,7 @@ "@types/node": "20.14.11", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", + "@types/semver": "7.5.8", "@types/tough-cookie": "4.0.5", "@typescript-eslint/eslint-plugin": "7.17.0", "@typescript-eslint/parser": "7.17.0", diff --git a/src/config-ui/vite/public/style.css b/src/config-ui/vite/public/style.css index ce1c5ce..154ce71 100644 --- a/src/config-ui/vite/public/style.css +++ b/src/config-ui/vite/public/style.css @@ -1,4 +1,824 @@ +/* Theme Variables */ +:root { + /* Red Theme */ + --config-ui-x-red-bg: #d32f2f1a; + --config-ui-x-red-border: #d32f2f1a; + --config-ui-x-red-text: #f44336; + + /* Pink Theme */ + --config-ui-x-pink-bg: #c2185b1a; + --config-ui-x-pink-border: #c2185b1a; + --config-ui-x-pink-text: #e91e63; + + /* Purple Theme */ + --config-ui-x-purple-bg: #4213671a; + --config-ui-x-purple-border: #4213671a; + --config-ui-x-purple-text: #9c27b0; + + /* Indigo Theme */ + --config-ui-x-indigo-bg: #303f9f1a; + --config-ui-x-indigo-border: #303f9f1a; + --config-ui-x-indigo-text: #3f51b5; + + /* Blue Theme */ + --config-ui-x-blue-bg: #1976d21a; + --config-ui-x-blue-border: #1976d21a; + --config-ui-x-blue-text: #2196f3; + + /* Blue Grey Theme */ + --config-ui-x-blue-grey-bg: #455a641a; + --config-ui-x-blue-grey-border: #455a641a; + --config-ui-x-blue-grey-text: #607d8b; + + /* Navi Blue Theme */ + --config-ui-x-navi-blue-bg: #0000701a; + --config-ui-x-navi-blue-border: #0000701a; + --config-ui-x-navi-blue-text: #009; + + /* Green Theme */ + --config-ui-x-green-bg: #388e3c1a; + --config-ui-x-green-border: #388e3c1a; + --config-ui-x-green-text: #4caf50; + + /* Orange Theme */ + --config-ui-x-orange-bg: #ef6c001a; + --config-ui-x-orange-border: #ef6c001a; + --config-ui-x-orange-text: #ff9800; + + /* Grey Theme */ + --config-ui-x-grey-bg: #6161611a; + --config-ui-x-grey-border: #6161611a; + --config-ui-x-grey-text: #9e9e9e; + + /* Brown Theme */ + --config-ui-x-brown-bg: #4e342e1a; + --config-ui-x-brown-border: #4e342e1a; + --config-ui-x-brown-text: #795548; + + /* Amber Theme */ + --config-ui-x-amber-bg: #ff8f001a; + --config-ui-x-amber-border: #ff8f001a; + --config-ui-x-amber-text: #ffc107; + + /* Teal Theme */ + --config-ui-x-teal-bg: #00695c1a; + --config-ui-x-teal-border: #00695c1a; + --config-ui-x-teal-text: #009688; + + /* Cyan Theme */ + --config-ui-x-cyan-bg: #00838f1a; + --config-ui-x-cyan-border: #00838f1a; + --config-ui-x-cyan-text: #00bcd4; + + /* Deep Purple Theme */ + --config-ui-x-deep-purple-bg: #4527a01a; + --config-ui-x-deep-purple-border: #4527a01a; + --config-ui-x-deep-purple-text: #673ab7; + + /* Dark Mode Theme */ + --config-ui-x-dark-mode-bg: #ffa0001a; + --config-ui-x-dark-mode-border: #ffa0001a; + --config-ui-x-dark-mode-text: #ffa000; + + /* Dark Mode v1 Theme */ + --config-ui-x-dark-mode-v1-bg: #ffa0001a; + --config-ui-x-dark-mode-v1-border: #ffa0001a; + --config-ui-x-dark-mode-v1-text: #ffa000; + + /* Dark Mode Red Theme */ + --config-ui-x-dark-mode-red-bg: #d32f2f1a; + --config-ui-x-dark-mode-red-border: #d32f2f1a; + --config-ui-x-dark-mode-red-text: #d32f2f; + + /* Dark Mode Pink Theme */ + --config-ui-x-dark-mode-pink-bg: #c2185b1a; + --config-ui-x-dark-mode-pink-border: #c2185b1a; + --config-ui-x-dark-mode-pink-text: #c2185b; + + /* Dark Mode Purple Theme */ + --config-ui-x-dark-mode-purple-bg: #9c27b01a; + --config-ui-x-dark-mode-purple-border: #9c27b01a; + --config-ui-x-dark-mode-purple-text: #9c27b0; + + /* Dark Mode Indigo Theme */ + --config-ui-x-dark-mode-indigo-bg: #3f51b51a; + --config-ui-x-dark-mode-indigo-border: #3f51b51a; + --config-ui-x-dark-mode-indigo-text: #3f51b5; + + /* Dark Mode Blue Theme */ + --config-ui-x-dark-mode-blue-bg: #2196f31a; + --config-ui-x-dark-mode-blue-border: #2196f31a; + --config-ui-x-dark-mode-blue-text: #2196f3; + + /* Dark Mode Blue Grey Theme */ + --config-ui-x-dark-mode-blue-grey-bg: #607d8b1a; + --config-ui-x-dark-mode-blue-grey-border: #607d8b1a; + --config-ui-x-dark-mode-blue-grey-text: #607d8b; + + /* Dark Mode Green Theme */ + --config-ui-x-dark-mode-green-bg: #388e3c1a; + --config-ui-x-dark-mode-green-border: #388e3c1a; + --config-ui-x-dark-mode-green-text: #388e3c; + + /* Dark Mode Grey Theme */ + --config-ui-x-dark-mode-grey-bg: #9e9e9e1a; + --config-ui-x-dark-mode-grey-border: #9e9e9e1a; + --config-ui-x-dark-mode-grey-text: #9e9e9e; + + /* Dark Mode Brown Theme */ + --config-ui-x-dark-mode-brown-bg: #7955481a; + --config-ui-x-dark-mode-brown-border: #7955481a; + --config-ui-x-dark-mode-brown-text: #795548; + + /* Dark Mode Teal Theme */ + --config-ui-x-dark-mode-teal-bg: #0096881a; + --config-ui-x-dark-mode-teal-border: #0096881a; + --config-ui-x-dark-mode-teal-text: #009688; + + /* Dark Mode Cyan Theme */ + --config-ui-x-dark-mode-cyan-bg: #00838f1a; + --config-ui-x-dark-mode-cyan-border: #00838f1a; + --config-ui-x-dark-mode-cyan-text: #00838f; +} + +/* Nav links */ +.config-ui-x-red .nav-link { + color: var(--config-ui-x-red-text); +} + +.config-ui-x-pink .nav-link { + color: var(--config-ui-x-pink-text); +} + +.config-ui-x-purple .nav-link { + color: var(--config-ui-x-purple-text); +} + +.config-ui-x-indigo .nav-link { + color: var(--config-ui-x-indigo-text); +} + +.config-ui-x-blue .nav-link { + color: var(--config-ui-x-blue-text); +} + +.config-ui-x-blue-grey .nav-link { + color: var(--config-ui-x-blue-grey-text); +} + +.config-ui-x-navi-blue .nav-link { + color: var(--config-ui-x-navi-blue-text); +} + +.config-ui-x-green .nav-link { + color: var(--config-ui-x-green-text); +} + +.config-ui-x-orange .nav-link { + color: var(--config-ui-x-orange-text); +} + +.config-ui-x-grey .nav-link { + color: var(--config-ui-x-grey-text); +} + +.config-ui-x-brown .nav-link { + color: var(--config-ui-x-brown-text); +} + +.config-ui-x-amber .nav-link { + color: var(--config-ui-x-amber-text); +} + +.config-ui-x-teal .nav-link { + color: var(--config-ui-x-teal-text); +} + +.config-ui-x-cyan .nav-link { + color: var(--config-ui-x-cyan-text); +} + +.config-ui-x-deep-purple .nav-link { + color: var(--config-ui-x-deep-purple-text); +} + +.config-ui-x-dark-mode .nav-link { + color: var(--config-ui-x-dark-mode-text); +} + +.config-ui-x-dark-mode-v1 .nav-link { + color: var(--config-ui-x-dark-mode-v1-text); +} + +.config-ui-x-dark-mode-red .nav-link { + color: var(--config-ui-x-dark-mode-red-text); +} + +.config-ui-x-dark-mode-pink .nav-link { + color: var(--config-ui-x-dark-mode-pink-text); +} + +.config-ui-x-dark-mode-purple .nav-link { + color: var(--config-ui-x-dark-mode-purple-text); +} + +.config-ui-x-dark-mode-indigo .nav-link { + color: var(--config-ui-x-dark-mode-indigo-text); +} + +.config-ui-x-dark-mode-blue .nav-link { + color: var(--config-ui-x-dark-mode-blue-text); +} + +.config-ui-x-dark-mode-blue-grey .nav-link { + color: var(--config-ui-x-dark-mode-blue-grey-text); +} + +.config-ui-x-dark-mode-green .nav-link { + color: var(--config-ui-x-dark-mode-green-text); +} + +.config-ui-x-dark-mode-grey .nav-link { + color: var(--config-ui-x-dark-mode-grey-text); +} + +.config-ui-x-dark-mode-brown .nav-link { + color: var(--config-ui-x-dark-mode-brown-text); +} + +.config-ui-x-dark-mode-teal .nav-link { + color: var(--config-ui-x-dark-mode-teal-text); +} + +.config-ui-x-dark-mode-cyan .nav-link { + color: var(--config-ui-x-dark-mode-cyan-text); +} + +/* Nav links hover */ +.config-ui-x-red .nav-link:hover, +.config-ui-x-red .nav-link:focus, +.config-ui-x-pink .nav-link:hover, +.config-ui-x-pink .nav-link:focus, +.config-ui-x-purple .nav-link:hover, +.config-ui-x-purple .nav-link:focus, +.config-ui-x-indigo .nav-link:hover, +.config-ui-x-indigo .nav-link:focus, +.config-ui-x-blue .nav-link:hover, +.config-ui-x-blue .nav-link:focus, +.config-ui-x-blue-grey .nav-link:hover, +.config-ui-x-blue-grey .nav-link:focus, +.config-ui-x-navi-blue .nav-link:hover, +.config-ui-x-navi-blue .nav-link:focus, +.config-ui-x-green .nav-link:hover, +.config-ui-x-green .nav-link:focus, +.config-ui-x-orange .nav-link:hover, +.config-ui-x-orange .nav-link:focus, +.config-ui-x-grey .nav-link:hover, +.config-ui-x-grey .nav-link:focus, +.config-ui-x-brown .nav-link:hover, +.config-ui-x-brown .nav-link:focus, +.config-ui-x-amber .nav-link:hover, +.config-ui-x-amber .nav-link:focus, +.config-ui-x-teal .nav-link:hover, +.config-ui-x-teal .nav-link:focus, +.config-ui-x-cyan .nav-link:hover, +.config-ui-x-cyan .nav-link:focus, +.config-ui-x-deep-purple .nav-link:hover, +.config-ui-x-deep-purple .nav-link:focus, +.config-ui-x-dark-mode .nav-link:hover, +.config-ui-x-dark-mode .nav-link:focus, +.config-ui-x-dark-mode-v1 .nav-link:hover, +.config-ui-x-dark-mode-v1 .nav-link:focus, +.config-ui-x-dark-mode-red .nav-link:hover, +.config-ui-x-dark-mode-red .nav-link:focus, +.config-ui-x-dark-mode-pink .nav-link:hover, +.config-ui-x-dark-mode-pink .nav-link:focus, +.config-ui-x-dark-mode-purple .nav-link:hover, +.config-ui-x-dark-mode-purple .nav-link:focus, +.config-ui-x-dark-mode-indigo .nav-link:hover, +.config-ui-x-dark-mode-indigo .nav-link:focus, +.config-ui-x-dark-mode-blue .nav-link:hover, +.config-ui-x-dark-mode-blue .nav-link:focus, +.config-ui-x-dark-mode-blue-grey .nav-link:hover, +.config-ui-x-dark-mode-blue-grey .nav-link:focus, +.config-ui-x-dark-mode-green .nav-link:hover, +.config-ui-x-dark-mode-green .nav-link:focus, +.config-ui-x-dark-mode-grey .nav-link:hover, +.config-ui-x-dark-mode-grey .nav-link:focus, +.config-ui-x-dark-mode-brown .nav-link:hover, +.config-ui-x-dark-mode-brown .nav-link:focus { + filter: brightness(1.2); +} + /* Navigation tabs */ .nav-tabs .nav-link { color: inherit; } + +/* Checkbox */ +.config-ui-x-red .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-red-text); +} + +.config-ui-x-pink .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-pink-text); +} + +.config-ui-x-purple .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-purple-text); +} + +.config-ui-x-indigo .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-indigo-text); +} + +.config-ui-x-blue .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-blue-text); +} + +.config-ui-x-blue-grey .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-blue-grey-text); +} + +.config-ui-x-navi-blue .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-navi-blue-text); +} + +.config-ui-x-green .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-green-text); +} + +.config-ui-x-orange .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-orange-text); +} + +.config-ui-x-grey .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-grey-text); +} + +.config-ui-x-brown .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-brown-text); +} + +.config-ui-x-amber .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-amber-text); +} + +.config-ui-x-teal .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-teal-text); +} + +.config-ui-x-cyan .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-cyan-text); +} + +.config-ui-x-deep-purple .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-deep-purple-text); +} + +.config-ui-x-dark-mode .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-text); +} + +.config-ui-x-dark-mode-v1 .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-v1-text); +} + +.config-ui-x-dark-mode-red .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-red-text); +} + +.config-ui-x-dark-mode-pink .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-pink-text); +} + +.config-ui-x-dark-mode-purple .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-purple-text); +} + +.config-ui-x-dark-mode-indigo .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-indigo-text); +} + +.config-ui-x-dark-mode-blue .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-blue-text); +} + +.config-ui-x-dark-mode-blue-grey .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-blue-grey-text); +} + +.config-ui-x-dark-mode-green .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-green-text); +} + +.config-ui-x-dark-mode-grey .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-grey-text); +} + +.config-ui-x-dark-mode-brown .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-brown-text); +} + +.config-ui-x-dark-mode-teal .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-teal-text); +} + +.config-ui-x-dark-mode-cyan .form-check-input[type="checkbox"] { + border-color: var(--config-ui-x-dark-mode-cyan-text); +} + +/* Checkbox checked */ +.config-ui-x-red .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-red-text); +} + +.config-ui-x-pink .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-pink-text); +} + +.config-ui-x-purple .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-purple-text); +} + +.config-ui-x-indigo .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-indigo-text); +} + +.config-ui-x-blue .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-blue-text); +} + +.config-ui-x-blue-grey .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-blue-grey-text); +} + +.config-ui-x-navi-blue .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-navi-blue-text); +} + +.config-ui-x-green .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-green-text); +} + +.config-ui-x-orange .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-orange-text); +} + +.config-ui-x-grey .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-grey-text); +} + +.config-ui-x-brown .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-brown-text); +} + +.config-ui-x-amber .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-amber-text); +} + +.config-ui-x-teal .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-teal-text); +} + +.config-ui-x-cyan .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-cyan-text); +} + +.config-ui-x-deep-purple .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-deep-purple-text); +} + +.config-ui-x-dark-mode .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-text); +} + +.config-ui-x-dark-mode-v1 .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-v1-text); +} + +.config-ui-x-dark-mode-red .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-red-text); +} + +.config-ui-x-dark-mode-pink .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-pink-text); +} + +.config-ui-x-dark-mode-purple .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-purple-text); +} + +.config-ui-x-dark-mode-indigo .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-indigo-text); +} + +.config-ui-x-dark-mode-blue .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-blue-text); +} + +.config-ui-x-dark-mode-blue-grey .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-blue-grey-text); +} + +.config-ui-x-dark-mode-green .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-green-text); +} + +.config-ui-x-dark-mode-grey .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-grey-text); +} + +.config-ui-x-dark-mode-brown .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-brown-text); +} + +.config-ui-x-dark-mode-teal .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-teal-text); +} + +.config-ui-x-dark-mode-cyan .form-check-input:checked[type="checkbox"] { + background-color: var(--config-ui-x-dark-mode-cyan-text); +} + +/* Accordion button */ +.config-ui-x-red .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-red-bg); + border-color: var(--config-ui-x-red-border); + color: var(--config-ui-x-red-text); +} + +.config-ui-x-pink .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-pink-bg); + border-color: var(--config-ui-x-pink-border); + color: var(--config-ui-x-pink-text); +} + +.config-ui-x-purple .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-purple-bg); + border-color: var(--config-ui-x-purple-border); + color: var(--config-ui-x-purple-text); +} + +.config-ui-x-indigo .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-indigo-bg); + border-color: var(--config-ui-x-indigo-border); + color: var(--config-ui-x-indigo-text); +} + +.config-ui-x-blue .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-blue-bg); + border-color: var(--config-ui-x-blue-border); + color: var(--config-ui-x-blue-text); +} + +.config-ui-x-blue-grey .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-blue-grey-bg); + border-color: var(--config-ui-x-blue-grey-border); + color: var(--config-ui-x-blue-grey-text); +} + +.config-ui-x-navi-blue .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-navi-blue-bg); + border-color: var(--config-ui-x-navi-blue-border); + color: var(--config-ui-x-navi-blue-text); +} + +.config-ui-x-green .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-green-bg); + border-color: var(--config-ui-x-green-border); + color: var(--config-ui-x-green-text); +} + +.config-ui-x-orange .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-orange-bg); + border-color: var(--config-ui-x-orange-border); + color: var(--config-ui-x-orange-text); +} + +.config-ui-x-grey .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-grey-bg); + border-color: var(--config-ui-x-grey-border); + color: var(--config-ui-x-grey-text); +} + +.config-ui-x-brown .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-brown-bg); + border-color: var(--config-ui-x-brown-border); + color: var(--config-ui-x-brown-text); +} + +.config-ui-x-amber .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-amber-bg); + border-color: var(--config-ui-x-amber-border); + color: var(--config-ui-x-amber-text); +} + +.config-ui-x-teal .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-teal-bg); + border-color: var(--config-ui-x-teal-border); + color: var(--config-ui-x-teal-text); +} + +.config-ui-x-cyan .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-cyan-bg); + border-color: var(--config-ui-x-cyan-border); + color: var(--config-ui-x-cyan-text); +} + +.config-ui-x-deep-purple .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-deep-purple-bg); + border-color: var(--config-ui-x-deep-purple-border); + color: var(--config-ui-x-deep-purple-text); +} + +.config-ui-x-dark-mode .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-bg); + border-color: var(--config-ui-x-dark-mode-border); + color: var(--config-ui-x-dark-mode-text); +} + +.config-ui-x-dark-mode-v1 .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-v1-bg); + border-color: var(--config-ui-x-dark-mode-v1-border); + color: var(--config-ui-x-dark-mode-v1-text); +} + +.config-ui-x-dark-mode-red .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-red-bg); + border-color: var(--config-ui-x-dark-mode-red-border); + color: var(--config-ui-x-dark-mode-red-text); +} + +.config-ui-x-dark-mode-pink .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-pink-bg); + border-color: var(--config-ui-x-dark-mode-pink-border); + color: var(--config-ui-x-dark-mode-pink-text); +} + +.config-ui-x-dark-mode-purple .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-purple-bg); + border-color: var(--config-ui-x-dark-mode-purple-border); + color: var(--config-ui-x-dark-mode-purple-text); +} + +.config-ui-x-dark-mode-indigo .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-indigo-bg); + border-color: var(--config-ui-x-dark-mode-indigo-border); + color: var(--config-ui-x-dark-mode-indigo-text); +} + +.config-ui-x-dark-mode-blue .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-blue-bg); + border-color: var(--config-ui-x-dark-mode-blue-border); + color: var(--config-ui-x-dark-mode-blue-text); +} + +.config-ui-x-dark-mode-blue-grey .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-blue-grey-bg); + border-color: var(--config-ui-x-dark-mode-blue-grey-border); + color: var(--config-ui-x-dark-mode-blue-grey-text); +} + +.config-ui-x-dark-mode-green .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-green-bg); + border-color: var(--config-ui-x-dark-mode-green-border); + color: var(--config-ui-x-dark-mode-green-text); +} + +.config-ui-x-dark-mode-grey .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-grey-bg); + border-color: var(--config-ui-x-dark-mode-grey-border); + color: var(--config-ui-x-dark-mode-grey-text); +} + +.config-ui-x-dark-mode-brown .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-brown-bg); + border-color: var(--config-ui-x-dark-mode-brown-border); + color: var(--config-ui-x-dark-mode-brown-text); +} + +.config-ui-x-dark-mode-teal .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-teal-bg); + border-color: var(--config-ui-x-dark-mode-teal-border); + color: var(--config-ui-x-dark-mode-teal-text); +} + +.config-ui-x-dark-mode-cyan .accordion-button:not(.collapsed) { + background-color: var(--config-ui-x-dark-mode-cyan-bg); + border-color: var(--config-ui-x-dark-mode-cyan-border); + color: var(--config-ui-x-dark-mode-cyan-text); +} + +/* Background text */ +.config-ui-x-red .text-bg-primary { + background-color: var(--config-ui-x-red-text) !important; +} + +.config-ui-x-pink .text-bg-primary { + background-color: var(--config-ui-x-pink-text) !important; +} + +.config-ui-x-purple .text-bg-primary { + background-color: var(--config-ui-x-purple-text) !important; +} + +.config-ui-x-indigo .text-bg-primary { + background-color: var(--config-ui-x-indigo-text) !important; +} + +.config-ui-x-blue .text-bg-primary { + background-color: var(--config-ui-x-blue-text) !important; +} + +.config-ui-x-blue-grey .text-bg-primary { + background-color: var(--config-ui-x-blue-grey-text) !important; +} + +.config-ui-x-navi-blue .text-bg-primary { + background-color: var(--config-ui-x-navi-blue-text) !important; +} + +.config-ui-x-green .text-bg-primary { + background-color: var(--config-ui-x-green-text) !important; +} + +.config-ui-x-orange .text-bg-primary { + background-color: var(--config-ui-x-orange-text) !important; +} + +.config-ui-x-grey .text-bg-primary { + background-color: var(--config-ui-x-grey-text) !important; +} + +.config-ui-x-brown .text-bg-primary { + background-color: var(--config-ui-x-brown-text) !important; +} + +.config-ui-x-amber .text-bg-primary { + background-color: var(--config-ui-x-amber-text) !important; +} + +.config-ui-x-teal .text-bg-primary { + background-color: var(--config-ui-x-teal-text) !important; +} + +.config-ui-x-cyan .text-bg-primary { + background-color: var(--config-ui-x-cyan-text) !important; +} + +.config-ui-x-deep-purple .text-bg-primary { + background-color: var(--config-ui-x-deep-purple-text) !important; +} + +.config-ui-x-dark-mode .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-text) !important; +} + +.config-ui-x-dark-mode-v1 .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-v1-text) !important; +} + +.config-ui-x-dark-mode-red .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-red-text) !important; +} + +.config-ui-x-dark-mode-pink .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-pink-text) !important; +} + +.config-ui-x-dark-mode-purple .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-purple-text) !important; +} + +.config-ui-x-dark-mode-indigo .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-indigo-text) !important; +} + +.config-ui-x-dark-mode-blue .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-blue-text) !important; +} + +.config-ui-x-dark-mode-blue-grey .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-blue-grey-text) !important; +} + +.config-ui-x-dark-mode-green .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-green-text) !important; +} + +.config-ui-x-dark-mode-grey .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-grey-text) !important; +} + +.config-ui-x-dark-mode-brown .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-brown-text) !important; +} + +.config-ui-x-dark-mode-teal .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-teal-text) !important; +} + +.config-ui-x-dark-mode-cyan .text-bg-primary { + background-color: var(--config-ui-x-dark-mode-cyan-text) !important; +} diff --git a/src/config-ui/vite/src/components/fingerprint-table.tsx b/src/config-ui/vite/src/components/fingerprint-table.tsx new file mode 100644 index 0000000..2c268db --- /dev/null +++ b/src/config-ui/vite/src/components/fingerprint-table.tsx @@ -0,0 +1,216 @@ +import _ from 'lodash'; +import React, { useEffect, useState } from 'react'; + +import { styles } from '@/config-ui/vite/src/styles/components/fingerprint-table.js'; +import type { + FingerprintTableParsedObject, + FingerprintTableProps, + FingerprintTableRenderTableData, + FingerprintTableRenderTablePropertyProperty, + FingerprintTableRenderTablePropertyReturns, + FingerprintTableRenderTableReturns, + FingerprintTableRenderTableValueProperty, + FingerprintTableRenderTableValueReturns, + FingerprintTableRenderTableValueValue, + FingerprintTableStatus, +} from '@/types/config-ui.d.ts'; + +/** + * Fingerprint table. + * + * @constructor + * + * @since 1.0.0 + */ +export default function FingerprintTable(props: FingerprintTableProps) { + const { fingerprint } = props; + + const [parsedObject, setParsedObject] = useState({}); + const [status, setStatus] = useState('loading'); + + /** + * Fingerprint table - Render table property. + * + * @param {FingerprintTableRenderTablePropertyProperty} property - Property. + * + * @returns {FingerprintTableRenderTablePropertyReturns} + * + * @since 1.0.0 + */ + const renderTableProperty = (property: FingerprintTableRenderTablePropertyProperty): FingerprintTableRenderTablePropertyReturns => { + const properties = { + adBlock: 'Blocking Ads', + addBehavior: 'Add Behaviour Support (IE only)', + architecture: 'Architecture', + availableScreenResolution: 'Available Screen Resolution', + canvas: 'Canvas', + colorDepth: 'Color Depth', + cookieSupport: 'Cookie Support', + cpuClass: 'CPU Class', + doNotTrack: 'Do Not Track', + fonts: 'Fonts', + indexedDb: 'IndexedDB Support', + language: 'Language', + localStorage: 'Local Storage Support', + major: 'Major', + maxTouchPoints: 'Max Touch Points', + model: 'Model', + name: 'Name', + openDatabase: 'Web SQL Database Support', + pixelRatio: 'Pixel Ratio', + platform: 'Platform', + plugins: 'Plugins', + screenResolution: 'Screen Resolution', + sessionStorage: 'Session Storage Support', + timezone: 'Time Zone', + timezoneOffset: 'Time Zone (UTC Offset)', + touchEvent: 'Touch Event', + touchStart: 'Touch Start', + touchSupport: 'Touch Support', + type: 'Type', + uaBrowser: 'Browser', + uaCPU: 'CPU', + uaDevice: 'Device', + uaEngine: 'Browser Engine', + uaOS: 'Operating System', + uaPlatform: 'Platform', + uaString: 'User Agent', + userTamperBrowser: 'Tampered - Browser', + userTamperLanguage: 'Tampered - Language', + userTamperOS: 'Tampered - Operating System', + userTamperScreenResolution: 'Tampered - Screen Resolution', + vendor: 'Vendor', + version: 'Version', + webGl: 'WebGL', + }; + + return _.get(properties, [property], property); + }; + + /** + * Fingerprint table - Render table value. + * + * @param {FingerprintTableRenderTableValueProperty} property - Property. + * @param {FingerprintTableRenderTableValueValue} value - Value. + * + * @returns {FingerprintTableRenderTableValueReturns} + * + * @since 1.0.0 + */ + const renderTableValue = (property: FingerprintTableRenderTableValueProperty, value: FingerprintTableRenderTableValueValue): FingerprintTableRenderTableValueReturns => { + if (typeof value === 'string') { + // If the selected properties have a comma-delimited list. + if (['fonts', 'plugins'].includes(property)) { + const items = value.split(',').map((item) => item.trim()).filter((item) => item.length > 0); + + return ( +
    + { + items.map((item) => ( +
  • {item}
  • + )) + } +
+ ); + } + + return value; + } + + // If the value does not exist. + if (value === null) { + return 'N/A'; + } + + // If value is "true". + if (value === true) { + return 'Yes'; + } + + // If value is "false". + if (value === false) { + return 'No'; + } + + return JSON.stringify(value); + }; + + /** + * Fingerprint table - Render table. + * + * @param {FingerprintTableRenderTableData} data - Data. + * + * @returns {FingerprintTableRenderTableReturns} + * + * @since 1.0.0 + */ + const renderTable = (data: FingerprintTableRenderTableData): FingerprintTableRenderTableReturns => { + if (typeof data !== 'object' || data === null) { + return {data}; + } + + return ( + + + { + Object.entries(data).map(([property, value]) => ( + + + + + )) + } + +
{renderTableProperty(property)} + { + (typeof value === 'object' && value !== null) ? ( + renderTable(value) + ) : ( + renderTableValue(property, value) + ) + } +
+ ); + }; + + useEffect(() => { + try { + const parsedFingerprint = JSON.parse(atob(fingerprint)); + + // Valid fingerprints are always a plain object. + if (!_.isPlainObject(parsedFingerprint)) { + setStatus('failed'); + + return; + } + + setParsedObject(parsedFingerprint); + setStatus('success'); + } catch (error) { + setStatus('failed'); + } + }, [fingerprint]); + + // If parsing is successful. + if (status === 'success') { + return ( +
+

The information shown below is for your eyes only. Fingerprints are considered your secondary password. Leaking this will compromise the MFA security to your ADT Pulse account.

+ { + renderTable(_.get(parsedObject, ['fingerprint'])) + } +
+ ); + } + + // If parsing has failed. + if (status === 'failed') { + return ( +

Failed to parse your fingerprint. Please re-run the setup wizard.

+ ); + } + + return ( +

Parsing your fingerprint, please wait...

+ ); +} diff --git a/src/config-ui/vite/src/pages/settings-classic.tsx b/src/config-ui/vite/src/pages/settings-classic.tsx new file mode 100644 index 0000000..12a80f0 --- /dev/null +++ b/src/config-ui/vite/src/pages/settings-classic.tsx @@ -0,0 +1,53 @@ +import React, { useEffect, useState } from 'react'; + +import type { SettingsClassicProps } from '@/types/config-ui.d.ts'; + +/** + * Settings classic. + * + * @constructor + * + * @since 1.0.0 + */ +export default function SettingsClassic(props: SettingsClassicProps) { + const { homebridge, setView } = props; + + const [ready, setReady] = useState(false); + + useEffect(() => { + (async () => { + if (homebridge === undefined) { + return; + } + + homebridge.showSpinner(); + + // Making sure the UI does not randomly flash. + await new Promise((resolve) => { + setTimeout(resolve, 1000); + }); + + // In case the previous view was modern settings. + homebridge.showSchemaForm(); + + // Once the schema form shows, set the view to "ready". + setReady(true); + + homebridge.hideSpinner(); + })(); + }, []); + + if (ready) { + return ( + + ); + } + + return null; +} diff --git a/src/config-ui/vite/src/pages/settings-fingerprint.tsx b/src/config-ui/vite/src/pages/settings-fingerprint.tsx new file mode 100644 index 0000000..f26f5f7 --- /dev/null +++ b/src/config-ui/vite/src/pages/settings-fingerprint.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +import FingerprintTable from '@/config-ui/vite/src/components/fingerprint-table.js'; +import type { SettingsFingerprintProps } from '@/types/config-ui.d.ts'; + +/** + * Settings fingerprint. + * + * @constructor + * + * @since 1.0.0 + */ +export default function SettingsFingerprint(props: SettingsFingerprintProps) { + const { fingerprint } = props; + + if (fingerprint !== '' || import.meta.env.DEV) { + return ( + <> +

This section allows you to explore the contents of the randomly generated browser fingerprint used with your account. If you would like to refresh your fingerprint, re-run the setup wizard located in the "System" tab.

+ + + ); + } + + return null; +} diff --git a/src/config-ui/vite/src/pages/settings-general.tsx b/src/config-ui/vite/src/pages/settings-general.tsx new file mode 100644 index 0000000..5bcb3f9 --- /dev/null +++ b/src/config-ui/vite/src/pages/settings-general.tsx @@ -0,0 +1,200 @@ +import React from 'react'; +import { Controller } from 'react-hook-form'; + +import type { + SettingsGeneralHandleOptionsChangeChecked, + SettingsGeneralHandleOptionsChangeReturns, + SettingsGeneralHandleOptionsChangeValue, + SettingsGeneralOptionsCheckboxes, + SettingsGeneralProps, +} from '@/types/config-ui.d.ts'; + +/** + * Settings general. + * + * @constructor + * + * @since 1.0.0 + */ +export default function SettingsGeneral(props: SettingsGeneralProps) { + const { control, getValues, setValue } = props; + + // Checkboxes for "Advanced Options". + const optionsCheckboxes: SettingsGeneralOptionsCheckboxes = [ + { + id: 'disable-alarm-ringing-switch', + label: 'Disable "Alarm Ringing" switch', + value: 'disableAlarmRingingSwitch', + }, + { + id: 'ignore-sensor-problem-status', + label: 'Ignore "Sensor Problem" Panel Status', + value: 'ignoreSensorProblemStatus', + }, + ]; + + /** + * Settings general - Handle options change. + * + * @param {SettingsGeneralHandleOptionsChangeChecked} checked - Checked. + * @param {SettingsGeneralHandleOptionsChangeValue} value - Value. + * + * @returns {SettingsGeneralHandleOptionsChangeReturns} + * + * @since 1.0.0 + */ + const handleOptionsChange = (checked: SettingsGeneralHandleOptionsChangeChecked, value: SettingsGeneralHandleOptionsChangeValue): SettingsGeneralHandleOptionsChangeReturns => { + const previousValues = getValues('options'); + + if (checked) { + if (!previousValues.includes(value)) { + setValue('options', [...previousValues, value]); + } + } else { + const updatedValues = previousValues.filter((previousValue) => previousValue !== value); + + setValue('options', updatedValues); + } + }; + + return ( + <> +
+ (( + <> + +
+ {(fieldState.error) ? fieldState.error.message : 'Enter a unique name for this plugin. The name will mainly be used for identification purposes, such as in Homebridge logs.'} +
+ + ))} + /> +
+
+
+
+ (( + <> + +
+ {(fieldState.error) ? fieldState.error.message : 'Choose the operational mode for this plugin. Debug mode is enabled only when Homebridge debug mode is on; there is no separate setting for this.'} +
+ + ))} + /> +
+
+
+
+ (( + <> + +
+ {(fieldState.error) ? fieldState.error.message : 'Choose the synchronization speed for this plugin. Designed to enhance the performance of devices with older hardware. Results in slower device updates.'} +
+ + ))} + /> +
+
+
+
+
Advanced Options
+ { + optionsCheckboxes.map((optionsCheckbox) => ( +
+ ( + + )} + /> +
+ )) + } +
+ Customize the features of this plugin. Please note these advanced options will disable expected functionality. Only enable them if necessary. +
+
+ + ); +} diff --git a/src/config-ui/vite/src/pages/settings-login.tsx b/src/config-ui/vite/src/pages/settings-login.tsx new file mode 100644 index 0000000..14bddc6 --- /dev/null +++ b/src/config-ui/vite/src/pages/settings-login.tsx @@ -0,0 +1,124 @@ +import React from 'react'; +import { Controller } from 'react-hook-form'; + +import type { SettingsLoginProps } from '@/types/config-ui.d.ts'; + +/** + * Settings login. + * + * @constructor + * + * @since 1.0.0 + */ +export default function SettingsLogin(props: SettingsLoginProps) { + const { control, getValues } = props; + + return ( + <> +
+ (( + <> + +
+ {(fieldState.error) ? fieldState.error.message : 'Select the portal region based on where you are subscribed in.'} +
+ + ))} + /> +
+
+
+
+ (( + <> + +
+ {(fieldState.error) ? fieldState.error.message : 'Provide the username you use to login to the portal.'} +
+ + ))} + /> +
+
+
+
+ (( + <> + +
+ {(fieldState.error) ? fieldState.error.message : 'Provide the password you use to login to the portal.'} +
+ + ))} + /> +
+
+
+ + ); +} diff --git a/src/config-ui/vite/src/pages/settings-plugin.tsx b/src/config-ui/vite/src/pages/settings-plugin.tsx new file mode 100644 index 0000000..8da7b69 --- /dev/null +++ b/src/config-ui/vite/src/pages/settings-plugin.tsx @@ -0,0 +1,162 @@ +import _ from 'lodash'; +import React, { useEffect, useState } from 'react'; + +import type { SettingsPluginProps } from '@/types/config-ui.d.ts'; + +/** + * Settings plugin. + * + * @constructor + * + * @since 1.0.0 + */ +export default function SettingsPlugin(props: SettingsPluginProps) { + const { homebridge, setView } = props; + + const [information, setInformation] = useState({ + name: 'homebridge-adt-pulse', + version: '1.0.0', + verified: false, + instanceName: 'Unknown', + instanceId: 'Unknown', + homebridgeVersion: 'Unknown', + nodeVersion: 'Unknown', + platform: 'Unknown', + runningOn: 'Unknown', + }); + + useEffect(() => { + (async () => { + if (homebridge === undefined) { + return; + } + + const runningNames = { + runningInDocker: 'Docker', + runningInFreeBSD: 'FreeBSD', + runningInLinux: 'Linux', + runningInPackageMode: 'Package Mode', + runningInSynologyPackage: 'Synology Package', + runningOnRaspberryPi: 'Raspberry Pi', + }; + const running = Object.entries(homebridge.serverEnv.env) + .filter(([key, value]) => key.startsWith('running') && value === true) + .map(([key]) => _.get(runningNames, [key], '') as string); + + setInformation({ + name: _.get(homebridge, ['plugin', 'name'], ''), + version: _.get(homebridge, ['plugin', 'installedVersion'], ''), + verified: _.get(homebridge, ['plugin', 'verifiedPlugin'], false), + instanceName: _.get(homebridge, ['serverEnv', 'env', 'homebridgeInstanceName'], ''), + instanceId: _.get(homebridge, ['serverEnv', 'env', 'instanceId'], ''), + homebridgeVersion: _.get(homebridge, ['serverEnv', 'env', 'homebridgeVersion'], ''), + nodeVersion: _.get(homebridge, ['serverEnv', 'env', 'nodeVersion'], ''), + platform: _.get(homebridge, ['serverEnv', 'env', 'platform'], ''), + runningOn: (running.length > 0) ? running.join(', ') : 'Unknown', + }); + })(); + }, [homebridge]); + + return ( + <> +
+

+ + You are currently + {' '} + { + _.sample([ + 'running', + 'enjoying', + 'utilizing', + 'rocking', + 'dominating', + ]) + } + +

+
+

+ {information.name} + {' '} + + v + {information.version} + +

+ verified +
+ +
+
+
System Information
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Instance Name: + {information.instanceName} +
Instance ID: + {information.instanceId} +
Homebridge Version: + {information.homebridgeVersion} +
Node.js Version + {information.nodeVersion} +
Platform + {information.platform} +
Running on: + {information.runningOn} +
+
+
+ + +
+ + ); +} diff --git a/src/config-ui/vite/src/pages/settings-sensors.tsx b/src/config-ui/vite/src/pages/settings-sensors.tsx new file mode 100644 index 0000000..1953b7a --- /dev/null +++ b/src/config-ui/vite/src/pages/settings-sensors.tsx @@ -0,0 +1,295 @@ +import React from 'react'; +import { Controller, useFieldArray, useWatch } from 'react-hook-form'; + +import type { SettingsSensorsGetSensorHeaderIndex, SettingsSensorsGetSensorHeaderReturns, SettingsSensorsProps } from '@/types/config-ui.d.ts'; + +/** + * Settings sensors. + * + * @constructor + * + * @since 1.0.0 + */ +export default function SettingsSensors(props: SettingsSensorsProps) { + const { control } = props; + + const { fields: sensors, append, remove } = useFieldArray({ + control, + name: 'sensors', + }); + const watch = useWatch({ + control, + name: 'sensors', + }); + + /** + * Settings sensors - Get sensor header. + * + * @param {SettingsSensorsGetSensorHeaderIndex} index - Index. + * + * @returns {SettingsSensorsGetSensorHeaderReturns} + * + * @since 1.0.0 + */ + const getSensorHeader = (index: SettingsSensorsGetSensorHeaderIndex): SettingsSensorsGetSensorHeaderReturns => { + const sensor = watch[index]; + + if (sensor === undefined) { + return ''; + } + + const sensorName = sensor.name; + const sensorAdtName = sensor.adtName; + + if (sensorName && sensorAdtName) { + return `${sensorName} (${sensorAdtName})`; + } + + if (sensorName) { + return `${sensorName}`; + } + + if (sensorAdtName) { + return `${sensorAdtName}`; + } + + return 'Sensor'; + }; + + return ( + <> +

This section allows you to define your ADT connected sensors here. Sensors include connected devices like "Door/Window Sensor" or "Motion Sensor". If you would like to update your sensors, re-run the setup wizard.

+

+ A maximum of 147 sensors can be added (3 slots are reserved for the gateway, security panel, and alarm ringing switch). + {' '} + Z-Wave connected accessories are not supported. +

+
+ { + sensors.map((sensor, index) => ( +
+
+ +
+
+
+
+ ( + <> + +
+ { + (fieldState.error) ? fieldState.error.message : ( + <> + Optional. + {' '} + Provide a display name for this sensor to differentiate it from the names assigned by ADT technicians during installation. + + ) + } +
+ + )} + /> +
+
+
+
+ ( + <> + +
+ { + (fieldState.error) ? fieldState.error.message : ( + <> + Specify the + {' '} + exact name + {' '} + associated with the sensor you want to add. Double-check the names to ensure they don't include extra characters. + + ) + } +
+ + )} + /> +
+
+
+
+ ( + <> + +
+ { + (fieldState.error) ? fieldState.error.message : ( + <> + Specify the + {' '} + exact zone + {' '} + associated with the sensor you want to add. Double-check the zone to ensure the correct sensor is added. + + ) + } +
+ + )} + /> +
+
+
+
+ ( + <> + +
+ { + (fieldState.error) ? fieldState.error.message : ( + <> + Select the + {' '} + type + {' '} + associated with the sensor you want to add. Ensure your selection matches the sensor type, as selecting the wrong type may lead to incorrect status detection. + + ) + } +
+ + )} + /> +
+ +
+
+
+ )) + } +
+ { + (sensors.length <= 147) ? ( +
+ +
+ ) : null + } + + ); +} diff --git a/src/config-ui/vite/src/pages/settings.tsx b/src/config-ui/vite/src/pages/settings.tsx index bff07b0..83c900c 100644 --- a/src/config-ui/vite/src/pages/settings.tsx +++ b/src/config-ui/vite/src/pages/settings.tsx @@ -1,8 +1,14 @@ import { zodResolver } from '@hookform/resolvers/zod'; -import React, { useEffect } from 'react'; -import { Controller, useForm } from 'react-hook-form'; +import _ from 'lodash'; +import React, { useEffect, useState } from 'react'; +import { useForm } from 'react-hook-form'; import { z } from 'zod'; +import SettingsFingerprint from '@/config-ui/vite/src/pages/settings-fingerprint.js'; +import SettingsGeneral from '@/config-ui/vite/src/pages/settings-general.js'; +import SettingsLogin from '@/config-ui/vite/src/pages/settings-login.js'; +import SettingsPlugin from '@/config-ui/vite/src/pages/settings-plugin.js'; +import SettingsSensors from '@/config-ui/vite/src/pages/settings-sensors.js'; import { platformConfig } from '@/lib/schema.js'; import type { SettingsProps } from '@/types/config-ui.d.ts'; @@ -18,13 +24,14 @@ export default function Settings(props: SettingsProps) { const { control, - formState, - // handleSubmit, + getValues, reset, + setValue, watch, } = useForm>({ mode: 'onTouched', defaultValues: { + platform: 'ADTPulse', name: 'ADT Pulse', subdomain: 'portal', username: '', @@ -37,13 +44,9 @@ export default function Settings(props: SettingsProps) { }, resolver: zodResolver(platformConfig), }); + const [ready, setReady] = useState(false); - const optionsState = watch('options'); // TODO - - useEffect(() => { - console.log('formState', formState); // TODO - console.log('optionsState', optionsState); // TODO - }, [optionsState]); + const formChanges = watch(); useEffect(() => { (async () => { @@ -51,197 +54,90 @@ export default function Settings(props: SettingsProps) { return; } + homebridge.showSpinner(); + const configs = await homebridge.getPluginConfig(); - const config = configs[0]; + const config = configs[0] ?? {}; // Set the most up-to-date config. - reset(config); + reset(_.merge({ + platform: 'ADTPulse' as 'ADTPulse', + name: 'ADT Pulse', + subdomain: 'portal' as 'portal', + username: '', + password: '', + fingerprint: '', + mode: 'normal' as 'normal', + speed: 1 as 1, + options: [], + sensors: [], + }, config)); + + // In case the previous view was classic settings. + homebridge.hideSchemaForm(); + + // Making sure the UI does not randomly flash. + await new Promise((resolve) => { + setTimeout(resolve, 500); + }); + + // Once the schema form hides, set the view to "ready". + setReady(true); - console.log('config', config); // TODO + homebridge.hideSpinner(); })(); }, []); - return ( - <> - -
-
-
- (( - <> - -
- {(fieldState.error) ? fieldState.error.message : 'Enter a unique name for this plugin. The name will mainly be used for identification purposes, such as in Homebridge logs.'} -
- - ))} - /> + useEffect(() => { + (async () => { + if (homebridge === undefined) { + return; + } + + await homebridge.updatePluginConfig([formChanges]); + })(); + }, [formChanges]); + + if (ready) { + return ( + <> + +
+
+
-
-
-
- (( - <> - -
- {(fieldState.error) ? fieldState.error.message : 'Choose the operational mode for this plugin. Debug mode is enabled only when Homebridge debug mode is on; there is no separate setting for this.'} -
- - ))} - /> -
-
-
-
- (( - <> - -
- {(fieldState.error) ? fieldState.error.message : 'Choose the synchronization speed for this plugin. Designed to enhance the performance of devices with older hardware. Results in slower device updates.'} -
- - ))} - /> -
-
+
+
-
- (( - <> -
Advanced Options
-
- -
-
- -
-
- {(fieldState.error) ? fieldState.error.message : 'Customize the features of this plugin. Please note these advanced options will disable expected functionality. Only enable them if necessary.'} -
- - ))} - /> +
+ +
+
+ +
+
+
-
- login -
-
- fingerprint -
-
- sensors -
-
- system - -
-
- - ); + + ); + } + + return null; } diff --git a/src/config-ui/vite/src/pages/setup-complete.tsx b/src/config-ui/vite/src/pages/setup-complete.tsx index 264c053..4eaff10 100644 --- a/src/config-ui/vite/src/pages/setup-complete.tsx +++ b/src/config-ui/vite/src/pages/setup-complete.tsx @@ -30,7 +30,7 @@ export default function SetupComplete(props: SetupCompleteProps) { homebridge.showSpinner(); const configs = await homebridge.getPluginConfig(); - const config = configs[0]; + const config = configs[0] ?? {}; const generateConfigResponse = await homebridge.request('/generate-config', { oldConfig: config, updateSensors, @@ -46,7 +46,7 @@ export default function SetupComplete(props: SetupCompleteProps) { return (
- Setup Complete + Setup Complete

Setup Complete

Restart Homebridge to apply your configuration changes.

diff --git a/src/config-ui/vite/src/pages/setup-sensors.tsx b/src/config-ui/vite/src/pages/setup-sensors.tsx index 2c1d240..aec494b 100644 --- a/src/config-ui/vite/src/pages/setup-sensors.tsx +++ b/src/config-ui/vite/src/pages/setup-sensors.tsx @@ -44,7 +44,7 @@ export default function SetupSensors(props: SetupSensorsProps) { } const configs = await homebridge.getPluginConfig(); - const config = configs[0]; + const config = configs[0] ?? {}; // For user experience purposes. if (_.get(config, ['sensors'], []).length > 0) { diff --git a/src/config-ui/vite/src/pages/setup-welcome.tsx b/src/config-ui/vite/src/pages/setup-welcome.tsx index a915fb5..e996352 100644 --- a/src/config-ui/vite/src/pages/setup-welcome.tsx +++ b/src/config-ui/vite/src/pages/setup-welcome.tsx @@ -16,7 +16,7 @@ export default function SetupWelcome(props: SetupWelcomeProps) { return (
- ADT Pulse for Homebridge + ADT Pulse for Homebridge

Welcome to

ADT Pulse

Homebridge plugin for ADT Pulse Security System

diff --git a/src/config-ui/vite/src/router.tsx b/src/config-ui/vite/src/router.tsx index e0659e3..7abc8e4 100644 --- a/src/config-ui/vite/src/router.tsx +++ b/src/config-ui/vite/src/router.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import ScreenToggle from '@/config-ui/vite/src/components/screen-toggle.js'; import Settings from '@/config-ui/vite/src/pages/settings.js'; +import SettingsClassic from '@/config-ui/vite/src/pages/settings-classic.js'; import Setup from '@/config-ui/vite/src/pages/setup.js'; import type { RouterProps, RouterView } from '@/types/config-ui.d.ts'; @@ -29,8 +30,7 @@ export default function Router(props: RouterProps) { if (configs.length === 0) { setView('setup'); } else { - // setView('settings'); - homebridge.showSchemaForm(); // TODO Will be replaced in a future version. + setView('settings'); } })(); }, [homebridge]); @@ -40,6 +40,9 @@ export default function Router(props: RouterProps) { {(view === 'settings') ? ( ) : null} + {(view === 'settings-classic') ? ( + + ) : null} {(view === 'setup') ? ( ) : null} diff --git a/src/config-ui/vite/src/styles/components/fingerprint-table.ts b/src/config-ui/vite/src/styles/components/fingerprint-table.ts new file mode 100644 index 0000000..1543e29 --- /dev/null +++ b/src/config-ui/vite/src/styles/components/fingerprint-table.ts @@ -0,0 +1,14 @@ +import type { Styles } from '@/types/config-ui.d.ts'; + +/** + * Styles. + * + * @returns {Styles} + * + * @since 1.0.0 + */ +export const styles: Styles = { + tableHead: { + width: 175, + }, +}; diff --git a/src/config-ui/vite/src/styles/pages/setup-complete.ts b/src/config-ui/vite/src/styles/pages/setup-complete.ts index 163ecd9..4f0f945 100644 --- a/src/config-ui/vite/src/styles/pages/setup-complete.ts +++ b/src/config-ui/vite/src/styles/pages/setup-complete.ts @@ -8,5 +8,7 @@ import type { Styles } from '@/types/config-ui.d.ts'; * @since 1.0.0 */ export const styles: Styles = { - height: 110, + image: { + height: 110, + }, }; diff --git a/src/config-ui/vite/src/styles/pages/setup-welcome.ts b/src/config-ui/vite/src/styles/pages/setup-welcome.ts index 163ecd9..4f0f945 100644 --- a/src/config-ui/vite/src/styles/pages/setup-welcome.ts +++ b/src/config-ui/vite/src/styles/pages/setup-welcome.ts @@ -8,5 +8,7 @@ import type { Styles } from '@/types/config-ui.d.ts'; * @since 1.0.0 */ export const styles: Styles = { - height: 110, + image: { + height: 110, + }, }; diff --git a/src/lib/auth.ts b/src/lib/auth.ts index aecf4fe..72e8700 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -370,7 +370,7 @@ export class ADTPulseAuth { debugLog(this.#internal.logger, 'auth.ts / ADTPulseAuth.getVerificationMethods()', 'info', `Request path valid ➜ ${axiosSignInRequestPathValid}`); } - // If the final URL of sessions.axiosSignIn is the summary page. + // Test if the login response does not require further verification. if (requestPathSummarySummary.test(axiosSignInRequestPath)) { if (this.#internal.debug) { debugLog(this.#internal.logger, 'auth.ts / ADTPulseAuth.getVerificationMethods()', 'info', `Verification methods not required from "${this.#internal.baseUrl}"`); @@ -389,20 +389,22 @@ export class ADTPulseAuth { }; } - // If the final URL of sessions.axiosSignIn is the login page. - if (requestPathAccessSignInEXxPartnerAdt.test(axiosSignInRequestPath)) { + // Test if the login response had an error. + if (requestPathAccessSignIn.test(axiosSignInRequestPath) || requestPathAccessSignInEXxPartnerAdt.test(axiosSignInRequestPath)) { + const errorMessage = fetchErrorMessage(sessions.axiosSignIn); + if (this.#internal.debug) { - debugLog(this.#internal.logger, 'auth.ts / ADTPulseAuth.getVerificationMethods()', 'error', 'Invalid username and/or password'); + debugLog(this.#internal.logger, 'auth.ts / ADTPulseAuth.getVerificationMethods()', 'error', errorMessage ?? 'Unknown error'); } - // Check if "this instance" was not signed in during this time. - this.handleLoginFailure(axiosSignInRequestPath, sessions.axiosSignIn); + // Mark the session for "this instance" as de-authenticated. + this.#session.status = 'logged-out'; return { action: 'GET_VERIFICATION_METHODS', success: false, info: { - message: 'Invalid username and/or password', + message: errorMessage ?? 'Unknown error', }, }; } @@ -1656,8 +1658,8 @@ export class ADTPulseAuth { * ➜ [ * { * adtName: 'Sensor 1', - * adtType: 'doorWindow', * adtZone: 1, + * adtType: 'doorWindow', * }, * ] * diff --git a/src/lib/platform.ts b/src/lib/platform.ts index e4347c0..e884138 100644 --- a/src/lib/platform.ts +++ b/src/lib/platform.ts @@ -1152,7 +1152,7 @@ export class ADTPulsePlatform implements ADTPulsePlatformPlugin { // If sensor was not found, it could be that the config was wrong. if (sensor === undefined) { - this.#log.warn(`Attempted to add or update ${chalk.underline(name)} (adtName: ${adtName}, adtType: ${adtType}, adtZone: ${adtZone}) accessory that does not exist on the portal.`); + this.#log.warn(`Attempted to add or update ${chalk.underline(name)} (adtName: ${adtName}, adtZone: ${adtZone}, adtType: ${adtType}) accessory that does not exist on the portal.`); continue; } @@ -1163,8 +1163,8 @@ export class ADTPulsePlatform implements ADTPulsePlatformPlugin { id, name: name ?? adtName, originalName: adtName, - type: adtType, zone: adtZone, + type: adtType, category: 'SENSOR', manufacturer: 'ADT', model: sensor.deviceType, diff --git a/src/lib/schema.ts b/src/lib/schema.ts index e5fce1b..02cae6d 100644 --- a/src/lib/schema.ts +++ b/src/lib/schema.ts @@ -183,8 +183,9 @@ export const platformConfig = z.object({ z.literal('ignoreSensorProblemStatus'), ])).default([]), sensors: z.array(z.object({ - name: z.string().min(1).max(50).optional(), + name: z.string().min(0).max(50).optional(), adtName: z.string().min(1).max(100), + adtZone: z.number().min(1).max(99), adtType: z.union([ z.literal('co'), z.literal('doorWindow'), @@ -196,6 +197,5 @@ export const platformConfig = z.object({ z.literal('shock'), z.literal('temperature'), ]), - adtZone: z.number().min(1).max(99), })).min(0).max(148).default([]), }); diff --git a/src/lib/utility.ts b/src/lib/utility.ts index 24fc3a0..ee49d58 100644 --- a/src/lib/utility.ts +++ b/src/lib/utility.ts @@ -8,6 +8,7 @@ import { createHash } from 'node:crypto'; import { createRequire } from 'node:module'; import os from 'node:os'; import util from 'node:util'; +import semver from 'semver'; import { collectionDoSubmitHandlers, @@ -830,8 +831,6 @@ export function isMaintenancePeriod(): IsMaintenancePeriodReturns { minute: 0, second: 0, millisecond: 0, - }).plus({ - days: 1, }); // Current time is between 10 PM and 4 AM PST. @@ -893,8 +892,8 @@ export async function isPluginOutdated(): IsPluginOutdatedReturns { try { const fetchedVersion = await latestVersion('homebridge-adt-pulse'); - // Simple equals comparison is enough, no need to over-complicate things. - if (currentVersion !== fetchedVersion) { + // Check if plugin is outdated. + if (semver.gt(fetchedVersion, currentVersion)) { return true; } } catch { @@ -1381,8 +1380,8 @@ export function parseSensorsTable(type: ParseOrbSensorsTableType, elements: Pars if (condensedType !== undefined) { (sensors as ParseOrbSensorsTableSensors<'sensors-config'>).push({ adtName: cleanedName, - adtType: condensedType, adtZone: cleanedZone, + adtType: condensedType, }); } } else { diff --git a/src/types/config-ui.d.ts b/src/types/config-ui.d.ts index ae6b398..6fb4571 100644 --- a/src/types/config-ui.d.ts +++ b/src/types/config-ui.d.ts @@ -1,4 +1,6 @@ import type { IHomebridgePluginUi } from '@homebridge/plugin-ui-utils/dist/ui.interface'; +import type { ReactNode } from 'react'; +import type { Control, UseFormGetValues, UseFormSetValue } from 'react-hook-form'; import type { z } from 'zod'; import type { ADTPulseAuth } from '@/lib/auth.js'; @@ -8,6 +10,7 @@ import type { configServerValidate, platformConfig, } from '@/lib/schema.js'; +import type { PluginSettingOptions } from '@/types/constant.d.ts'; import type { ApiResponse, CurrentView, MfaDevice } from '@/types/shared.d.ts'; /** @@ -140,6 +143,50 @@ export type ADTPulseConfigServerValidateReturnsInfo = null; export type ADTPulseConfigServerValidateReturns = Promise>; +/** + * Fingerprint table. + * + * @since 1.0.0 + */ +export type FingerprintTablePropsFingerprint = string; + +export type FingerprintTableProps = { + fingerprint: FingerprintTablePropsFingerprint; +}; + +export type FingerprintTableParsedObject = object; + +export type FingerprintTableStatus = 'failed' | 'loading' | 'success'; + +/** + * Fingerprint table - Render table property. + * + * @since 1.0.0 + */ +export type FingerprintTableRenderTablePropertyProperty = string; + +export type FingerprintTableRenderTablePropertyReturns = string; + +/** + * Fingerprint table - Render table value. + * + * @since 1.0.0 + */ +export type FingerprintTableRenderTableValueProperty = string; + +export type FingerprintTableRenderTableValueValue = unknown; + +export type FingerprintTableRenderTableValueReturns = ReactNode; + +/** + * Fingerprint table - Render table. + * + * @since 1.0.0 + */ +export type FingerprintTableRenderTableData = object; + +export type FingerprintTableRenderTableReturns = ReactNode; + /** * Router. * @@ -184,6 +231,121 @@ export type SettingsProps = { setView: SettingsPropsSetView; }; +/** + * Settings classic. + * + * @since 1.0.0 + */ +export type SettingsClassicPropsHomebridge = IHomebridgePluginUi | undefined; + +export type SettingsClassicPropsSetView = React.Dispatch>; + +export type SettingsClassicProps = { + homebridge: SettingsClassicPropsHomebridge; + setView: SettingsClassicPropsSetView; +}; + +/** + * Settings fingerprint. + * + * @since 1.0.0 + */ +export type SettingsFingerprintPropsFingerprint = string; + +export type SettingsFingerprintProps = { + fingerprint: SettingsFingerprintPropsFingerprint; +}; + +/** + * Settings general. + * + * @since 1.0.0 + */ +export type SettingsGeneralPropsControl = Control>; + +export type SettingsGeneralPropsGetValues = UseFormGetValues>; + +export type SettingsGeneralPropsSetValue = UseFormSetValue>; + +export type SettingsGeneralProps = { + control: SettingsGeneralPropsControl; + getValues: SettingsGeneralPropsGetValues; + setValue: SettingsGeneralPropsSetValue; +}; + +export type SettingsGeneralOptionsCheckboxId = string; + +export type SettingsGeneralOptionsCheckboxLabel = string; + +export type SettingsGeneralOptionsCheckboxValue = PluginSettingOptions; + +export type SettingsGeneralOptionsCheckbox = { + id: SettingsGeneralOptionsCheckboxId; + label: SettingsGeneralOptionsCheckboxLabel; + value: SettingsGeneralOptionsCheckboxValue; +}; + +export type SettingsGeneralOptionsCheckboxes = SettingsGeneralOptionsCheckbox[]; + +/** + * Settings general - Handle options change. + * + * @since 1.0.0 + */ +export type SettingsGeneralHandleOptionsChangeChecked = boolean; + +export type SettingsGeneralHandleOptionsChangeValue = PluginSettingOptions; + +export type SettingsGeneralHandleOptionsChangeReturns = void; + +/** + * Settings login. + * + * @since 1.0.0 + */ +export type SettingsLoginPropsControl = Control>; + +export type SettingsLoginPropsGetValues = UseFormGetValues>; + +export type SettingsLoginProps = { + control: SettingsLoginPropsControl; + getValues: SettingsLoginPropsGetValues; +}; + +/** + * Settings plugin. + * + * @since 1.0.0 + */ +export type SettingsPluginHomebridge = IHomebridgePluginUi | undefined; + +export type SettingsPluginPropsSetView = React.Dispatch>; + +export type SettingsPluginProps = { + homebridge: SettingsPluginHomebridge; + setView: SettingsPluginPropsSetView; +}; + +/** + * Settings sensors. + * + * @since 1.0.0 + */ +export type SettingsSensorsPropsControl = Control>; + +export type SettingsSensorsProps = { + control: SettingsSensorsPropsControl; +}; + +/** + * Settings sensors - Get sensor header. + * + * @since 1.0.0 + */ +export type SettingsSensorsGetSensorHeaderIndex = number; + +export type SettingsSensorsGetSensorHeaderReturns = string; + /** * Setup. * @@ -359,4 +521,4 @@ export type SetupWelcomeProps = { * * @since 1.0.0 */ -export type Styles = React.CSSProperties; +export type Styles = Record; diff --git a/src/types/constant.d.ts b/src/types/constant.d.ts index fd2577c..219d273 100644 --- a/src/types/constant.d.ts +++ b/src/types/constant.d.ts @@ -63,6 +63,15 @@ export type PluginLogLevel = | 'success' | 'warn'; +/** + * Plugin setting options. + * + * @since 1.0.0 + */ +export type PluginSettingOptions = + 'disableAlarmRingingSwitch' + | 'ignoreSensorProblemStatus'; + /** * Portal device gateway status. * diff --git a/src/types/shared.d.ts b/src/types/shared.d.ts index c8afcb2..1d1549d 100644 --- a/src/types/shared.d.ts +++ b/src/types/shared.d.ts @@ -156,7 +156,7 @@ export type Credentials = { * * @since 1.0.0 */ -export type CurrentView = 'setup' | 'settings' | undefined; +export type CurrentView = 'settings' | 'settings-classic' | 'setup' | undefined; /** * Debug parser. @@ -577,14 +577,14 @@ export type PortalVersionContent = { */ export type SensorConfigAdtName = string; -export type SensorConfigAdtType = PluginDeviceSensorType; - export type SensorConfigAdtZone = number; +export type SensorConfigAdtType = PluginDeviceSensorType; + export type SensorConfig = { adtName: SensorConfigAdtName; - adtType: SensorConfigAdtType; adtZone: SensorConfigAdtZone; + adtType: SensorConfigAdtType; }; /** diff --git a/tsconfig.json b/tsconfig.json index 68c4c79..c03efbc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -41,6 +41,7 @@ "luxon", "react", "react-dom", + "semver", "tough-cookie" ] },