mirror of
https://github.com/tsightler/ring-mqtt.git
synced 2025-09-26 21:01:12 +08:00
Minor syntax cleanups
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"mqtt_url": "mqtt://localhost:1883",
|
||||
"mqtt_url": "mqtt://192.168.1.57:1883",
|
||||
"mqtt_options": "",
|
||||
"livestream_user": "",
|
||||
"livestream_pass": "",
|
||||
|
@@ -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()
|
||||
}
|
||||
|
@@ -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])
|
||||
|
@@ -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 } : {},
|
||||
|
@@ -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}`
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
@@ -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':
|
||||
|
@@ -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
|
||||
}
|
||||
})
|
||||
|
@@ -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
|
||||
|
@@ -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'
|
||||
|
@@ -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)
|
||||
|
@@ -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})`)
|
||||
|
@@ -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) {
|
||||
|
@@ -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 ||
|
||||
|
@@ -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: {
|
||||
|
@@ -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',
|
||||
|
@@ -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'
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
@@ -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.
|
||||
# ==============================================================================
|
||||
|
||||
|
@@ -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.
|
||||
# ==============================================================================
|
||||
|
||||
|
@@ -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}`)
|
||||
|
@@ -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()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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 = {
|
||||
|
@@ -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()
|
||||
|
@@ -67,7 +67,7 @@ export class WeriftPeerConnection extends Subscribed {
|
||||
const audioTransceiver = pc.addTransceiver('audio', {
|
||||
direction: 'sendrecv',
|
||||
})
|
||||
|
||||
|
||||
const videoTransceiver = pc.addTransceiver('video', {
|
||||
direction: 'recvonly',
|
||||
})
|
||||
|
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ export class StreamingConnectionBase extends Subscribed {
|
||||
onClose.subscribe(() => {
|
||||
this.callEnded()
|
||||
}),
|
||||
|
||||
|
||||
this.pc.onConnectionState.subscribe((state) => {
|
||||
if (state === 'failed') {
|
||||
this.callEnded()
|
||||
|
@@ -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()
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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,
|
||||
|
@@ -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",
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user