From 4778e3fc9a84f28a30ee96ca9363ccb2c8d95621 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 11 Jun 2019 02:37:57 +0200 Subject: [PATCH] PhilipsHue: Add support for the hue indoor motion sensor --- philipshue/devicepluginphilipshue.cpp | 258 ++++++++++++------ philipshue/devicepluginphilipshue.h | 18 +- philipshue/devicepluginphilipshue.json | 153 +++++++++++ philipshue/huedevice.h | 2 +- ...eoutdoorsensor.cpp => huemotionsensor.cpp} | 77 +++--- .../{hueoutdoorsensor.h => huemotionsensor.h} | 60 +++- philipshue/philipshue.pro | 8 +- 7 files changed, 430 insertions(+), 146 deletions(-) rename philipshue/{hueoutdoorsensor.cpp => huemotionsensor.cpp} (67%) rename philipshue/{hueoutdoorsensor.h => huemotionsensor.h} (55%) diff --git a/philipshue/devicepluginphilipshue.cpp b/philipshue/devicepluginphilipshue.cpp index a145db29..c46c21e9 100644 --- a/philipshue/devicepluginphilipshue.cpp +++ b/philipshue/devicepluginphilipshue.cpp @@ -322,11 +322,44 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device) return Device::DeviceSetupStatusSuccess; } + // Hue Motion sensor + if (device->deviceClassId() == motionSensorDeviceClassId) { + qCDebug(dcPhilipsHue) << "Setup Hue motion sensor" << device->params(); + + HueIndoorSensor *motionSensor = new HueIndoorSensor(this); + motionSensor->setTimeout(device->setting(motionSensorSettingsTimeoutParamTypeId).toUInt()); + motionSensor->setUuid(device->paramValue(motionSensorDeviceUuidParamTypeId).toString()); + motionSensor->setModelId(device->paramValue(motionSensorDeviceModelIdParamTypeId).toString()); + motionSensor->setTemperatureSensorId(device->paramValue(motionSensorDeviceSensorIdTemperatureParamTypeId).toInt()); + motionSensor->setTemperatureSensorUuid(device->paramValue(motionSensorDeviceSensorUuidTemperatureParamTypeId).toString()); + motionSensor->setPresenceSensorId(device->paramValue(motionSensorDeviceSensorIdPresenceParamTypeId).toInt()); + motionSensor->setPresenceSensorUuid(device->paramValue(motionSensorDeviceSensorUuidPresenceParamTypeId).toString()); + motionSensor->setLightSensorId(device->paramValue(motionSensorDeviceSensorIdLightParamTypeId).toInt()); + motionSensor->setLightSensorUuid(device->paramValue(motionSensorDeviceSensorUuidLightParamTypeId).toString()); + + connect(motionSensor, &HueMotionSensor::reachableChanged, this, &DevicePluginPhilipsHue::onMotionSensorReachableChanged); + connect(motionSensor, &HueMotionSensor::batteryLevelChanged, this, &DevicePluginPhilipsHue::onMotionSensorBatteryLevelChanged); + connect(motionSensor, &HueMotionSensor::temperatureChanged, this, &DevicePluginPhilipsHue::onMotionSensorTemperatureChanged); + connect(motionSensor, &HueMotionSensor::presenceChanged, this, &DevicePluginPhilipsHue::onMotionSensorPresenceChanged); + connect(motionSensor, &HueMotionSensor::lightIntensityChanged, this, &DevicePluginPhilipsHue::onMotionSensorLightIntensityChanged); + + connect(device, &Device::settingChanged, motionSensor, [motionSensor](const ParamTypeId ¶mTypeId, const QVariant &value){ + if (paramTypeId == motionSensorSettingsTimeoutParamTypeId) { + motionSensor->setTimeout(value.toUInt()); + } + }); + + m_motionSensors.insert(motionSensor, device); + + return Device::DeviceSetupStatusSuccess; + } + // Hue Outdoor sensor if (device->deviceClassId() == outdoorSensorDeviceClassId) { qCDebug(dcPhilipsHue) << "Setup Hue Outdoor sensor" << device->params(); - HueOutdoorSensor *outdoorSensor = new HueOutdoorSensor(this); + HueMotionSensor *outdoorSensor = new HueOutdoorSensor(this); + outdoorSensor->setTimeout(device->setting(outdoorSensorSettingsTimeoutParamTypeId).toUInt()); outdoorSensor->setUuid(device->paramValue(outdoorSensorDeviceUuidParamTypeId).toString()); outdoorSensor->setModelId(device->paramValue(outdoorSensorDeviceModelIdParamTypeId).toString()); outdoorSensor->setTemperatureSensorId(device->paramValue(outdoorSensorDeviceSensorIdTemperatureParamTypeId).toInt()); @@ -336,13 +369,19 @@ Device::DeviceSetupStatus DevicePluginPhilipsHue::setupDevice(Device *device) outdoorSensor->setLightSensorId(device->paramValue(outdoorSensorDeviceSensorIdLightParamTypeId).toInt()); outdoorSensor->setLightSensorUuid(device->paramValue(outdoorSensorDeviceSensorUuidLightParamTypeId).toString()); - connect(outdoorSensor, &HueOutdoorSensor::reachableChanged, this, &DevicePluginPhilipsHue::onOutdoorSensorReachableChanged); - connect(outdoorSensor, &HueOutdoorSensor::batteryLevelChanged, this, &DevicePluginPhilipsHue::onOutdoorSensorBatteryLevelChanged); - connect(outdoorSensor, &HueOutdoorSensor::temperatureChanged, this, &DevicePluginPhilipsHue::onOutdoorSensorTemperatureChanged); - connect(outdoorSensor, &HueOutdoorSensor::presenceChanged, this, &DevicePluginPhilipsHue::onOutdoorSensorPresenceChanged); - connect(outdoorSensor, &HueOutdoorSensor::lightIntensityChanged, this, &DevicePluginPhilipsHue::onOutdoorSensorLightIntensityChanged); + connect(outdoorSensor, &HueMotionSensor::reachableChanged, this, &DevicePluginPhilipsHue::onMotionSensorReachableChanged); + connect(outdoorSensor, &HueMotionSensor::batteryLevelChanged, this, &DevicePluginPhilipsHue::onMotionSensorBatteryLevelChanged); + connect(outdoorSensor, &HueMotionSensor::temperatureChanged, this, &DevicePluginPhilipsHue::onMotionSensorTemperatureChanged); + connect(outdoorSensor, &HueMotionSensor::presenceChanged, this, &DevicePluginPhilipsHue::onMotionSensorPresenceChanged); + connect(outdoorSensor, &HueMotionSensor::lightIntensityChanged, this, &DevicePluginPhilipsHue::onMotionSensorLightIntensityChanged); - m_outdoorSensors.insert(outdoorSensor, device); + connect(device, &Device::settingChanged, outdoorSensor, [outdoorSensor](const ParamTypeId ¶mTypeId, const QVariant &value){ + if (paramTypeId == outdoorSensorSettingsTimeoutParamTypeId) { + outdoorSensor->setTimeout(value.toUInt()); + } + }); + + m_motionSensors.insert(outdoorSensor, device); return Device::DeviceSetupStatusSuccess; } @@ -384,8 +423,8 @@ void DevicePluginPhilipsHue::deviceRemoved(Device *device) } if (device->deviceClassId() == outdoorSensorDeviceClassId) { - HueOutdoorSensor *outdoorSensor = m_outdoorSensors.key(device); - m_outdoorSensors.remove(outdoorSensor); + HueMotionSensor *outdoorSensor = m_motionSensors.key(device); + m_motionSensors.remove(outdoorSensor); outdoorSensor->deleteLater(); } } @@ -856,41 +895,41 @@ void DevicePluginPhilipsHue::onRemoteButtonEvent(int buttonCode) emitEvent(Event(id, m_remotes.value(remote)->id(), ParamList() << param)); } -void DevicePluginPhilipsHue::onOutdoorSensorReachableChanged(bool reachable) +void DevicePluginPhilipsHue::onMotionSensorReachableChanged(bool reachable) { - HueOutdoorSensor *sensor = static_cast(sender()); - Device *sensorDevice = m_outdoorSensors.value(sensor); - sensorDevice->setStateValue(outdoorSensorConnectedStateTypeId, reachable); + HueMotionSensor *sensor = static_cast(sender()); + Device *sensorDevice = m_motionSensors.value(sensor); + sensorDevice->setStateValue(sensor->connectedStateTypeId(), reachable); } -void DevicePluginPhilipsHue::onOutdoorSensorBatteryLevelChanged(int batteryLevel) +void DevicePluginPhilipsHue::onMotionSensorBatteryLevelChanged(int batteryLevel) { - HueOutdoorSensor *sensor = static_cast(sender()); - Device *sensorDevice = m_outdoorSensors.value(sensor); - sensorDevice->setStateValue(outdoorSensorBatteryLevelStateTypeId, batteryLevel); - sensorDevice->setStateValue(outdoorSensorBatteryCriticalStateTypeId, (batteryLevel < 5)); + HueMotionSensor *sensor = static_cast(sender()); + Device *sensorDevice = m_motionSensors.value(sensor); + sensorDevice->setStateValue(sensor->batteryLevelStateTypeId(), batteryLevel); + sensorDevice->setStateValue(sensor->batteryCriticalStateTypeId(), (batteryLevel < 5)); } -void DevicePluginPhilipsHue::onOutdoorSensorTemperatureChanged(double temperature) +void DevicePluginPhilipsHue::onMotionSensorTemperatureChanged(double temperature) { - HueOutdoorSensor *sensor = static_cast(sender()); - Device *sensorDevice = m_outdoorSensors.value(sensor); - sensorDevice->setStateValue(outdoorSensorTemperatureStateTypeId, temperature); + HueMotionSensor *sensor = static_cast(sender()); + Device *sensorDevice = m_motionSensors.value(sensor); + sensorDevice->setStateValue(sensor->temperatureStateTypeId(), temperature); } -void DevicePluginPhilipsHue::onOutdoorSensorPresenceChanged(bool presence) +void DevicePluginPhilipsHue::onMotionSensorPresenceChanged(bool presence) { - HueOutdoorSensor *sensor = static_cast(sender()); - Device *sensorDevice = m_outdoorSensors.value(sensor); - sensorDevice->setStateValue(outdoorSensorIsPresentStateTypeId, presence); - if (presence) sensorDevice->setStateValue(outdoorSensorLastSeenTimeStateTypeId, QDateTime::currentDateTime().toTime_t()); + HueMotionSensor *sensor = static_cast(sender()); + Device *sensorDevice = m_motionSensors.value(sensor); + sensorDevice->setStateValue(sensor->isPresentStateTypeId(), presence); + if (presence) sensorDevice->setStateValue(sensor->lastSeenTimeStateTypeId(), QDateTime::currentDateTime().toTime_t()); } -void DevicePluginPhilipsHue::onOutdoorSensorLightIntensityChanged(double lightIntensity) +void DevicePluginPhilipsHue::onMotionSensorLightIntensityChanged(double lightIntensity) { - HueOutdoorSensor *sensor = static_cast(sender()); - Device *sensorDevice = m_outdoorSensors.value(sensor); - sensorDevice->setStateValue(outdoorSensorLightIntensityStateTypeId, lightIntensity); + HueMotionSensor *sensor = static_cast(sender()); + Device *sensorDevice = m_motionSensors.value(sensor); + sensorDevice->setStateValue(sensor->lightIntensityStateTypeId(), lightIntensity); } void DevicePluginPhilipsHue::refreshLight(Device *device) @@ -1103,7 +1142,7 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device // Create sensors if not already added QVariantMap sensorsMap = jsonDoc.toVariant().toMap(); - QHash outdoorSensors; + QHash motionSensors; foreach (const QString &sensorId, sensorsMap.keys()) { QVariantMap sensorMap = sensorsMap.value(sensorId).toMap(); @@ -1132,7 +1171,8 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device descriptor.setParams(params); emit autoDevicesAppeared(tapDeviceClassId, {descriptor}); qCDebug(dcPhilipsHue()) << "Found hue tap:" << sensorMap << tapDeviceClassId; - } else if (model == "SML002") { + + } else if (model == "SML001" || model == "SML002") { // Get the base uuid from this sensor QString baseUuid = HueDevice::getBaseUuid(uuid); @@ -1140,52 +1180,69 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device if (sensorMap.value("type").toString() == "ZLLTemperature") { qCDebug(dcPhilipsHue()) << "Found temperature sensor from OurdoorSensor:" << baseUuid << sensorMap; // Check if we haven outdoor sensor for this temperature sensor - if (outdoorSensors.keys().contains(baseUuid)) { - HueOutdoorSensor *outdoorSensor = outdoorSensors.value(baseUuid); - outdoorSensor->setTemperatureSensorUuid(uuid); - outdoorSensor->setTemperatureSensorId(sensorId.toInt()); + if (motionSensors.contains(baseUuid)) { + HueMotionSensor *motionSensor = motionSensors.value(baseUuid); + motionSensor->setTemperatureSensorUuid(uuid); + motionSensor->setTemperatureSensorId(sensorId.toInt()); } else { // Create an outdoor sensor - HueOutdoorSensor *outdoorSensor = new HueOutdoorSensor(this); - outdoorSensor->setModelId(model); - outdoorSensor->setUuid(baseUuid); - outdoorSensor->setTemperatureSensorUuid(uuid); - outdoorSensor->setTemperatureSensorId(sensorId.toInt()); - outdoorSensors.insert(baseUuid, outdoorSensor); + HueMotionSensor *motionSensor = nullptr; + if (model == "SML001") { + motionSensor = new HueIndoorSensor(this); + } else { + motionSensor = new HueOutdoorSensor(this); + } + motionSensor->setModelId(model); + motionSensor->setUuid(baseUuid); + motionSensor->setTemperatureSensorUuid(uuid); + motionSensor->setTemperatureSensorId(sensorId.toInt()); + motionSensors.insert(baseUuid, motionSensor); } } if (sensorMap.value("type").toString() == "ZLLPresence") { qCDebug(dcPhilipsHue()) << "Found presence sensor from OurdoorSensor:" << baseUuid << sensorMap; // Check if we haven outdoor sensor for this presence sensor - if (outdoorSensors.keys().contains(baseUuid)) { - HueOutdoorSensor *outdoorSensor = outdoorSensors.value(baseUuid); - outdoorSensor->setPresenceSensorUuid(uuid); - outdoorSensor->setPresenceSensorId(sensorId.toInt()); + if (motionSensors.contains(baseUuid)) { + HueMotionSensor *motionSensor = motionSensors.value(baseUuid); + motionSensor->setPresenceSensorUuid(uuid); + motionSensor->setPresenceSensorId(sensorId.toInt()); } else { // Create an outdoor sensor - HueOutdoorSensor *outdoorSensor = new HueOutdoorSensor(this); - outdoorSensor->setUuid(baseUuid); - outdoorSensor->setPresenceSensorUuid(uuid); - outdoorSensor->setPresenceSensorId(sensorId.toInt()); - outdoorSensors.insert(baseUuid, outdoorSensor); + HueMotionSensor *motionSensor = nullptr; + if (model == "SML001") { + motionSensor = new HueIndoorSensor(this); + } else { + motionSensor = new HueOutdoorSensor(this); + } + motionSensor->setModelId(model); + motionSensor->setUuid(baseUuid); + motionSensor->setPresenceSensorUuid(uuid); + motionSensor->setPresenceSensorId(sensorId.toInt()); + motionSensors.insert(baseUuid, motionSensor); } } if (sensorMap.value("type").toString() == "ZLLLightLevel") { qCDebug(dcPhilipsHue()) << "Found light sensor from OurdoorSensor:" << sensorMap; // Check if we haven outdoor sensor for this light sensor - if (outdoorSensors.keys().contains(baseUuid)) { - HueOutdoorSensor *outdoorSensor = outdoorSensors.value(baseUuid); - outdoorSensor->setLightSensorUuid(uuid); - outdoorSensor->setLightSensorId(sensorId.toInt()); + if (motionSensors.contains(baseUuid)) { + HueMotionSensor *motionSensor = motionSensors.value(baseUuid); + motionSensor->setLightSensorUuid(uuid); + motionSensor->setLightSensorId(sensorId.toInt()); } else { // Create an outdoor sensor - HueOutdoorSensor *outdoorSensor = new HueOutdoorSensor(this); - outdoorSensor->setUuid(baseUuid); - outdoorSensor->setLightSensorUuid(uuid); - outdoorSensor->setLightSensorId(sensorId.toInt()); - outdoorSensors.insert(baseUuid, outdoorSensor); + HueMotionSensor *motionSensor = nullptr; + if (model == "SML001") { + motionSensor = new HueIndoorSensor(this); + } else { + motionSensor = new HueOutdoorSensor(this); + } + motionSensor->setModelId(model); + motionSensor->setUuid(baseUuid); + motionSensor->setLightSensorUuid(uuid); + motionSensor->setLightSensorId(sensorId.toInt()); + motionSensors.insert(baseUuid, motionSensor); } } } else { @@ -1194,28 +1251,43 @@ void DevicePluginPhilipsHue::processBridgeSensorDiscoveryResponse(Device *device } // Create outdoor sensors if there are any new sensors found - foreach (HueOutdoorSensor *outdoorSensor, outdoorSensors.values()) { - QString baseUuid = outdoorSensors.key(outdoorSensor); - if (outdoorSensor->isValid()) { - DeviceDescriptor descriptor(outdoorSensorDeviceClassId, "Philips Hue Outdoor sensor", baseUuid, device->id()); - ParamList params; - params.append(Param(outdoorSensorDeviceUuidParamTypeId, outdoorSensor->uuid())); - params.append(Param(outdoorSensorDeviceModelIdParamTypeId, outdoorSensor->modelId())); - params.append(Param(outdoorSensorDeviceSensorUuidTemperatureParamTypeId, outdoorSensor->temperatureSensorUuid())); - params.append(Param(outdoorSensorDeviceSensorIdTemperatureParamTypeId, outdoorSensor->temperatureSensorId())); - params.append(Param(outdoorSensorDeviceSensorUuidPresenceParamTypeId, outdoorSensor->presenceSensorUuid())); - params.append(Param(outdoorSensorDeviceSensorIdPresenceParamTypeId, outdoorSensor->presenceSensorId())); - params.append(Param(outdoorSensorDeviceSensorUuidLightParamTypeId, outdoorSensor->lightSensorUuid())); - params.append(Param(outdoorSensorDeviceSensorIdLightParamTypeId, outdoorSensor->lightSensorId())); - descriptor.setParams(params); - - qCDebug(dcPhilipsHue()) << "Found new Outdoor sensor" << baseUuid << outdoorSensorDeviceClassId; - emit autoDevicesAppeared(outdoorSensorDeviceClassId, {descriptor}); + foreach (HueMotionSensor *motionSensor, motionSensors.values()) { + QString baseUuid = motionSensors.key(motionSensor); + if (motionSensor->isValid()) { + if (motionSensor->modelId() == "SML001") { + DeviceDescriptor descriptor(motionSensorDeviceClassId, tr("Philips Hue Motion sensor"), baseUuid, device->id()); + ParamList params; + params.append(Param(motionSensorDeviceUuidParamTypeId, motionSensor->uuid())); + params.append(Param(motionSensorDeviceModelIdParamTypeId, motionSensor->modelId())); + params.append(Param(motionSensorDeviceSensorUuidTemperatureParamTypeId, motionSensor->temperatureSensorUuid())); + params.append(Param(motionSensorDeviceSensorIdTemperatureParamTypeId, motionSensor->temperatureSensorId())); + params.append(Param(motionSensorDeviceSensorUuidPresenceParamTypeId, motionSensor->presenceSensorUuid())); + params.append(Param(motionSensorDeviceSensorIdPresenceParamTypeId, motionSensor->presenceSensorId())); + params.append(Param(motionSensorDeviceSensorUuidLightParamTypeId, motionSensor->lightSensorUuid())); + params.append(Param(motionSensorDeviceSensorIdLightParamTypeId, motionSensor->lightSensorId())); + descriptor.setParams(params); + qCDebug(dcPhilipsHue()) << "Found new motion sensor" << baseUuid << outdoorSensorDeviceClassId; + emit autoDevicesAppeared(motionSensorDeviceClassId, {descriptor}); + } else if (motionSensor->modelId() == "SML002") { + DeviceDescriptor descriptor(outdoorSensorDeviceClassId, tr("Philips Hue Outdoor sensor"), baseUuid, device->id()); + ParamList params; + params.append(Param(outdoorSensorDeviceUuidParamTypeId, motionSensor->uuid())); + params.append(Param(outdoorSensorDeviceModelIdParamTypeId, motionSensor->modelId())); + params.append(Param(outdoorSensorDeviceSensorUuidTemperatureParamTypeId, motionSensor->temperatureSensorUuid())); + params.append(Param(outdoorSensorDeviceSensorIdTemperatureParamTypeId, motionSensor->temperatureSensorId())); + params.append(Param(outdoorSensorDeviceSensorUuidPresenceParamTypeId, motionSensor->presenceSensorUuid())); + params.append(Param(outdoorSensorDeviceSensorIdPresenceParamTypeId, motionSensor->presenceSensorId())); + params.append(Param(outdoorSensorDeviceSensorUuidLightParamTypeId, motionSensor->lightSensorUuid())); + params.append(Param(outdoorSensorDeviceSensorIdLightParamTypeId, motionSensor->lightSensorId())); + descriptor.setParams(params); + qCDebug(dcPhilipsHue()) << "Found new outdoor sensor" << baseUuid << outdoorSensorDeviceClassId; + emit autoDevicesAppeared(outdoorSensorDeviceClassId, {descriptor}); + } } // Clean up - outdoorSensors.remove(baseUuid); - outdoorSensor->deleteLater(); + motionSensors.remove(baseUuid); + motionSensor->deleteLater(); } } @@ -1353,9 +1425,9 @@ void DevicePluginPhilipsHue::processSensorsRefreshResponse(Device *device, const } // Outdoor sensors - foreach (HueOutdoorSensor *outdoorSensor, m_outdoorSensors.keys()) { - if (outdoorSensor->hasSensor(sensorId.toInt()) && m_outdoorSensors.value(outdoorSensor)->parentId() == device->id()) { - outdoorSensor->updateStates(sensorMap); + foreach (HueMotionSensor *motionSensor, m_motionSensors.keys()) { + if (motionSensor->hasSensor(sensorId.toInt()) && m_motionSensors.value(motionSensor)->parentId() == device->id()) { + motionSensor->updateStates(sensorMap); } } } @@ -1535,12 +1607,10 @@ void DevicePluginPhilipsHue::bridgeReachableChanged(Device *device, const bool & } } - foreach (HueOutdoorSensor *outdoorSensor, m_outdoorSensors.keys()) { - if (m_outdoorSensors.value(outdoorSensor)->parentId() == device->id()) { - outdoorSensor->setReachable(false); - if (m_outdoorSensors.value(outdoorSensor)->deviceClassId() == outdoorSensorDeviceClassId) { - m_outdoorSensors.value(outdoorSensor)->setStateValue(outdoorSensorConnectedStateTypeId, false); - } + foreach (HueMotionSensor *motionSensor, m_motionSensors.keys()) { + if (m_motionSensors.value(motionSensor)->parentId() == device->id()) { + motionSensor->setReachable(false); + m_motionSensors.value(motionSensor)->setStateValue(motionSensor->connectedStateTypeId(), false); } } } @@ -1607,6 +1677,16 @@ bool DevicePluginPhilipsHue::sensorAlreadyAdded(const QString &uuid) return true; } } + // Motion sensor consits out of 3 sensors + if (device->deviceClassId() == motionSensorDeviceClassId) { + if (device->paramValue(motionSensorDeviceSensorUuidLightParamTypeId).toString() == uuid) { + return true; + } else if (device->paramValue(motionSensorDeviceSensorUuidPresenceParamTypeId).toString() == uuid) { + return true; + } else if (device->paramValue(motionSensorDeviceSensorUuidTemperatureParamTypeId).toString() == uuid) { + return true; + } + } } return false; diff --git a/philipshue/devicepluginphilipshue.h b/philipshue/devicepluginphilipshue.h index 5f66921c..5dc275ab 100644 --- a/philipshue/devicepluginphilipshue.h +++ b/philipshue/devicepluginphilipshue.h @@ -29,8 +29,10 @@ #include "huelight.h" #include "hueremote.h" #include "pairinginfo.h" +#include "huemotionsensor.h" +#include "huemotionsensor.h" + #include "plugintimer.h" -#include "hueoutdoorsensor.h" #include "network/networkaccessmanager.h" #include "network/upnp/upnpdiscovery.h" @@ -61,12 +63,12 @@ private slots: void remoteStateChanged(); void onRemoteButtonEvent(int buttonCode); - // Outdoor sensor - void onOutdoorSensorReachableChanged(bool reachable); - void onOutdoorSensorBatteryLevelChanged(int batteryLevel); - void onOutdoorSensorTemperatureChanged(double temperature); - void onOutdoorSensorPresenceChanged(bool presence); - void onOutdoorSensorLightIntensityChanged(double lightIntensity); + // Motion sensor + void onMotionSensorReachableChanged(bool reachable); + void onMotionSensorBatteryLevelChanged(int batteryLevel); + void onMotionSensorTemperatureChanged(double temperature); + void onMotionSensorPresenceChanged(bool presence); + void onMotionSensorLightIntensityChanged(double lightIntensity); private slots: void networkManagerReplyReady(); @@ -105,7 +107,7 @@ private: QHash m_bridges; QHash m_lights; QHash m_remotes; - QHash m_outdoorSensors; + QHash m_motionSensors; void refreshLight(Device *device); void refreshBridge(Device *device); diff --git a/philipshue/devicepluginphilipshue.json b/philipshue/devicepluginphilipshue.json index b5a5d12c..03f9afb0 100644 --- a/philipshue/devicepluginphilipshue.json +++ b/philipshue/devicepluginphilipshue.json @@ -671,6 +671,18 @@ "readOnly": true } ], + "settingsTypes": [ + { + "id": "21d461b2-b4dd-4a70-b3d5-aaedc88605a4", + "name": "timeout", + "displayName": "Time period", + "type": "uint", + "unit": "Seconds", + "defaultValue": 10, + "minValue": 10 + } + + ], "stateTypes": [ { "id": "9fe43e6b-3c29-43a9-bb96-3b80eacc10db", @@ -735,6 +747,147 @@ "defaultValue": 0 } ] + }, + { + "id": "25b79fff-4b88-4af8-b06c-2fe246238790", + "name": "motionSensor", + "displayName": "Hue Motion Sensor", + "interfaces": ["presencesensor", "temperaturesensor", "lightsensor", "batterylevel", "connectable"], + "createMethods": ["auto"], + "paramTypes": [ + { + "id": "9cb488b7-a76f-4389-a6b5-b36250246f2b", + "name": "modelId", + "displayName": "Model id", + "type" : "QString", + "readOnly": true + }, + { + "id": "3e9aad4c-1dff-42c0-bbd4-cdd8635c01a7", + "name": "uuid", + "displayName": "Uuid", + "type" : "QString", + "readOnly": true + }, + { + "id": "c9e81e29-f8d4-4370-ada2-f48b32def1fe", + "name": "sensorIdTemperature", + "displayName": "Temperature sensor id", + "type" : "int", + "readOnly": true + }, + { + "id": "aa29b5f1-5589-4fa9-bbd4-8869723c037c", + "name": "sensorUuidTemperature", + "displayName": "Temperature sensor uuid", + "type" : "QString", + "readOnly": true + }, + { + "id": "337b2c6c-e3bf-495c-943c-b45fa08add37", + "name": "sensorIdPresence", + "displayName": "Presence sensor id", + "type" : "int", + "readOnly": true + }, + { + "id": "3829bddb-e722-4724-be36-3a8402738581", + "name": "sensorUuidPresence", + "displayName": "Presence sensor uuid", + "type" : "QString", + "readOnly": true + }, + { + "id": "04fba73e-730e-437a-b6f2-10df21296af5", + "name": "sensorIdLight", + "displayName": "Light sensor id", + "type" : "int", + "readOnly": true + }, + { + "id": "171cc2e7-7a95-4116-986c-66d75e3e23eb", + "name": "sensorUuidLight", + "displayName": "Light sensor uuid", + "type" : "QString", + "readOnly": true + } + ], + "settingsTypes": [ + { + "id": "beedc4af-c107-4c53-be25-fd01a349fd35", + "name": "timeout", + "displayName": "Time period", + "type": "uint", + "unit": "Seconds", + "defaultValue": 10, + "minValue": 10 + } + + ], + "stateTypes": [ + { + "id": "19c28b69-a9c2-4908-8255-7681f72c2d92", + "name": "connected", + "displayName": "Reachable", + "displayNameEvent": "Reachable changed", + "defaultValue": false, + "type": "bool" + }, + { + "id": "ac463b30-24af-4352-84da-19a3ffc906bd", + "name": "batteryLevel", + "displayName": "Battery", + "displayNameEvent": "Battery changed", + "type": "int", + "unit": "Percentage", + "defaultValue": 0, + "minValue": 0, + "maxValue": 100 + }, + { + "id": "d7c4e143-6f03-411e-a12e-dd22806270fd", + "name": "batteryCritical", + "displayName": "Battery critical", + "displayNameEvent": "Battery critical changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "63ee79f7-702b-48c1-86cf-8ddebb78bae6", + "name": "temperature", + "displayName": "Temperature", + "displayNameEvent": "Temperature changed", + "unit": "DegreeCelsius", + "type": "double", + "defaultValue": 0 + }, + { + "id": "064f48c1-f86d-4a0a-bdae-3420123dff3f", + "name": "lightIntensity", + "displayName": "Ambient light", + "displayNameEvent": "Ambient light changed", + "unit": "Lux", + "type": "double", + "defaultValue": 0 + }, + { + "id": "e38ee39c-c77f-40b5-b122-4efc411da0ed", + "name": "isPresent", + "displayName": "Person is present", + "displayNameEvent": "Person is present changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "ef2e564e-2443-448f-bcd9-f85a1126ee6a", + "name": "lastSeenTime", + "displayName": "Last seen time", + "displayNameEvent": "Last seen time changed", + "type": "int", + "unit": "UnixTime", + "defaultValue": 0 + } + ] } ] } diff --git a/philipshue/huedevice.h b/philipshue/huedevice.h index 8da8a80e..4251dcfb 100644 --- a/philipshue/huedevice.h +++ b/philipshue/huedevice.h @@ -33,7 +33,7 @@ class HueDevice : public QObject { Q_OBJECT public: - explicit HueDevice(QObject *parent = 0); + explicit HueDevice(QObject *parent = nullptr); int id() const; void setId(const int &id); diff --git a/philipshue/hueoutdoorsensor.cpp b/philipshue/huemotionsensor.cpp similarity index 67% rename from philipshue/hueoutdoorsensor.cpp rename to philipshue/huemotionsensor.cpp index 05c3879a..3413e0a5 100644 --- a/philipshue/hueoutdoorsensor.cpp +++ b/philipshue/huemotionsensor.cpp @@ -20,106 +20,109 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#include "hueoutdoorsensor.h" +#include "huemotionsensor.h" #include "extern-plugininfo.h" -HueOutdoorSensor::HueOutdoorSensor(QObject *parent) : +HueMotionSensor::HueMotionSensor(QObject *parent) : HueDevice(parent) { - + m_timeout.setInterval(10000); + connect(&m_timeout, &QTimer::timeout, this, [this](){ + if (m_presence) { + qCDebug(dcPhilipsHue) << "Motion sensor timeout reached" << m_timeout.interval(); + m_presence = false; + emit presenceChanged(m_presence); + } + }); } -QString HueOutdoorSensor::uuid() const +void HueMotionSensor::setTimeout(int timeout) { - return m_uuid; + // The sensor keeps emitting presence = true for 10 secs, let's subtract that time from the timeout to compensate + m_timeout.setInterval((timeout - 9)* 1000); } -void HueOutdoorSensor::setUuid(const QString &uuid) -{ - m_uuid = uuid; -} - -int HueOutdoorSensor::temperatureSensorId() const +int HueMotionSensor::temperatureSensorId() const { return m_temperatureSensorId; } -void HueOutdoorSensor::setTemperatureSensorId(int sensorId) +void HueMotionSensor::setTemperatureSensorId(int sensorId) { m_temperatureSensorId = sensorId; } -QString HueOutdoorSensor::temperatureSensorUuid() const +QString HueMotionSensor::temperatureSensorUuid() const { return m_temperatureSensorUuid; } -void HueOutdoorSensor::setTemperatureSensorUuid(const QString &temperatureSensorUuid) +void HueMotionSensor::setTemperatureSensorUuid(const QString &temperatureSensorUuid) { m_temperatureSensorUuid = temperatureSensorUuid; } -int HueOutdoorSensor::presenceSensorId() const +int HueMotionSensor::presenceSensorId() const { return m_presenceSensorId; } -void HueOutdoorSensor::setPresenceSensorId(int sensorId) +void HueMotionSensor::setPresenceSensorId(int sensorId) { m_presenceSensorId = sensorId; } -QString HueOutdoorSensor::presenceSensorUuid() const +QString HueMotionSensor::presenceSensorUuid() const { return m_presenceSensorUuid; } -void HueOutdoorSensor::setPresenceSensorUuid(const QString &presenceSensorUuid) +void HueMotionSensor::setPresenceSensorUuid(const QString &presenceSensorUuid) { m_presenceSensorUuid = presenceSensorUuid; } -int HueOutdoorSensor::lightSensorId() const +int HueMotionSensor::lightSensorId() const { return m_lightSensorId; } -void HueOutdoorSensor::setLightSensorId(int sensorId) +void HueMotionSensor::setLightSensorId(int sensorId) { m_lightSensorId = sensorId; } -QString HueOutdoorSensor::lightSensorUuid() const +QString HueMotionSensor::lightSensorUuid() const { return m_lightSensorUuid; } -void HueOutdoorSensor::setLightSensorUuid(const QString &lightSensorUuid) +void HueMotionSensor::setLightSensorUuid(const QString &lightSensorUuid) { m_lightSensorUuid = lightSensorUuid; } -double HueOutdoorSensor::temperature() const +double HueMotionSensor::temperature() const { return m_temperature; } -double HueOutdoorSensor::lightIntensity() const +double HueMotionSensor::lightIntensity() const { return m_lightIntensity; } -bool HueOutdoorSensor::present() const +bool HueMotionSensor::present() const { return m_presence; } -int HueOutdoorSensor::batteryLevel() const +int HueMotionSensor::batteryLevel() const { return m_batteryLevel; } -void HueOutdoorSensor::updateStates(const QVariantMap &sensorMap) +void HueMotionSensor::updateStates(const QVariantMap &sensorMap) { //qCDebug(dcPhilipsHue()) << "Outdoor sensor: Process sensor map" << qUtf8Printable(QJsonDocument::fromVariant(sensorMap).toJson(QJsonDocument::Indented)); @@ -144,17 +147,21 @@ void HueOutdoorSensor::updateStates(const QVariantMap &sensorMap) if (m_temperature != temperature) { m_temperature = temperature; emit temperatureChanged(m_temperature); - qCDebug(dcPhilipsHue) << "Outdoor sensor temperature changed" << m_temperature; + qCDebug(dcPhilipsHue) << "Motion sensor temperature changed" << m_temperature; } } // If presence sensor if (sensorMap.value("uniqueid").toString() == m_presenceSensorUuid) { bool presence = stateMap.value("presence", false).toBool(); - if (m_presence != presence) { - m_presence = presence; - emit presenceChanged(m_presence); - qCDebug(dcPhilipsHue) << "Outdoor sensor presence changed" << presence; + if (presence) { + if (!m_presence) { + m_presence = true; + emit presenceChanged(m_presence); + qCDebug(dcPhilipsHue) << "Motion sensor presence changed" << presence; + } + qCDebug(dcPhilipsHue) << "Motion sensor restarting timeout" << m_timeout.interval(); + m_timeout.start(); } } @@ -169,17 +176,17 @@ void HueOutdoorSensor::updateStates(const QVariantMap &sensorMap) } } -bool HueOutdoorSensor::isValid() +bool HueMotionSensor::isValid() { return !m_temperatureSensorUuid.isEmpty() && !m_presenceSensorUuid.isEmpty() && !m_lightSensorUuid.isEmpty(); } -bool HueOutdoorSensor::hasSensor(int sensorId) +bool HueMotionSensor::hasSensor(int sensorId) { return m_temperatureSensorId == sensorId || m_presenceSensorId == sensorId || m_lightSensorId == sensorId; } -bool HueOutdoorSensor::hasSensor(const QString &sensorUuid) +bool HueMotionSensor::hasSensor(const QString &sensorUuid) { return m_temperatureSensorUuid == sensorUuid || m_presenceSensorUuid == sensorUuid || m_lightSensorUuid == sensorUuid; } diff --git a/philipshue/hueoutdoorsensor.h b/philipshue/huemotionsensor.h similarity index 55% rename from philipshue/hueoutdoorsensor.h rename to philipshue/huemotionsensor.h index d5e0b919..e75f3cf0 100644 --- a/philipshue/hueoutdoorsensor.h +++ b/philipshue/huemotionsensor.h @@ -20,21 +20,23 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef HUEOUTDOORSENSOR_H -#define HUEOUTDOORSENSOR_H +#ifndef HUEMOTIONSENSOR_H +#define HUEMOTIONSENSOR_H #include +#include +#include "extern-plugininfo.h" #include "huedevice.h" -class HueOutdoorSensor : public HueDevice +class HueMotionSensor : public HueDevice { Q_OBJECT public: - explicit HueOutdoorSensor(QObject *parent = nullptr); + explicit HueMotionSensor(QObject *parent = nullptr); + virtual ~HueMotionSensor() = default; - QString uuid() const; - void setUuid(const QString &uuid); + void setTimeout(int timeout); int temperatureSensorId() const; void setTemperatureSensorId(int sensorId); @@ -65,10 +67,16 @@ public: bool hasSensor(int sensorId); bool hasSensor(const QString &sensorUuid); + virtual StateTypeId connectedStateTypeId() const = 0; + virtual StateTypeId temperatureStateTypeId() const = 0; + virtual StateTypeId lightIntensityStateTypeId() const = 0; + virtual StateTypeId isPresentStateTypeId() const = 0; + virtual StateTypeId lastSeenTimeStateTypeId() const = 0; + virtual StateTypeId batteryLevelStateTypeId() const = 0; + virtual StateTypeId batteryCriticalStateTypeId() const = 0; + private: // Params - QString m_uuid; - int m_temperatureSensorId; QString m_temperatureSensorUuid; @@ -78,6 +86,8 @@ private: int m_lightSensorId; QString m_lightSensorUuid; + QTimer m_timeout; + // States QString m_lastUpdate; double m_temperature = 0; @@ -93,4 +103,36 @@ signals: }; -#endif // HUEOUTDOORSENSOR_H +class HueIndoorSensor: public HueMotionSensor +{ + Q_OBJECT +public: + HueIndoorSensor(QObject *parent = nullptr): HueMotionSensor(parent) {} + + StateTypeId connectedStateTypeId() const override { return motionSensorConnectedStateTypeId; } + StateTypeId temperatureStateTypeId() const override { return motionSensorTemperatureStateTypeId; } + StateTypeId lightIntensityStateTypeId() const override { return motionSensorLightIntensityStateTypeId; } + StateTypeId isPresentStateTypeId() const override { return motionSensorIsPresentStateTypeId; } + StateTypeId lastSeenTimeStateTypeId() const override { return motionSensorLastSeenTimeStateTypeId; } + StateTypeId batteryLevelStateTypeId() const override { return motionSensorBatteryLevelStateTypeId; } + StateTypeId batteryCriticalStateTypeId() const override { return motionSensorBatteryCriticalStateTypeId; } + +}; + +class HueOutdoorSensor: public HueMotionSensor +{ + Q_OBJECT +public: + HueOutdoorSensor(QObject *parent = nullptr): HueMotionSensor(parent) {} + + StateTypeId connectedStateTypeId() const override { return outdoorSensorTemperatureStateTypeId; } + StateTypeId temperatureStateTypeId() const override { return outdoorSensorTemperatureStateTypeId; } + StateTypeId lightIntensityStateTypeId() const override { return outdoorSensorLightIntensityStateTypeId; } + StateTypeId isPresentStateTypeId() const override { return outdoorSensorIsPresentStateTypeId; } + StateTypeId lastSeenTimeStateTypeId() const override { return outdoorSensorLastSeenTimeStateTypeId; } + StateTypeId batteryLevelStateTypeId() const override { return outdoorSensorBatteryLevelStateTypeId; } + StateTypeId batteryCriticalStateTypeId() const override { return outdoorSensorBatteryCriticalStateTypeId; } + +}; + +#endif // HUEMOTIONSENSOR_H diff --git a/philipshue/philipshue.pro b/philipshue/philipshue.pro index 63e5a1cc..d666a866 100644 --- a/philipshue/philipshue.pro +++ b/philipshue/philipshue.pro @@ -10,10 +10,10 @@ SOURCES += \ #light.cpp \ huebridge.cpp \ huelight.cpp \ + huemotionsensor.cpp \ pairinginfo.cpp \ hueremote.cpp \ - huedevice.cpp \ - hueoutdoorsensor.cpp + huedevice.cpp HEADERS += \ devicepluginphilipshue.h \ @@ -22,10 +22,10 @@ HEADERS += \ #lightinterface.h \ huebridge.h \ huelight.h \ + huemotionsensor.h \ pairinginfo.h \ hueremote.h \ - huedevice.h \ - hueoutdoorsensor.h + huedevice.h