Skip to content

Commit c696e32

Browse files
authored
Merge pull request #24 from gonicus/busylight-github
feat: support for usb busylights
2 parents ddd118b + b833c5b commit c696e32

25 files changed

+501
-59
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Here's a short feature list:
3535
* Configurable Togglers (i.e. for call queues, CFNL, etc.)
3636
* Upgrade call to Jitsi Meet session
3737
* Support for various hardware headsets (i.e. Yealink, Jabra)
38+
* Support for various busylights
3839
* Custom audio device profiles or managed by your system
3940
* [mpris](https://specifications.freedesktop.org/mpris-spec/latest/) for
4041
stopping other audio sources on incoming calls
@@ -72,6 +73,12 @@ make this list more complete by opening an [issue](https://github.com/gonicus/go
7273
| Jabra | EVOLVE LINK | AEMS |
7374
| Yealink | WH62 | AEMSLOR |
7475

76+
# Busylights known to be supported
77+
78+
| Manufacturer | Model |
79+
| ------------ | ------------------ |
80+
| Luxafor | Flag |
81+
| kuando | Busylight UC Omega |
7582

7683
# Installing _GOnnect_
7784

resources/flatpak/de.gonicus.gonnect.metainfo.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
<li xml:lang="de">Anruf in Jitsi Meet weiterführen</li>
5959
<li>Support for various hardware headsets (i.e. Yealink, Jabra)</li>
6060
<li xml:lang="de">Unterstützung für diverse Headsets (z.B. Yealink, Jabra)</li>
61+
<li>Support for various busylights</li>
62+
<li xml:lang="de">Unterstützung für diverse Busylights</li>
6163
<li>Custom audio device profiles or managed by your system</li>
6264
<li xml:lang="de">Benutzerdefinierte oder vom System verwaltete Audiogeräteprofile</li>
6365
<li>Stopping other audio sources on incoming calls</li>

src/Application.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include "SIPCallManager.h"
88
#include "SystemTrayMenu.h"
99
#include "AddressBookManager.h"
10-
#include "HeadsetDevices.h"
10+
#include "USBDevices.h"
1111

1212
#include <sys/socket.h>
1313
#include <unistd.h>
@@ -46,7 +46,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
4646

4747
setQuitOnLastWindowClosed(false);
4848

49-
HeadsetDevices::instance().initialize();
49+
USBDevices::instance().initialize();
5050

5151
StateManager::instance().setParent(this);
5252
SearchProvider::instance().setParent(this);
@@ -185,7 +185,7 @@ Application::~Application()
185185
void Application::shutdown()
186186
{
187187
NotificationManager::instance().shutdown();
188-
HeadsetDevices::instance().shutdown();
188+
USBDevices::instance().shutdown();
189189

190190
if (m_initialized) {
191191
SIPManager::instance().shutdown();

src/CMakeLists.txt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,14 +336,23 @@ qt_add_qml_module(gonnect
336336
usb/HeadsetDevice.h
337337
usb/HeadsetDeviceProxy.cpp
338338
usb/HeadsetDeviceProxy.h
339-
usb/HeadsetDevices.cpp
340-
usb/HeadsetDevices.h
339+
usb/USBDevices.cpp
340+
usb/USBDevices.h
341341
usb/ReportDescriptorEnums.h
342342
usb/ReportDescriptorEnums.cpp
343343
usb/ReportDescriptorParser.h
344344
usb/ReportDescriptorParser.cpp
345345
usb/ReportDescriptorStructs.h
346346
usb/ReportDescriptorStructs.cpp
347+
348+
usb/busylight/IBusylightDevice.h
349+
usb/busylight/IBusylightDevice.cpp
350+
usb/busylight/BusylightDeviceManager.h
351+
usb/busylight/BusylightDeviceManager.cpp
352+
usb/busylight/KuandoOmega.h
353+
usb/busylight/KuandoOmega.cpp
354+
usb/busylight/LuxaforFlag.h
355+
usb/busylight/LuxaforFlag.cpp
347356
)
348357

349358
set_target_properties(gonnect PROPERTIES
@@ -396,6 +405,7 @@ target_include_directories(gonnect
396405
contacts
397406
ui
398407
usb
408+
usb/busylight
399409
media
400410
${PJSIP_STATIC_INCLUDE_DIRS}
401411
${CMAKE_CURRENT_BINARY_DIR}

src/StateManager.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,15 @@ void StateManager::ActivateAction(const QString &action_name, const QVariantList
198198
}
199199
}
200200

201-
void StateManager::Open(const QStringList &, const QVariantMap &)
201+
void StateManager::Open(const QStringList &args, const QVariantMap &)
202202
{
203-
qobject_cast<Application *>(Application::instance())->rootWindow()->show();
203+
if (args.length()) {
204+
QVariantList vArgs;
205+
for (auto& arg : std::as_const(args)) {
206+
vArgs.push_back(arg);
207+
}
208+
ActivateAction("invoke", vArgs, {});
209+
} else {
210+
qobject_cast<Application *>(Application::instance())->rootWindow()->show();
211+
}
204212
}

src/sip/Ringer.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include "SIPAudioManager.h"
77
#include "Ringer.h"
88
#include "AppSettings.h"
9-
#include "HeadsetDevices.h"
9+
#include "USBDevices.h"
1010
#include "HeadsetDeviceProxy.h"
1111
#include "SystemTrayMenu.h"
1212

@@ -32,7 +32,7 @@ void Ringer::start(qreal customVolume)
3232
// Prefer headset ringer?
3333
if (settings.value(QString("audio%1/preferExternalRinger").arg(currentProfile), false)
3434
.toBool()) {
35-
auto proxy = HeadsetDevices::instance().getProxy();
35+
auto proxy = USBDevices::instance().getHeadsetDeviceProxy();
3636
if (proxy->available()) {
3737
proxy->setRing(true);
3838
return;
@@ -116,7 +116,7 @@ void Ringer::playbackStateChanged(QMediaPlayer::PlaybackState state)
116116

117117
void Ringer::stop()
118118
{
119-
auto proxy = HeadsetDevices::instance().getProxy();
119+
auto proxy = USBDevices::instance().getHeadsetDeviceProxy();
120120
proxy->setRing(false);
121121

122122
SystemTrayMenu::instance().setRinging(false);

src/sip/SIPAudioManager.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include "media/AudioPort.h"
1111

1212
#include "HeadsetDeviceProxy.h"
13-
#include "HeadsetDevices.h"
13+
#include "USBDevices.h"
1414

1515
Q_LOGGING_CATEGORY(lcSIPAudioManager, "gonnect.sip.audio")
1616

@@ -44,14 +44,14 @@ SIPAudioManager::SIPAudioManager(QObject *parent) : QObject(parent)
4444
}
4545

4646
// Headset device mute handling
47-
auto &hds = HeadsetDevices::instance();
48-
auto dev = hds.getProxy();
47+
auto &hds = USBDevices::instance();
48+
auto dev = hds.getHeadsetDeviceProxy();
4949
dev->setMute(m_isAudioCaptureMuted);
5050
});
5151

5252
// Headset device mute handling
53-
auto &hds = HeadsetDevices::instance();
54-
auto dev = hds.getProxy();
53+
auto &hds = USBDevices::instance();
54+
auto dev = hds.getHeadsetDeviceProxy();
5555
connect(dev, &HeadsetDeviceProxy::mute, this,
5656
[this, dev]() { setProperty("isAudioCaptureMuted", dev->getMute()); });
5757
}

src/sip/SIPAudioManager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ class SIPAudioManager : public QObject
7777
qreal playbackAudioVolume() const;
7878
void setPlaybackAudioVolume(qreal volume);
7979

80+
bool isAudioCaptureMuted() const { return m_isAudioCaptureMuted; }
81+
8082
unsigned currentProfile() const { return m_currentAudioProfile; }
8183

8284
~SIPAudioManager() = default;

src/sip/SIPCall.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
#include "media/Sniffer.h"
1616
#include "ViewHelper.h"
1717
#include "HeadsetDeviceProxy.h"
18-
#include "HeadsetDevices.h"
18+
#include "USBDevices.h"
1919
#include "Notification.h"
2020
#include "NotificationManager.h"
2121
#include "AvatarManager.h"
22+
#include "BusylightDeviceManager.h"
2223

2324
#include "pjsua-lib/pjsua.h"
2425

@@ -43,7 +44,7 @@ SIPCall::SIPCall(SIPAccount *account, int callId, const QString &contactId, bool
4344
emit SIPCallManager::instance().meetingRequested(accountId, callId);
4445
});
4546

46-
m_proxy = HeadsetDevices::instance().getProxy();
47+
m_proxy = USBDevices::instance().getHeadsetDeviceProxy();
4748

4849
// Initialize basic call info
4950
// This can only be done here for incoming calls, because an outgoing call has its infos not set
@@ -156,6 +157,7 @@ void SIPCall::onCallState(pj::OnCallStateParam &prm)
156157
emit establishedChanged();
157158

158159
m_proxy->setBusyLine(true);
160+
BusylightDeviceManager::instance().switchOn(Qt::GlobalColor::red);
159161

160162
m_earlyMediaActive = false;
161163
emit earlyMediaActiveChanged();
@@ -186,6 +188,7 @@ void SIPCall::onCallState(pj::OnCallStateParam &prm)
186188

187189
if (m_isEstablished && SIPCallManager::instance().calls().count() == 1) {
188190
m_proxy->setIdle();
191+
BusylightDeviceManager::instance().switchOff();
189192
}
190193

191194
qCInfo(lcSIPCall).nospace() << "Call state disconnected, reason: " << ci.lastReason << ", "

src/sip/SIPCallManager.cpp

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "SIPManager.h"
55
#include "SIPCallManager.h"
66
#include "SIPAccountManager.h"
7+
#include "SIPAudioManager.h"
78
#include "ExternalMediaManager.h"
89
#include "Notification.h"
910
#include "NotificationManager.h"
@@ -16,9 +17,10 @@
1617
#include "StateManager.h"
1718
#include "ViewHelper.h"
1819
#include "AvatarManager.h"
19-
#include "HeadsetDevices.h"
20+
#include "USBDevices.h"
2021
#include "HeadsetDeviceProxy.h"
2122
#include "AddressBook.h"
23+
#include "BusylightDeviceManager.h"
2224

2325
Q_LOGGING_CATEGORY(lcSIPCallManager, "gonnect.sip.callmanager")
2426

@@ -28,6 +30,8 @@ SIPCallManager::SIPCallManager(QObject *parent) : QObject(parent)
2830
{
2931
connect(this, &SIPCallManager::incomingCall, this, &SIPCallManager::onIncomingCall);
3032
connect(this, &SIPCallManager::incomingCall, this, &SIPCallManager::updateCallCount);
33+
connect(this, &SIPCallManager::isHoldingChanged, this, &SIPCallManager::updateBusylightState);
34+
connect(&SIPAudioManager::instance(), &SIPAudioManager::isAudioCaptureMutedChanged, this, &SIPCallManager::updateBusylightState);
3135

3236
m_dtmfTimer.setInterval(PJSUA_CALL_SEND_DTMF_DURATION_DEFAULT + 10);
3337
m_dtmfTimer.callOnTimeout(this, &SIPCallManager::dispatchDtmfBuffer);
@@ -41,8 +45,8 @@ SIPCallManager::SIPCallManager(QObject *parent) : QObject(parent)
4145
});
4246

4347
// React on Headset events
44-
auto &hds = HeadsetDevices::instance();
45-
auto dev = hds.getProxy();
48+
auto &hds = USBDevices::instance();
49+
auto dev = hds.getHeadsetDeviceProxy();
4650
connect(dev, &HeadsetDeviceProxy::hookSwitch, this, [dev, this]() {
4751
// Were're busy with one call -> end call
4852
if (!dev->getHookSwitch() && m_calls.count() == 1) {
@@ -171,6 +175,10 @@ void SIPCallManager::onIncomingCall(SIPCall *call)
171175
auto ringer = new Ringer(n);
172176
ringer->start();
173177

178+
BusylightDeviceManager::instance().startBlinking(Qt::GlobalColor::green);
179+
connect(n, &QObject::destroyed, this,
180+
[]() { BusylightDeviceManager::instance().stopBlinking(); });
181+
174182
pj::CallOpParam prm;
175183
prm.statusCode = PJSIP_SC_RINGING;
176184
call->answer(prm);
@@ -559,6 +567,16 @@ void SIPCallManager::toggleHold()
559567
}
560568
}
561569

570+
bool SIPCallManager::isOneCallOnHold() const
571+
{
572+
for (const auto call : std::as_const(m_calls)) {
573+
if (call->isHolding()) {
574+
return true;
575+
}
576+
}
577+
return false;
578+
}
579+
562580
void SIPCallManager::addCall(SIPCall *call)
563581
{
564582
m_calls.push_back(call);
@@ -844,3 +862,19 @@ void SIPCallManager::updateBlockTimerRunning()
844862
m_blockCleanTimer.start();
845863
}
846864
}
865+
866+
void SIPCallManager::updateBusylightState()
867+
{
868+
auto& busylightDevManager = BusylightDeviceManager::instance();
869+
870+
QColor color(Qt::GlobalColor::red);
871+
if (SIPAudioManager::instance().isAudioCaptureMuted()) {
872+
color.setRgb(255, 165, 0);
873+
}
874+
875+
if (isOneCallOnHold()) {
876+
busylightDevManager.startBlinking(color);
877+
} else {
878+
busylightDevManager.switchOn(color);
879+
}
880+
}

0 commit comments

Comments
 (0)