mirror of
https://github.com/tsightler/ring-mqtt.git
synced 2025-09-26 21:01:12 +08:00
Implement event based engine
* First steps toward EventEmitter based core
This commit is contained in:
@@ -1,8 +1,3 @@
|
||||
const debug = {
|
||||
mqtt: require('debug')('ring-mqtt'),
|
||||
attr: require('debug')('ring-attr'),
|
||||
disc: require('debug')('ring-disc')
|
||||
}
|
||||
const utils = require('../lib/utils')
|
||||
const colors = require('colors/safe')
|
||||
|
||||
@@ -10,8 +5,6 @@ const colors = require('colors/safe')
|
||||
class RingDevice {
|
||||
constructor(deviceInfo, category, primaryAttribute, deviceId, locationId) {
|
||||
this.device = deviceInfo.device
|
||||
this.mqttClient = deviceInfo.mqttClient
|
||||
this.config = deviceInfo.config
|
||||
this.deviceId = deviceId
|
||||
this.locationId = locationId
|
||||
this.availabilityState = 'unpublished'
|
||||
@@ -20,11 +13,10 @@ class RingDevice {
|
||||
return this.availabilityState === 'online' ? true : false
|
||||
}
|
||||
this.debug = (message, debugType) => {
|
||||
debugType = debugType ? debugType : 'mqtt'
|
||||
debug[debugType](colors.green(`[${this.device.name}] `)+message)
|
||||
utils.debug(debugType === 'disc' ? message : colors.green(`[${this.device.name}] `)+message, debugType ? debugType : 'mqtt')
|
||||
}
|
||||
// Build device base and availability topic
|
||||
this.deviceTopic = `${this.config.ring_topic}/${this.locationId}/${category}/${this.deviceId}`
|
||||
this.deviceTopic = `${utils.config.ring_topic}/${this.locationId}/${category}/${this.deviceId}`
|
||||
this.availabilityTopic = `${this.deviceTopic}/status`
|
||||
|
||||
if (primaryAttribute !== 'disable') {
|
||||
@@ -39,7 +31,7 @@ class RingDevice {
|
||||
// and publishes this message to the Home Assistant config topic
|
||||
async publishDiscovery() {
|
||||
const debugMsg = (this.availabilityState === 'unpublished') ? 'Publishing new ' : 'Republishing existing '
|
||||
debug.disc(debugMsg+'device id: '+this.deviceId, 'disc')
|
||||
this.debug(debugMsg+'device id: '+this.deviceId, 'disc')
|
||||
|
||||
Object.keys(this.entity).forEach(entityKey => {
|
||||
const entity = this.entity[entityKey]
|
||||
@@ -102,8 +94,8 @@ class RingDevice {
|
||||
? { icon: entity.icon }
|
||||
: entityKey === "info"
|
||||
? { icon: 'mdi:information-outline' } : {},
|
||||
... entity.component === 'alarm_control_panel' && this.config.disarm_code
|
||||
? { code: this.config.disarm_code.toString(),
|
||||
... entity.component === 'alarm_control_panel' && utils.config.disarm_code
|
||||
? { code: utils.config.disarm_code.toString(),
|
||||
code_arm_required: false,
|
||||
code_disarm_required: true } : {},
|
||||
... entity.hasOwnProperty('brightness_scale')
|
||||
@@ -149,9 +141,9 @@ class RingDevice {
|
||||
}
|
||||
|
||||
const configTopic = `homeassistant/${entity.component}/${this.locationId}/${this.deviceId}_${entityKey}/config`
|
||||
debug.disc(`HASS config topic: ${configTopic}`)
|
||||
debug.disc(discoveryMessage)
|
||||
this.publishMqtt(configTopic, JSON.stringify(discoveryMessage), false)
|
||||
this.debug(`HASS config topic: ${configTopic}`, 'disc')
|
||||
this.debug(discoveryMessage, 'disc')
|
||||
this.mqttPublish(configTopic, JSON.stringify(discoveryMessage), false)
|
||||
|
||||
// On first publish store generated topics in entities object and subscribe to command topics
|
||||
if (!this.entity[entityKey].hasOwnProperty('published')) {
|
||||
@@ -159,7 +151,7 @@ class RingDevice {
|
||||
Object.keys(discoveryMessage).filter(property => property.match('topic')).forEach(topic => {
|
||||
this.entity[entityKey][topic] = discoveryMessage[topic]
|
||||
if (topic.match('command_topic')) {
|
||||
this.mqttClient.subscribe(discoveryMessage[topic])
|
||||
utils.event.emit('mqttSubscribe', discoveryMessage[topic])
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -187,18 +179,18 @@ class RingDevice {
|
||||
return filteredAttributes
|
||||
}, {})
|
||||
if (Object.keys(entityAttributes).length > 0) {
|
||||
this.publishMqtt(this.entity[entityKey].json_attributes_topic, JSON.stringify(entityAttributes), 'attr')
|
||||
this.mqttPublish(this.entity[entityKey].json_attributes_topic, JSON.stringify(entityAttributes), 'attr')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Publish state messages with debug
|
||||
publishMqtt(topic, message, debugType) {
|
||||
mqttPublish(topic, message, debugType) {
|
||||
if (debugType !== false) {
|
||||
this.debug(colors.blue(`${topic} `)+colors.cyan(`${message}`), debugType)
|
||||
}
|
||||
this.mqttClient.publish(topic, (typeof message === 'number') ? message.toString() : message, { qos: 1 })
|
||||
utils.event.emit('mqttPublish', topic, message)
|
||||
}
|
||||
|
||||
// Set state topic online
|
||||
@@ -206,7 +198,7 @@ class RingDevice {
|
||||
if (this.shutdown) { return } // Supress any delayed online state messages if ring-mqtt is shutting down
|
||||
const debugType = (this.availabilityState === 'online') ? false : 'mqtt'
|
||||
this.availabilityState = 'online'
|
||||
this.publishMqtt(this.availabilityTopic, this.availabilityState, debugType)
|
||||
this.mqttPublish(this.availabilityTopic, this.availabilityState, debugType)
|
||||
await utils.sleep(2)
|
||||
}
|
||||
|
||||
@@ -214,7 +206,7 @@ class RingDevice {
|
||||
offline() {
|
||||
const debugType = (this.availabilityState === 'offline') ? false : 'mqtt'
|
||||
this.availabilityState = 'offline'
|
||||
this.publishMqtt(this.availabilityTopic, this.availabilityState, debugType)
|
||||
this.mqttPublish(this.availabilityTopic, this.availabilityState, debugType)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -119,7 +119,7 @@ class RingSocketDevice extends RingDevice {
|
||||
&& this.device.data.hasOwnProperty('networks') && this.device.data.networks.hasOwnProperty('wlan0')
|
||||
? { wirelessNetwork: this.device.data.networks.wlan0.ssid, wirelessSignal: this.device.data.networks.wlan0.rssi } : {}
|
||||
}
|
||||
this.publishMqtt(this.entity.info.state_topic, JSON.stringify(attributes), 'attr')
|
||||
this.mqttPublish(this.entity.info.state_topic, JSON.stringify(attributes), 'attr')
|
||||
this.publishAttributeEntities(attributes)
|
||||
}
|
||||
}
|
||||
|
@@ -35,11 +35,11 @@ class BaseStation extends RingSocketDevice {
|
||||
|
||||
if (this.entity.hasOwnProperty('volume')) {
|
||||
const currentVolume = (this.device.data.volume && !isNaN(this.device.data.volume) ? Math.round(100 * this.device.data.volume) : 0)
|
||||
this.publishMqtt(this.entity.volume.state_topic, currentVolume.toString())
|
||||
this.mqttPublish(this.entity.volume.state_topic, currentVolume.toString())
|
||||
|
||||
// Eventually remove this but for now this attempts to delete the old light component based volume control from Home Assistant
|
||||
if (isPublish) {
|
||||
this.publishMqtt('homeassistant/light/'+this.locationId+'/'+this.deviceId+'_audio/config', '', false)
|
||||
this.mqttPublish('homeassistant/light/'+this.locationId+'/'+this.deviceId+'_audio/config', '', false)
|
||||
}
|
||||
}
|
||||
this.publishAttributes()
|
||||
|
@@ -37,11 +37,11 @@ class BeamOutdoorPlug extends RingSocketDevice {
|
||||
}
|
||||
|
||||
publishOutlet1State() {
|
||||
this.publishMqtt(this.entity.outlet1.state_topic, this.childDevices.outlet1.data.on ? "ON" : "OFF")
|
||||
this.mqttPublish(this.entity.outlet1.state_topic, this.childDevices.outlet1.data.on ? "ON" : "OFF")
|
||||
}
|
||||
|
||||
publishOutlet2State() {
|
||||
this.publishMqtt(this.entity.outlet2.state_topic, this.childDevices.outlet2.data.on ? "ON" : "OFF")
|
||||
this.mqttPublish(this.entity.outlet2.state_topic, this.childDevices.outlet2.data.on ? "ON" : "OFF")
|
||||
}
|
||||
|
||||
// Process messages from MQTT command topic
|
||||
|
@@ -51,8 +51,8 @@ class Beam extends RingSocketDevice {
|
||||
icon: 'hass:timer'
|
||||
}
|
||||
|
||||
if (this.config.hasOwnProperty('beam_duration') && this.config.beam_duration > 0) {
|
||||
this.entity.beam_duration.state = this.config.beam_duration
|
||||
if (utils.config.hasOwnProperty('beam_duration') && utils.config.beam_duration > 0) {
|
||||
this.entity.beam_duration.state = utils.config.beam_duration
|
||||
} else {
|
||||
this.entity.beam_duration.state = this.device.data.hasOwnProperty('onDuration') ? this.device.data.onDuration : 0
|
||||
}
|
||||
@@ -61,16 +61,16 @@ class Beam extends RingSocketDevice {
|
||||
publishData() {
|
||||
if (this.entity.hasOwnProperty('motion') && this.entity.motion.hasOwnProperty('state_topic')) {
|
||||
const motionState = this.device.data.motionStatus === 'faulted' ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity.motion.state_topic, motionState)
|
||||
this.mqttPublish(this.entity.motion.state_topic, motionState)
|
||||
}
|
||||
if (this.entity.hasOwnProperty('light') && this.entity.light.hasOwnProperty('state_topic')) {
|
||||
const switchState = this.device.data.on ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity.light.state_topic, switchState)
|
||||
this.mqttPublish(this.entity.light.state_topic, switchState)
|
||||
if (this.entity.light.hasOwnProperty('brightness_state_topic')) {
|
||||
const switchLevel = (this.device.data.level && !isNaN(this.device.data.level) ? Math.round(100 * this.device.data.level) : 0)
|
||||
this.publishMqtt(this.entity.light.brightness_state_topic, switchLevel.toString())
|
||||
this.mqttPublish(this.entity.light.brightness_state_topic, switchLevel.toString())
|
||||
}
|
||||
this.publishMqtt(this.entity.beam_duration.state_topic, this.entity.beam_duration.state.toString())
|
||||
this.mqttPublish(this.entity.beam_duration.state_topic, this.entity.beam_duration.state.toString())
|
||||
}
|
||||
if (!this.isLightGroup) {
|
||||
this.publishAttributes()
|
||||
@@ -145,7 +145,7 @@ class Beam extends RingSocketDevice {
|
||||
this.debug('Light duration command received but out of range (0-32767)')
|
||||
} else {
|
||||
this.entity.beam_duration.state = parseInt(duration)
|
||||
this.publishMqtt(this.entity.beam_duration.state_topic, this.entity.beam_duration.state.toString())
|
||||
this.mqttPublish(this.entity.beam_duration.state_topic, this.entity.beam_duration.state.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
const RingPolledDevice = require('./base-polled-device')
|
||||
const utils = require( '../lib/utils' )
|
||||
const colors = require('colors/safe')
|
||||
const RingPolledDevice = require('./base-polled-device')
|
||||
const { clientApi } = require('../node_modules/@tsightler/ring-client-api/lib/api/rest-client')
|
||||
const P2J = require('pipe2jpeg')
|
||||
const net = require('net');
|
||||
const getPort = require('get-port')
|
||||
@@ -47,8 +46,8 @@ class Camera extends RingPolledDevice {
|
||||
status: 'inactive',
|
||||
publishedStatus: '',
|
||||
session: false,
|
||||
rtspPublishUrl: (this.config.livestream_user && this.config.livestream_pass)
|
||||
? `rtsp://${this.config.livestream_user}:${this.config.livestream_pass}@localhost:8554/${this.deviceId}_live`
|
||||
rtspPublishUrl: (utils.config.livestream_user && utils.config.livestream_pass)
|
||||
? `rtsp://${utils.config.livestream_user}:${utils.config.livestream_pass}@localhost:8554/${this.deviceId}_live`
|
||||
: `rtsp://localhost:8554/${this.deviceId}_live`
|
||||
},
|
||||
event: {
|
||||
@@ -60,8 +59,8 @@ class Camera extends RingPolledDevice {
|
||||
recordingUrl: null,
|
||||
recordingUrlExpire: null,
|
||||
pollCycle: 0,
|
||||
rtspPublishUrl: (this.config.livestream_user && this.config.livestream_pass)
|
||||
? `rtsp://${this.config.livestream_user}:${this.config.livestream_pass}@localhost:8554/${this.deviceId}_event`
|
||||
rtspPublishUrl: (utils.config.livestream_user && utils.config.livestream_pass)
|
||||
? `rtsp://${utils.config.livestream_user}:${utils.config.livestream_pass}@localhost:8554/${this.deviceId}_event`
|
||||
: `rtsp://localhost:8554/${this.deviceId}_event`
|
||||
},
|
||||
snapshot: {
|
||||
@@ -93,10 +92,10 @@ class Camera extends RingPolledDevice {
|
||||
} : {}
|
||||
}
|
||||
|
||||
if (this.config.snapshot_mode.match(/^(motion|interval|all)$/)) {
|
||||
this.data.snapshot.motion = (this.config.snapshot_mode.match(/^(motion|all)$/)) ? true : false
|
||||
if (utils.config.snapshot_mode.match(/^(motion|interval|all)$/)) {
|
||||
this.data.snapshot.motion = (utils.config.snapshot_mode.match(/^(motion|all)$/)) ? true : false
|
||||
|
||||
if (this.config.snapshot_mode.match(/^(interval|all)$/)) {
|
||||
if (utils.config.snapshot_mode.match(/^(interval|all)$/)) {
|
||||
this.data.snapshot.autoInterval = true
|
||||
if (this.device.operatingOnBattery) {
|
||||
if (this.device.data.settings.hasOwnProperty('lite_24x7') && this.device.data.settings.lite_24x7.enabled) {
|
||||
@@ -252,8 +251,8 @@ class Camera extends RingPolledDevice {
|
||||
|
||||
// Set some helper attributes for streaming
|
||||
this.data.stream.live.stillImageURL = `https://${stillImageUrlBase}:8123{{ states.camera.${this.device.name.toLowerCase().replace(" ","_")}_snapshot.attributes.entity_picture }}`,
|
||||
this.data.stream.live.streamSource = (this.config.livestream_user && this.config.livestream_pass)
|
||||
? `rtsp://${this.config.livestream_user}:${this.config.livestream_pass}@${streamSourceUrlBase}:8554/${this.deviceId}_live`
|
||||
this.data.stream.live.streamSource = (utils.config.livestream_user && utils.config.livestream_pass)
|
||||
? `rtsp://${utils.config.livestream_user}:${utils.config.livestream_pass}@${streamSourceUrlBase}:8554/${this.deviceId}_live`
|
||||
: `rtsp://${streamSourceUrlBase}:8554/${this.deviceId}_live`
|
||||
}
|
||||
|
||||
@@ -357,7 +356,7 @@ class Camera extends RingPolledDevice {
|
||||
// Publish ding state and attributes
|
||||
publishDingState(dingKind) {
|
||||
const dingState = this.data[dingKind].active_ding ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity[dingKind].state_topic, dingState)
|
||||
this.mqttPublish(this.entity[dingKind].state_topic, dingState)
|
||||
|
||||
if (dingKind === 'motion') {
|
||||
this.publishMotionAttributes()
|
||||
@@ -376,7 +375,7 @@ class Camera extends RingPolledDevice {
|
||||
this.data.motion.detection_enabled = this.device.data.settings.motion_detection_enabled
|
||||
attributes.motionDetectionEnabled = this.data.motion.detection_enabled
|
||||
}
|
||||
this.publishMqtt(this.entity.motion.json_attributes_topic, JSON.stringify(attributes), 'attr')
|
||||
this.mqttPublish(this.entity.motion.json_attributes_topic, JSON.stringify(attributes), 'attr')
|
||||
}
|
||||
|
||||
publishDingAttributes() {
|
||||
@@ -384,7 +383,7 @@ class Camera extends RingPolledDevice {
|
||||
lastDing: this.data.ding.last_ding,
|
||||
lastDingTime: this.data.ding.last_ding_time
|
||||
}
|
||||
this.publishMqtt(this.entity.ding.json_attributes_topic, JSON.stringify(attributes), 'attr')
|
||||
this.mqttPublish(this.entity.ding.json_attributes_topic, JSON.stringify(attributes), 'attr')
|
||||
}
|
||||
|
||||
// Publish camera state for polled attributes (light/siren state, etc)
|
||||
@@ -395,14 +394,14 @@ class Camera extends RingPolledDevice {
|
||||
const lightState = this.device.data.led_status === 'on' ? 'ON' : 'OFF'
|
||||
if ((lightState !== this.data.light.state && Date.now()/1000 - this.data.light.setTime > 30) || isPublish) {
|
||||
this.data.light.state = lightState
|
||||
this.publishMqtt(this.entity.light.state_topic, this.data.light.state)
|
||||
this.mqttPublish(this.entity.light.state_topic, this.data.light.state)
|
||||
}
|
||||
}
|
||||
if (this.device.hasSiren) {
|
||||
const sirenState = this.device.data.siren_status.seconds_remaining > 0 ? 'ON' : 'OFF'
|
||||
if (sirenState !== this.data.siren.state || isPublish) {
|
||||
this.data.siren.state = sirenState
|
||||
this.publishMqtt(this.entity.siren.state_topic, this.data.siren.state)
|
||||
this.mqttPublish(this.entity.siren.state_topic, this.data.siren.state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,14 +429,14 @@ class Camera extends RingPolledDevice {
|
||||
}
|
||||
attributes.stream_Source = this.data.stream.live.streamSource
|
||||
attributes.still_Image_URL = this.data.stream.live.stillImageURL
|
||||
this.publishMqtt(this.entity.info.state_topic, JSON.stringify(attributes), 'attr')
|
||||
this.mqttPublish(this.entity.info.state_topic, JSON.stringify(attributes), 'attr')
|
||||
this.publishAttributeEntities(attributes)
|
||||
}
|
||||
}
|
||||
|
||||
publishSnapshotInterval(isPublish) {
|
||||
if (isPublish) {
|
||||
this.publishMqtt(this.entity.snapshot_interval.state_topic, this.data.snapshot.interval.toString())
|
||||
this.mqttPublish(this.entity.snapshot_interval.state_topic, this.data.snapshot.interval.toString())
|
||||
} else {
|
||||
// Update snapshot frequency in case it's changed
|
||||
if (this.data.snapshot.autoInterval && this.data.snapshot.interval !== this.device.data.settings.lite_24x7.frequency_secs) {
|
||||
@@ -445,7 +444,7 @@ class Camera extends RingPolledDevice {
|
||||
clearTimeout(this.data.snapshot.intervalTimerId)
|
||||
this.scheduleSnapshotRefresh()
|
||||
}
|
||||
this.publishMqtt(this.entity.snapshot_interval.state_topic, this.data.snapshot.interval.toString())
|
||||
this.mqttPublish(this.entity.snapshot_interval.state_topic, this.data.snapshot.interval.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,13 +454,13 @@ class Camera extends RingPolledDevice {
|
||||
const streamState = (this.data.stream[type].status === 'active' || this.data.stream[type].status === 'activating') ? 'ON' : 'OFF'
|
||||
if (streamState !== this.data.stream[type].state || isPublish) {
|
||||
this.data.stream[type].state = streamState
|
||||
this.publishMqtt(this.entity[entityProp].state_topic, this.data.stream[type].state)
|
||||
this.mqttPublish(this.entity[entityProp].state_topic, this.data.stream[type].state)
|
||||
}
|
||||
|
||||
if (this.data.stream[type].publishedStatus !== this.data.stream[type].status || isPublish) {
|
||||
this.data.stream[type].publishedStatus = this.data.stream[type].status
|
||||
const attributes = { status: this.data.stream[type].status }
|
||||
this.publishMqtt(this.entity[entityProp].json_attributes_topic, JSON.stringify(attributes), 'attr')
|
||||
this.mqttPublish(this.entity[entityProp].json_attributes_topic, JSON.stringify(attributes), 'attr')
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -469,20 +468,20 @@ class Camera extends RingPolledDevice {
|
||||
publishStreamSelectState(isPublish) {
|
||||
if (this.data.event_select.state !== this.data.event_select.publishedState || isPublish) {
|
||||
this.data.event_select.publishedState = this.data.event_select.state
|
||||
this.publishMqtt(this.entity.event_select.state_topic, this.data.event_select.state)
|
||||
this.mqttPublish(this.entity.event_select.state_topic, this.data.event_select.state)
|
||||
}
|
||||
const attributes = {
|
||||
recordingUrl: this.data.stream.event.recordingUrl,
|
||||
eventId: this.data.stream.event.dingId
|
||||
}
|
||||
this.publishMqtt(this.entity.event_select.json_attributes_topic, JSON.stringify(attributes), 'attr')
|
||||
this.mqttPublish(this.entity.event_select.json_attributes_topic, JSON.stringify(attributes), 'attr')
|
||||
}
|
||||
|
||||
// Publish snapshot image/metadata
|
||||
publishSnapshot() {
|
||||
this.debug(colors.blue(`${this.entity.snapshot.topic}`)+' '+colors.cyan('<binary_image_data>'))
|
||||
this.publishMqtt(this.entity.snapshot.topic, this.data.snapshot.currentImage, false)
|
||||
this.publishMqtt(this.entity.snapshot.json_attributes_topic, JSON.stringify({ timestamp: this.data.snapshot.timestamp }), 'attr')
|
||||
this.mqttPublish(this.entity.snapshot.topic, this.data.snapshot.currentImage, false)
|
||||
this.mqttPublish(this.entity.snapshot.json_attributes_topic, JSON.stringify({ timestamp: this.data.snapshot.timestamp }), 'attr')
|
||||
}
|
||||
|
||||
// Refresh snapshot on scheduled interval
|
||||
@@ -495,45 +494,42 @@ class Camera extends RingPolledDevice {
|
||||
}
|
||||
|
||||
refreshSnapshot(type) {
|
||||
if (this.device.operatingOnBattery) {
|
||||
// Battery cameras can't take snapshots while streaming so check if there's an active stream
|
||||
if (type === 'interval' && this.data.stream.live.status.match(/^(inactive|failed)$/)) {
|
||||
// It's an interval snapshot and no active local stream, assume a standard snapshot will work
|
||||
this.updateSnapshot(type)
|
||||
} else {
|
||||
// If a local live stream active, or it's a motion snapshot, grab an image from the stream
|
||||
this.data.snapshot.update = true
|
||||
if (type === 'motion') {
|
||||
this.debug('Motion event detected on battery powered camera snapshot will be updated from live stream')
|
||||
}
|
||||
// If it's a motion event, and there's no active stream, start one so that snapshots can be grabbed from it
|
||||
if (!this.data.stream.snapshot.active) {
|
||||
this.startRtspReadStream('snapshot', this.data.stream.snapshot.duration)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Line powered cameras can take snapshots all the time
|
||||
if (!this.device.operatingOnBattery || (type === 'interval' && this.data.stream.live.status.match(/^(inactive|failed)$/))) {
|
||||
// For line powered cameras, or battery cameras with no active stream,
|
||||
// assume a regular snapshot update request will work
|
||||
this.updateSnapshot(type)
|
||||
} else {
|
||||
this.data.snapshot.update = true
|
||||
|
||||
// Battery powered cameras can take a snapshot while recording so, if it's a motion
|
||||
// event or there's an active local stream, grab a key frame to use for the snapshot
|
||||
if (type === 'motion') {
|
||||
this.debug('Motion event detected on battery powered camera, snapshot will be updated from live stream')
|
||||
}
|
||||
|
||||
// If there's no existing active local stream, start one as it's required to get a snapshot
|
||||
if (!this.data.stream.snapshot.active) {
|
||||
this.startRtspReadStream('snapshot', this.data.stream.snapshot.duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async updateSnapshot(type) {
|
||||
let newSnapshot
|
||||
let newSnapshot = false
|
||||
|
||||
if (this.device.snapshotsAreBlocked) {
|
||||
this.debug('Snapshots are unavailable, check if motion capture is disabled manually or via modes settings')
|
||||
return
|
||||
}
|
||||
|
||||
if (type === 'motion') {
|
||||
this.debug('Motion event detected for line powered camera, forcing a non-cached snapshot update')
|
||||
}
|
||||
|
||||
try {
|
||||
this.debug('Requesting updated snapshot')
|
||||
newSnapshot = (type === 'motion')
|
||||
? await this.device.getNextSnapshot({ force: true })
|
||||
: await this.device.getSnapshot()
|
||||
if (type === 'motion') {
|
||||
this.debug('Requesting an updated motion snapshot')
|
||||
newSnapshot = await this.device.getNextSnapshot({ force: true })
|
||||
} else {
|
||||
this.debug('Requesting an updated interval snapshot')
|
||||
newSnapshot = await this.device.getSnapshot()
|
||||
}
|
||||
} catch (error) {
|
||||
this.debug(error)
|
||||
this.debug('Failed to retrieve updated snapshot')
|
||||
@@ -827,7 +823,7 @@ class Camera extends RingPolledDevice {
|
||||
this.data.light.setTime = Math.floor(Date.now()/1000)
|
||||
await this.device.setLight(command === 'on' ? true : false)
|
||||
this.data.light.state = command === 'on' ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity.light.state_topic, this.data.light.state)
|
||||
this.mqttPublish(this.entity.light.state_topic, this.data.light.state)
|
||||
break;
|
||||
default:
|
||||
this.debug('Received unknown command for light')
|
||||
|
@@ -69,25 +69,25 @@ class Chime extends RingPolledDevice {
|
||||
|
||||
// Polled states are published only if value changes or it's a device publish
|
||||
if (volumeState !== this.data.volume || isPublish) {
|
||||
this.publishMqtt(this.entity.volume.state_topic, volumeState.toString())
|
||||
this.mqttPublish(this.entity.volume.state_topic, volumeState.toString())
|
||||
this.data.volume = volumeState
|
||||
}
|
||||
|
||||
if (snoozeState !== this.data.snooze || isPublish) {
|
||||
this.publishMqtt(this.entity.snooze.state_topic, snoozeState)
|
||||
this.mqttPublish(this.entity.snooze.state_topic, snoozeState)
|
||||
this.data.snooze = snoozeState
|
||||
}
|
||||
|
||||
if (snoozeMinutesRemaining !== this.data.snooze_minutes_remaining || isPublish) {
|
||||
this.publishMqtt(this.entity.snooze.json_attributes_topic, JSON.stringify({ minutes_remaining: snoozeMinutesRemaining }), 'attr')
|
||||
this.mqttPublish(this.entity.snooze.json_attributes_topic, JSON.stringify({ minutes_remaining: snoozeMinutesRemaining }), 'attr')
|
||||
this.data.snooze_minutes_remaining = snoozeMinutesRemaining
|
||||
}
|
||||
|
||||
// Local states are published only for publish/republish
|
||||
if (isPublish) {
|
||||
this.publishMqtt(this.entity.snooze_minutes.state_topic, this.data.snooze_minutes.toString())
|
||||
this.publishMqtt(this.entity.play_ding_sound.state_topic, this.data.play_ding_sound)
|
||||
this.publishMqtt(this.entity.play_motion_sound.state_topic, this.data.play_motion_sound)
|
||||
this.mqttPublish(this.entity.snooze_minutes.state_topic, this.data.snooze_minutes.toString())
|
||||
this.mqttPublish(this.entity.play_ding_sound.state_topic, this.data.play_ding_sound)
|
||||
this.mqttPublish(this.entity.play_motion_sound.state_topic, this.data.play_motion_sound)
|
||||
this.publishAttributes()
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,7 @@ class Chime extends RingPolledDevice {
|
||||
attributes.wirelessSignal = deviceHealth.latest_signal_strength
|
||||
attributes.firmwareStatus = deviceHealth.firmware
|
||||
attributes.lastUpdate = deviceHealth.updated_at.slice(0,-6)+"Z"
|
||||
this.publishMqtt(this.entity.info.state_topic, JSON.stringify(attributes), 'attr')
|
||||
this.mqttPublish(this.entity.info.state_topic, JSON.stringify(attributes), 'attr')
|
||||
this.publishAttributeEntities(attributes)
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ class Chime extends RingPolledDevice {
|
||||
this.debug('Snooze minutes command received but out of range (0-1440 minutes)')
|
||||
} else {
|
||||
this.data.snooze_minutes = parseInt(minutes)
|
||||
this.publishMqtt(this.entity.snooze_minutes.state_topic, this.data.snooze_minutes.toString())
|
||||
this.mqttPublish(this.entity.snooze_minutes.state_topic, this.data.snooze_minutes.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,10 +179,10 @@ class Chime extends RingPolledDevice {
|
||||
|
||||
switch(command) {
|
||||
case 'on':
|
||||
this.publishMqtt(this.entity[`play_${chimeType}_sound`].state_topic, 'ON')
|
||||
this.mqttPublish(this.entity[`play_${chimeType}_sound`].state_topic, 'ON')
|
||||
await this.device.playSound(chimeType)
|
||||
await utils.sleep(5)
|
||||
this.publishMqtt(this.entity[`play_${chimeType}_sound`].state_topic, 'OFF')
|
||||
this.mqttPublish(this.entity[`play_${chimeType}_sound`].state_topic, 'OFF')
|
||||
break;
|
||||
case 'off': {
|
||||
break;
|
||||
|
@@ -1,5 +1,4 @@
|
||||
const RingSocketDevice = require('./base-socket-device')
|
||||
const { RingDeviceType } = require('@tsightler/ring-client-api')
|
||||
|
||||
class CoAlarm extends RingSocketDevice {
|
||||
constructor(deviceInfo) {
|
||||
@@ -18,7 +17,7 @@ class CoAlarm extends RingSocketDevice {
|
||||
|
||||
publishData() {
|
||||
const coState = this.device.data.alarmStatus === 'active' ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity.co.state_topic, coState)
|
||||
this.mqttPublish(this.entity.co.state_topic, coState)
|
||||
this.publishAttributes()
|
||||
}
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ class ContactSensor extends RingSocketDevice {
|
||||
|
||||
publishData() {
|
||||
const contactState = this.device.data.faulted ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity[this.entityName].state_topic, contactState)
|
||||
this.mqttPublish(this.entity[this.entityName].state_topic, contactState)
|
||||
this.publishAttributes()
|
||||
}
|
||||
}
|
||||
|
@@ -33,13 +33,13 @@ class Fan extends RingSocketDevice {
|
||||
// Publish device state
|
||||
// targetFanPercent is a small hack to work around Home Assistant UI behavior
|
||||
if (this.data.targetFanPercent && this.data.targetFanPercent !== fanPercent) {
|
||||
this.publishMqtt(this.entity.fan.percentage_state_topic, this.data.targetFanPercent.toString())
|
||||
this.mqttPublish(this.entity.fan.percentage_state_topic, this.data.targetFanPercent.toString())
|
||||
this.data.targetFanPercent = undefined
|
||||
} else {
|
||||
this.publishMqtt(this.entity.fan.percentage_state_topic, fanPercent.toString())
|
||||
this.mqttPublish(this.entity.fan.percentage_state_topic, fanPercent.toString())
|
||||
}
|
||||
this.publishMqtt(this.entity.fan.state_topic, fanState)
|
||||
this.publishMqtt(this.entity.fan.preset_mode_state_topic, fanPreset)
|
||||
this.mqttPublish(this.entity.fan.state_topic, fanState)
|
||||
this.mqttPublish(this.entity.fan.preset_mode_state_topic, fanPreset)
|
||||
|
||||
// Publish device attributes (batterylevel, tamper status)
|
||||
this.publishAttributes()
|
||||
|
@@ -20,8 +20,8 @@ class FloodFreezeSensor extends RingSocketDevice {
|
||||
publishData() {
|
||||
const floodState = this.device.data.flood && this.device.data.flood.faulted ? 'ON' : 'OFF'
|
||||
const freezeState = this.device.data.freeze && this.device.data.freeze.faulted ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity.flood.state_topic, floodState)
|
||||
this.publishMqtt(this.entity.freeze.state_topic, freezeState)
|
||||
this.mqttPublish(this.entity.flood.state_topic, floodState)
|
||||
this.mqttPublish(this.entity.freeze.state_topic, freezeState)
|
||||
this.publishAttributes()
|
||||
}
|
||||
}
|
||||
|
@@ -17,11 +17,11 @@ class Keypad extends RingSocketDevice {
|
||||
const isPublish = data === undefined ? true : false
|
||||
if (isPublish) {
|
||||
// Eventually remove this but for now this attempts to delete the old light component based volume control from Home Assistant
|
||||
this.publishMqtt('homeassistant/light/'+this.locationId+'/'+this.deviceId+'_audio/config', '', false)
|
||||
this.mqttPublish('homeassistant/light/'+this.locationId+'/'+this.deviceId+'_audio/config', '', false)
|
||||
}
|
||||
|
||||
const currentVolume = (this.device.data.volume && !isNaN(this.device.data.volume) ? Math.round(100 * this.device.data.volume) : 0)
|
||||
this.publishMqtt(this.entity.volume.state_topic, currentVolume.toString())
|
||||
this.mqttPublish(this.entity.volume.state_topic, currentVolume.toString())
|
||||
this.publishAttributes()
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,7 @@ class Lock extends RingSocketDevice {
|
||||
default:
|
||||
lockState = 'UNKNOWN'
|
||||
}
|
||||
this.publishMqtt(this.entity.lock.state_topic, lockState)
|
||||
this.mqttPublish(this.entity.lock.state_topic, lockState)
|
||||
this.publishAttributes()
|
||||
}
|
||||
|
||||
|
@@ -37,7 +37,7 @@ class ModesPanel extends RingPolledDevice {
|
||||
default:
|
||||
mqttMode = 'disarmed'
|
||||
}
|
||||
this.publishMqtt(this.entity.mode.state_topic, mqttMode)
|
||||
this.mqttPublish(this.entity.mode.state_topic, mqttMode)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,7 @@ class MotionSensor extends RingSocketDevice {
|
||||
|
||||
publishData() {
|
||||
const sensorState = this.device.data.faulted ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity.motion.state_topic, sensorState)
|
||||
this.mqttPublish(this.entity.motion.state_topic, sensorState)
|
||||
this.publishAttributes()
|
||||
}
|
||||
}
|
||||
|
@@ -15,8 +15,8 @@ class MultiLevelSwitch extends RingSocketDevice {
|
||||
publishData() {
|
||||
const switchState = this.device.data.on ? "ON" : "OFF"
|
||||
const switchLevel = (this.device.data.level && !isNaN(this.device.data.level) ? Math.round(100 * this.device.data.level) : 0)
|
||||
this.publishMqtt(this.entity.light.state_topic, switchState)
|
||||
this.publishMqtt(this.entity.light.brightness_state_topic, switchLevel.toString())
|
||||
this.mqttPublish(this.entity.light.state_topic, switchState)
|
||||
this.mqttPublish(this.entity.light.brightness_state_topic, switchLevel.toString())
|
||||
this.publishAttributes()
|
||||
}
|
||||
|
||||
|
@@ -25,7 +25,7 @@ class SecurityPanel extends RingSocketDevice {
|
||||
state: false,
|
||||
icon: 'mdi:transit-skip'
|
||||
},
|
||||
...this.config.enable_panic ? {
|
||||
...utils.config.enable_panic ? {
|
||||
police: {
|
||||
component: 'switch',
|
||||
name: `${this.device.location.name} Panic - Police`,
|
||||
@@ -68,15 +68,15 @@ class SecurityPanel extends RingSocketDevice {
|
||||
alarmMode = 'unknown'
|
||||
}
|
||||
}
|
||||
this.publishMqtt(this.entity.alarm.state_topic, alarmMode)
|
||||
this.mqttPublish(this.entity.alarm.state_topic, alarmMode)
|
||||
|
||||
const sirenState = (this.device.data.siren && this.device.data.siren.state === 'on') ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity.siren.state_topic, sirenState)
|
||||
this.mqttPublish(this.entity.siren.state_topic, sirenState)
|
||||
|
||||
const bypassState = this.entity.bypass.state ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity.bypass.state_topic, bypassState)
|
||||
this.mqttPublish(this.entity.bypass.state_topic, bypassState)
|
||||
|
||||
if (this.config.enable_panic) {
|
||||
if (utils.config.enable_panic) {
|
||||
let policeState = 'OFF'
|
||||
let fireState = 'OFF'
|
||||
const alarmState = this.device.data.alarmInfo ? this.device.data.alarmInfo.state : ''
|
||||
@@ -93,8 +93,8 @@ class SecurityPanel extends RingSocketDevice {
|
||||
fireState = 'ON'
|
||||
this.debug('Fire alarm is active for '+this.device.location.name)
|
||||
}
|
||||
this.publishMqtt(this.entity.police.state_topic, policeState)
|
||||
this.publishMqtt(this.entity.fire.state_topic, fireState)
|
||||
this.mqttPublish(this.entity.police.state_topic, policeState)
|
||||
this.mqttPublish(this.entity.fire.state_topic, fireState)
|
||||
}
|
||||
|
||||
this.publishAttributes()
|
||||
@@ -106,7 +106,7 @@ class SecurityPanel extends RingSocketDevice {
|
||||
exitDelayMs = this.device.data.transitionDelayEndTimestamp - Date.now()
|
||||
if (exitDelayMs <= 0) {
|
||||
// Publish device sensor state
|
||||
this.publishMqtt(this.entity.alarm.state_topic, 'armed_away')
|
||||
this.mqttPublish(this.entity.alarm.state_topic, 'armed_away')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -27,14 +27,14 @@ class Siren extends RingSocketDevice {
|
||||
const isPublish = data === undefined ? true : false
|
||||
if (isPublish) {
|
||||
// Eventually remove this but for now this attempts to delete the old siren binary_sensor
|
||||
this.publishMqtt('homeassistant/binary_sensor/'+this.locationId+'/'+this.deviceId+'_siren/config', '', false)
|
||||
this.mqttPublish('homeassistant/binary_sensor/'+this.locationId+'/'+this.deviceId+'_siren/config', '', false)
|
||||
}
|
||||
|
||||
const sirenState = this.device.data.sirenStatus === 'active' ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity.siren.state_topic, sirenState)
|
||||
this.mqttPublish(this.entity.siren.state_topic, sirenState)
|
||||
if (this.entity.hasOwnProperty('volume')) {
|
||||
const currentVolume = (this.device.data.volume && !isNaN(this.device.data.volume) ? Math.round(1 * this.device.data.volume) : 0)
|
||||
this.publishMqtt(this.entity.volume.state_topic, currentVolume)
|
||||
this.mqttPublish(this.entity.volume.state_topic, currentVolume)
|
||||
}
|
||||
this.publishAttributes()
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ class SmokeAlarm extends RingSocketDevice {
|
||||
|
||||
publishData() {
|
||||
const smokeState = this.device.data.alarmStatus === 'active' ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity.smoke.state_topic, smokeState)
|
||||
this.mqttPublish(this.entity.smoke.state_topic, smokeState)
|
||||
this.publishAttributes()
|
||||
}
|
||||
}
|
||||
|
@@ -20,8 +20,8 @@ class SmokeCoListener extends RingSocketDevice {
|
||||
publishData() {
|
||||
const smokeState = this.device.data.smoke && this.device.data.smoke.alarmStatus === 'active' ? 'ON' : 'OFF'
|
||||
const coState = this.device.data.co && this.device.data.co.alarmStatus === 'active' ? 'ON' : 'OFF'
|
||||
this.publishMqtt(this.entity.smoke.state_topic, smokeState)
|
||||
this.publishMqtt(this.entity.co.state_topic, coState)
|
||||
this.mqttPublish(this.entity.smoke.state_topic, smokeState)
|
||||
this.mqttPublish(this.entity.co.state_topic, coState)
|
||||
this.publishAttributes()
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ class Switch extends RingSocketDevice {
|
||||
}
|
||||
|
||||
publishData() {
|
||||
this.publishMqtt(this.entity[this.component].state_topic, this.device.data.on ? "ON" : "OFF")
|
||||
this.mqttPublish(this.entity[this.component].state_topic, this.device.data.on ? "ON" : "OFF")
|
||||
this.publishAttributes()
|
||||
}
|
||||
|
||||
|
@@ -15,7 +15,7 @@ class TemperatureSensor extends RingSocketDevice {
|
||||
|
||||
publishData() {
|
||||
const temperature = this.device.data.celsius.toString()
|
||||
this.publishMqtt(this.entity.temperature.state_topic, temperature)
|
||||
this.mqttPublish(this.entity.temperature.state_topic, temperature)
|
||||
this.publishAttributes()
|
||||
}
|
||||
}
|
||||
|
@@ -69,11 +69,11 @@ class Thermostat extends RingSocketDevice {
|
||||
|
||||
const mode = this.data.currentMode()
|
||||
await this.clearStateOnAutoModeSwitch(mode)
|
||||
this.publishMqtt(this.entity.thermostat.mode_state_topic, mode)
|
||||
this.mqttPublish(this.entity.thermostat.mode_state_topic, mode)
|
||||
this.data.publishedMode = mode
|
||||
this.publishSetpoints(mode)
|
||||
this.publishMqtt(this.entity.thermostat.fan_mode_state_topic, this.data.fanMode())
|
||||
this.publishMqtt(this.entity.thermostat.aux_state_topic, this.data.auxMode())
|
||||
this.mqttPublish(this.entity.thermostat.fan_mode_state_topic, this.data.fanMode())
|
||||
this.mqttPublish(this.entity.thermostat.aux_state_topic, this.data.auxMode())
|
||||
this.publishOperatingMode()
|
||||
|
||||
if (isPublish) { this.publishTemperature() }
|
||||
@@ -116,20 +116,20 @@ class Thermostat extends RingSocketDevice {
|
||||
// as soon as the update is completed
|
||||
this.data.autoSetPoint.low = this.device.data.setPoint-this.device.data.deadBand
|
||||
this.data.autoSetPoint.high = this.device.data.setPoint+this.device.data.deadBand
|
||||
this.publishMqtt(this.entity.thermostat.temperature_low_state_topic, this.data.autoSetPoint.low)
|
||||
this.publishMqtt(this.entity.thermostat.temperature_high_state_topic, this.data.autoSetPoint.high)
|
||||
this.mqttPublish(this.entity.thermostat.temperature_low_state_topic, this.data.autoSetPoint.low)
|
||||
this.mqttPublish(this.entity.thermostat.temperature_high_state_topic, this.data.autoSetPoint.high)
|
||||
}
|
||||
} else {
|
||||
this.publishMqtt(this.entity.thermostat.temperature_state_topic, this.data.setPoint())
|
||||
this.mqttPublish(this.entity.thermostat.temperature_state_topic, this.data.setPoint())
|
||||
}
|
||||
}
|
||||
|
||||
publishOperatingMode() {
|
||||
this.publishMqtt(this.entity.thermostat.action_topic, this.data.operatingMode())
|
||||
this.mqttPublish(this.entity.thermostat.action_topic, this.data.operatingMode())
|
||||
}
|
||||
|
||||
publishTemperature() {
|
||||
this.publishMqtt(this.entity.thermostat.current_temperature_topic, this.data.temperature())
|
||||
this.mqttPublish(this.entity.thermostat.current_temperature_topic, this.data.temperature())
|
||||
}
|
||||
|
||||
// Process messages from MQTT command topic
|
||||
@@ -163,14 +163,14 @@ class Thermostat extends RingSocketDevice {
|
||||
const mode = value.toLowerCase()
|
||||
switch(mode) {
|
||||
case 'off':
|
||||
this.publishMqtt(this.entity.thermostat.action_topic, mode)
|
||||
this.mqttPublish(this.entity.thermostat.action_topic, mode)
|
||||
case 'cool':
|
||||
case 'heat':
|
||||
case 'auto':
|
||||
case 'aux':
|
||||
if (this.entity.thermostat.modes.map(e => e.toLocaleLowerCase()).includes(mode) || mode === 'aux') {
|
||||
this.device.setInfo({ device: { v1: { mode } } })
|
||||
this.publishMqtt(this.entity.thermostat.mode_state_topic, mode)
|
||||
this.mqttPublish(this.entity.thermostat.mode_state_topic, mode)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -195,7 +195,7 @@ class Thermostat extends RingSocketDevice {
|
||||
} else {
|
||||
this.debug(`Received set target temperature to ${value}`)
|
||||
this.device.setInfo({ device: { v1: { setPoint: Number(value) } } })
|
||||
this.publishMqtt(this.entity.thermostat.temperature_state_topic, value)
|
||||
this.mqttPublish(this.entity.thermostat.temperature_state_topic, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,8 +235,8 @@ class Thermostat extends RingSocketDevice {
|
||||
}
|
||||
|
||||
this.device.setInfo({ device: { v1: { setPoint, deadBand } } })
|
||||
this.publishMqtt(this.entity.thermostat.temperature_low_state_topic, this.data.autoSetPoint.low)
|
||||
this.publishMqtt(this.entity.thermostat.temperature_high_state_topic, this.data.autoSetPoint.high)
|
||||
this.mqttPublish(this.entity.thermostat.temperature_low_state_topic, this.data.autoSetPoint.low)
|
||||
this.mqttPublish(this.entity.thermostat.temperature_high_state_topic, this.data.autoSetPoint.high)
|
||||
this.data.setPointInProgress = false
|
||||
}
|
||||
}
|
||||
@@ -254,7 +254,7 @@ class Thermostat extends RingSocketDevice {
|
||||
const fanMode = value.toLowerCase()
|
||||
if (this.entity.thermostat.fan_modes.map(e => e.toLocaleLowerCase()).includes(fanMode)) {
|
||||
this.device.setInfo({ device: { v1: { fanMode }}})
|
||||
this.publishMqtt(this.entity.thermostat.fan_mode_state_topic, fanMode.replace(/^./, str => str.toUpperCase()))
|
||||
this.mqttPublish(this.entity.thermostat.fan_mode_state_topic, fanMode.replace(/^./, str => str.toUpperCase()))
|
||||
} else {
|
||||
this.debug('Received invalid fan mode command')
|
||||
}
|
||||
@@ -268,7 +268,7 @@ class Thermostat extends RingSocketDevice {
|
||||
case 'off':
|
||||
const mode = auxMode === 'on' ? 'aux' : 'heat'
|
||||
this.device.setInfo({ device: { v1: { mode } } })
|
||||
this.publishMqtt(this.entity.thermostat.aux_state_topic, auxMode.toUpperCase())
|
||||
this.mqttPublish(this.entity.thermostat.aux_state_topic, auxMode.toUpperCase())
|
||||
break;
|
||||
default:
|
||||
this.debug('Received invalid aux mode command')
|
||||
|
@@ -8,12 +8,10 @@ class Config {
|
||||
switch (process.env.RUNMODE) {
|
||||
case 'docker':
|
||||
this.file = '/data/config.json'
|
||||
this.runMode = 'docker'
|
||||
this.loadConfigEnv()
|
||||
break;
|
||||
case 'addon':
|
||||
this.file = '/data/options.json'
|
||||
this.runMode = 'addon'
|
||||
this.loadConfigFile()
|
||||
// For addon always set MQTT values from environment (set from HA API via bashio)
|
||||
this.data.host = process.env.MQTTHOST
|
||||
@@ -27,8 +25,8 @@ class Config {
|
||||
} else {
|
||||
this.file = require('path').dirname(require.main.filename)+'/config.json'
|
||||
}
|
||||
this.runMode = 'standard'
|
||||
this.loadConfigFile()
|
||||
process.env.RUNMODE = 'standard'
|
||||
}
|
||||
|
||||
// If there's still no configured settings, force some defaults.
|
||||
@@ -91,7 +89,7 @@ class Config {
|
||||
}
|
||||
|
||||
async updateConfig() {
|
||||
if (this.runMode === 'standard' && this.data.hasOwnProperty('ring_token')) {
|
||||
if (process.env.RUNMODE === 'standard' && this.data.hasOwnProperty('ring_token')) {
|
||||
try {
|
||||
debug ('Updating config file to remove legacy ring_token value...')
|
||||
delete this.data.ring_token
|
||||
|
90
lib/mqtt.js
Normal file
90
lib/mqtt.js
Normal file
@@ -0,0 +1,90 @@
|
||||
const mqttApi = require('mqtt')
|
||||
const debug = require('debug')('ring-mqtt')
|
||||
const colors = require('colors/safe')
|
||||
const utils = require('./utils')
|
||||
|
||||
class Mqtt {
|
||||
constructor() {
|
||||
this.client = false
|
||||
this.connected = false
|
||||
}
|
||||
|
||||
async startEventListeners() {
|
||||
utils.event.on('ringState', async (state) => {
|
||||
if (!this.client && state === 'connected') {
|
||||
// Ring API connected, short wait before starting MQTT client
|
||||
await utils.sleep(2)
|
||||
this.init()
|
||||
}
|
||||
})
|
||||
|
||||
utils.event.on('mqttPublish', (topic, message) => {
|
||||
this.client.publish(topic, (typeof message === 'number') ? message.toString() : message, { qos: 1 })
|
||||
})
|
||||
|
||||
utils.event.on('mqttSubscribe', (topic) => {
|
||||
this.client.subscribe(topic)
|
||||
})
|
||||
}
|
||||
|
||||
async init() {
|
||||
try {
|
||||
debug('Attempting connection to MQTT broker...')
|
||||
this.client = await this.connect()
|
||||
this.startMqttListeners()
|
||||
|
||||
// Subscribe to configured/default/legacay Home Assistant status topics
|
||||
this.client.subscribe(utils.config.hass_topic)
|
||||
this.client.subscribe('hass/status')
|
||||
this.client.subscribe('hassio/status')
|
||||
} catch (error) {
|
||||
debug(error)
|
||||
debug(colors.red(`Could not authenticate to MQTT broker. Please check the broker and configuration settings.`))
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Initiate the connection to MQTT broker
|
||||
async connect() {
|
||||
const mqttClient = await mqttApi.connect({
|
||||
username: utils.config.mqtt_user ? utils.config.mqtt_user : null,
|
||||
password: utils.config.mqtt_pass ? utils.config.mqtt_pass : null,
|
||||
host: utils.config.host,
|
||||
port: utils.config.port
|
||||
});
|
||||
return mqttClient
|
||||
}
|
||||
|
||||
startMqttListeners() {
|
||||
// On MQTT connect/reconnect send config/state information after delay
|
||||
this.client.on('connect', () => {
|
||||
if (!this.connected) {
|
||||
this.connected = true
|
||||
utils.event.emit('mqttState', 'connected')
|
||||
}
|
||||
})
|
||||
|
||||
this.client.on('reconnect', () => {
|
||||
if (this.connected) {
|
||||
debug('Connection to MQTT broker lost. Attempting to reconnect...')
|
||||
} else {
|
||||
debug('Attempting to reconnect to MQTT broker...')
|
||||
}
|
||||
this.connected = false
|
||||
utils.event.emit('mqttState', 'disconnected')
|
||||
})
|
||||
|
||||
this.client.on('error', (error) => {
|
||||
debug('Unable to connect to MQTT broker', error.message)
|
||||
this.connected = false
|
||||
utils.event.emit('mqttState', 'disconnected')
|
||||
})
|
||||
|
||||
// Process MQTT messages from subscribed command topics
|
||||
this.client.on('message', (topic, message) => {
|
||||
utils.event.emit('mqttMessage', topic, message)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Mqtt()
|
133
lib/ring.js
133
lib/ring.js
@@ -1,9 +1,9 @@
|
||||
const { RingApi, RingDeviceType, RingCamera, RingChime } = require('@tsightler/ring-client-api')
|
||||
const mqttApi = require('mqtt')
|
||||
const mqtt = require('./mqtt')
|
||||
const debug = require('debug')('ring-mqtt')
|
||||
const colors = require('colors/safe')
|
||||
const utils = require('./utils.js')
|
||||
const rss = require('./rtsp-simple-server.js')
|
||||
const utils = require('./utils')
|
||||
const rss = require('./rtsp-simple-server')
|
||||
const BaseStation = require('../devices/base-station')
|
||||
const Beam = require('../devices/beam')
|
||||
const BeamOutdoorPlug = require('../devices/beam-outdoor-plug')
|
||||
@@ -34,18 +34,47 @@ class RingMqtt {
|
||||
this.devices = new Array()
|
||||
this.mqttConnected = false
|
||||
this.republishCount = 6 // Republish config/state this many times after startup or HA start/restart
|
||||
this.startEventListeners()
|
||||
mqtt.startEventListeners()
|
||||
}
|
||||
|
||||
async init(ringAuth, config, tokenSource) {
|
||||
this.config = config
|
||||
async startEventListeners() {
|
||||
utils.event.on('mqttState', (state) => {
|
||||
if (state === 'connected') {
|
||||
this.mqttConnected = true
|
||||
debug('MQTT connection established, processing Ring locations...')
|
||||
this.processLocations()
|
||||
} else {
|
||||
this.mqttConnected = false
|
||||
}
|
||||
})
|
||||
|
||||
utils.event.on('mqttMessage', (topic, message) => {
|
||||
this.processMqttCommand(topic, message)
|
||||
})
|
||||
}
|
||||
|
||||
async init(state, generatedToken) {
|
||||
|
||||
const ringAuth = {
|
||||
refreshToken: generatedToken ? generatedToken : state.ring_token,
|
||||
systemId: state.systemId,
|
||||
controlCenterDisplayName: (process.env.RUNMODE === 'addon') ? 'ring-mqtt-addon' : 'ring-mqtt',
|
||||
...utils.config.enable_cameras ? { cameraStatusPollingSeconds: 20, cameraDingsPollingSeconds: 2 } : {},
|
||||
...utils.config.enable_modes ? { locationModePollingSeconds: 20 } : {},
|
||||
...!(utils.config.location_ids === undefined || utils.config.location_ids == 0) ? { locationIds: utils.config.location_ids } : {}
|
||||
}
|
||||
|
||||
try {
|
||||
debug(`Attempting connection to Ring API using ${tokenSource} refresh token.`)
|
||||
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')
|
||||
debug(`Successfully established connection to Ring API using ${generatedToken ? 'generated' : 'saved'} token`)
|
||||
} catch(error) {
|
||||
this.client = false
|
||||
debug(colors.brightYellow(error.message))
|
||||
debug(colors.brightYellow(`Failed to establish connection to Ring API using ${tokenSource} refresh token.`))
|
||||
debug(colors.brightYellow(`Failed to establish connection to Ring API using ${generatedToken ? 'generated' : 'saved'} refresh token`))
|
||||
}
|
||||
return this.client
|
||||
}
|
||||
@@ -133,14 +162,14 @@ class RingMqtt {
|
||||
|
||||
// Get all location devices and, if camera support is enabled, cameras and chimes
|
||||
const devices = await location.getDevices()
|
||||
if (this.config.enable_cameras) {
|
||||
if (utils.config.enable_cameras) {
|
||||
cameras = await location.cameras
|
||||
chimes = await location.chimes
|
||||
}
|
||||
const allDevices = [...devices, ...cameras, ...chimes]
|
||||
|
||||
// Add modes panel, if configured and the location supports it
|
||||
if (this.config.enable_modes && (await location.supportsLocationModeSwitching())) {
|
||||
if (utils.config.enable_modes && (await location.supportsLocationModeSwitching())) {
|
||||
allDevices.push({
|
||||
deviceType: 'location.mode',
|
||||
location: location,
|
||||
@@ -211,8 +240,6 @@ class RingMqtt {
|
||||
async getDevice(device, allDevices) {
|
||||
const deviceInfo = {
|
||||
device: device,
|
||||
mqttClient: this.mqttClient,
|
||||
config: this.config,
|
||||
...allDevices.filter(d => d.data.parentZid === device.id).length
|
||||
? { childDevices: allDevices.filter(d => d.data.parentZid === device.id) } : {},
|
||||
...(device.data && device.data.hasOwnProperty('parentZid'))
|
||||
@@ -326,75 +353,23 @@ class RingMqtt {
|
||||
}
|
||||
}
|
||||
|
||||
async initMqtt() {
|
||||
// Initiate connection to MQTT broker
|
||||
try {
|
||||
debug('Attempting connection to MQTT broker...')
|
||||
async processMqttCommand(topic, message) {
|
||||
message = message.toString()
|
||||
if (topic === utils.config.hass_topic || topic === 'hass/status' || topic === 'hassio/status') {
|
||||
this.republishDevices()
|
||||
} else {
|
||||
// Parse topic to get location/device ID
|
||||
const deviceTopicLevels = (topic.substring(utils.config.ring_topic.length)).replace(/^\/+/, '').split('/')
|
||||
const locationId = deviceTopicLevels[0]
|
||||
const deviceId = deviceTopicLevels[2]
|
||||
|
||||
this.mqttClient = await mqttApi.connect({
|
||||
username: this.config.mqtt_user ? this.config.mqtt_user : null,
|
||||
password: this.config.mqtt_pass ? this.config.mqtt_pass : null,
|
||||
host: this.config.host,
|
||||
port: this.config.port
|
||||
});
|
||||
|
||||
// Monitor configured/default Home Assistant status topic
|
||||
this.mqttClient.subscribe(this.config.hass_topic)
|
||||
// Monitor legacy Home Assistant status topics
|
||||
this.mqttClient.subscribe('hass/status')
|
||||
this.mqttClient.subscribe('hassio/status')
|
||||
|
||||
this.mqttClient.on('connect', async () => {
|
||||
if (!this.mqttConnected) {
|
||||
this.mqttConnected = true
|
||||
debug('MQTT connection established, processing Ring location...')
|
||||
}
|
||||
this.processLocations()
|
||||
})
|
||||
|
||||
this.mqttClient.on('reconnect', () => {
|
||||
debug(this.mqttConnected
|
||||
? 'Connection to MQTT broker lost. Attempting to reconnect...'
|
||||
: 'Attempting to reconnect to MQTT broker...'
|
||||
)
|
||||
this.mqttConnected = false
|
||||
})
|
||||
|
||||
this.mqttClient.on('error', (error) => {
|
||||
debug('Unable to connect to MQTT broker.', error.message)
|
||||
this.mqttConnected = false
|
||||
})
|
||||
|
||||
// Process MQTT messages from subscribed command topics
|
||||
this.mqttClient.on('message', async (topic, message) => {
|
||||
message = message.toString()
|
||||
if (topic === this.config.hass_topic || topic === 'hass/status' || topic === 'hassio/status') {
|
||||
debug(`Home Assistant state topic ${topic} received message: ${message}`)
|
||||
if (message === 'online') {
|
||||
this.republishDevices()
|
||||
}
|
||||
} else {
|
||||
// Parse topic to get location/device ID
|
||||
const ringTopicLevels = (this.config.ring_topic).split('/').length
|
||||
const splitTopic = topic.split('/')
|
||||
const locationId = splitTopic[ringTopicLevels]
|
||||
const deviceId = splitTopic[ringTopicLevels + 2]
|
||||
|
||||
// Find existing device by matching location & device ID
|
||||
const cmdDevice = this.devices.find(d => (d.deviceId == deviceId && d.locationId == locationId))
|
||||
|
||||
if (cmdDevice) {
|
||||
const componentCommand = topic.split("/").slice(-2).join("/")
|
||||
cmdDevice.processCommand(message, componentCommand)
|
||||
} else {
|
||||
debug(`Received MQTT message for device Id ${deviceId} at location Id ${locationId} but could not find matching device`)
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
debug(error)
|
||||
debug(colors.red(`Could not authenticate to MQTT broker. Please check the broker and configuration settings.`))
|
||||
process.exit(1)
|
||||
// Find existing device by matching location & device ID
|
||||
const cmdDevice = this.devices.find(d => (d.deviceId == deviceId && d.locationId == locationId))
|
||||
if (cmdDevice) {
|
||||
cmdDevice.processCommand(message, topic.split("/").slice(-2).join("/"))
|
||||
} else {
|
||||
debug('Received MQTT message for device Id '+deviceId+' at location Id '+locationId+' but could not find matching device')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
const debug = require('debug')('ring-rtsp')
|
||||
const colors = require('colors/safe')
|
||||
const utils = require('./utils.js')
|
||||
const utils = require('./utils')
|
||||
const { spawn } = require('child_process')
|
||||
const readline = require('readline')
|
||||
const got = require('got')
|
||||
@@ -72,11 +72,11 @@ class RtspSimpleServer {
|
||||
runOnDemandRestart: false,
|
||||
runOnDemandStartTimeout: '10s',
|
||||
runOnDemandCloseAfter: '5s',
|
||||
...(camera.config.livestream_user && camera.config.livestream_pass) ? {
|
||||
publishUser: camera.config.livestream_user,
|
||||
publishPass: camera.config.livestream_pass,
|
||||
readUser: camera.config.livestream_user,
|
||||
readPass: camera.config.livestream_pass
|
||||
...(utils.config.livestream_user && utils.config.livestream_pass) ? {
|
||||
publishUser: utils.config.livestream_user,
|
||||
publishPass: utils.config.livestream_pass,
|
||||
readUser: utils.config.livestream_user,
|
||||
readPass: utils.config.livestream_pass
|
||||
} : {}
|
||||
}
|
||||
|
||||
@@ -93,11 +93,11 @@ class RtspSimpleServer {
|
||||
runOnDemandRestart: false,
|
||||
runOnDemandStartTimeout: '10s',
|
||||
runOnDemandCloseAfter: '5s',
|
||||
...(camera.config.livestream_user && camera.config.livestream_pass) ? {
|
||||
publishUser: camera.config.livestream_user,
|
||||
publishPass: camera.config.livestream_pass,
|
||||
readUser: camera.config.livestream_user,
|
||||
readPass: camera.config.livestream_pass
|
||||
...(utils.config.livestream_user && utils.config.livestream_pass) ? {
|
||||
publishUser: utils.config.livestream_user,
|
||||
publishPass: utils.config.livestream_pass,
|
||||
readUser: utils.config.livestream_user,
|
||||
readPass: utils.config.livestream_pass
|
||||
} : {}
|
||||
}
|
||||
|
||||
|
@@ -16,7 +16,7 @@ class State {
|
||||
|
||||
async init(config) {
|
||||
this.config = config
|
||||
this.file = (this.config.runMode === 'standard')
|
||||
this.file = (process.env.RUNMODE === 'standard')
|
||||
? require('path').dirname(require.main.filename)+'/ring-state.json'
|
||||
: this.file = '/data/ring-state.json'
|
||||
await this.loadStateData()
|
||||
@@ -43,7 +43,7 @@ class State {
|
||||
|
||||
async initStateData() {
|
||||
this.data.systemId = (createHash('sha256').update(randomBytes(32)).digest('hex'))
|
||||
if (this.config.runMode === 'standard' && this.config.data.hasOwnProperty('ring_token') && this.config.data.ring_token) {
|
||||
if (process.env.RUNMODE === 'standard' && this.config.data.hasOwnProperty('ring_token') && this.config.data.ring_token) {
|
||||
debug(colors.brightYellow('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()
|
||||
|
@@ -1,5 +1,6 @@
|
||||
const { RingRestClient } = require('../node_modules/@tsightler/ring-client-api/lib/api/rest-client')
|
||||
const debug = require('debug')('ring-mqtt')
|
||||
const utils = require('./utils')
|
||||
const express = require('express')
|
||||
const bodyParser = require("body-parser")
|
||||
|
||||
@@ -7,27 +8,23 @@ class TokenApp {
|
||||
constructor() {
|
||||
this.app = express()
|
||||
this.listener = false
|
||||
this.ringConnected = false
|
||||
|
||||
// Helper property to pass values between main code and web server
|
||||
this.token = {
|
||||
connected: '',
|
||||
generatedInternal: '',
|
||||
generatedListener: function(val) {},
|
||||
set generated(val) {
|
||||
this.generatedInternal = val;
|
||||
this.generatedListener(val);
|
||||
},
|
||||
get generated() {
|
||||
return this.generatedInternal;
|
||||
},
|
||||
registerListener: function(listener) {
|
||||
this.generatedListener = listener;
|
||||
}
|
||||
if (process.env.RUNMODE === 'addon') {
|
||||
tokenApp.start()
|
||||
}
|
||||
|
||||
utils.event.on('ringState', async (state) => {
|
||||
if (state === 'connected') {
|
||||
this.ringConnected = true
|
||||
} else {
|
||||
this.ringConnected = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Super simple web service to acquire refresh tokens
|
||||
async start(runMode) {
|
||||
async start() {
|
||||
if (this.listener) {
|
||||
return
|
||||
}
|
||||
@@ -36,15 +33,17 @@ class TokenApp {
|
||||
let restClient
|
||||
|
||||
this.listener = this.app.listen(55123, () => {
|
||||
if (runMode !== 'addon') {
|
||||
debug('Use http://<host_ip_address>:55123/ to generate a valid token.')
|
||||
if (process.env.RUNMODE === 'addon') {
|
||||
debug('Open the Web UI from the addon Info tab to generate an authentication token.')
|
||||
} else {
|
||||
debug('Use the Web UI at http://<host_ip_address>:55123/ to generate an authentication token.')
|
||||
}
|
||||
})
|
||||
|
||||
this.app.use(bodyParser.urlencoded({ extended: false }))
|
||||
|
||||
this.app.get('/', (req, res) => {
|
||||
if (!this.token.connected) {
|
||||
if (!this.connected) {
|
||||
res.sendFile('account.html', {root: webdir})
|
||||
} else {
|
||||
res.sendFile('connected.html', {root: webdir})
|
||||
@@ -78,8 +77,8 @@ class TokenApp {
|
||||
const code = req.body.code
|
||||
try {
|
||||
generatedToken = await restClient.getAuth(code)
|
||||
} catch(_) {
|
||||
generatedToken = ''
|
||||
} catch(err) {
|
||||
generatedToken = false
|
||||
const errormsg = 'The 2FA code was not accepted, please verify the code and try again.'
|
||||
debug(errormsg)
|
||||
res.cookie('error', errormsg, { maxAge: 1000, encode: String })
|
||||
@@ -87,7 +86,7 @@ class TokenApp {
|
||||
}
|
||||
if (generatedToken) {
|
||||
res.sendFile('restart.html', {root: webdir})
|
||||
this.token.generated = generatedToken.refresh_token
|
||||
utils.event.emit('generatedToken', generatedToken.refresh_token)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
27
lib/utils.js
27
lib/utils.js
@@ -3,13 +3,17 @@ const debug = {
|
||||
attr: require('debug')('ring-attr'),
|
||||
disc: require('debug')('ring-disc')
|
||||
}
|
||||
const colors = require('colors/safe')
|
||||
const config = require('./config')
|
||||
const dns = require('dns')
|
||||
const os = require('os')
|
||||
const { promisify } = require('util')
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
|
||||
class Utils {
|
||||
// Define a few helper variables for sharing
|
||||
event = new EventEmitter()
|
||||
config = config.data
|
||||
|
||||
class Utils
|
||||
{
|
||||
// Sleep function (seconds)
|
||||
sleep(sec) {
|
||||
return this.msleep(sec*1000)
|
||||
@@ -45,20 +49,9 @@ class Utils
|
||||
}
|
||||
}
|
||||
|
||||
log(message, level, category) {
|
||||
category = category ? category : 'mqtt'
|
||||
switch (level) {
|
||||
case 'info':
|
||||
debug[category](colors.green(`[${this.deviceData.name}] `)+message)
|
||||
break;
|
||||
case 'warn':
|
||||
debug[category](colors.brightYellow(`[${this.deviceData.name}] `)+message)
|
||||
break;
|
||||
case 'error':
|
||||
debug[category](colors.brightRed(`[${this.deviceData.name}] `)+message)
|
||||
default:
|
||||
debug[category](message)
|
||||
}
|
||||
debug(message, debugType) {
|
||||
debugType = debugType ? debugType : 'mqtt'
|
||||
debug[debugType](message)
|
||||
}
|
||||
}
|
||||
|
||||
|
37
ring-mqtt.js
37
ring-mqtt.js
@@ -1,13 +1,12 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const config = require('./lib/config')
|
||||
const state = require('./lib/state')
|
||||
const ring = require('./lib/ring')
|
||||
const isOnline = require('is-online')
|
||||
const debug = require('debug')('ring-mqtt')
|
||||
const colors = require('colors/safe')
|
||||
const utils = require('./lib/utils.js')
|
||||
const tokenApp = require('./lib/tokenapp.js')
|
||||
const utils = require('./lib/utils')
|
||||
const tokenApp = require('./lib/tokenapp')
|
||||
|
||||
// Setup Exit Handlers
|
||||
process.on('exit', processExit.bind(null, 0))
|
||||
@@ -69,52 +68,32 @@ const main = async(generatedToken) => {
|
||||
await utils.sleep(10)
|
||||
}
|
||||
|
||||
const ringAuth = {
|
||||
refreshToken: generatedToken ? generatedToken : state.data.ring_token,
|
||||
systemId: state.data.systemId,
|
||||
controlCenterDisplayName: (config.runMode === 'addon') ? 'ring-mqtt-addon' : 'ring-mqtt',
|
||||
...config.data.enable_cameras ? { cameraStatusPollingSeconds: 20, cameraDingsPollingSeconds: 2 } : {},
|
||||
...config.data.enable_modes ? { locationModePollingSeconds: 20 } : {},
|
||||
...!(config.data.location_ids === undefined || config.data.location_ids == 0) ? { locationIds: config.data.location_ids } : {}
|
||||
}
|
||||
|
||||
const tokenSource = generatedToken ? 'generated' : 'saved'
|
||||
if (await ring.init(ringAuth, config.data, tokenSource)) {
|
||||
debug(`Successfully established connection to Ring API using ${tokenSource} token`)
|
||||
|
||||
if (await ring.init(state.data, generatedToken)) {
|
||||
// Subscribe to token update events and save new tokens to state file
|
||||
ring.client.onRefreshTokenUpdated.subscribe(({ newRefreshToken, oldRefreshToken }) => {
|
||||
state.updateToken(newRefreshToken, oldRefreshToken)
|
||||
})
|
||||
|
||||
// Only leave the web UI active if this is the addon
|
||||
if (config.runMode !== 'addon') {
|
||||
if (process.env.RUNMODE === 'addon') {
|
||||
tokenApp.stop()
|
||||
}
|
||||
|
||||
// Connection to Ring API is successful, pause for a few seconds and then initialize MQTT connection
|
||||
await utils.sleep(2)
|
||||
ring.initMqtt()
|
||||
} else {
|
||||
debug(colors.brightRed('Failed to connect to Ring API using saved token, generate a new token using the Web UI.'))
|
||||
debug(colors.brightRed('Authentication will be automatically retried in 60 seconds using the existing token.'))
|
||||
tokenApp.start(config.runMode)
|
||||
tokenApp.start()
|
||||
await utils.sleep(60)
|
||||
main()
|
||||
}
|
||||
} else {
|
||||
// If a refresh token was not found, start Web UI for token generator
|
||||
debug(colors.brightRed('No refresh token was found in state file, generate a token using the Web UI.'))
|
||||
tokenApp.start(config.runMode)
|
||||
tokenApp.start()
|
||||
}
|
||||
}
|
||||
|
||||
if (config.runMode === 'addon') {
|
||||
tokenApp.start(config.runMode)
|
||||
}
|
||||
|
||||
// Subscribe to token updates from token Web UI
|
||||
tokenApp.token.registerListener(function(generatedToken) {
|
||||
// Listen for token updates from token Web UI
|
||||
utils.event.on('generatedToken', (generatedToken) => {
|
||||
main(generatedToken)
|
||||
})
|
||||
|
||||
|
Reference in New Issue
Block a user