mirror of
https://github.com/tsightler/ring-mqtt.git
synced 2025-10-28 11:01:38 +08:00
100 lines
3.7 KiB
JavaScript
100 lines
3.7 KiB
JavaScript
const debug = require('debug')('ring-mqtt')
|
|
const colors = require('colors/safe')
|
|
const fs = require('fs')
|
|
const utils = require( '../lib/utils' )
|
|
const { createHash, randomBytes } = require('crypto')
|
|
const writeFileAtomic = require('write-file-atomic')
|
|
|
|
class State {
|
|
constructor() {
|
|
this.valid = false
|
|
this.writeScheduled = false
|
|
this.data = {
|
|
ring_token: '',
|
|
systemId: '',
|
|
devices: {}
|
|
}
|
|
}
|
|
|
|
async init(config) {
|
|
this.config = config
|
|
this.file = (process.env.RUNMODE === 'standard')
|
|
? require('path').dirname(require.main.filename)+'/ring-state.json'
|
|
: this.file = '/data/ring-state.json'
|
|
await this.loadStateData()
|
|
|
|
utils.event.on(`save_device_state`, (deviceId, stateData) => {
|
|
this.data.devices[deviceId] = stateData
|
|
this.updateStateFile()
|
|
})
|
|
|
|
utils.event.on('get_device_state', (deviceId) => {
|
|
if (this.data.devices.hasOwnProperty(deviceId)) {
|
|
utils.event.emit(`device_state_${deviceId}`, this.data.devices[deviceId])
|
|
} else {
|
|
utils.event.emit(`device_state_${deviceId}`, false)
|
|
}
|
|
})
|
|
}
|
|
|
|
async loadStateData() {
|
|
if (fs.existsSync(this.file)) {
|
|
debug('Reading latest data from state file: '+this.file)
|
|
try {
|
|
this.data = require(this.file)
|
|
this.valid = true
|
|
if (!this.data.hasOwnProperty('systemId')) {
|
|
this.data.systemId = (createHash('sha256').update(randomBytes(32)).digest('hex'))
|
|
}
|
|
// Convert legacy state file with empty device array
|
|
if (!this.data.hasOwnProperty('devices') || Array.isArray(this.data.devices)) {
|
|
this.data.devices = {}
|
|
}
|
|
} catch {
|
|
debug(err.message)
|
|
debug(colors.red('Saved state file exist but could not be parsed!'))
|
|
await this.initStateData()
|
|
}
|
|
} else {
|
|
await this.initStateData()
|
|
}
|
|
}
|
|
|
|
async initStateData() {
|
|
this.data.systemId = (createHash('sha256').update(randomBytes(32)).digest('hex'))
|
|
if (process.env.RUNMODE === 'standard' && this.config.data.hasOwnProperty('ring_token') && this.config.data.ring_token) {
|
|
debug(colors.yellow('State file '+this.file+' not found, creating new state file using existing ring_token from config file.'))
|
|
this.updateToken(this.config.data.ring_token, true)
|
|
await this.config.updateConfig()
|
|
} else {
|
|
debug(colors.yellow('State file '+this.file+' not found. No saved state data available.'))
|
|
}
|
|
}
|
|
|
|
async updateToken(newRefreshToken, oldRefreshToken) {
|
|
if (oldRefreshToken) {
|
|
this.data.ring_token = newRefreshToken
|
|
this.updateStateFile()
|
|
}
|
|
}
|
|
|
|
async updateStateFile() {
|
|
// The writeDelay flag is a hack to keep from writing too often when there are burst
|
|
// of state updates such as during startup. If a state file update is already scheduled
|
|
// then calls to this function are skipped.
|
|
if (!this.writeScheduled) {
|
|
this.writeScheduled = true
|
|
await utils.sleep(5)
|
|
this.writeScheduled = false
|
|
try {
|
|
await writeFileAtomic(this.file, JSON.stringify(this.data))
|
|
debug('Successfully saved updated refresh token in state file: '+this.file)
|
|
} catch (err) {
|
|
debug(colors.red('Failed to save updated refresh token in state file: '+this.file))
|
|
debug(err.message)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new State() |