506 lines
13 KiB
C++
506 lines
13 KiB
C++
/*******************************************************************************
|
|
Copyright © 2017 Philips Lighting Holding B.V.
|
|
All Rights Reserved.
|
|
********************************************************************************/
|
|
|
|
#include "ConnectionFlow.h"
|
|
#include <algorithm>
|
|
#include "huestream/config/HueConfig.h"
|
|
|
|
namespace huestream {
|
|
namespace connectionflow {
|
|
|
|
#define DISPATCH_P0(method) \
|
|
MESSAGE_DISPATCH_P0(_factory->GetMessageDispatcher(), ConnectionFlow::method);
|
|
|
|
#define DISPATCH_P1(method, value) \
|
|
MESSAGE_DISPATCH_P1(_factory->GetMessageDispatcher(), ConnectionFlow::method, value);
|
|
|
|
#define DISPATCH_P2(method, value1, value2) \
|
|
MESSAGE_DISPATCH_P2(_factory->GetMessageDispatcher(), ConnectionFlow::method, value1, value2);
|
|
|
|
ConnectionFlow::ConnectionFlow(ConnectionFlowFactoryPtr factory) :
|
|
_state(Idle), _request(None), _ongoingAuthenticationCount(0),
|
|
_backgroundDiscoveredBridges(make_shared<BridgeList>()), _activeBridge(make_shared<Bridge>()) {
|
|
_factory = factory;
|
|
_activeBridge->Clear();
|
|
_backgroundDiscoveredBridges->clear();
|
|
}
|
|
|
|
ConnectionFlow::~ConnectionFlow() {
|
|
}
|
|
|
|
PROP_IMPL(ConnectionFlow, BridgePtr, activeBridge, ActiveBridge);
|
|
|
|
void ConnectionFlow::Load() {
|
|
DISPATCH_P0(DoLoad);
|
|
}
|
|
|
|
void ConnectionFlow::DoLoad() {
|
|
if (!Start(LoadBridge))
|
|
return;
|
|
|
|
StartLoading();
|
|
}
|
|
|
|
void ConnectionFlow::ConnectToBridge() {
|
|
DISPATCH_P0(DoConnectToBridge);
|
|
}
|
|
|
|
void ConnectionFlow::DoConnectToBridge() {
|
|
if (!Start(Connect))
|
|
return;
|
|
|
|
if (!_backgroundDiscoveredBridges->empty()) {
|
|
auto bridges = _backgroundDiscoveredBridges;
|
|
_backgroundDiscoveredBridges = make_shared<BridgeList>();
|
|
_backgroundDiscoveredBridges->clear();
|
|
StartAuthentication(bridges);
|
|
} else {
|
|
StartLoading();
|
|
}
|
|
}
|
|
|
|
void ConnectionFlow::ConnectToBridgeBackground() {
|
|
DISPATCH_P0(DoConnectToBridgeBackground);
|
|
}
|
|
|
|
void ConnectionFlow::DoConnectToBridgeBackground() {
|
|
if (!Start(ConnectBackground))
|
|
return;
|
|
|
|
StartLoading();
|
|
}
|
|
|
|
void ConnectionFlow::ConnectToBridgeWithIp(const string &ipAddress) {
|
|
DISPATCH_P1(DoConnectToBridgeWithIp, ipAddress);
|
|
}
|
|
|
|
void ConnectionFlow::DoConnectToBridgeWithIp(const string &ipAddress) {
|
|
if (!Start(ConnectIp))
|
|
return;
|
|
|
|
auto bridge = make_shared<Bridge>();
|
|
bridge->SetIpAddress(ipAddress);
|
|
bridge->SetIsValidIp(true);
|
|
|
|
StartAuthentication(bridge);
|
|
}
|
|
|
|
void ConnectionFlow::SetManual(BridgePtr bridge) {
|
|
DISPATCH_P1(DoSetManual, bridge);
|
|
};
|
|
|
|
void ConnectionFlow::DoSetManual(BridgePtr bridge) {
|
|
if (!Start(SetManualBridge))
|
|
return;
|
|
|
|
_activeBridge = bridge;
|
|
StartRetrieveFullConfig();
|
|
}
|
|
|
|
void ConnectionFlow::ResetBridge() {
|
|
DISPATCH_P0(DoResetBridge);
|
|
};
|
|
|
|
void ConnectionFlow::DoResetBridge() {
|
|
if (!Start(Reset))
|
|
return;
|
|
|
|
BridgePtr bridge = make_shared<Bridge>();
|
|
bridge->Clear();
|
|
_activeBridge = bridge;
|
|
|
|
NewMessage(FeedbackMessage(_request, DONE_RESET, _activeBridge));
|
|
|
|
StartSaving();
|
|
}
|
|
|
|
void ConnectionFlow::SelectGroup(int id) {
|
|
DISPATCH_P1(DoSelectGroup, id);
|
|
};
|
|
|
|
void ConnectionFlow::DoSelectGroup(int id) {
|
|
if (!Start(Select))
|
|
return;
|
|
|
|
if (!_activeBridge->SelectGroup(id)) {
|
|
ReportActionRequired();
|
|
}
|
|
|
|
StartSaving();
|
|
}
|
|
|
|
void ConnectionFlow::StartStream() {
|
|
DISPATCH_P0(DoStartStream);
|
|
};
|
|
|
|
void ConnectionFlow::DoStartStream() {
|
|
if (!Start(Activate))
|
|
return;
|
|
|
|
ActivateStreaming();
|
|
}
|
|
|
|
void ConnectionFlow::Abort() {
|
|
DISPATCH_P0(DoAbort);
|
|
}
|
|
|
|
void ConnectionFlow::DoAbort() {
|
|
if (_state == Idle)
|
|
return;
|
|
|
|
if (_state == SearchingFirst || _state == SearchingRetry) {
|
|
_bridgeSearcher->Abort();
|
|
}
|
|
|
|
if (_state == Authenticating) {
|
|
_ongoingAuthenticationCount = 0;
|
|
}
|
|
|
|
NewMessage(FeedbackMessage(_request, DONE_ABORTED));
|
|
Finish();
|
|
}
|
|
|
|
bool ConnectionFlow::Start(RequestType request) {
|
|
if (_state != Idle)
|
|
return false;
|
|
|
|
_request = request;
|
|
NewMessage(FeedbackMessage(_request, PROCEDURE_STARTED, _activeBridge));
|
|
return true;
|
|
}
|
|
|
|
void ConnectionFlow::NewMessage(const FeedbackMessage &message) {
|
|
}
|
|
|
|
|
|
void ConnectionFlow::StartLoading() {
|
|
NewMessage(FeedbackMessage(_request, START_LOADING));
|
|
_state = Loading;
|
|
HueConfig::GetInstance()->GetStorageAccessor()->Load([this](OperationResult r, BridgePtr b) {
|
|
DISPATCH_P2(LoadingCompleted, r, b);
|
|
});
|
|
}
|
|
|
|
void ConnectionFlow::LoadingCompleted(OperationResult result, BridgePtr bridge) {
|
|
if (_state != Loading)
|
|
return;
|
|
|
|
_activeBridge = bridge;
|
|
|
|
if (result == OPERATION_SUCCESS) {
|
|
NewMessage(FeedbackMessage(_request, FINISH_LOADING_BRIDGE_CONFIGURED, _activeBridge));
|
|
} else {
|
|
NewMessage(FeedbackMessage(_request, FINISH_LOADING_NO_BRIDGE_CONFIGURED, _activeBridge));
|
|
_activeBridge->Clear();
|
|
}
|
|
|
|
if (_request == LoadBridge) {
|
|
Finish();
|
|
return;
|
|
}
|
|
|
|
if (_activeBridge->HasEverBeenAuthorizedForStreaming()) {
|
|
StartRetrieveFullConfig();
|
|
} else {
|
|
StartBridgeSearch();
|
|
}
|
|
}
|
|
|
|
|
|
void ConnectionFlow::StartBridgeSearch() {
|
|
NewMessage(FeedbackMessage(_request, START_SEARCHING));
|
|
|
|
bool bruteForce = false;
|
|
if (_state == SearchingFirst) {
|
|
_state = SearchingRetry;
|
|
bruteForce = true;
|
|
} else {
|
|
_state = SearchingFirst;
|
|
}
|
|
|
|
_bridgeSearcher = _factory->CreateSearcher();
|
|
_bridgeSearcher->SearchNew(bruteForce, [this](BridgeListPtr bridges) {
|
|
DISPATCH_P1(BridgeSearchCompleted, bridges);
|
|
});
|
|
}
|
|
|
|
void ConnectionFlow::BridgeSearchCompleted(BridgeListPtr bridges) {
|
|
if (_state != SearchingFirst && _state != SearchingRetry)
|
|
return;
|
|
|
|
_bridgeSearcher.reset();
|
|
|
|
FilterInvalidIpAddresses(bridges);
|
|
|
|
if (bridges->size() == 0) {
|
|
NewMessage(FeedbackMessage(_request, FINISH_SEARCHING_NO_BRIDGES_FOUND));
|
|
|
|
if (_request == ConnectBackground) {
|
|
NewMessage(FeedbackMessage(_request, DONE_NO_BRIDGE_FOUND));
|
|
Finish();
|
|
} else if (_state == SearchingFirst) {
|
|
StartBridgeSearch();
|
|
} else {
|
|
ReportActionRequired();
|
|
StartSaving();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (ContainsValidBridge(bridges)) {
|
|
NewMessage(FeedbackMessage(_request, FINISH_SEARCH_BRIDGES_FOUND));
|
|
if (RediscoverLostActiveBridge(bridges)) {
|
|
StartRetrieveFullConfig();
|
|
return;
|
|
}
|
|
if (_request == ConnectBackground) {
|
|
NewMessage(FeedbackMessage(_request, DONE_BRIDGE_FOUND));
|
|
_backgroundDiscoveredBridges = bridges;
|
|
Finish();
|
|
return;
|
|
}
|
|
} else {
|
|
NewMessage(FeedbackMessage(_request, FINISH_SEARCHING_INVALID_BRIDGES_FOUND));
|
|
|
|
if (_request == ConnectBackground) {
|
|
if (_activeBridge->GetStatus() == BRIDGE_EMPTY) {
|
|
NewMessage(FeedbackMessage(_request, DONE_NO_BRIDGE_FOUND));
|
|
Finish();
|
|
} else {
|
|
ReportActionRequired();
|
|
StartSaving();
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
StartAuthentication(bridges);
|
|
}
|
|
|
|
void ConnectionFlow::FilterInvalidIpAddresses(BridgeListPtr bridges) {
|
|
auto i = std::begin(*bridges);
|
|
|
|
while (i != std::end(*bridges)) {
|
|
if (!(*i)->IsValidIp()) {
|
|
i = bridges->erase(i);
|
|
}
|
|
else {
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ConnectionFlow::ContainsValidBridge(BridgeListPtr bridges) {
|
|
// TODO Bridgediscovery should return model id as well
|
|
/*
|
|
for (auto bridge : *bridges) {
|
|
if (bridge->IsValidModelId()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
bool ConnectionFlow::RediscoverLostActiveBridge(BridgeListPtr bridges) {
|
|
bool found = false;
|
|
if (!_activeBridge->IsValidIp() && _activeBridge->IsAuthorized() && _activeBridge->IsValidClientKey() && _activeBridge->IsValidModelId()) {
|
|
for (auto i = bridges->begin(); i < bridges->end(); ++i) {
|
|
if ((*i)->GetId() == _activeBridge->GetId()) {
|
|
_activeBridge->SetIpAddress((*i)->GetIpAddress());
|
|
_activeBridge->SetIsValidIp(true);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
void ConnectionFlow::StartAuthentication(BridgePtr bridge) {
|
|
auto bridges = make_shared<BridgeList>();
|
|
bridges->push_back(bridge);
|
|
StartAuthentication(bridges);
|
|
}
|
|
|
|
void ConnectionFlow::StartAuthentication(BridgeListPtr bridges) {
|
|
NewMessage(FeedbackMessage(_request, START_AUTHORIZING));
|
|
NewMessage(FeedbackMessage(_request, PRESS_PUSH_LINK));
|
|
_state = Authenticating;
|
|
_ongoingAuthenticationCount = bridges->size();
|
|
|
|
for (auto bridge : *bridges) {
|
|
PushLinkBridge(bridge);
|
|
}
|
|
}
|
|
|
|
void ConnectionFlow::AuthenticationCompleted(BridgePtr bridge) {
|
|
if (_state != Authenticating)
|
|
return;
|
|
|
|
if (!bridge->IsValidIp()) {
|
|
_ongoingAuthenticationCount--;
|
|
if (_ongoingAuthenticationCount == 0) {
|
|
NewMessage(FeedbackMessage(_request, FINISH_AUTHORIZING_FAILED));
|
|
StartBridgeSearch();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (bridge->IsAuthorized()) {
|
|
_ongoingAuthenticationCount--;
|
|
_activeBridge = bridge;
|
|
NewMessage(FeedbackMessage(_request, FINISH_AUTHORIZING_AUTHORIZED, _activeBridge));
|
|
StartRetrieveFullConfig();
|
|
} else {
|
|
PushLinkBridge(bridge);
|
|
}
|
|
}
|
|
|
|
void ConnectionFlow::PushLinkBridge(BridgePtr bridge) {
|
|
_factory->CreateAuthenticator()->Authenticate(bridge, [this](BridgePtr b) {
|
|
DISPATCH_P1(AuthenticationCompleted, b);
|
|
});
|
|
}
|
|
|
|
|
|
void ConnectionFlow::StartRetrieveFullConfig() {
|
|
NewMessage(FeedbackMessage(_request, START_RETRIEVING, _activeBridge));
|
|
_state = Retrieving;
|
|
auto r = _factory->CreateFullConfigRetriever();
|
|
r->Execute(_activeBridge, [this](OperationResult result, BridgePtr bridge) {
|
|
DISPATCH_P2(RetrieveFullConfigCompleted, result, bridge);
|
|
});
|
|
}
|
|
|
|
void ConnectionFlow::RetrieveFullConfigCompleted(OperationResult result, BridgePtr bridge) {
|
|
if (_state != Retrieving)
|
|
return;
|
|
|
|
_activeBridge = bridge;
|
|
|
|
if (result == OPERATION_FAILED) {
|
|
NewMessage(FeedbackMessage(_request, FINISH_RETRIEVING_FAILED, _activeBridge));
|
|
if (!bridge->IsValidIp() && bridge->IsAuthorized()) {
|
|
StartBridgeSearch();
|
|
return;
|
|
}
|
|
if (bridge->IsValidIp() && !bridge->IsAuthorized()) {
|
|
StartAuthentication(bridge);
|
|
return;
|
|
}
|
|
bridge->SetIsValidIp(false);
|
|
bridge->SetIsAuthorized(false);
|
|
ReportActionRequired();
|
|
} else {
|
|
BridgeStatus status = _activeBridge->GetStatus();
|
|
if (status == BRIDGE_INVALID_MODEL ||
|
|
status == BRIDGE_INVALID_VERSION ||
|
|
status == BRIDGE_INVALID_CLIENTKEY ||
|
|
status == BRIDGE_NO_GROUP_AVAILABLE) {
|
|
NewMessage(FeedbackMessage(_request, FINISH_RETRIEVING_ACTION_REQUIRED, _activeBridge));
|
|
ReportActionRequired();
|
|
} else if ((_activeBridge->GetGroup() == nullptr) && (_activeBridge->SelectGroupIfOnlyOneOption() == false)) {
|
|
NewMessage(FeedbackMessage(_request, FINISH_RETRIEVING_ACTION_REQUIRED, _activeBridge));
|
|
ReportActionRequired();
|
|
} else {
|
|
NewMessage(FeedbackMessage(_request, FINISH_RETRIEVING_READY_TO_START, _activeBridge));
|
|
}
|
|
}
|
|
|
|
StartSaving();
|
|
}
|
|
|
|
void ConnectionFlow::ReportActionRequired() {
|
|
NewMessage(FeedbackMessage(_request, DONE_ACTION_REQUIRED, _activeBridge));
|
|
|
|
BridgeStatus status = _activeBridge->GetStatus();
|
|
|
|
switch (status) {
|
|
case BRIDGE_EMPTY:
|
|
NewMessage(FeedbackMessage(_request, NO_BRIDGE_FOUND, _activeBridge));
|
|
break;
|
|
case BRIDGE_NOT_CONNECTABLE:
|
|
NewMessage(FeedbackMessage(_request, BRIDGE_NOT_FOUND, _activeBridge));
|
|
break;
|
|
case BRIDGE_INVALID_MODEL:
|
|
NewMessage(FeedbackMessage(_request, INVALID_MODEL, _activeBridge));
|
|
break;
|
|
case BRIDGE_INVALID_VERSION:
|
|
NewMessage(FeedbackMessage(_request, INVALID_VERSION, _activeBridge));
|
|
break;
|
|
case BRIDGE_NO_GROUP_AVAILABLE:
|
|
NewMessage(FeedbackMessage(_request, NO_GROUP_AVAILABLE, _activeBridge));
|
|
break;
|
|
case BRIDGE_INVALID_GROUP_SELECTED:
|
|
NewMessage(FeedbackMessage(_request, SELECT_GROUP, _activeBridge));
|
|
break;
|
|
case BRIDGE_BUSY:
|
|
NewMessage(FeedbackMessage(_request, BUSY_STREAMING, _activeBridge));
|
|
break;
|
|
default:
|
|
NewMessage(FeedbackMessage(_request, INTERNAL_ERROR, _activeBridge));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ConnectionFlow::StartSaving() {
|
|
NewMessage(FeedbackMessage(_request, START_SAVING, _activeBridge));
|
|
_state = Saving;
|
|
HueConfig::GetInstance()->GetStorageAccessor()->Save(_activeBridge, [this](OperationResult r) {
|
|
DISPATCH_P1(SavingCompleted, r);
|
|
});
|
|
}
|
|
|
|
void ConnectionFlow::SavingCompleted(OperationResult r) {
|
|
if (r == OPERATION_SUCCESS) {
|
|
NewMessage(FeedbackMessage(_request, FINISH_SAVING_SAVED, _activeBridge));
|
|
} else {
|
|
NewMessage(FeedbackMessage(_request, FINISH_SAVING_FAILED, _activeBridge));
|
|
}
|
|
|
|
_activeBridge->SetIsBusy(false);
|
|
if (HueConfig::GetInstance()->GetHueSettings()->AutoStartAtConnection() && _activeBridge->IsStreamable()) {
|
|
ActivateStreaming();
|
|
} else {
|
|
Finish();
|
|
}
|
|
}
|
|
|
|
void ConnectionFlow::ActivateStreaming() {
|
|
NewMessage(FeedbackMessage(_request, START_ACTIVATING, _activeBridge));
|
|
_state = Activating;
|
|
|
|
bool result;
|
|
if (HueConfig::GetInstance()->GetHueSettings()->UseRenderThread()) {
|
|
result = HueConfig::GetInstance()->GetStream()->StartWithRenderThread(_activeBridge);
|
|
} else {
|
|
result = HueConfig::GetInstance()->GetStream()->Start(_activeBridge);
|
|
}
|
|
|
|
if (result) {
|
|
NewMessage(FeedbackMessage(_request, FINISH_ACTIVATING_ACTIVE, _activeBridge));
|
|
} else {
|
|
NewMessage(FeedbackMessage(_request, FINISH_ACTIVATING_FAILED, _activeBridge));
|
|
ReportActionRequired();
|
|
}
|
|
|
|
Finish();
|
|
}
|
|
|
|
void ConnectionFlow::Finish() {
|
|
if (_activeBridge->GetStatus() == BRIDGE_OK) {
|
|
NewMessage(FeedbackMessage(_request, DONE_COMPLETED, _activeBridge));
|
|
}
|
|
NewMessage(FeedbackMessage(_request, PROCEDURE_FINISHED, _activeBridge));
|
|
_state = Idle;
|
|
_request = None;
|
|
}
|
|
|
|
}
|
|
}
|