Initial device state framework

This commit is contained in:
tsightler
2022-04-03 12:56:38 -04:00
parent 5765b60537
commit c9478d4a94
32 changed files with 181 additions and 99 deletions

View File

@@ -22,6 +22,9 @@ class RingPolledDevice extends RingDevice {
})
this.monitorHeartbeat()
// Request saved state for device
utils.event.emit('get_device_state', this.deviceId)
}
// Publish device discovery, set online, and send all state data

View File

@@ -12,6 +12,7 @@ class RingDevice {
this.isOnline = () => {
return this.availabilityState === 'online' ? true : false
}
this.debug = (message, debugType) => {
utils.debug(debugType === 'disc' ? message : colors.green(`[${this.device.name}] `)+message, debugType ? debugType : 'mqtt')
}
@@ -19,10 +20,22 @@ class RingDevice {
this.deviceTopic = `${utils.config.ring_topic}/${this.locationId}/${category}/${this.deviceId}`
this.availabilityTopic = `${this.deviceTopic}/status`
if (deviceInfo.hasOwnProperty('childDevices')) {
this.childDevices = deviceInfo.childDevices
}
if (deviceInfo.hasOwnProperty('parentDevices')) {
this.parentDevices = deviceInfo.parentDevices
}
// Initialize device with saved state data
utils.event.on(`device_state_${this.deviceId}`, (stateData) => {
this.init(stateData)
if (primaryAttribute !== 'disable') {
this.initAttributeEntities(primaryAttribute)
this.schedulePublishAttributes()
}
})
}
// This function loops through each entity of the device, creates a unique
@@ -151,7 +164,7 @@ class RingDevice {
Object.keys(discoveryMessage).filter(property => property.match('topic')).forEach(topic => {
this.entity[entityKey][topic] = discoveryMessage[topic]
if (topic.match('command_topic')) {
utils.event.emit('mqttSubscribe', discoveryMessage[topic])
utils.event.emit('mqtt_subscribe', discoveryMessage[topic])
utils.event.on(discoveryMessage[topic], (command, message) => {
this.processCommand(command, message)
})
@@ -193,7 +206,7 @@ class RingDevice {
if (debugType !== false) {
this.debug(colors.blue(`${topic} `)+colors.cyan(`${message}`), debugType)
}
utils.event.emit('mqttPublish', topic, message)
utils.event.emit('mqtt_publish', topic, message)
}
// Set state topic online

View File

@@ -17,6 +17,9 @@ class RingSocketDevice extends RingDevice {
this.device.onData.subscribe((data) => {
if (this.isOnline()) { this.publishState(data) }
})
// Request saved state for device
utils.event.emit('get_device_state', this.deviceId)
}
// Publish device discovery, set online, and send all state data

View File

@@ -4,14 +4,12 @@ const RingSocketDevice = require('./base-socket-device')
class BaseStation extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm', 'acStatus')
}
async init() {
this.deviceData.mdl = 'Alarm Base Station'
this.deviceData.name = this.device.location.name + ' Base Station'
this.initVolumeEntity()
}
// Check if account has access to control base state volume and initialize topics if so
async initVolumeEntity() {
const origVolume = (this.device.data.volume && !isNaN(this.device.data.volume) ? this.device.data.volume : 0)
const testVolume = (origVolume === 1) ? .99 : origVolume+.01
this.device.setVolume(testVolume)

View File

@@ -4,28 +4,29 @@ const { RingDeviceType } = require('ring-client-api')
class BeamOutdoorPlug extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'lighting')
this.deviceData.mdl = 'Outdoor Smart Plug'
this.childDevices = {
outlet1: deviceInfo.childDevices.find(d => d.deviceType === RingDeviceType.BeamsSwitch && d.data.relToParentZid === "1"),
outlet2: deviceInfo.childDevices.find(d => d.deviceType === RingDeviceType.BeamsSwitch && d.data.relToParentZid === "2")
}
init() {
this.deviceData.mdl = 'Outdoor Smart Plug'
this.outlet1 = this.childDevices.find(d => d.deviceType === RingDeviceType.BeamsSwitch && d.data.relToParentZid === "1"),
this.outlet2 = this.childDevices.find(d => d.deviceType === RingDeviceType.BeamsSwitch && d.data.relToParentZid === "2")
this.entity.outlet1 = {
component: (this.childDevices.outlet1.data.categoryId === 2) ? 'light' : 'switch',
name: `${this.childDevices.outlet1.name}`
component: (this.outlet1.data.categoryId === 2) ? 'light' : 'switch',
name: `${this.outlet1.name}`
}
this.entity.outlet2 = {
component: (this.childDevices.outlet2.data.categoryId === 2) ? 'light' : 'switch',
name: `${this.childDevices.outlet2.name}`
component: (this.outlet2.data.categoryId === 2) ? 'light' : 'switch',
name: `${this.outlet2.name}`
}
this.childDevices.outlet1.onData.subscribe((data) => {
this.outlet1.onData.subscribe((data) => {
if (this.isOnline()) { this.publishOutlet1State() }
})
this.childDevices.outlet2.onData.subscribe((data) => {
this.outlet2.onData.subscribe((data) => {
if (this.isOnline()) { this.publishOutlet2State() }
})
}
@@ -37,11 +38,11 @@ class BeamOutdoorPlug extends RingSocketDevice {
}
publishOutlet1State() {
this.mqttPublish(this.entity.outlet1.state_topic, this.childDevices.outlet1.data.on ? "ON" : "OFF")
this.mqttPublish(this.entity.outlet1.state_topic, this.outlet1.data.on ? "ON" : "OFF")
}
publishOutlet2State() {
this.mqttPublish(this.entity.outlet2.state_topic, this.childDevices.outlet2.data.on ? "ON" : "OFF")
this.mqttPublish(this.entity.outlet2.state_topic, this.outlet2.data.on ? "ON" : "OFF")
}
// Process messages from MQTT command topic
@@ -73,7 +74,7 @@ class BeamOutdoorPlug extends RingSocketDevice {
const duration = 32767
const on = command === 'on' ? true : false
const data = on ? { lightMode: 'on', duration } : { lightMode: 'default' }
this.childDevices[outletId].sendCommand('light-mode.set', data)
this[outletId].sendCommand('light-mode.set', data)
break;
}
default:

View File

@@ -3,7 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class Beam extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'lighting')
}
init() {
// Setup device topics based on capabilities.
switch (this.device.data.deviceType) {
case 'group.light-group.beams':

View File

@@ -4,7 +4,9 @@ const { RingDeviceType } = require('ring-client-api')
class BinarySensor extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
let device_class = 'None'
// Override icons and and topics

View File

@@ -3,6 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class Bridge extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm', 'commStatus')
}
init() {
this.deviceData.mdl = 'Bridge'
this.deviceData.name = this.device.location.name + ' Bridge'
}

View File

@@ -11,7 +11,9 @@ const rss = require('../lib/rtsp-simple-server')
class Camera extends RingPolledDevice {
constructor(deviceInfo) {
super(deviceInfo, 'camera')
}
init(stateData) {
this.data = {
motion: {
active_ding: false,
@@ -109,6 +111,27 @@ class Camera extends RingPolledDevice {
}
}
if (stateData) {
if (utils.config.snapshot_mode.match(/^(interval|all)$/)) {
if (stateData.hasOwnProperty('snapshot')) {
this.data.snapshot.autoInterval = stateData.snapshot.hasOwnProperty('autoInterval')
? stateData.snapshot.autoInterval
: this.data.snapshot.autoInterval
if (!this.data.snapshot.autoInterval) {
this.data.snapshot.interval = stateData.snapshot.hasOwnProperty('interval')
? stateData.snapshot.interval
: this.data.snapshot.interval
}
}
}
if (stateData.hasOwnProperty('event_select')) {
this.data.event_select.state = stateData.event_select.hasOwnProperty('state')
? stateData.event_select.state
: this.data.event_select.state
}
}
this.entity = {
...this.entity,
motion: {
@@ -178,7 +201,7 @@ class Camera extends RingPolledDevice {
}
}
utils.event.on(`${this.deviceId}_livestream`, (state) => {
utils.event.on(`livestream_${this.deviceId}`, (state) => {
switch (state) {
case 'active':
if (this.data.stream.live.status !== 'active') {
@@ -207,34 +230,6 @@ class Camera extends RingPolledDevice {
if (this.isOnline()) { this.processDing(ding) }
})
utils.event.on(`deviceState_${this.deviceId}`, (stateData) => {
this.initDeviceState(stateData)
})
utils.event.emit('getDeviceState', this.deviceId)
}
async initDeviceState(stateData) {
if (stateData) {
if (utils.config.snapshot_mode.match(/^(interval|all)$/)) {
if (stateData.hasOwnProperty('snapshot')) {
this.data.snapshot.autoInterval = stateData.snapshot.hasOwnProperty('autoInterval')
? stateData.snapshot.autoInterval
: this.data.snapshot.autoInterval
if (!this.data.snapshot.autoInterval) {
this.data.snapshot.interval = stateData.snapshot.hasOwnProperty('interval')
? stateData.snapshot.interval
: this.data.snapshot.interval
}
}
}
if (stateData.hasOwnProperty('event_select')) {
this.data.event_select.state = stateData.event_select.hasOwnProperty('state')
? stateData.event_select.state
: this.data.event_select.state
}
}
if (this.data.snapshot.interval > 0) {
this.scheduleSnapshotRefresh()
}
@@ -249,10 +244,10 @@ class Camera extends RingPolledDevice {
interval: this.data.snapshot.interval
},
event_select: {
state: this.data.snapshot.state
state: this.data.event_select.state
}
}
utils.event.emit(`saveDeviceState`, this.deviceId, stateData)
utils.event.emit(`save_device_state`, this.deviceId, stateData)
}
// Build standard and optional entities for device

View File

@@ -4,7 +4,9 @@ const RingPolledDevice = require('./base-polled-device')
class Chime extends RingPolledDevice {
constructor(deviceInfo) {
super(deviceInfo, 'chime')
}
init(stateData) {
this.data = {
volume: null,
snooze: null,
@@ -14,6 +16,12 @@ class Chime extends RingPolledDevice {
play_motion_sound: 'OFF'
}
if (stateData) {
this.data.snooze_minutes = (stateData.hasOwnProperty('snooze_minutes'))
? stateData.snooze_minutes
: this.data.snooze_minutes
}
// Define entities for this device
this.entity = {
...this.entity,
@@ -48,6 +56,15 @@ class Chime extends RingPolledDevice {
value_template: '{{ value_json["lastUpdate"] | default("") }}'
}
}
this.saveDeviceState()
}
saveDeviceState() {
const stateData = {
snooze_minutes: this.data.snooze_minutes
}
utils.event.emit(`save_device_state`, this.deviceId, stateData)
}
initAttributeEntities() {

View File

@@ -3,9 +3,12 @@ const RingSocketDevice = require('./base-socket-device')
class CoAlarm extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = 'CO Alarm'
this.deviceData.mf = (deviceInfo.hasOwnProperty('parentDevice') && deviceInfo.parentDevice.data && deviceInfo.parentDevice.data.hasOwnProperty('manufacturerName'))
? deviceInfo.parentDevice.data.manufacturerName
this.deviceData.mf = (this.hasOwnProperty('parentDevice') && this.parentDevice.hasOwnProperty('data') && this.parentDevice.data.hasOwnProperty('manufacturerName'))
? this.parentDevice.data.manufacturerName
: 'Ring'
this.entity.co = {

View File

@@ -4,6 +4,9 @@ const RingSocketDevice = require('./base-socket-device')
class Fan extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = 'Fan Control'
this.entity.fan = {

View File

@@ -3,6 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class FloodFreezeSensor extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = 'Flood & Freeze Sensor'
this.entity.flood = {

View File

@@ -3,6 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class Keypad extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = 'Security Keypad'
this.entity.volume = {

View File

@@ -3,6 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class Lock extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = 'Lock'
this.entity.lock = {

View File

@@ -4,6 +4,9 @@ const RingPolledDevice = require('./base-polled-device')
class ModesPanel extends RingPolledDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm', 'disable')
}
init() {
this.deviceData.mdl = 'Mode Control Panel'
this.deviceData.name = `${this.device.location.name} Mode`

View File

@@ -3,6 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class MultiLevelSwitch extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = 'Dimming Light'
this.entity.light = {

View File

@@ -3,6 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class RangeExtender extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm', 'acStatus')
}
init() {
this.deviceData.mdl = 'Z-Wave Range Extender'
this.deviceData.name = this.device.location.name + ' Range Extender'
}

View File

@@ -5,6 +5,9 @@ const RingSocketDevice = require('./base-socket-device')
class SecurityPanel extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm', 'alarmState')
}
init() {
this.deviceData.mdl = 'Alarm Control Panel'
this.deviceData.name = `${this.device.location.name} Alarm`
@@ -41,7 +44,7 @@ class SecurityPanel extends RingSocketDevice {
}
publishState() {
var alarmMode
let alarmMode
const alarmInfo = this.device.data.alarmInfo ? this.device.data.alarmInfo : []
// If alarm is active report triggered or, if entry-delay, pending
@@ -150,7 +153,7 @@ class SecurityPanel extends RingSocketDevice {
while (retries-- > 0 && !(setAlarmSuccess)) {
let bypassDeviceIds = []
// If arming bypass arming mode is enabled, get device ids requiring bypass
// If arming bypass mode is enabled, get device ids requiring bypass
if (message.toLowerCase() !== 'disarm' && this.entity.bypass.state) {
const bypassDevices = (await this.device.location.getDevices()).filter((device) => {
return (

View File

@@ -3,7 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class Siren extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = (this.device.data.deviceType === 'siren.outdoor-strobe') ? 'Outdoor Siren' : 'Siren'
this.entity = {
...this.entity,

View File

@@ -3,6 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class SmokeAlarm extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = 'Smoke Alarm'
this.entity.smoke = {

View File

@@ -3,6 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class SmokeCoListener extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = 'Smoke & CO Listener'
this.entity.smoke = {

View File

@@ -3,6 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class Switch extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = (this.device.data.categoryId === 2) ? 'Light' : 'Switch'
this.component = (this.device.data.categoryId === 2) ? 'light' : 'switch'

View File

@@ -3,6 +3,9 @@ const RingSocketDevice = require('./base-socket-device')
class TemperatureSensor extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = 'Temperature Sensor'
this.entity.temperature = {

View File

@@ -5,12 +5,13 @@ const utils = require( '../lib/utils' )
class Thermostat extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
}
init() {
this.deviceData.mdl = 'Thermostat'
this.childDevices = {
operatingStatus: deviceInfo.childDevices.find(d => d.deviceType === 'thermostat-operating-status'),
temperatureSensor: deviceInfo.childDevices.find(d => d.deviceType === RingDeviceType.TemperatureSensor)
}
this.operatingStatus = this.childDevices.find(d => d.deviceType === 'thermostat-operating-status'),
this.temperatureSensor = this.childDevices.find(d => d.deviceType === RingDeviceType.TemperatureSensor)
this.entity.thermostat = {
component: 'climate',
@@ -28,16 +29,16 @@ class Thermostat extends RingSocketDevice {
setPoint: (() => {
return this.device.data.setPoint
? this.device.data.setPoint
: this.childDevices.temperatureSensor.data.celsius
: this.temperatureSensor.data.celsius
}),
operatingMode: (() => {
return this.childDevices.operatingStatus.data.operatingMode !== 'off'
? `${this.childDevices.operatingStatus.data.operatingMode}ing`
return this.operatingStatus.data.operatingMode !== 'off'
? `${this.operatingStatus.data.operatingMode}ing`
: this.device.data.mode === 'off'
? 'off'
: this.device.data.fanMode === 'on' ? 'fan' : 'idle'
}),
temperature: (() => { return this.childDevices.temperatureSensor.data.celsius }),
temperature: (() => { return this.temperatureSensor.data.celsius }),
... this.entity.thermostat.modes.includes('auto')
? {
autoSetPointInProgress: false,
@@ -49,14 +50,14 @@ class Thermostat extends RingSocketDevice {
} : {}
}
this.childDevices.operatingStatus.onData.subscribe(() => {
this.operatingStatus.onData.subscribe(() => {
if (this.isOnline()) {
this.publishOperatingMode()
this.publishAttributes()
}
})
this.childDevices.temperatureSensor.onData.subscribe(() => {
this.temperatureSensor.onData.subscribe(() => {
if (this.isOnline()) {
this.publishTemperature()
this.publishAttributes()

View File

@@ -13,7 +13,7 @@ class Main {
constructor() {
// Start event listeners
utils.event.on('generatedToken', (generatedToken) => {
utils.event.on('generated_token', (generatedToken) => {
this.init(generatedToken)
})

View File

@@ -9,7 +9,7 @@ class Mqtt {
this.connected = false
// Configure event listeners
utils.event.on('ringState', async (state) => {
utils.event.on('ring_state', async (state) => {
if (!this.client && state === 'connected') {
// Ring API connected, short wait before starting MQTT client
await utils.sleep(2)
@@ -17,13 +17,13 @@ class Mqtt {
}
})
utils.event.on('mqttPublish', (topic, message) => {
utils.event.on('mqtt_publish', (topic, message) => {
this.client.publish(topic, (typeof message === 'number') ? message.toString() : message, { qos: 1 }, () => {
// Just a dummy callback to prevent MQTT memory leak in 4.3.5/4.3.6
})
})
utils.event.on('mqttSubscribe', (topic) => {
utils.event.on('mqtt_subscribe', (topic) => {
this.client.subscribe(topic)
})
}
@@ -61,7 +61,7 @@ class Mqtt {
this.client.on('connect', () => {
if (!this.connected) {
this.connected = true
utils.event.emit('mqttState', 'connected')
utils.event.emit('mqtt_state', 'connected')
}
})
@@ -72,20 +72,20 @@ class Mqtt {
debug('Attempting to reconnect to MQTT broker...')
}
this.connected = false
utils.event.emit('mqttState', 'disconnected')
utils.event.emit('mqtt_state', 'disconnected')
})
this.client.on('error', (error) => {
debug('Unable to connect to MQTT broker', error.message)
this.connected = false
utils.event.emit('mqttState', 'disconnected')
utils.event.emit('mqtt_state', 'disconnected')
})
// Process MQTT messages from subscribed command topics
this.client.on('message', (topic, message) => {
message = message.toString()
if (topic === utils.config.hass_topic || topic === 'hass/status' || topic === 'hassio/status') {
utils.event.emit('haStatus', topic, message)
utils.event.emit('ha_status', topic, message)
} else {
utils.event.emit(topic, topic.split("/").slice(-2).join("/"), message)
}

View File

@@ -35,7 +35,7 @@ class RingMqtt {
this.republishCount = 6 // Republish config/state this many times after startup or HA start/restart
// Configure event listeners
utils.event.on('mqttState', (state) => {
utils.event.on('mqtt_state', (state) => {
if (state === 'connected') {
this.mqttConnected = true
debug('MQTT connection established, processing Ring locations...')
@@ -45,7 +45,7 @@ class RingMqtt {
}
})
utils.event.on('haStatus', async (topic, message) => {
utils.event.on('ha_status', async (topic, message) => {
debug('Home Assistant state topic '+topic+' received message: '+message)
if (message === 'online') {
// Republish devices and state if restart of HA is detected
@@ -77,7 +77,7 @@ class RingMqtt {
debug(`Attempting connection to Ring API using ${generatedToken ? 'generated' : 'saved'} refresh token...`)
this.client = new RingApi(ringAuth)
await this.client.getProfile()
utils.event.emit('ringState', 'connected')
utils.event.emit('ring_state', 'connected')
debug(`Successfully established connection to Ring API using ${generatedToken ? 'generated' : 'saved'} token`)
// Subscribe to token update events and save new tokens to state file
@@ -145,12 +145,12 @@ class RingMqtt {
// Get all Ring locations
const locations = await this.client.getLocations()
debug(colors.green('-'.repeat(82)))
debug(colors.green('-'.repeat(90)))
debug(colors.white('This account has access to the following locations:'))
locations.map(function(location) {
debug(' '+colors.green(location.name)+colors.cyan(` (${location.id})`))
})
debug(colors.green(' '.repeat(82)))
debug(colors.green(' '.repeat(90)))
debug(colors.brightYellow('IMPORTANT: ')+colors.white('All alarm and smart lighting hubs at these locations *MUST* be online '))
debug(colors.white(' or device discovery for that location will hang indefinitely! '))
debug(colors.white(' If desired, use the "location_ids" config option to restrict discovery '))
@@ -162,7 +162,7 @@ class RingMqtt {
let chimes = new Array()
const unsupportedDevices = new Array()
debug(colors.green('-'.repeat(82)))
debug(colors.green('-'.repeat(90)))
// If new location, set custom properties and add to location list
if (this.locations.find(l => l.locationId == location.locationId)) {
debug(colors.white('Existing location: ')+colors.green(location.name)+colors.cyan(` (${location.id})`))
@@ -238,7 +238,7 @@ class RingMqtt {
debug(colors.yellow(` Unsupported device: ${deviceType}`))
})
}
debug(colors.green('-'.repeat(82)))
debug(colors.green('-'.repeat(90)))
debug('Ring location/device data updated, sleeping for 5 seconds.')
await utils.sleep(2)
const cameras = await this.devices.filter(d => d.device instanceof RingCamera)

View File

@@ -16,7 +16,7 @@ class RtspSimpleServer {
this.cameras = cameras
}
this.started = true
debug(colors.green('-'.repeat(73)))
debug(colors.green('-'.repeat(90)))
debug('Starting rtsp-simple-server process...')
this.rssProcess = spawn('rtsp-simple-server', [`${__dirname}/../config/rtsp-simple-server.yml`], {
@@ -108,7 +108,7 @@ class RtspSimpleServer {
}
}
await utils.msleep(100)
debug(colors.green('-'.repeat(73)))
debug(colors.green('-'.repeat(90)))
}
async getPathDetails(path) {

View File

@@ -8,7 +8,7 @@ const writeFileAtomic = require('write-file-atomic')
class State {
constructor() {
this.valid = false
this.writeDelay = false
this.writeScheduled = false
this.data = {
ring_token: '',
systemId: '',
@@ -23,16 +23,16 @@ class State {
: this.file = '/data/ring-state.json'
await this.loadStateData()
utils.event.on(`saveDeviceState`, (deviceId, stateData) => {
utils.event.on(`save_device_state`, (deviceId, stateData) => {
this.data.devices[deviceId] = stateData
this.updateStateFile()
})
utils.event.on('getDeviceState', (deviceId) => {
utils.event.on('get_device_state', (deviceId) => {
if (this.data.devices.hasOwnProperty(deviceId)) {
utils.event.emit(`deviceState_${deviceId}`, this.data.devices[deviceId])
utils.event.emit(`device_state_${deviceId}`, this.data.devices[deviceId])
} else {
utils.event.emit(`deviceState_${deviceId}`, false)
utils.event.emit(`device_state_${deviceId}`, false)
}
})
}
@@ -79,9 +79,13 @@ class State {
}
async updateStateFile() {
if (!this.writeDelay) {
this.writeDelay = true
await utils.sleep(10)
// 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)
@@ -89,7 +93,6 @@ class State {
debug(colors.red('Failed to save updated refresh token in state file: '+this.file))
debug(err.message)
}
this.writeDelay = false
}
}
}

View File

@@ -49,15 +49,15 @@ class StreamWorkers {
if (workerId >= 0) {
switch (data.state) {
case 'active':
utils.event.emit(`${deviceId}_livestream`, 'active')
utils.event.emit(`livestream_${deviceId}`, 'active')
this.streamWorkers[workerId].sessions[deviceId].streamData.sessionId = data.liveCallData.sessionId
break;
case 'inactive':
utils.event.emit(`${deviceId}_livestream`, 'inactive')
utils.event.emit(`livestream_${deviceId}`, 'inactive')
delete this.streamWorkers[workerId].sessions[deviceId]
break;
case 'failed':
utils.event.emit(`${deviceId}_livestream`, 'failed')
utils.event.emit(`livestream_${deviceId}`, 'failed')
delete this.streamWorkers[workerId].sessions[deviceId]
break;
}

View File

@@ -14,7 +14,7 @@ class TokenApp {
this.start()
}
utils.event.on('ringState', async (state) => {
utils.event.on('ring_state', async (state) => {
if (state === 'connected') {
this.ringConnected = true
@@ -87,7 +87,7 @@ class TokenApp {
}
if (generatedToken) {
res.sendFile('restart.html', {root: webdir})
utils.event.emit('generatedToken', generatedToken.refresh_token)
utils.event.emit('generated_token', generatedToken.refresh_token)
}
})
}