JD2022-TU1/main/extern/Hue/libhuestream/huestream/connect/ConnectionFlow.cpp

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;
}
}
}