-
Notifications
You must be signed in to change notification settings - Fork 106
/
init-ring-mqtt.js
117 lines (104 loc) · 4.07 KB
/
init-ring-mqtt.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env node
import fs from 'fs'
import { dirname } from 'path'
import { fileURLToPath } from 'url'
import { readFile } from 'fs/promises'
import writeFileAtomic from 'write-file-atomic'
import { createHash, randomBytes } from 'crypto'
import { RingRestClient } from 'ring-client-api/rest-client'
import { requestInput } from './node_modules/ring-client-api/lib/util.js'
async function getRefreshToken(systemId) {
let generatedToken
const email = await requestInput('Email: ')
const password = await requestInput('Password: ')
const restClient = new RingRestClient({
email,
password,
controlCenterDisplayName: `ring-mqtt-${systemId.slice(-5)}`,
systemId: systemId
})
try {
await restClient.getCurrentAuth()
} catch(err) {
if (restClient.using2fa) {
console.log('Username/Password was accepted, waiting for 2FA code to be entered.')
} else {
throw(err.message)
}
}
while(!generatedToken) {
const code = await requestInput('2FA Code: ')
try {
generatedToken = await restClient.getAuth(code)
return generatedToken.refresh_token
} catch {
throw('Failed to validate the entered 2FA code. (error: invalid_code)')
}
}
}
const main = async() => {
let refresh_token
let stateData = {}
// If running in Docker set state file path as appropriate
const stateFile = (fs.existsSync('/etc/cont-init.d/ring-mqtt.sh'))
? '/data/ring-state.json'
: dirname(fileURLToPath(new URL(import.meta.url)))+'/ring-state.json'
const configFile = (fs.existsSync('/etc/cont-init.d/ring-mqtt.sh'))
? '/data/config.json'
: dirname(fileURLToPath(new URL(import.meta.url)))+'/config.json'
if (fs.existsSync(stateFile)) {
console.log('Reading latest data from state file: '+stateFile)
try {
stateData = JSON.parse(await readFile(stateFile))
} catch(err) {
console.log(err.message)
console.log('Saved state file '+stateFile+' exist but could not be parsed!')
console.log('To create new state file please rename/delete existing file and re-run this tool.')
process.exit(1)
}
}
if (!stateData.hasOwnProperty('systemId') || (stateData.hasOwnProperty('systemId') && !stateData.systemId)) {
stateData.systemId = (createHash('sha256').update(randomBytes(32)).digest('hex'))
}
try {
refresh_token = await getRefreshToken(stateData.systemId)
} catch(err) {
console.log(err)
console.log('Please re-run this tool to retry authentication.')
process.exit(1)
}
stateData.ring_token = refresh_token
try {
await writeFileAtomic(stateFile, JSON.stringify(stateData))
console.log(`State file ${stateFile} saved with updated refresh token.`)
console.log(`Device name: ring-mqtt-${stateData.systemId.slice(-5)}`)
} catch (err) {
console.log('Saving state file '+stateFile+' failed with error: ')
console.log(err)
}
if (!fs.existsSync(configFile)) {
try {
const configData = {
"mqtt_url": "mqtt://localhost:1883",
"mqtt_options": "",
"livestream_user": "",
"livestream_pass": "",
"disarm_code": "",
"enable_cameras": true,
"enable_modes": false,
"enable_panic": false,
"hass_topic": "homeassistant/status",
"ring_topic": "ring",
"location_ids": []
}
const mqttUrl = await requestInput('MQTT URL (enter to skip and edit config manually): ')
configData.mqtt_url = mqttUrl ? mqttUrl : configData.mqtt_url
await writeFileAtomic(configFile, JSON.stringify(configData, null, 4))
console.log('New config file written to '+configFile)
} catch (err) {
console.log('Failed to create new config file at '+stateFile)
console.log(err)
}
}
}
main()