Skip to content

Commit

Permalink
oh-sipclient: Add call status Item to report call status to server (#…
Browse files Browse the repository at this point in the history
…2648)

This adds the option to specify a String Item, to which the call status
is sent.
This allows to track the usage of oh-sipclient from rules and scripts.

---------

Signed-off-by: Florian Hotze <florianh_dev@icloud.com>
  • Loading branch information
florian-h05 authored Jul 10, 2024
1 parent 553591d commit 43abf2f
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 21 deletions.
5 changes: 5 additions & 0 deletions bundles/org.openhab.ui/doc/components/oh-sipclient-card.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ Usage is explained at the [`oh-sipclient` component docs](/docs/ui/components/oh
Automatically dial the SIP address when loaded
</PropDescription>
</PropBlock>
<PropBlock type="TEXT" name="stateItem" label="State Item" context="item">
<PropDescription>
String Item to publish the current SIP connection state to the openHAB server and make it accessible from rules etc.
</PropDescription>
</PropBlock>
<PropBlock type="BOOLEAN" name="enableSIPDebug" label="Enable SIP Debug">
<PropDescription>
Enable SIP debugging to the browser console (dev tools)
Expand Down
26 changes: 26 additions & 0 deletions bundles/org.openhab.ui/doc/components/oh-sipclient.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,27 @@ This can be achieved by configuring the widget as usual, but setting SIP usernam
1. Insert your SIP account credentials, they are used instead of those stored on the openHAB server.
1. Insert the SIP address/phone number of your SIP account, it is used to hide your local identity from the call dial.

## SIP Connection State Item

The advanced `sipStateItem` property allows to define a String Item to publish the current SIP connection state to the openHAB server.

There are the following basic states:

- `connected`: The SIP client has connected to the SIP server and ready to make outgoing calls.
- `registered`: The SIP client has registered at the SIP server and ready to receive calls.
- `disconnected`: The SIP client has disconnected from the SIP server.

In addition, there are the following call states, which provide the caller ID of the remote party, e.g. `**620@fritz.box` after a colon:

- `incoming`: An incoming call is received, e.g. `incoming:**620@fritz.box`.
- `incoming-accepted`: An incoming call has been accepted and is now active, e.g. `incoming-accepted:**620@fritz.box`.
- `outgoing`: An outgoing call is started.
- `outgoing-accepted`: An outgoing call has been accepted and is now active.
- `ended`: The call has been ended.
- `failed`: The call has failed.

The `sipStateItem` is useful to track the SIP connection state on the openHAB server and work with it, e.g. in rules.

## Configuration

<!-- DO NOT REMOVE the following comments -->
Expand Down Expand Up @@ -132,6 +153,11 @@ This can be achieved by configuring the widget as usual, but setting SIP usernam
Automatically dial the SIP address when loaded
</PropDescription>
</PropBlock>
<PropBlock type="TEXT" name="stateItem" label="State Item" context="item">
<PropDescription>
String Item to publish the current SIP connection state to the openHAB server and make it accessible from rules etc.
</PropDescription>
</PropBlock>
<PropBlock type="BOOLEAN" name="enableSIPDebug" label="Enable SIP Debug">
<PropDescription>
Enable SIP debugging to the browser console (dev tools)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { pt, pn, pb } from '../helpers.js'
import { pt, pn, pb, pi } from '../helpers.js'

export default () => [
pn('iconSize', 'Icon Size', 'Size of the icon(s) in px'),
Expand All @@ -16,5 +16,6 @@ export default () => [
pb('disableRegister', 'Disable REGISTER', 'SIP registration can be disabled in case you only want to initiate calls, but not receive calls with the SIP widgets.').a(),
pt('autoAnswer', 'Auto Answer', 'Automatically answer an incoming call from one of the comma delimited SIP addresses (<code>userInfo@hostname</code>, <code>userInfo</code>, ...) or use * for all incoming calls.').a(),
pt('autoDial', 'Auto Dial', 'Automatically dial the SIP address when loaded').a(),
pi('sipStateItem', 'SIP State Item', 'String Item to publish the current SIP connection state to the openHAB server and make it accessible from rules etc.').a(),
pb('enableSIPDebug', 'Enable SIP Debug', 'Enable SIP debugging to the browser console (dev tools)').a()
]
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@
</f7-button>
<f7-button :style="computedButtonStyle" icon-f7="phone_down_fill" icon-color="red" :icon-size="config.iconSize" @click.stop="session.terminate()" />
</f7-segmented>
<!-- Show hangup button and DTM button (if configured) for ongoing call -->
<f7-segmented v-else style="width: 100%; height: 100%">
<!-- Show hangup button for outgoing call -->
<f7-button v-if="session && session.isInProgress()" :style="computedButtonStyle" icon-f7="phone_down_fill" icon-color="yellow" :icon-size="config.iconSize" @click.stop="session.terminate()" />
<!-- Show hangup button for ongoing call -->
<f7-button v-else-if="session && !session.isEnded()" :style="computedButtonStyle" icon-f7="phone_down_fill" icon-color="red" :icon-size="config.iconSize" @click.stop="session.terminate()" />
<!-- Show send dtmf button if in a call and feature is enabled-->
<!-- Show send DTMF button if in a call if DTMF string is configured -->
<f7-button v-if="session && !session.isInProgress() && !session.isEnded() && config.dtmfString && config.dtmfString.length > 0" :style="computedButtonStyle" icon-f7="number_square" icon-color="orange" :icon-size="config.iconSize" @click.stop="sendDTMF()" />
</f7-segmented>
</div>
Expand Down Expand Up @@ -53,7 +54,7 @@ import { OhSIPClientDefinition } from '@/assets/definitions/widgets/system'
import foregroundService from '../widget-foreground-service'
import { actionsMixin } from '../widget-actions'
import WidgetConfigPopup from '@/components/pagedesigner/widget-config-popup.vue'
import { WidgetDefinition, pg, pt } from '@/assets/definitions/widgets/helpers.js'
import { WidgetDefinition, pg, pt, pi } from '@/assets/definitions/widgets/helpers.js'
// Thanks to Joseph Sardin, https://bigsoundbank.com
// ringFile source: https://bigsoundbank.com/detail-0375-phone-ring-5.html
Expand All @@ -67,7 +68,6 @@ export default {
session: null,
remoteParty: '',
phonebook: new Map(),
loggerPrefix: 'oh-sipclient',
showLocalVideo: false,
stream: null
}
Expand Down Expand Up @@ -150,17 +150,20 @@ export default {
// Update connected status on connection changes
this.phone.on('connected', () => {
this.connected = true
this.updateStateItem('connected')
console.info(this.LOGGER_PREFIX + ': Connected to SIP server')
if (this.config.autoDial && this.config.disableRegister === true) {
this.autoDial()
}
console.info(this.loggerPrefix + ': Connected to SIP server')
})
this.phone.on('disconnected', () => {
this.connected = false
console.info(this.loggerPrefix + ': Disconnected from SIP server')
this.updateStateItem('disconnected')
console.info(this.LOGGER_PREFIX + ': Disconnected from SIP server')
})
this.phone.on('registered', () => {
console.info(this.loggerPrefix + ': SIP registration successful')
this.updateStateItem('registered')
console.info(this.LOGGER_PREFIX + ': SIP registration successful')
if (this.config.autoDial) {
// give a little time to account for an incoming call after registration before calling
setTimeout(() => this.autoDial(), 1000)
Expand All @@ -170,32 +173,36 @@ export default {
// Register event for new incoming or outgoing call event
this.phone.on('newRTCSession', (data) => {
this.session = data.session
const remoteParty = this.session.remote_identity.uri.user
const remotePartyWithHost = `${this.session.remote_identity.uri.user}@${this.session.remote_identity.uri.host}`
this.remoteParty = (this.phonebook.size > 0) ? this.phonebook.get(this.session.remote_identity.uri.user) : this.session.remote_identity.uri.user
if (this.session.direction === 'outgoing') {
this.updateStateItem('outgoing:' + remotePartyWithHost)
// Handle accepted call
this.session.on('accepted', () => {
this.stopTones()
console.info(this.loggerPrefix + ': Outgoing call in progress')
this.updateStateItem('outgoing-accepted:' + remotePartyWithHost)
console.info(this.LOGGER_PREFIX + ': Outgoing call in progress')
})
} else if (this.session.direction === 'incoming') {
console.info(this.loggerPrefix + ': Incoming call from ' + this.remoteParty)
console.info(this.LOGGER_PREFIX + ': Incoming call from ' + this.remoteParty)
this.playTone(ringFile)
this.updateStateItem('incoming:' + remotePartyWithHost)
// Handle accepted call
this.session.on('accepted', () => {
console.info(this.loggerPrefix + ': Incoming call in progress')
this.updateStateItem('incoming-accepted:' + remotePartyWithHost)
console.info(this.LOGGER_PREFIX + ': Incoming call in progress')
})
if (this.config.autoAnswer) {
const autoAnswer = this.config.autoAnswer.toString()
if (autoAnswer.trim() === '*') {
this.answer()
} else {
const userInfo = this.session.remote_identity.uri.user
const userInfoHost = `${this.session.remote_identity.uri.user}@${this.session.remote_identity.uri.host}`
const parts = autoAnswer.split(',')
parts.forEach(part => {
if ((part.indexOf('@') > 0 && part === userInfoHost) || part === userInfo) {
if ((part.indexOf('@') > 0 && part === remotePartyWithHost) || part === remoteParty) {
this.answer()
}
})
Expand All @@ -205,13 +212,15 @@ export default {
// Handle ended call
this.session.on('ended', () => {
this.stopMedia()
console.info(this.loggerPrefix + ': Call ended')
this.updateStateItem('ended:' + remotePartyWithHost)
console.info(this.LOGGER_PREFIX + ': Call ended')
})
// Handle failed call
this.session.on('failed', (event) => {
this.stopTones()
this.stopMedia()
console.info(this.loggerPrefix + ': Call failed. Reason: ' + event.cause)
this.updateStateItem('failed:' + remotePartyWithHost)
console.info(this.LOGGER_PREFIX + ': Call failed. Reason: ' + event.cause)
})
})
this.phone.start()
Expand All @@ -223,13 +232,13 @@ export default {
*/
playTone (file) {
if (this.config.enableTones === true) {
console.info(this.loggerPrefix + ': Starting to play tone')
console.info(this.LOGGER_PREFIX + ': Starting to play tone')
this.audio = new Audio(file)
// Play tone
this.audio.loop = true
this.audio.load()
this.audio.play().catch(error => {
console.debug(this.loggerPrefix + ': Play tone: ' + error)
console.debug(this.LOGGER_PREFIX + ': Play tone: ' + error)
})
}
},
Expand All @@ -238,7 +247,7 @@ export default {
*/
stopTones () {
if (this.config.enableTones === true) {
console.info(this.loggerPrefix + ': Stop playing tone')
console.info(this.LOGGER_PREFIX + ': Stop playing tone')
this.audio.pause()
}
},
Expand Down Expand Up @@ -280,7 +289,7 @@ export default {
}
},
call (target) {
console.info(this.loggerPrefix + ': Calling ' + this.remoteParty + ' ...')
console.info(this.LOGGER_PREFIX + ': Calling ' + this.remoteParty + ' ...')
this.phone.call(target, { mediaConstraints: { audio: true, video: this.config.enableVideo } })
this.attachMedia()
this.playTone(ringBackFile)
Expand All @@ -298,7 +307,7 @@ export default {
this.session.sendDTMF(this.config.dtmfString, options)
},
localSettingsPopup () {
console.info(this.loggerPrefix + ': Opening local settings popup.')
console.info(this.LOGGER_PREFIX + ': Opening local settings popup.')
const popup = { component: WidgetConfigPopup }
this.$f7router.navigate({ url: 'local-sip-settings', route: { path: 'local-sip-settings', popup } }, {
props: {
Expand All @@ -312,7 +321,8 @@ export default {
pt('username', 'Local SIP Username', 'Used instead of the username from widget settings and stored on the openHAB server.'),
pt('password', 'Local SIP Password', 'Used instead of the password from the widget settings and stored on the openHAB server.'),
pt('ownSipAddress', 'Local SIP Address', 'SIP Address (phone number) of this local client. Used by the client to remove itself from the dial ' +
'popup, which is configured with the phonebook option in the widget settings.')
'popup, which is configured with the phonebook option in the widget settings.'),
pi('sipStateItem', 'State Item', 'Used instead of the SIP connection state Item from the widget settings and stored on the openHAB server.').a()
])
}
})
Expand Down Expand Up @@ -357,7 +367,14 @@ export default {
if (!session || !(session.isInProgress() || session.isEstablished())) {
this.call(this.config.autoDial.toString())
}
},
updateStateItem (newStatus) {
if (!this.config.sipStateItem) return
this.$store.dispatch('sendCommand', { itemName: this.config.sipStateItem, cmd: newStatus })
}
},
created () {
this.LOGGER_PREFIX = 'oh-sipclient'
}
}
</script>

0 comments on commit 43abf2f

Please sign in to comment.