mirror of
				https://github.com/tsightler/ring-mqtt.git
				synced 2025-10-31 20:12:40 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			101 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			101 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import chalk from 'chalk'
 | |
| import fs from 'fs'
 | |
| import { readFile } from 'fs/promises'
 | |
| import { dirname } from 'path'
 | |
| import { fileURLToPath } from 'url'
 | |
| import utils from './utils.js'
 | |
| import { createHash, randomBytes } from 'crypto'
 | |
| import writeFileAtomic from 'write-file-atomic'
 | |
| import debugModule from 'debug'
 | |
| const debug = debugModule('ring-mqtt')
 | |
| 
 | |
| export default new class State {
 | |
|     constructor() {
 | |
|         this.valid = false
 | |
|         this.writeScheduled = false
 | |
|         this.data = {
 | |
|             ring_token: '',
 | |
|             systemId: '',
 | |
|             devices: {}
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async init() {
 | |
|         this.file = (process.env.RUNMODE === 'standard')
 | |
|             ? dirname(fileURLToPath(new URL('.', import.meta.url)))+'/ring-state.json'
 | |
|             : '/data/ring-state.json'
 | |
|         await this.loadStateData()
 | |
| 
 | |
|         // Only temporary to remove any legacy values from state file
 | |
|         if (this.data.hasOwnProperty('push_credentials')) {
 | |
|             delete this.data.push_credentials
 | |
|             await this.saveStateFile()
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async loadStateData() {
 | |
|         if (fs.existsSync(this.file)) {
 | |
|             debug('Reading latest data from state file: '+this.file)
 | |
|             try {
 | |
|                 this.data = JSON.parse(await readFile(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 (err) {
 | |
|                 debug(err.message)
 | |
|                 debug(chalk.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'))
 | |
|         debug(chalk.yellow('State file '+this.file+' not found. No saved state data available.'))
 | |
|     }
 | |
| 
 | |
|     async saveStateFile() {
 | |
|         // The writeScheduled 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(1)
 | |
|             this.writeScheduled = false
 | |
|             try {
 | |
|                 await writeFileAtomic(this.file, JSON.stringify(this.data))
 | |
|                 debug('Successfully saved updated state file: '+this.file)
 | |
|             } catch (err) {
 | |
|                 debug(chalk.red('Failed to save updated state file: '+this.file))
 | |
|                 debug(err.message)
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     updateToken(newRefreshToken) {
 | |
|         debug('Saving updated refresh token to state file')
 | |
|         this.data.ring_token = newRefreshToken
 | |
|         this.saveStateFile()
 | |
|     }
 | |
| 
 | |
|     setDeviceSavedState(deviceId, stateData) {
 | |
|         this.data.devices[deviceId] = stateData
 | |
|         this.saveStateFile()
 | |
|     }
 | |
| 
 | |
|     getDeviceSavedState(deviceId) {
 | |
|         return this.data.devices.hasOwnProperty(deviceId) ? this.data.devices[deviceId] : false
 | |
|     }
 | |
| 
 | |
|     getAllSavedStates() {
 | |
|         return this.data.devices
 | |
|     }
 | |
| }
 | 
