diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp b/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp index 748afae8..ef44a667 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.cpp @@ -76,7 +76,7 @@ DevicePluginPhilipsHue::DevicePluginPhilipsHue() { m_timer = new QTimer(this); m_timer->setSingleShot(false); - m_timer->setInterval(1000); + m_timer->setInterval(1500); connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); } @@ -120,6 +120,7 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev // loaded bridge qCDebug(dcPhilipsHue) << "Setup Hue Bridge" << device->params(); + HueBridge *bridge = new HueBridge(this); bridge->setId(device->paramValue("id").toString()); bridge->setApiKey(device->paramValue("api key").toString()); @@ -136,6 +137,7 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev // hue color light if (device->deviceClassId() == hueLightDeviceClassId) { qCDebug(dcPhilipsHue) << "Setup Hue color light" << device->params(); + HueLight *hueLight = new HueLight(this); hueLight->setId(device->paramValue("light id").toInt()); hueLight->setHostAddress(QHostAddress(device->paramValue("host address").toString())); @@ -145,14 +147,14 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev hueLight->setUuid(device->paramValue("uuid").toString()); hueLight->setType(device->paramValue("type").toString()); hueLight->setBridgeId(DeviceId(device->paramValue("bridge").toString())); + device->setParentId(hueLight->bridgeId()); connect(hueLight, &HueLight::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged); - device->setParentId(device->paramValue("bridge").toString()); m_lights.insert(hueLight, device); refreshLight(device); - setLightName(device, device->paramValue("name").toString()); + return DeviceManager::DeviceSetupStatusSuccess; } @@ -169,12 +171,11 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev hueLight->setType(device->paramValue("type").toString()); hueLight->setUuid(device->paramValue("uuid").toString()); hueLight->setBridgeId(DeviceId(device->paramValue("bridge").toString())); + device->setParentId(hueLight->bridgeId()); connect(hueLight, &HueLight::stateChanged, this, &DevicePluginPhilipsHue::lightStateChanged); - device->setParentId(device->paramValue("bridge").toString()); m_lights.insert(hueLight, device); - refreshLight(device); setLightName(device, device->paramValue("name").toString()); @@ -186,7 +187,7 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev qCDebug(dcPhilipsHue) << "Setup Hue remote" << device->params(); HueRemote *hueRemote = new HueRemote(this); - hueRemote->setId(device->paramValue("light id").toInt()); + hueRemote->setId(device->paramValue("sensor id").toInt()); hueRemote->setHostAddress(QHostAddress(device->paramValue("host address").toString())); hueRemote->setName(device->paramValue("name").toString()); hueRemote->setApiKey(device->paramValue("api key").toString()); @@ -194,10 +195,11 @@ DeviceManager::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *dev hueRemote->setType(device->paramValue("type").toString()); hueRemote->setUuid(device->paramValue("uuid").toString()); hueRemote->setBridgeId(DeviceId(device->paramValue("bridge").toString())); + device->setParentId(hueRemote->bridgeId()); connect(hueRemote, &HueRemote::stateChanged, this, &DevicePluginPhilipsHue::remoteStateChanged); + connect(hueRemote, &HueRemote::buttonPressed, this, &DevicePluginPhilipsHue::onRemoteButtonEvent); - device->setParentId(device->paramValue("bridge").toString()); m_remotes.insert(hueRemote, device); return DeviceManager::DeviceSetupStatusSuccess; } @@ -219,6 +221,12 @@ void DevicePluginPhilipsHue::deviceRemoved(Device *device) light->deleteLater(); } + if (device->deviceClassId() == hueRemoteDeviceClassId) { + HueRemote *remote = m_remotes.key(device); + m_remotes.remove(remote); + remote->deleteLater(); + } + if (myDevices().isEmpty()) m_timer->stop(); } @@ -323,7 +331,7 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) // check HTTP status code if (status != 200) { qCWarning(dcPhilipsHue) << "Bridge light discovery error:" << status << reply->errorString(); - onBridgeError(device); + bridgeReachableChanged(device, false); reply->deleteLater(); return; } @@ -335,7 +343,7 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) // check HTTP status code if (status != 200) { qCWarning(dcPhilipsHue) << "Bridge sensor discovery error:" << status << reply->errorString(); - onBridgeError(device); + bridgeReachableChanged(device, false); reply->deleteLater(); return; } @@ -347,11 +355,10 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) // check HTTP status code if (status != 200) { qCWarning(dcPhilipsHue) << "Bridge search new devices error:" << status << reply->errorString(); - onBridgeError(device); + bridgeReachableChanged(device, false); reply->deleteLater(); return; } - discoverBridgeDevices(m_bridges.key(device)); } else if (m_lightRefreshRequests.keys().contains(reply)) { @@ -360,7 +367,7 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) // check HTTP status code if (status != 200) { qCWarning(dcPhilipsHue) << "Refresh Hue Light request error:" << status << reply->errorString(); - onBridgeError(device); + bridgeReachableChanged(device, false); reply->deleteLater(); return; } @@ -372,8 +379,8 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) // check HTTP status code if (status != 200) { if (device->stateValue(bridgeReachableStateTypeId).toBool()) { - qCWarning(dcPhilipsHue) << "Refresh Hue Bridge request error:" << status << reply->errorString(); - onBridgeError(device); + qCWarning(dcPhilipsHue) << "Refresh Hue lights request error:" << status << reply->errorString(); + bridgeReachableChanged(device, false); } reply->deleteLater(); return; @@ -381,13 +388,13 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) processLightsRefreshResponse(device, reply->readAll()); } else if (m_sensorsRefreshRequests.keys().contains(reply)) { - Device *device = m_lightsRefreshRequests.take(reply); + Device *device = m_sensorsRefreshRequests.take(reply); // check HTTP status code if (status != 200) { if (device->stateValue(bridgeReachableStateTypeId).toBool()) { - qCWarning(dcPhilipsHue) << "Refresh Hue Bridge request error:" << status << reply->errorString(); - onBridgeError(device); + qCWarning(dcPhilipsHue) << "Refresh Hue sensors request error:" << status << reply->errorString(); + bridgeReachableChanged(device, false); } reply->deleteLater(); return; @@ -401,7 +408,7 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) if (status != 200) { if (device->stateValue(bridgeReachableStateTypeId).toBool()) { qCWarning(dcPhilipsHue) << "Refresh Hue Bridge request error:" << status << reply->errorString(); - onBridgeError(device); + bridgeReachableChanged(device, false); } reply->deleteLater(); return; @@ -414,13 +421,12 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) // check HTTP status code if (status != 200) { qCWarning(dcPhilipsHue) << "Execute Hue Light action request error:" << status << reply->errorString(); - onBridgeError(actionInfo.first); + bridgeReachableChanged(actionInfo.first, false); emit actionExecutionFinished(actionInfo.second, DeviceManager::DeviceErrorHardwareNotAvailable); reply->deleteLater(); return; } - QByteArray data = reply->readAll(); - processActionResponse(actionInfo.first, actionInfo.second, data); + processActionResponse(actionInfo.first, actionInfo.second, reply->readAll()); } else if (m_lightSetNameRequests.keys().contains(reply)) { Device *device = m_lightSetNameRequests.take(reply); @@ -428,7 +434,7 @@ void DevicePluginPhilipsHue::networkManagerReplyReady(QNetworkReply *reply) // check HTTP status code if (status != 200) { qCWarning(dcPhilipsHue) << "Set name of Hue Light request error:" << status << reply->errorString(); - onBridgeError(device); + bridgeReachableChanged(device, false); reply->deleteLater(); return; } @@ -442,12 +448,13 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, { qCDebug(dcPhilipsHue) << "Execute action" << action.actionTypeId() << action.params(); + // color light if (device->deviceClassId() == hueLightDeviceClassId) { HueLight *light = m_lights.key(device); if (!light->reachable()) { return DeviceManager::DeviceErrorHardwareNotAvailable; - qCWarning(dcPhilipsHue) << "Light not reachable"; + qCWarning(dcPhilipsHue) << "Light" << light->name() << "not reachable"; } if (action.actionTypeId() == huePowerActionTypeId) { @@ -478,12 +485,13 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, return DeviceManager::DeviceErrorActionTypeNotFound; } + // white light if (device->deviceClassId() == hueWhiteLightDeviceClassId) { HueLight *light = m_lights.key(device); if (!light->reachable()) { return DeviceManager::DeviceErrorHardwareNotAvailable; - qCWarning(dcPhilipsHue) << "Light not reachable"; + qCWarning(dcPhilipsHue) << "Light" << light->name() << "not reachable"; } if (action.actionTypeId() == huePowerActionTypeId) { @@ -503,13 +511,12 @@ DeviceManager::DeviceError DevicePluginPhilipsHue::executeAction(Device *device, } if (device->deviceClassId() == hueBridgeDeviceClassId) { - + HueBridge *bridge = m_bridges.key(device); if (!device->stateValue(bridgeReachableStateTypeId).toBool()) { - qCWarning(dcPhilipsHue) << "Bridge not reachable"; + qCWarning(dcPhilipsHue) << "Bridge" << bridge->hostAddress().toString() << "not reachable"; return DeviceManager::DeviceErrorHardwareNotAvailable; } - HueBridge *bridge = m_bridges.key(device); if (action.actionTypeId() == searchNewDevicesActionTypeId) { searchNewDevices(bridge); return DeviceManager::DeviceErrorNoError; @@ -532,6 +539,7 @@ void DevicePluginPhilipsHue::lightStateChanged() qCWarning(dcPhilipsHue) << "Could not find device for light" << light->name(); return; } + if (device->deviceClassId() == hueLightDeviceClassId) { device->setStateValue(hueReachableStateTypeId, light->reachable()); device->setStateValue(hueColorStateTypeId, QVariant::fromValue(light->color())); @@ -560,18 +568,44 @@ void DevicePluginPhilipsHue::remoteStateChanged() device->setStateValue(batteryStateTypeId, remote->battery()); } +void DevicePluginPhilipsHue::onRemoteButtonEvent(const int &buttonCode) +{ + HueRemote *remote = static_cast(sender()); + + switch (buttonCode) { + case HueRemote::OnPressed: + emitEvent(Event(onPressedEventTypeId, m_remotes.value(remote)->id())); + break; + case HueRemote::OnLongPressed: + emitEvent(Event(onLongPressedEventTypeId, m_remotes.value(remote)->id())); + break; + case HueRemote::DimUpPressed: + emitEvent(Event(dimUpPressedEventTypeId, m_remotes.value(remote)->id())); + break; + case HueRemote::DimUpLongPressed: + emitEvent(Event(dimUpLongPressedEventTypeId, m_remotes.value(remote)->id())); + break; + case HueRemote::DimDownPressed: + emitEvent(Event(dimDownPressedEventTypeId, m_remotes.value(remote)->id())); + break; + case HueRemote::DimDownLongPressed: + emitEvent(Event(dimDownLongPressedEventTypeId, m_remotes.value(remote)->id())); + break; + case HueRemote::OffPressed: + emitEvent(Event(offPressedEventTypeId, m_remotes.value(remote)->id())); + break; + case HueRemote::OffLongPressed: + emitEvent(Event(offLongPressedEventTypeId, m_remotes.value(remote)->id())); + break; + default: + break; + } +} + void DevicePluginPhilipsHue::onTimeout() { foreach (Device *device, m_bridges.values()) { - HueBridge *bridge = m_bridges.key(device); -// if (!device->stateValue(hueReachableStateTypeId).toBool()) { -// if (!m_bridgeRefreshRequests.values().contains(device)) -// refreshBridge(device); -// } else { - refreshBridge(device); - refreshLights(bridge); - //refreshSensors(bridge); -// } + refreshBridge(device); } } @@ -678,7 +712,7 @@ void DevicePluginPhilipsHue::processNUpnpResponse(const QByteArray &data) params.append(Param("host address", bridgeMap.value("internalipaddress").toString())); params.append(Param("api key", QString())); params.append(Param("mac address", QString())); - params.append(Param("id", bridgeMap.value("id").toString().toLower())); + params.append(Param("id", bridgeMap.value("internalipaddress").toString().toLower())); params.append(Param("zigbee channel", -1)); descriptor.setParams(params); deviceDescriptors.append(descriptor); @@ -784,9 +818,8 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device return; } - // create Lights if not already added + // create sensors if not already added QList sensorDescriptors; - QVariantMap sensorsMap = jsonDoc.toVariant().toMap(); foreach (QString sensorId, sensorsMap.keys()) { QVariantMap sensorMap = sensorsMap.value(sensorId).toMap(); @@ -798,7 +831,7 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device continue; // check if this is a white light - if (model == "RWL021") { + if (model == "RWL021" || model == "RWL020") { DeviceDescriptor descriptor(hueRemoteDeviceClassId, "Philips Hue Remote", sensorMap.value("name").toString()); ParamList params; params.append(Param("name", sensorMap.value("name").toString())); @@ -814,7 +847,10 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device qCDebug(dcPhilipsHue) << "Found new remote" << sensorMap.value("name").toString() << model; } } - emit autoDevicesAppeared(hueRemoteDeviceClassId, sensorDescriptors); + + if (!sensorDescriptors.isEmpty()) + emit autoDevicesAppeared(hueRemoteDeviceClassId, sensorDescriptors); + } void DevicePluginPhilipsHue::processLightRefreshResponse(Device *device, const QByteArray &data) @@ -855,14 +891,14 @@ void DevicePluginPhilipsHue::processBridgeRefreshResponse(Device *device, const if (!jsonDoc.toVariant().toList().isEmpty()) { qCWarning(dcPhilipsHue) << "Failed to refresh Hue Bridge:" << jsonDoc.toVariant().toList().first().toMap().value("error").toMap().value("description").toString(); - onBridgeError(device); + bridgeReachableChanged(device, false); return; } QVariantMap configMap = jsonDoc.toVariant().toMap(); // mark bridge as reachable - device->setStateValue(bridgeReachableStateTypeId, true); + bridgeReachableChanged(device, true); device->setStateValue(apiVersionStateTypeId, configMap.value("apiversion").toString()); device->setStateValue(softwareVersionStateTypeId, configMap.value("swversion").toString()); @@ -883,6 +919,15 @@ void DevicePluginPhilipsHue::processBridgeRefreshResponse(Device *device, const default: break; } + + + + // do lights/sensor update right after successfull bridge update + HueBridge *bridge = m_bridges.key(device); + refreshLights(bridge); + if (!m_remotes.isEmpty()) + refreshSensors(bridge); + } void DevicePluginPhilipsHue::processLightsRefreshResponse(Device *device, const QByteArray &data) @@ -939,15 +984,12 @@ void DevicePluginPhilipsHue::processSensorsRefreshResponse(Device *device, const // Update sensor states QVariantMap sensorsMap = jsonDoc.toVariant().toMap(); - - qCDebug(dcPhilipsHue) << sensorsMap; - foreach (const QString &sensorId, sensorsMap.keys()) { QVariantMap sensorMap = sensorsMap.value(sensorId).toMap(); foreach (HueRemote *remote, m_remotes.keys()) { if (remote->id() == sensorId.toInt() && remote->bridgeId() == device->id()) { - qCDebug(dcPhilipsHue) << "update remote" << remote->id() << remote->name(); - remote->updateStates(sensorMap.value("state").toMap()); + //qCDebug(dcPhilipsHue) << "update remote" << remote->id() << remote->name(); + remote->updateStates(sensorMap.value("state").toMap(), sensorMap.value("config").toMap()); } } } @@ -1061,6 +1103,13 @@ void DevicePluginPhilipsHue::processInformationResponse(PairingInfo *pairingInfo bridge->setName(response.value("name").toString()); bridge->setZigbeeChannel(response.value("zigbeechannel").toInt()); + if (bridgeAlreadyAdded(bridge->id())) { + qCWarning(dcPhilipsHue) << "Bridge with id" << bridge->id() << "already added."; + emit pairingFinished(pairingInfo->pairingTransactionId(), DeviceManager::DeviceSetupStatusFailure); + bridge->deleteLater(); + pairingInfo->deleteLater(); + } + m_unconfiguredBridges.append(bridge); emit pairingFinished(pairingInfo->pairingTransactionId(), DeviceManager::DeviceSetupStatusSuccess); @@ -1096,18 +1145,31 @@ void DevicePluginPhilipsHue::processActionResponse(Device *device, const ActionI emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError); } -void DevicePluginPhilipsHue::onBridgeError(Device *device) +void DevicePluginPhilipsHue::bridgeReachableChanged(Device *device, const bool &reachable) { - // mark bridge and lamps unreachable - if (device->deviceClassId() == hueBridgeDeviceClassId) { - device->setStateValue(bridgeReachableStateTypeId, false); - foreach (HueLight *light, m_lights.keys()) { - if (light->bridgeId() == device->id()) { - light->setReachable(false); - device->setStateValue(hueReachableStateTypeId, false); + if (reachable) { + device->setStateValue(bridgeReachableStateTypeId, true); + } else { + // mark bridge and corresponding hue devices unreachable + if (device->deviceClassId() == hueBridgeDeviceClassId) { + device->setStateValue(bridgeReachableStateTypeId, false); + + foreach (HueLight *light, m_lights.keys()) { + if (light->bridgeId() == device->id()) { + light->setReachable(false); + m_lights.value(light)->setStateValue(hueReachableStateTypeId, false); + } + } + + foreach (HueRemote *remote, m_remotes.keys()) { + if (remote->bridgeId() == device->id()) { + remote->setReachable(false); + m_remotes.value(remote)->setStateValue(hueReachableStateTypeId, false); + } } } } + } bool DevicePluginPhilipsHue::bridgeAlreadyAdded(const QString &id) diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.h b/plugins/deviceplugins/philipshue/devicepluginphilipshue.h index c47ed82f..f38b28ce 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.h +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.h @@ -58,6 +58,7 @@ public slots: private slots: void lightStateChanged(); void remoteStateChanged(); + void onRemoteButtonEvent(const int &buttonCode); void onTimeout(); private: @@ -108,7 +109,7 @@ private: void processInformationResponse(PairingInfo *pairingInfo, const QByteArray &data); void processActionResponse(Device *device, const ActionId actionId, const QByteArray &data); - void onBridgeError(Device *device); + void bridgeReachableChanged(Device *device, const bool &reachable); bool bridgeAlreadyAdded(const QString &id); bool lightAlreadyAdded(const QString &uuid); diff --git a/plugins/deviceplugins/philipshue/devicepluginphilipshue.json b/plugins/deviceplugins/philipshue/devicepluginphilipshue.json index 80253ca8..b9a96616 100644 --- a/plugins/deviceplugins/philipshue/devicepluginphilipshue.json +++ b/plugins/deviceplugins/philipshue/devicepluginphilipshue.json @@ -399,23 +399,43 @@ "eventTypes": [ { "id": "de769db0-4c31-46cf-9760-dbc6f9209c26", - "idName": "onButton", - "name": "on" + "idName": "onPressed", + "name": "on pressed" + }, + { + "id": "6c5e596b-7c15-40bb-af9d-c778a6b0f30e", + "idName": "onLongPressed", + "name": "on long pressed" }, { "id": "8e3d6a62-6a19-4e9a-a25b-e1da2e56ede9", - "idName": "brightnessUpButton", - "name": "brightness up" + "idName": "dimUpPressed", + "name": "dim up pressed" + }, + { + "id": "53d3c9af-3e25-4116-b22b-38d897bc20aa", + "idName": "dimUpLongPressed", + "name": "dim up long pressed" }, { "id": "efd8b972-9a37-43f2-b9bc-f9dfe144a96d", - "idName": "brightnessDownButton", - "name": "brightness down" + "idName": "dimDownPressed", + "name": "dim down pressed" + }, + { + "id": "1986d4c6-4c9f-4e43-ba70-0ff06c6f177b", + "idName": "dimDownLongPressed", + "name": "dim down long pressed" }, { "id": "7c2a58f1-137c-4bf3-8f9e-453dff020487", - "idName": "offButton", - "name": "off" + "idName": "offPressed", + "name": "off pressed" + }, + { + "id": "d69306eb-ea52-4841-9e26-89c69e9cf6fc", + "idName": "offLongPressed", + "name": "off long pressed" } ] } diff --git a/plugins/deviceplugins/philipshue/hueremote.cpp b/plugins/deviceplugins/philipshue/hueremote.cpp index f5cda7db..2755511a 100644 --- a/plugins/deviceplugins/philipshue/hueremote.cpp +++ b/plugins/deviceplugins/philipshue/hueremote.cpp @@ -36,12 +36,21 @@ void HueRemote::setBattery(const int &battery) m_battery = battery; } -void HueRemote::updateStates(const QVariantMap &statesMap) +void HueRemote::updateStates(const QVariantMap &statesMap, const QVariantMap &configMap) { - qCDebug(dcPhilipsHue) << statesMap; - - + setReachable(configMap.value("reachable", false).toBool()); + setBattery(configMap.value("battery", 0).toInt()); emit stateChanged(); + + QString lastUpdate = statesMap.value("lastupdated").toString(); + if (m_lastUpdate != lastUpdate) { + m_lastUpdate = lastUpdate; + + int buttonCode = statesMap.value("buttonevent").toInt(); + qCDebug(dcPhilipsHue) << "button pressed" << buttonCode; + + emit buttonPressed(buttonCode); + } } diff --git a/plugins/deviceplugins/philipshue/hueremote.h b/plugins/deviceplugins/philipshue/hueremote.h index 38568ffa..45298c3f 100644 --- a/plugins/deviceplugins/philipshue/hueremote.h +++ b/plugins/deviceplugins/philipshue/hueremote.h @@ -34,24 +34,32 @@ class HueRemote : public HueDevice { Q_OBJECT public: + enum ButtonCode { + OnLongPressed = 1001, + OnPressed = 1002, + DimUpLongPressed = 2001, + DimUpPressed = 2002, + DimDownLongPressed = 3001, + DimDownPressed = 3002, + OffLongPressed = 4001, + OffPressed = 4002 + }; + explicit HueRemote(QObject *parent = 0); int battery() const; void setBattery(const int &battery); - void updateStates(const QVariantMap &statesMap); + void updateStates(const QVariantMap &statesMap, const QVariantMap &configMap); private: int m_battery; + QString m_lastUpdate; signals: void stateChanged(); - void onPressed(); - void brightnessUpPressed(); - void brightnessDownPressed(); - void offPressed(); + void buttonPressed(const int &buttonCode); -public slots: }; #endif // HUEREMOTE_H