Minor syntax cleanups

This commit is contained in:
tsightler
2023-06-30 14:07:58 -04:00
parent 5b529ff3ab
commit f13af473a6
38 changed files with 156 additions and 148 deletions

View File

@@ -1,5 +1,5 @@
{
"mqtt_url": "mqtt://localhost:1883",
"mqtt_url": "mqtt://192.168.1.57:1883",
"mqtt_options": "",
"livestream_user": "",
"livestream_pass": "",

View File

@@ -7,8 +7,8 @@ export default class RingPolledDevice extends RingDevice {
super(deviceInfo, category, primaryAttribute, 'polled')
this.heartbeat = 3
// Sevice data for Home Assistant device registry
this.deviceData = {
// Sevice data for Home Assistant device registry
this.deviceData = {
ids: [ this.deviceId ],
name: this.device.name,
mf: 'Ring',
@@ -38,7 +38,7 @@ export default class RingPolledDevice extends RingDevice {
// the heartbeat is constantly reset in the data publish function due to data
// polling events however, if something interrupts the connection, polling stops
// and this function will decrement until the heartbeat reaches zero. In this case
// this function sets the device status offline. When polling resumes the heartbeat
// this function sets the device status offline. When polling resumes the heartbeat
// is set > 0 and this function will set the device back online after a short delay.
async monitorHeartbeat() {
if (this.heartbeat > 0) {
@@ -53,10 +53,10 @@ export default class RingPolledDevice extends RingDevice {
}
this.heartbeat--
} else {
if (this.availabilityState !== 'offline') {
if (this.availabilityState !== 'offline') {
this.offline()
}
}
}
await utils.sleep(20)
this.monitorHeartbeat()
}

View File

@@ -10,8 +10,8 @@ export default class RingDevice {
this.locationId = apiType === 'socket' ? deviceInfo.device.location.locationId : deviceInfo.device.data.location_id
this.availabilityState = 'unpublished'
this.entity = {}
this.isOnline = () => {
return this.availabilityState === 'online' ? true : false
this.isOnline = () => {
return this.availabilityState === 'online' ? true : false
}
this.debug = (message, debugType) => {
@@ -54,7 +54,7 @@ export default class RingDevice {
: entity.component === 'camera'
? `${entityTopic}/image`
: `${entityTopic}/state`
// ***** Build a Home Assistant style MQTT discovery message *****
// Legacy versions of ring-mqtt created entity names and IDs for single function devices
// without using any type of suffix. To maintain compatibility with older versions, entities
@@ -77,7 +77,7 @@ export default class RingDevice {
: entity.hasOwnProperty('isLegacyEntity') // Use legacy entity ID generation
? { unique_id: `${this.deviceId}` }
: { unique_id: `${this.deviceId}_${entityKey}` },
... entity.component === 'camera'
... entity.component === 'camera'
? { topic: entityStateTopic }
: entity.component === 'climate'
? { mode_state_topic: entityStateTopic }
@@ -97,19 +97,19 @@ export default class RingDevice {
... entity.hasOwnProperty('max')
? { max: entity.max } : {},
... entity.hasOwnProperty('attributes')
? { json_attributes_topic: `${entityTopic}/attributes` }
? { json_attributes_topic: `${entityTopic}/attributes` }
: entityKey === "info"
? { json_attributes_topic: `${entityStateTopic}` } : {},
... entity.hasOwnProperty('icon')
? { icon: entity.icon }
: entityKey === "info"
? { icon: entity.icon }
: entityKey === "info"
? { icon: 'mdi:information-outline' } : {},
... 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')
? { brightness_state_topic: `${entityTopic}/brightness_state`,
? { brightness_state_topic: `${entityTopic}/brightness_state`,
brightness_command_topic: `${entityTopic}/brightness_command`,
brightness_scale: entity.brightness_scale } : {},
... entity.component === 'fan'
@@ -169,7 +169,7 @@ export default class RingDevice {
this.debug(`Received invalid or null value to command topic ${command}`)
}
})
// For camera stream entities subscribe to IPC broker topics as well
if (entityKey === 'stream' || entityKey === 'event_stream') {
utils.event.emit('mqtt_ipc_subscribe', discoveryMessage[topic])

View File

@@ -7,7 +7,7 @@ export default class RingSocketDevice extends RingDevice {
super(deviceInfo, category, primaryAttribute, 'socket')
// Set default device data for Home Assistant device registry
this.deviceData = {
this.deviceData = {
ids: [ this.deviceId ],
name: this.device.name,
mf: (this.device.data && this.device.data.manufacturerName) ? this.device.data.manufacturerName : 'Ring',
@@ -40,7 +40,7 @@ export default class RingSocketDevice extends RingDevice {
? { value_template: `{{ value_json["${primaryAttribute}"] | default("") }}` }
: { value_template: '{{ value_json["commStatus"] | default("") }}' }
},
...this.device.data.hasOwnProperty('batteryLevel') ? {
...this.device.data.hasOwnProperty('batteryLevel') ? {
battery: {
component: 'sensor',
device_class: 'battery',
@@ -90,27 +90,27 @@ export default class RingSocketDevice extends RingDevice {
? { auxBatteryLevel: this.device.data.auxBattery.level === 99 ? 100 : this.device.data.auxBattery.level } : {},
... (this.device.data.hasOwnProperty('auxBattery') && this.device.data.auxBattery.hasOwnProperty('status'))
? { auxBatteryStatus: this.device.data.auxBattery.status } : {},
... this.device.data.hasOwnProperty('brightness')
... this.device.data.hasOwnProperty('brightness')
? { brightness: this.device.data.brightness } : {},
... this.device.data.hasOwnProperty('chirps') && this.device.deviceType == 'security-keypad'
... this.device.data.hasOwnProperty('chirps') && this.device.deviceType == 'security-keypad'
? { chirps: this.device.data.chirps } : {},
... this.device.data.hasOwnProperty('commStatus')
... this.device.data.hasOwnProperty('commStatus')
? { commStatus: this.device.data.commStatus } : {},
... this.device.data.hasOwnProperty('firmwareUpdate')
... this.device.data.hasOwnProperty('firmwareUpdate')
? { firmwareStatus: this.device.data.firmwareUpdate.state } : {},
... this.device.data.hasOwnProperty('lastCommTime')
... this.device.data.hasOwnProperty('lastCommTime')
? { lastCommTime: utils.getISOTime(this.device.data.lastCommTime) } : {},
... this.device.data.hasOwnProperty('lastUpdate')
... this.device.data.hasOwnProperty('lastUpdate')
? { lastUpdate: utils.getISOTime(this.device.data.lastUpdate) } : {},
... this.device.data.hasOwnProperty('linkQuality')
... this.device.data.hasOwnProperty('linkQuality')
? { linkQuality: this.device.data.linkQuality } : {},
... this.device.data.hasOwnProperty('powerSave')
... this.device.data.hasOwnProperty('powerSave')
? { powerSave: this.device.data.powerSave } : {},
... this.device.data.hasOwnProperty('serialNumber')
... this.device.data.hasOwnProperty('serialNumber')
? { serialNumber: this.device.data.serialNumber } : {},
... this.device.data.hasOwnProperty('tamperStatus')
... this.device.data.hasOwnProperty('tamperStatus')
? { tamperStatus: this.device.data.tamperStatus } : {},
... this.device.data.hasOwnProperty('volume')
... this.device.data.hasOwnProperty('volume')
? {volume: this.device.data.volume } : {},
... this.device.data.hasOwnProperty('maxVolume')
? {maxVolume: this.device.data.maxVolume } : {},

View File

@@ -8,7 +8,7 @@ export default class BeamOutdoorPlug extends RingSocketDevice {
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.outlet1.data.categoryId === 2) ? 'light' : 'switch',
name: `${this.outlet1.name}`

View File

@@ -5,7 +5,7 @@ export default class Beam extends RingSocketDevice {
super(deviceInfo, 'lighting')
this.data = {}
// Setup device topics based on capabilities.
switch (this.device.data.deviceType) {
case 'group.light-group.beams':
@@ -30,7 +30,7 @@ export default class Beam extends RingSocketDevice {
break;
}
}
initMotionEntity() {
this.entity.motion = {
component: 'binary_sensor',
@@ -158,7 +158,7 @@ export default class Beam extends RingSocketDevice {
this.debug('Light duration command received but out of range (0-32767)')
} else {
this.data.beam_duration = parseInt(duration)
this.mqttPublish(this.entity.beam_duration.state_topic, this.data.beam_duration)
this.mqttPublish(this.entity.beam_duration.state_topic, this.data.beam_duration)
}
}
}

View File

@@ -39,7 +39,7 @@ parentPort.on("message", async(data) => {
parentPort.postMessage('Live stream transcoding process is starting')
await liveStream.startTranscoding({
// The native AVC video stream is copied to the RTSP server unmodified while the audio
// The native AVC video stream is copied to the RTSP server unmodified while the audio
// stream is converted into two output streams using both AAC and Opus codecs. This
// provides a stream with wide compatibility across various media player technologies.
audio: [
@@ -86,4 +86,4 @@ parentPort.on("message", async(data) => {
liveStream = false
}
}
})
})

View File

@@ -28,7 +28,7 @@ export default class Camera extends RingPolledDevice {
events: events.filter(event => event.event_type === 'motion'),
latestEventId: ''
},
...this.device.isDoorbot ? {
...this.device.isDoorbot ? {
ding: {
active_ding: false,
duration: savedState?.ding?.duration ? savedState.ding.duration : 180,
@@ -75,7 +75,7 @@ export default class Camera extends RingPolledDevice {
session: false,
publishedStatus: ''
},
keepalive:{
keepalive:{
active: false,
session: false,
expires: 0
@@ -104,7 +104,7 @@ export default class Camera extends RingPolledDevice {
}
} : {}
}
this.entity = {
...this.entity,
motion: {
@@ -127,7 +127,7 @@ export default class Camera extends RingPolledDevice {
component: 'select',
options: [
...this.device.isDoorbot
? [ 'Ding 1', 'Ding 2', 'Ding 3', 'Ding 4', 'Ding 5',
? [ 'Ding 1', 'Ding 2', 'Ding 3', 'Ding 4', 'Ding 5',
'Ding 1 (Transcoded)', 'Ding 2 (Transcoded)', 'Ding 3 (Transcoded)', 'Ding 4 (Transcoded)', 'Ding 5 (Transcoded)' ]
: [],
'Motion 1', 'Motion 2', 'Motion 3', 'Motion 4', 'Motion 5',
@@ -418,7 +418,7 @@ export default class Camera extends RingPolledDevice {
if (this.entity.event_select) {
this.publishEventSelectState(isPublish)
}
this.publishDingStates()
this.publishDingDurationState(isPublish)
this.publishSnapshotMode()
@@ -437,8 +437,8 @@ export default class Camera extends RingPolledDevice {
// Check for subscription to ding and motion events and attempt to resubscribe
if (this.device.isDoorbot && !this.device.data.subscribed === true) {
this.debug('Camera lost subscription to ding events, attempting to resubscribe...')
this.device.subscribeToDingEvents().catch(e => {
this.debug('Failed to resubscribe camera to ding events. Will retry in 60 seconds.')
this.device.subscribeToDingEvents().catch(e => {
this.debug('Failed to resubscribe camera to ding events. Will retry in 60 seconds.')
this.debug(e)
})
}
@@ -450,7 +450,7 @@ export default class Camera extends RingPolledDevice {
})
}
}
// Process a ding event
async processNotification(pushData) {
let dingKind
@@ -509,8 +509,8 @@ export default class Camera extends RingPolledDevice {
// Publishes all current ding states for this camera
publishDingStates() {
this.publishDingState('motion')
if (this.device.isDoorbot) {
this.publishDingState('ding')
if (this.device.isDoorbot) {
this.publishDingState('ding')
}
}
@@ -586,15 +586,15 @@ export default class Camera extends RingPolledDevice {
}
// Reports the level of the currently active battery, might be null if removed so report 0% in that case
attributes.batteryLevel = this.device.batteryLevel && utils.isNumeric(this.device.batteryLevel)
? this.device.batteryLevel
attributes.batteryLevel = this.device.batteryLevel && utils.isNumeric(this.device.batteryLevel)
? this.device.batteryLevel
: 0
// Must have at least one battery, but it might not be inserted, so report 0% in that case
attributes.batteryLife = this.device.data.hasOwnProperty('battery_life') && utils.isNumeric(this.device.data.battery_life)
attributes.batteryLife = this.device.data.hasOwnProperty('battery_life') && utils.isNumeric(this.device.data.battery_life)
? Number.parseFloat(this.device.data.battery_life)
: 0
if (this.hasBattery2) {
attributes.batteryLife2 = this.device.data.hasOwnProperty('battery_life_2') && utils.isNumeric(this.device.data.battery_life_2)
? Number.parseFloat(this.device.data.battery_life_2)
@@ -665,7 +665,7 @@ export default class Camera extends RingPolledDevice {
this.data.event_select.publishedState = this.data.event_select.state
this.mqttPublish(this.entity.event_select.state_topic, this.data.event_select.state)
}
const attributes = {
const attributes = {
recordingUrl: this.data.event_select.recordingUrl,
eventId: this.data.event_select.eventId
}
@@ -680,7 +680,7 @@ export default class Camera extends RingPolledDevice {
this.data[dingType].publishedDuration = this.data[dingType].duration
}
})
}
}
// Publish snapshot image/metadata
publishSnapshot() {
@@ -720,10 +720,10 @@ export default class Camera extends RingPolledDevice {
newSnapshot = await this.device.getNextSnapshot()
} else {
this.debug('Motion snapshot needed but notification did not contain image UUID and battery cameras are unable to snapshot while recording')
}
}
}
} catch (error) {
this.debug(error)
this.debug(error)
this.debug('Failed to retrieve updated snapshot')
}
@@ -794,7 +794,7 @@ export default class Camera extends RingPolledDevice {
try {
if (this.data.event_select.transcoded) {
// Ring transcoded videos are poorly optimized for RTSP streaming so they must be re-encoded on-the-fly
// Ring videos transcoded for download are poorly optimized for RTSP streaming so they must be re-encoded on-the-fly
this.data.stream.event.session = spawn(pathToFfmpeg, [
'-re',
'-i', this.data.event_select.recordingUrl,
@@ -858,10 +858,10 @@ export default class Camera extends RingPolledDevice {
const 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`
this.debug(`Starting a keepalive stream for camera`)
// Keepalive stream is used only when the live stream is started
// Keepalive stream is used only when the live stream is started
// manually. It copies only the audio stream to null output just to
// trigger rtsp server to start the on-demand stream and keep it running
// when there are no other RTSP readers.
@@ -905,7 +905,7 @@ export default class Camera extends RingPolledDevice {
try {
const events = await(this.getRecordedEvents(eventType, eventNumber))
if (events.length >= eventNumber) {
selectedEvent = events[eventNumber-1]
selectedEvent = events[eventNumber-1]
if (selectedEvent.event_id !== this.data.event_select.eventId || this.data.event_select.transcoded !== transcoded) {
if (this.data.event_select.recordingUrl) {
this.debug(`New ${this.data.event_select.state} event detected, updating the recording URL`)
@@ -964,7 +964,7 @@ export default class Camera extends RingPolledDevice {
: history.items.filter(i => i.recording_status === 'ready')
events = [...events, ...newEvents]
}
// Remove base64 padding characters from pagination key
paginationKey = history.pagination_key ? history.pagination_key.replace(/={1,2}$/, '') : false
@@ -1150,7 +1150,7 @@ export default class Camera extends RingPolledDevice {
this.data.snapshot.autoInterval = false
if (this.data.snapshot.mode === 'auto') {
if (this.data.snapshot.motion && this.data.snapshot.interval) {
this.data.snapshot.mode = 'all'
this.data.snapshot.mode = 'all'
} else if (this.data.snapshot.interval) {
this.data.snapshot.mode = 'interval'
} else if (this.data.snapshot.motion) {
@@ -1159,7 +1159,7 @@ export default class Camera extends RingPolledDevice {
this.data.snapshot.mode = 'disabled'
}
this.updateSnapshotMode()
this.publishSnapshotMode()
this.publishSnapshotMode()
}
clearInterval(this.data.snapshot.intervalTimerId)
this.scheduleSnapshotRefresh()
@@ -1174,7 +1174,7 @@ export default class Camera extends RingPolledDevice {
const mode = message[0].toUpperCase() + message.slice(1)
switch(mode) {
case 'Auto':
this.data.snapshot.autoInterval = true
this.data.snapshot.autoInterval = true
case 'Disabled':
case 'Motion':
case 'Interval':

View File

@@ -89,7 +89,7 @@ export default class Chime extends RingPolledDevice {
// Polled states are published only if value changes or it's a device publish
const volumeState = this.device.data.settings.volume
if (volumeState !== this.data.volume || isPublish) {
if (volumeState !== this.data.volume || isPublish) {
this.mqttPublish(this.entity.volume.state_topic, volumeState.toString())
this.data.volume = volumeState
}
@@ -108,7 +108,7 @@ export default class Chime extends RingPolledDevice {
if (this.entity.hasOwnProperty('nightlight_enabled')) {
const nightlightEnabled = this.device.data.settings.night_light_settings.light_sensor_enabled ? 'ON' : 'OFF'
const nightlightState = this.device.data.night_light_state.toUpperCase()
const nightlightState = this.device.data.night_light_state.toUpperCase()
if ((nightlightEnabled !== this.data.nightlight.enabled && Date.now()/1000 - this.data.nightlight.set_time > 30) || isPublish) {
this.data.nightlight.enabled = nightlightEnabled
this.mqttPublish(this.entity.nightlight_enabled.state_topic, this.data.nightlight.enabled)
@@ -162,7 +162,7 @@ export default class Chime extends RingPolledDevice {
break;
case 'snooze_minutes/command':
this.setSnoozeMinutes(message)
break;
break;
case 'volume/command':
this.setVolumeLevel(message)
break;
@@ -252,7 +252,7 @@ export default class Chime extends RingPolledDevice {
case 'OFF':
this.data.nightlight.set_time = Math.floor(Date.now()/1000)
await this.setDeviceSettings({
"night_light_settings": {
"night_light_settings": {
"light_sensor_enabled": command === 'ON' ? true : false
}
})

View File

@@ -29,7 +29,7 @@ export default class Fan extends RingSocketDevice {
} else {
this.debug(`ERROR - Could not determine fan preset value. Raw percent value: ${fanPercent}%`)
}
// Publish device state
// targetFanPercent is a small hack to work around Home Assistant UI behavior
if (this.data.targetFanPercent && this.data.targetFanPercent !== fanPercent) {
@@ -44,7 +44,7 @@ export default class Fan extends RingSocketDevice {
// Publish device attributes (batterylevel, tamper status)
this.publishAttributes()
}
// Process messages from MQTT command topic
processCommand(command, message) {
switch (command) {
@@ -92,7 +92,7 @@ export default class Fan extends RingSocketDevice {
return
} else if (setFanPercent < 10) {
this.debug(`Received fan speed of ${setFanPercent}% which is < 10%, overriding to 10%`)
setFanPercent = 10
setFanPercent = 10
} else if (setFanPercent > 100) {
this.debug(`Received fan speed of ${setFanPercent}% which is > 100%, overriding to 100%`)
setFanPercent = 100

View File

@@ -16,7 +16,7 @@ export default class FloodFreezeSensor extends RingSocketDevice {
unique_id: `${this.deviceId}_cold` // Legacy compatibility
}
}
publishState() {
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'

View File

@@ -60,7 +60,7 @@ export default class Keypad extends RingSocketDevice {
this.data.motion.publishedState = this.data.motion.state
}
}
processMotion() {
if (this.data.motion.timeout) {
clearTimeout(this.data.motion.timeout)

View File

@@ -16,7 +16,7 @@ export default class ModesPanel extends RingPolledDevice {
currentMode: undefined
}
}
publishState(data) {
const isPublish = data === undefined ? true : false
const mode = (isPublish) ? this.device.location.getLocationMode() : data
@@ -51,7 +51,7 @@ export default class ModesPanel extends RingPolledDevice {
this.debug(`Received message to unknown command topic: ${command}`)
}
}
// Set Alarm Mode on received MQTT command message
async setLocationMode(message) {
this.debug(`Received command set mode ${message} for location ${this.device.location.name} (${this.locationId})`)

View File

@@ -4,7 +4,7 @@ export default class MultiLevelSwitch extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
this.deviceData.mdl = 'Dimming Light'
this.entity.light = {
component: 'light',
brightness_scale: 100,
@@ -14,12 +14,12 @@ export default class MultiLevelSwitch extends RingSocketDevice {
publishState() {
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)
const switchLevel = (this.device.data.level && !isNaN(this.device.data.level) ? Math.round(100 * this.device.data.level) : 0)
this.mqttPublish(this.entity.light.state_topic, switchState)
this.mqttPublish(this.entity.light.brightness_state_topic, switchLevel.toString())
this.publishAttributes()
}
// Process messages from MQTT command topic
processCommand(command, message) {
switch (command) {

View File

@@ -10,6 +10,7 @@ export default class SecurityPanel extends RingSocketDevice {
this.deviceData.name = `${this.device.location.name} Alarm`
this.data = {
mode: this.device.data.mode,
attributes: {
lastArmedBy: 'Unknown',
lastArmedTime: '',
@@ -17,7 +18,7 @@ export default class SecurityPanel extends RingSocketDevice {
lastDisarmedTime: ''
}
}
this.entity = {
...this.entity,
alarm: {
@@ -31,12 +32,12 @@ export default class SecurityPanel extends RingSocketDevice {
name: `${this.device.location.name} Siren`
},
...utils.config().enable_panic ? {
police: {
police: {
component: 'switch',
name: `${this.device.location.name} Panic - Police`,
icon: 'mdi:police-badge'
},
fire: {
fire: {
component: 'switch',
name: `${this.device.location.name} Panic - Fire`,
icon: 'mdi:fire'
@@ -49,19 +50,20 @@ export default class SecurityPanel extends RingSocketDevice {
// Listen to raw data updates for all devices and pick out
// arm/disarm events for this security panel
this.device.location.onDataUpdate.subscribe(async (message) => {
if (this.isOnline() &&
console.log(JSON.stringify(message, null, 4))
if (this.isOnline() &&
message.datatype === 'DeviceInfoDocType' &&
message.body?.[0]?.general?.v2?.zid === this.deviceId &&
message.body[0].impulse?.v1?.[0] &&
message.body[0].impulse.v1.filter(i =>
message.body[0].impulse.v1.filter(i =>
i.impulseType.match('security-panel.mode-switched.') ||
i.impulseType.match('security-panel.exit-delay')
).length > 0
) {
) {
const impulse = message.body[0].impulse.v1
if (message.context) {
if (impulse.filter(i => i.impulseType.match(/some|all|exit-delay/)).length > 0) {
await this.updateAlarmAttributes(message.context, 'Armed')
await this.updateAlarmAttributes(message.context, 'Armed')
} else if (impulse.filter(i => i.impulseType.includes('none')).length > 0) {
await this.updateAlarmAttributes(message.context, 'Disarmed')
}
@@ -170,7 +172,7 @@ export default class SecurityPanel extends RingSocketDevice {
this.mqttPublish(this.entity.alarm.json_attributes_topic, JSON.stringify(this.data.attributes), 'attr')
this.publishAttributes()
}
async waitForExitDelay(exitDelayMs) {
await utils.msleep(exitDelayMs)
if (this.device.data.mode === 'all') {
@@ -220,8 +222,8 @@ export default class SecurityPanel extends RingSocketDevice {
if (message.toLowerCase() !== 'disarm') {
// During arming, check for sensors that require bypass
// Get all devices that allow bypass
const bypassDevices = (await this.device.location.getDevices()).filter(device =>
// Get all devices that allow bypass
const bypassDevices = (await this.device.location.getDevices()).filter(device =>
device.deviceType === RingDeviceType.ContactSensor ||
device.deviceType === RingDeviceType.RetrofitZone ||
device.deviceType === RingDeviceType.MotionSensor ||

View File

@@ -3,7 +3,7 @@ import RingSocketDevice from './base-socket-device.js'
export default class Siren extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
this.deviceData.mdl = (this.device.data.deviceType === 'siren.outdoor-strobe') ? 'Outdoor Siren' : 'Siren'
this.deviceData.mdl = (this.device.data.deviceType === 'siren.outdoor-strobe') ? 'Outdoor Siren' : 'Siren'
this.entity = {
...this.entity,
siren: {

View File

@@ -10,7 +10,7 @@ export default class SmokeAlarm extends RingSocketDevice {
if (this.hasOwnProperty('childDevices')) {
delete this.childDevices
}
this.entity.smoke = {
component: 'binary_sensor',
device_class: 'smoke',

View File

@@ -4,7 +4,7 @@ export default class SmokeCoListener extends RingSocketDevice {
constructor(deviceInfo) {
super(deviceInfo, 'alarm')
this.deviceData.mdl = 'Smoke & CO Listener'
this.entity.smoke = {
component: 'binary_sensor',
device_class: 'smoke'

View File

@@ -5,7 +5,7 @@ export default class Switch extends RingSocketDevice {
super(deviceInfo, 'alarm')
this.deviceData.mdl = (this.device.data.categoryId === 2) ? 'Light' : 'Switch'
this.component = (this.device.data.categoryId === 2) ? 'light' : 'switch'
this.entity[this.component] = {
component: this.component,
isLegacyEntity: true // Legacy compatibility

View File

@@ -26,18 +26,18 @@ export default class Thermostat extends RingSocketDevice {
setPoint: (() => {
return this.device.data.setPoint
? this.device.data.setPoint
: this.temperatureSensor.data.celsius
: this.temperatureSensor.data.celsius
}),
operatingMode: (() => {
operatingMode: (() => {
return this.operatingStatus.data.operatingMode !== 'off'
? `${this.operatingStatus.data.operatingMode}ing`
: this.device.data.mode === 'off'
? 'off'
: this.device.data.fanMode === 'on' ? 'fan' : 'idle'
: this.device.data.fanMode === 'on' ? 'fan' : 'idle'
}),
temperature: (() => { return this.temperatureSensor.data.celsius }),
... this.entity.thermostat.modes.includes('auto')
? {
? {
autoSetPointInProgress: false,
autoSetPoint: {
low: this.device.data.modeSetpoints.auto.setPoint-this.device.data.modeSetpoints.auto.deadBand,
@@ -47,15 +47,15 @@ export default class Thermostat extends RingSocketDevice {
} : {}
}
this.operatingStatus.onData.subscribe(() => {
if (this.isOnline()) {
this.operatingStatus.onData.subscribe(() => {
if (this.isOnline()) {
this.publishOperatingMode()
this.publishAttributes()
}
})
this.temperatureSensor.onData.subscribe(() => {
if (this.isOnline()) {
if (this.isOnline()) {
this.publishTemperature()
this.publishAttributes()
}
@@ -169,7 +169,7 @@ export default class Thermostat extends RingSocketDevice {
this.debug(`Received invalid set mode command`)
}
}
async setSetPoint(value) {
const mode = this.data.currentMode()
switch(mode) {
@@ -205,7 +205,7 @@ export default class Thermostat extends RingSocketDevice {
this.data.autoSetPoint[type] = Number(value)
// Home Assistant always sends both low/high values when changing range on dual-setpoint mode
// so this function will be called twice for every change. The code below locks for 100 milliseconds
// to allow time for the second value to be updated before proceeding to call the set function once.
// to allow time for the second value to be updated before proceeding to call the set function once.
if (!this.data.setPointInProgress) {
this.data.setPointInProgress = true
await utils.msleep(100)

View File

@@ -1,3 +1,9 @@
## v5.4.2
**Bugs Fixed**
- Suppress spurrious error message when user has no subscription
- Don't make stream source and still image URL attributes dependent on successful heath check data
- For high-power cameras request a non-cached snapshot for motion events even if no UUID (e.g. no subscription)
## v5.4.1
**Bugs Fixed**
- Fix alarm state not updating for various conditions (armed/disarmed with keypad, exit-delay, etc)

View File

@@ -3,8 +3,8 @@
# =============================================================================
# ring-mqtt run script for s6-init #
#
# This script automatically detects if it is running as the Home Assistant
# addon or a standard docker environment and takes actions as appropriate
# This script automatically detects if it is running as the Home Assistant
# addon or a standard docker environment and takes actions as appropriate
# for the detected environment.
# ==============================================================================

View File

@@ -3,8 +3,8 @@
# =============================================================================
# ring-mqtt run script for s6-init
#
# This script automatically detects if it is running as the Home Assistant
# addon or a standard docker environment and sets configuration variables as
# This script automatically detects if it is running as the Home Assistant
# addon or a standard docker environment and sets configuration variables as
# appropriate.
# ==============================================================================

View File

@@ -33,7 +33,7 @@ export default new class Config {
break;
default:
const configPath = dirname(fileURLToPath(new URL('.', import.meta.url)))+'/'
this.file = (process.env.RINGMQTT_CONFIG) ? configPath+process.env.RINGMQTT_CONFIG : configPath+'config.json'
this.file = (process.env.RINGMQTT_CONFIG) ? configPath+process.env.RINGMQTT_CONFIG : configPath+'config.json'
await this.loadConfigFile()
}
@@ -48,7 +48,7 @@ export default new class Config {
const mqttURL = new URL(this.data.mqtt_url)
debug(`MQTT URL: ${mqttURL.protocol}//${mqttURL.username ? mqttURL.username+':********@' : ''}${mqttURL.hostname}:${mqttURL.port}`)
}
// Create CONFIG object from file or envrionment variables
async loadConfigFile() {
debug('Configuration file: '+this.file)
@@ -73,7 +73,7 @@ export default new class Config {
debug(`Discovered invalid value for MQTT host: ${mqttURL.hostname}`)
debug('Overriding with default alias for Mosquitto MQTT addon')
mqttURL.hostname = 'core-mosquitto'
}
}
} else {
debug('No Home Assistant MQTT service found, using Home Assistant hostname as default')
mqttURL.hostname = process.env.HAHOSTNAME
@@ -93,7 +93,7 @@ export default new class Config {
debug(`Configured MQTT Port: ${mqttURL.port}`)
}
if (mqttURL.username === 'auto_username') {
if (mqttURL.username === 'auto_username') {
mqttURL.username = process.env.HAMQTTUSER ? process.env.HAMQTTUSER : ''
if (mqttURL.username) {
debug(`Discovered MQTT User: ${mqttURL.username}`)

View File

@@ -45,9 +45,9 @@ export default new class ExitHandler {
debug('Setting all devices offline...')
await utils.sleep(1)
ring.devices.forEach(ringDevice => {
if (ringDevice.availabilityState === 'online') {
if (ringDevice.availabilityState === 'online') {
ringDevice.shutdown = true
ringDevice.offline()
ringDevice.offline()
}
})
}

View File

@@ -20,10 +20,10 @@ export default new class Go2RTC {
debug(chalk.green('-'.repeat(90)))
debug('Creating go2rtc configuration and starting go2rtc process...')
const configFile = (process.env.RUNMODE === 'standard')
const configFile = (process.env.RUNMODE === 'standard')
? dirname(fileURLToPath(new URL('.', import.meta.url)))+'/config/go2rtc.yaml'
: '/data/go2rtc.yaml'
let config = {
log: {
level: "debug"
@@ -37,7 +37,7 @@ export default new class Go2RTC {
rtsp: {
listen: ":8554",
...(utils.config().livestream_user && utils.config().livestream_pass)
? {
? {
username: utils.config().livestream_user,
password: utils.config().livestream_pass
} : {},
@@ -51,9 +51,9 @@ export default new class Go2RTC {
if (cameras) {
config.streams = {}
for (const camera of cameras) {
config.streams[`${camera.deviceId}_live`] =
config.streams[`${camera.deviceId}_live`] =
`exec:${dirname(fileURLToPath(new URL('.', import.meta.url)))}/scripts/start-stream.sh ${camera.deviceId} live ${camera.deviceTopic} {output}`
config.streams[`${camera.deviceId}_event`] =
config.streams[`${camera.deviceId}_event`] =
`exec:${dirname(fileURLToPath(new URL('.', import.meta.url)))}/scripts/start-stream.sh ${camera.deviceId} event ${camera.deviceTopic} {output}`
}
try {
@@ -92,7 +92,7 @@ export default new class Go2RTC {
// Replace time in go2rtc log messages with tag
debug(line.replace(/^.*\d{2}:\d{2}:\d{2}\.\d{3}([^\s]+) /, chalk.green('[go2rtc] ')))
})
const stderrLine = readline.createInterface({ input: this.go2rtcProcess.stderr })
stderrLine.on('line', (line) => {
// Replace time in go2rtc log messages with tag

View File

@@ -18,7 +18,7 @@ export default new class Main {
}
console.error(data)
};
// Start event listeners
utils.event.on('generated_token', (generatedToken) => {
this.init(generatedToken)
@@ -32,7 +32,7 @@ export default new class Main {
await state.init()
tokenApp.setSystemId(state.data.systemId)
}
// Is there any usable token?
if (state.data.ring_token || generatedToken) {
// Wait for the network to be online and then attempt to connect to the Ring API using the token

View File

@@ -176,7 +176,7 @@ export default new class RingMqtt {
debug(chalk.green('-'.repeat(90)))
debug(chalk.white('Starting Device Discovery...'))
debug(' '.repeat(90))
// If new location, set custom properties and add to location list
if (this.locations.find(l => l.locationId == location.locationId)) {
debug(chalk.white('Existing location: ')+chalk.green(location.name)+chalk.cyan(` (${location.id})`))
@@ -189,7 +189,7 @@ export default new class RingMqtt {
// Get all location devices and, if camera support is enabled, cameras, chimes and intercoms
const devices = await location.getDevices()
if (utils.config().enable_cameras) {
if (utils.config().enable_cameras) {
cameras = location.cameras
chimes = location.chimes
intercoms = location.intercoms
@@ -247,7 +247,7 @@ export default new class RingMqtt {
this.devices.push(ringDevice)
}
}
if (ringDevice && !ringDevice.hasOwnProperty('parentDevice')) {
debug(chalk.white(foundMessage)+chalk.green(`${ringDevice.deviceData.name}`)+chalk.cyan(' ('+ringDevice.deviceId+')'))
if (ringDevice?.childDevices) {
@@ -280,7 +280,7 @@ export default new class RingMqtt {
}
await utils.sleep(3)
}
// Return supported device
async getDevice(device, allDevices, events) {
const deviceInfo = {

View File

@@ -13,7 +13,7 @@ export default new class State {
constructor() {
this.valid = false
this.writeScheduled = false
this.data = {
this.data = {
ring_token: '',
systemId: '',
devices: {}
@@ -21,7 +21,7 @@ export default new class State {
}
async init() {
this.file = (process.env.RUNMODE === 'standard')
this.file = (process.env.RUNMODE === 'standard')
? dirname(fileURLToPath(new URL('.', import.meta.url)))+'/ring-state.json'
: '/data/ring-state.json'
await this.loadStateData()

View File

@@ -67,7 +67,7 @@ export class WeriftPeerConnection extends Subscribed {
const audioTransceiver = pc.addTransceiver('audio', {
direction: 'sendrecv',
})
const videoTransceiver = pc.addTransceiver('video', {
direction: 'recvonly',
})

View File

@@ -33,7 +33,7 @@ export class RingEdgeConnection extends StreamingConnectionBase {
parentPort.postMessage(error)
this.callEnded()
})
}),
}),
// The ring-edge session needs a ping every 5 seconds to keep the connection alive
interval(5000).subscribe(() => {
this.sendSessionMessage('ping')
@@ -127,8 +127,8 @@ export class RingEdgeConnection extends StreamingConnectionBase {
else {
firstValueFrom(this.onSessionId)
.then(sendSessionMessage)
.catch((e) => {
//debug(e)
.catch((e) => {
//debug(e)
})
}
}

View File

@@ -38,7 +38,7 @@ export class StreamingConnectionBase extends Subscribed {
onClose.subscribe(() => {
this.callEnded()
}),
this.pc.onConnectionState.subscribe((state) => {
if (state === 'failed') {
this.callEnded()

View File

@@ -33,11 +33,11 @@ export class StreamingSession extends Subscribed {
bindToConnection(connection) {
this.addSubscriptions(
connection.onAudioRtp.subscribe(this.onAudioRtp),
connection.onVideoRtp.subscribe(this.onVideoRtp),
connection.onAudioRtp.subscribe(this.onAudioRtp),
connection.onVideoRtp.subscribe(this.onVideoRtp),
connection.onCallAnswered.subscribe((sdp) => {
this.onUsingOpus.next(sdp.toLocaleLowerCase().includes(' opus/'))
}),
}),
connection.onCallEnded.subscribe(() => this.callEnded()))
}
@@ -56,7 +56,7 @@ export class StreamingSession extends Subscribed {
}
const videoPort = await this.reservePort(1)
const audioPort = await this.reservePort(1)
const ringSdp = await Promise.race([
firstValueFrom(this.connection.onCallAnswered),
firstValueFrom(this.onCallEnded),
@@ -67,7 +67,7 @@ export class StreamingSession extends Subscribed {
return
}
const usingOpus = await this.isUsingOpus
const ffmpegInputArguments = [
'-hide_banner',
'-protocol_whitelist',
@@ -80,14 +80,14 @@ export class StreamingSession extends Subscribed {
'-i',
'pipe:'
]
const inputSdp = getCleanSdp(ringSdp)
.replace(/m=audio \d+/, `m=audio ${audioPort}`)
.replace(/m=video \d+/, `m=video ${videoPort}`)
const ff = new FfmpegProcess({
ffmpegArgs: ffmpegInputArguments.concat(
...(ffmpegOptions.audio || ['-acodec', 'aac']),
...(ffmpegOptions.audio || ['-acodec', 'aac']),
...(ffmpegOptions.video || ['-vcodec', 'copy']),
...(ffmpegOptions.output || [])),
ffmpegPath: pathToFfmpeg,
@@ -105,7 +105,7 @@ export class StreamingSession extends Subscribed {
this.onCallEnded.pipe(take(1)).subscribe(() => ff.stop())
ff.writeStdin(inputSdp)
// Request a key frame now that ffmpeg is ready to receive
this.requestKeyFrame()
}

View File

@@ -43,7 +43,7 @@ export default new class TokenApp {
if (this.listener) {
return
}
const webdir = dirname(fileURLToPath(new URL('.', import.meta.url)))+'/web'
let restClient
@@ -59,7 +59,7 @@ export default new class TokenApp {
res.sendFile('connected.html', {root: webdir})
} else {
res.sendFile('account.html', {root: webdir})
}
}
})
this.app.get(/.*force-token-generation$/, (req, res) => {
@@ -71,11 +71,11 @@ export default new class TokenApp {
res.cookie('systemId', `${process.env.RUNMODE === 'addon' ? 'ring-mqtt-addon' : 'ring-mqtt'}-${this.systemId.slice(-5)}`, { maxAge: 3600000, encode: String })
const email = req.body.email
const password = req.body.password
restClient = await new RingRestClient({
email,
restClient = await new RingRestClient({
email,
password,
controlCenterDisplayName: `${process.env.RUNMODE === 'addon' ? 'ring-mqtt-addon' : 'ring-mqtt'}-${this.systemId.slice(-5)}`,
systemId: this.systemId
systemId: this.systemId
})
// Check if the user/password was accepted
try {

View File

@@ -61,11 +61,11 @@ export default new class Utils {
getCpuCores() {
let detectedCores = 0
// Try to detect the number of physical cores. This is a slightly different
// technique vs what I've seen in other places, which seem to mostly depend
// Try to detect the number of physical cores. This is a slightly different
// technique vs what I've seen in other places, which seem to mostly depend
// on lscpu, which isn't installed by default on Alpine Linux. While I could
// just pull it in for the Docker image, it wasn't clear to me how common it
// is for lscpu to be installed on other distros so decided to try a different
// is for lscpu to be installed on other distros so decided to try a different
// technique.
//
// The code below checks if at least one cpu core_id file exist in sysfs and,

View File

@@ -1,6 +1,6 @@
{
"name": "ring-mqtt",
"version": "5.4.1",
"version": "5.4.2",
"type": "module",
"description": "Ring Devices via MQTT",
"main": "ring-mqtt.js",

View File

@@ -2,7 +2,7 @@
# Activate video stream on Ring cameras via ring-mqtt
# Intended only for use as on-demand script for rtsp-simple-server
# Requires mosquitto MQTT clients package to be installed
# Uses ring-mqtt internal IPC broker for communications with main process
# Uses ring-mqtt internal IPC broker for communications with main process
# Provides status updates and termintates stream on script exit
# Required command line arguments
@@ -47,7 +47,7 @@ logger() {
trap cleanup INT TERM QUIT
# This loop starts mosquitto_sub with a subscription on the camera stream topic that sends all received
# messages via file descriptor to the read process. On initial startup the script publishes the message
# messages via file descriptor to the read process. On initial startup the script publishes the message
# 'ON-DEMAND' to the stream command topic which lets ring-mqtt know that an RTSP client has requested
# the stream. Stream state is determined via the the detailed stream state messages received via the
# json_attributes_topic:

View File

@@ -5,7 +5,7 @@ if [ ! -d "/app/ring-mqtt-${BRANCH}" ]; then
echo "Updating ring-mqtt to the ${BRANCH} version..."
if [ "${BRANCH}" = "latest" ]; then
git clone https://github.com/tsightler/ring-mqtt ring-mqtt-latest
else
else
git clone -b dev https://github.com/tsightler/ring-mqtt ring-mqtt-dev
fi
cd "/app/ring-mqtt-${BRANCH}"
@@ -13,14 +13,14 @@ if [ ! -d "/app/ring-mqtt-${BRANCH}" ]; then
npm install --no-progress > /dev/null 2>&1
chmod +x ring-mqtt.js scripts/*.sh
# This runs the downloaded version of this script in case there are
# This runs the downloaded version of this script in case there are
# additonal component upgrade actions that need to be performed
exec "/app/ring-mqtt-${BRANCH}/scripts/update2branch.sh"
echo "-------------------------------------------------------"
else
# Branch has already been initialized, run any post-update command here
echo "The ring-mqtt-${BRANCH} branch has been updated."
APK_ARCH="$(apk --print-arch)"
GO2RTC_VERSION="v1.5.0"
case "${APK_ARCH}" in