Skip to content

Commit

Permalink
Fix homebridge ui by inlining js
Browse files Browse the repository at this point in the history
  • Loading branch information
dgreif committed Nov 11, 2024
1 parent 083ae61 commit 6f83b8e
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 172 deletions.
5 changes: 5 additions & 0 deletions .changeset/orange-pumpkins-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'homebridge-ring': patch
---

Fix homebridge ui
162 changes: 0 additions & 162 deletions packages/homebridge-ring/homebridge-ui/public/homebridge-ring-ui.ts

This file was deleted.

177 changes: 170 additions & 7 deletions packages/homebridge-ring/homebridge-ui/public/index.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,180 @@
<button id="ring-new-token" class="btn btn-link m-0 p-0" style="display: none;">
<button id="ring-new-token" class="btn btn-link m-0 p-0" style="display: none">
<i class="fa fa-redo mr-2"></i>
Generate New Refresh Token
</button>

<h4 id="ring-link-account-header" class="text-center primary-text mb-3" style="display: none;">
<h4
id="ring-link-account-header"
class="text-center primary-text mb-3"
style="display: none"
>
Link Ring Account
</h4>

<script type="module">
// Show the spinner while the JS is loading
homebridge.showSpinner()
const { homebridge } = window
const newTokenButton = document.getElementById('ring-new-token')
const linkAccountHeader = document.getElementById('ring-link-account-header')

// import the JS module for the custom UI
// Use the installed version of homebridge to bust the cache for the script
import(`./homebridge-ring-ui.js?v=${homebridge.plugin.installedVersion}`)
if (!newTokenButton) {
homebridge.toast.error('no ring-new-token element found!')
throw new Error('no ring-new-token element found!')
}

if (!linkAccountHeader) {
homebridge.toast.error('no ring-link-account-header element found!')
throw new Error('no ring-link-account-header element found!')
}

newTokenButton.addEventListener('click', () => {
showLoginForm()
})

/**
* Render the correct form, based on whether we have an existing refresh token
*/
async function renderForm() {
const [config] = await homebridge.getPluginConfig()
const needToken = !config?.refreshToken

homebridge.hideSpinner()

if (needToken) {
showLoginForm()
} else {
showStandardForm()
}
}
renderForm()

/**
* @param {string} refreshToken
*/
async function setRefreshToken(refreshToken) {
const [config, ...otherConfigs] = await homebridge.getPluginConfig()
await homebridge.updatePluginConfig([
{ ...config, refreshToken },
...otherConfigs,
])
homebridge.toast.success('Refresh Token Updated', 'Ring Login Successful')
showStandardForm()
}

function showStandardForm() {
newTokenButton?.style.setProperty('display', 'block')
linkAccountHeader?.style.setProperty('display', 'none')
homebridge.showSchemaForm()
}

function showLoginForm() {
// Hide the standard form
newTokenButton?.style.setProperty('display', 'none')
linkAccountHeader?.style.setProperty('display', 'block')
homebridge.hideSchemaForm()

// Create a new login form
const form = homebridge.createForm(
{
schema: {
type: 'object',
properties: {
email: {
title: 'Email',
type: 'string',
'x-schema-form': {
type: 'email',
},
required: true,
},
password: {
title: 'Password',
type: 'string',
'x-schema-form': {
type: 'password',
},
required: true,
},
},
},
},
{},
'Log In',
)

form.onSubmit(async ({ email, password }) => {
homebridge.showSpinner()

try {
/** @type {{ codePrompt: string } | { refreshToken: string }} */
const response = await homebridge.request('/send-code', {
email,
password,
})

if ('refreshToken' in response) {
// didn't need 2fa, return token without asking for code
setRefreshToken(response.refreshToken)
} else {
// Need a token, so show the token form
showTokenForm({
email,
password,
codePrompt: response.codePrompt,
})
}
} catch (/** @type {any} */ e) {
// eslint-disable-next-line no-console
console.error(e)
homebridge.toast.error(e.message, 'Ring Login Failed')
} finally {
homebridge.hideSpinner()
}
})
}

/**
* @param {{ email: string, password: string, codePrompt: string }} loginInfo
*/
function showTokenForm(loginInfo) {
const form = homebridge.createForm(
{
schema: {
type: 'object',
properties: {
code: {
title: 'Code',
type: 'string',
required: true,
description: loginInfo.codePrompt,
},
},
},
},
{},
'Link Account',
'Change Email',
)

form.onSubmit(async ({ code }) => {
homebridge.showSpinner()

try {
const { refreshToken } = await homebridge.request('/token', {
email: loginInfo.email,
password: loginInfo.password,
code,
})

setRefreshToken(refreshToken)
} catch (/** @type {any} */ e) {
// eslint-disable-next-line no-console
console.error(e)
homebridge.toast.error(e.message, 'Failed to Link Account')
} finally {
homebridge.hideSpinner()
}
})

form.onCancel(() => showLoginForm())
}
</script>
2 changes: 1 addition & 1 deletion packages/homebridge-ring/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"description": "Homebridge plugin for Ring doorbells, cameras, security alarm system and smart lighting",
"main": "lib/index.js",
"scripts": {
"build": "rm -rf lib && tsc && cp ./homebridge-ui/public/index.html ./lib/homebridge-ui/public/",
"build": "rm -rf lib && tsc && cp -r ./homebridge-ui/public ./lib/homebridge-ui/public/",
"lint": "eslint . --ext .ts && tsc --noEmit",
"dev": "concurrently -c yellow,blue --kill-others \"npm:dev:build\" \"npm:dev:run\" ",
"dev:build": "tsc --watch --preserveWatchOutput",
Expand Down
3 changes: 1 addition & 2 deletions packages/homebridge-ring/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
{
"extends": "tsconfig/tsconfig.json",
"compilerOptions": {
"outDir": "./lib",
"types": ["@homebridge/plugin-ui-utils/dist/ui.interface"],
"outDir": "lib"
},
"include": ["**/*.ts"]
}

0 comments on commit 6f83b8e

Please sign in to comment.