// SPDX-License-Identifier: GPL-3.0-or-later

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright (C) 2013 - 2024, nymea GmbH
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
*
* This file is part of nymea-plugins-genericthings.
*
* nymea-plugins-genericthings is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nymea-plugins-genericthings is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with nymea-plugins-genericthings. If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "integrationplugingenericsensors.h"
#include "plugininfo.h"

#include <QDebug>
#include <QtMath>

IntegrationPluginGenericSensors::IntegrationPluginGenericSensors()
{

}

void IntegrationPluginGenericSensors::setupThing(ThingSetupInfo *info)
{
    Thing *thing = info->thing();

    if (thing->thingClassId() == temperatureSensorThingClassId) {
        connect(thing, &Thing::settingChanged, thing, [thing](const ParamTypeId &settingTypeId, const QVariant &value){
            if (settingTypeId == temperatureSensorSettingsMinTempParamTypeId) {
                thing->setStateMinValue(temperatureSensorTemperatureStateTypeId, value);
            }
            if (settingTypeId == temperatureSensorSettingsMaxTempParamTypeId) {
                thing->setStateMaxValue(temperatureSensorTemperatureStateTypeId, value);
            }
        });
    }
    if (thing->thingClassId() == orpSensorThingClassId) {
        connect(thing, &Thing::settingChanged, thing, [thing](const ParamTypeId &settingTypeId, const QVariant &value){
            if (settingTypeId == orpSensorSettingsMinORPParamTypeId) {
                thing->setStateMinValue(orpSensorOrpStateTypeId, value);
            }
            if (settingTypeId == orpSensorSettingsMaxORPParamTypeId) {
                thing->setStateMaxValue(orpSensorOrpStateTypeId, value);
            }
        });
    }
    if (thing->thingClassId() == pressureSensorThingClassId) {
        connect(thing, &Thing::settingChanged, thing, [thing](const ParamTypeId &settingTypeId, const QVariant &value){
            if (settingTypeId == pressureSensorSettingsMinPressureParamTypeId) {
                thing->setStateMinValue(pressureSensorPressureStateTypeId, value);
            }
            if (settingTypeId == pressureSensorSettingsMaxPressureParamTypeId) {
                thing->setStateMaxValue(pressureSensorPressureStateTypeId, value);
            }
        });
    }
    if (thing->thingClassId() == coSensorThingClassId) {
        connect(thing, &Thing::settingChanged, thing, [thing](const ParamTypeId &settingTypeId, const QVariant &value){
            if (settingTypeId == coSensorSettingsMinCOParamTypeId) {
                thing->setStateMinValue(coSensorCoStateTypeId, value);
            }
            if (settingTypeId == coSensorSettingsMaxCOParamTypeId) {
                thing->setStateMaxValue(coSensorCoStateTypeId, value);
            }
        });
    }
    if (thing->thingClassId() == co2SensorThingClassId) {
        connect(thing, &Thing::settingChanged, thing, [thing](const ParamTypeId &settingTypeId, const QVariant &value){
            if (settingTypeId == co2SensorSettingsMinCO2ParamTypeId) {
                thing->setStateMinValue(co2SensorCo2StateTypeId, value);
            }
            if (settingTypeId == co2SensorSettingsMaxCO2ParamTypeId) {
                thing->setStateMaxValue(co2SensorCo2StateTypeId, value);
            }
        });
    }
    if (thing->thingClassId() == gasSensorThingClassId) {
        connect(thing, &Thing::settingChanged, thing, [thing](const ParamTypeId &settingTypeId, const QVariant &value){
            if (settingTypeId == gasSensorSettingsMinValueParamTypeId) {
                thing->setStateMinValue(gasSensorGasLevelStateTypeId, value);
            }
            if (settingTypeId == gasSensorSettingsMaxValueParamTypeId) {
                thing->setStateMaxValue(gasSensorGasLevelStateTypeId, value);
            }
        });
    }
    if (thing->thingClassId() == lightSensorThingClassId) {
        connect(thing, &Thing::settingChanged, thing, [thing](const ParamTypeId &settingTypeId, const QVariant &value){
            if (settingTypeId == lightSensorSettingsMinLuxParamTypeId) {
                thing->setStateMinValue(lightSensorLightIntensityStateTypeId, value);
            }
            if (settingTypeId == lightSensorSettingsMaxLuxParamTypeId) {
                thing->setStateMaxValue(lightSensorLightIntensityStateTypeId, value);
            }
        });
    }
    if (thing->thingClassId() == waterLevelSensorThingClassId) {
        connect(thing, &Thing::settingChanged, thing, [ thing](const ParamTypeId &settingTypeId, const QVariant &value){
            if (settingTypeId == waterLevelSensorSettingsCapacityParamTypeId) {
                double capacity = value.toDouble();
                double input = thing->stateValue(waterLevelSensorInputStateTypeId).toDouble();
                double minInputValue = thing->setting(waterLevelSensorSettingsMinInputValueParamTypeId).toDouble();
                double maxInputValue = thing->setting(waterLevelSensorSettingsMaxInputValueParamTypeId).toDouble();
                double normalizedInput = (input - minInputValue) / (maxInputValue - minInputValue);
                double waterLevel = normalizedInput * capacity;
                thing->setStateMaxValue(waterLevelSensorWaterLevelStateTypeId, capacity);
                thing->setStateValue(waterLevelSensorWaterLevelStateTypeId, waterLevel);
            }
        });
    }

    info->finish(Thing::ThingErrorNoError);
}

void IntegrationPluginGenericSensors::executeAction(ThingActionInfo *info)
{
    Thing *thing = info->thing();
    Action action = info->action();

    if (thing->thingClassId() == temperatureSensorThingClassId) {
        if (action.actionTypeId() == temperatureSensorTemperatureActionTypeId) {
            double value = info->action().param(temperatureSensorTemperatureActionTemperatureParamTypeId).value().toDouble();
            thing->setStateValue(temperatureSensorTemperatureStateTypeId, value);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else if (action.actionTypeId() == temperatureSensorInputActionTypeId) {
            double value = info->action().param(temperatureSensorInputActionInputParamTypeId).value().toDouble();
            thing->setStateValue(temperatureSensorInputStateTypeId, value);
            double inputMin = info->thing()->setting(temperatureSensorSettingsMinInputParamTypeId).toDouble();
            double inputMax = info->thing()->setting(temperatureSensorSettingsMaxInputParamTypeId).toDouble();
            double tempMin = info->thing()->setting(temperatureSensorSettingsMinTempParamTypeId).toDouble();
            double tempMax = info->thing()->setting(temperatureSensorSettingsMaxTempParamTypeId).toDouble();
            double newValue = mapDoubleValue(value, inputMin, inputMax, tempMin, tempMax);
            double roundingFactor = qPow(10, info->thing()->setting(temperatureSensorSettingsAccuracyParamTypeId).toInt());
            newValue = qRound(newValue * roundingFactor) / roundingFactor;
            thing->setStateValue(temperatureSensorTemperatureStateTypeId, newValue);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else {
            Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
        }
    } else if (thing->thingClassId() == humiditySensorThingClassId) {
        if (action.actionTypeId() == humiditySensorHumidityActionTypeId) {
            double value = info->action().param(humiditySensorHumidityActionHumidityParamTypeId).value().toDouble();
            thing->setStateValue(humiditySensorHumidityStateTypeId, value);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else if (action.actionTypeId() == humiditySensorInputActionTypeId) {
            double value = info->action().param(humiditySensorInputActionInputParamTypeId).value().toDouble();
            thing->setStateValue(humiditySensorInputStateTypeId, value);
            double inputMin = info->thing()->setting(humiditySensorSettingsMinInputParamTypeId).toDouble();
            double inputMax = info->thing()->setting(humiditySensorSettingsMaxInputParamTypeId).toDouble();
            double newValue = mapDoubleValue(value, inputMin, inputMax, 0, 100);
            double roundingFactor = qPow(10, info->thing()->setting(humiditySensorSettingsAccuracyParamTypeId).toInt());
            newValue = qRound(newValue * roundingFactor) / roundingFactor;
            thing->setStateValue(humiditySensorHumidityStateTypeId, newValue);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else {
            Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
        }
    } else if (thing->thingClassId() == moistureSensorThingClassId) {
        if (action.actionTypeId() == moistureSensorMoistureActionTypeId) {
            double value = info->action().param(moistureSensorInputActionInputParamTypeId).value().toDouble();
            thing->setStateValue(moistureSensorMoistureStateTypeId, value);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else if (action.actionTypeId() == moistureSensorInputActionTypeId) {
            double value = info->action().param(moistureSensorInputActionInputParamTypeId).value().toDouble();
            thing->setStateValue(moistureSensorInputStateTypeId, value);
            double inputMin = info->thing()->setting(moistureSensorSettingsMinInputParamTypeId).toDouble();
            double inputMax = info->thing()->setting(moistureSensorSettingsMaxInputParamTypeId).toDouble();
            double newValue = mapDoubleValue(value, inputMin, inputMax, 0, 100);
            double roundingFactor = qPow(10, info->thing()->setting(moistureSensorSettingsAccuracyParamTypeId).toInt());
            newValue = qRound(newValue * roundingFactor) / roundingFactor;
            thing->setStateValue(moistureSensorMoistureStateTypeId, newValue);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else {
            Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
        }
    } else if (thing->thingClassId() == phSensorThingClassId) {
        if (action.actionTypeId() == phSensorPhActionTypeId) {
            double value = info->action().paramValue(phSensorInputActionInputParamTypeId).toDouble();
            thing->setStateValue(phSensorPhStateTypeId, value);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else if (action.actionTypeId() == phSensorInputActionTypeId) {
            double value = info->action().paramValue(phSensorInputActionInputParamTypeId).toDouble();
            thing->setStateValue(phSensorInputStateTypeId, value);
            double min = info->thing()->setting(phSensorSettingsMinInputParamTypeId).toDouble();
            double max = info->thing()->setting(phSensorSettingsMaxInputParamTypeId).toDouble();
            double newValue = mapDoubleValue(value, min, max, 0, 14);
            double roundingFactor = qPow(10, info->thing()->setting(phSensorSettingsAccuracyParamTypeId).toInt());
            newValue = qRound(newValue * roundingFactor) / roundingFactor;
            thing->setStateValue(phSensorPhStateTypeId, newValue);
            info->finish(Thing::ThingErrorNoError);
            return;
        }

    } else if (thing->thingClassId() == orpSensorThingClassId) {
        if (action.actionTypeId() == orpSensorOrpActionTypeId) {
            double value = info->action().paramValue(orpSensorInputActionInputParamTypeId).toDouble();
            thing->setStateValue(orpSensorOrpStateTypeId, value);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else if (action.actionTypeId() == orpSensorInputActionTypeId) {
            double value = info->action().paramValue(orpSensorInputActionInputParamTypeId).toDouble();
            thing->setStateValue(orpSensorInputStateTypeId, value);
            double minInput = info->thing()->setting(orpSensorSettingsMinInputParamTypeId).toDouble();
            double maxInput = info->thing()->setting(orpSensorSettingsMaxInputParamTypeId).toDouble();
            double minOrp = info->thing()->setting(orpSensorSettingsMinORPParamTypeId).toDouble();
            double maxOrp = info->thing()->setting(orpSensorSettingsMaxORPParamTypeId).toDouble();
            double newValue = mapDoubleValue(value, minInput, maxInput, minOrp, maxOrp);
            double roundingFactor = qPow(10, info->thing()->setting(orpSensorSettingsAccuracyParamTypeId).toInt());
            newValue = qRound(newValue * roundingFactor) / roundingFactor;
            thing->setStateValue(orpSensorOrpStateTypeId, newValue);
            info->finish(Thing::ThingErrorNoError);
            return;
        }
    } else if (thing->thingClassId() == o2SensorThingClassId) {
        if (action.actionTypeId() == o2SensorO2saturationActionTypeId) {
            double value = info->action().paramValue(o2SensorO2saturationActionO2saturationParamTypeId).toDouble();
            thing->setStateValue(o2SensorO2saturationStateTypeId, value);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else if (action.actionTypeId() == o2SensorInputActionTypeId) {
            double value = info->action().paramValue(o2SensorInputActionInputParamTypeId).toDouble();
            thing->setStateValue(o2SensorInputStateTypeId, value);
            double minInput = info->thing()->setting(o2SensorSettingsMinInputParamTypeId).toDouble();
            double maxInput = info->thing()->setting(o2SensorSettingsMaxInputParamTypeId).toDouble();
            double newValue = mapDoubleValue(value, minInput, maxInput, 0, 100);
            double roundingFactor = qPow(10, info->thing()->setting(o2SensorSettingsAccuracyParamTypeId).toInt());
            newValue = qRound(newValue * roundingFactor) / roundingFactor;
            thing->setStateValue(o2SensorO2saturationStateTypeId, newValue);
            info->finish(Thing::ThingErrorNoError);
            return;
        }
    } else if (thing->thingClassId() == pressureSensorThingClassId) {
        if (action.actionTypeId() == pressureSensorPressureActionTypeId) {
            double value = info->action().param(pressureSensorPressureActionPressureParamTypeId).value().toDouble();
            thing->setStateValue(pressureSensorPressureStateTypeId, value);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else if (action.actionTypeId() == pressureSensorInputActionTypeId) {
            double value = info->action().param(pressureSensorInputActionInputParamTypeId).value().toDouble();
            thing->setStateValue(pressureSensorInputStateTypeId, value);
            double minInput = info->thing()->setting(pressureSensorSettingsMinInputParamTypeId).toDouble();
            double maxInput = info->thing()->setting(pressureSensorSettingsMaxInputParamTypeId).toDouble();
            double minPressure = info->thing()->setting(pressureSensorSettingsMinPressureParamTypeId).toDouble();
            double maxPressure = info->thing()->setting(pressureSensorSettingsMaxPressureParamTypeId).toDouble();
            double newValue = mapDoubleValue(value, minInput, maxInput, minPressure, maxPressure);
            double roundingFactor = qPow(10, info->thing()->setting(pressureSensorSettingsAccuracyParamTypeId).toInt());
            newValue = qRound(newValue * roundingFactor) / roundingFactor;
            thing->setStateValue(pressureSensorPressureStateTypeId, newValue);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else {
            Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
        }
    } else if (thing->thingClassId() == coSensorThingClassId) {
        if (action.actionTypeId() == coSensorCoActionTypeId) {
            double value = info->action().param(coSensorCoActionCoParamTypeId).value().toDouble();
            thing->setStateValue(coSensorCoStateTypeId, value);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else if (action.actionTypeId() == coSensorInputActionTypeId) {
            double value = info->action().param(coSensorInputActionInputParamTypeId).value().toDouble();
            thing->setStateValue(coSensorInputStateTypeId, value);
            double minInput = info->thing()->setting(coSensorSettingsMinInputParamTypeId).toDouble();
            double maxInput = info->thing()->setting(coSensorSettingsMaxInputParamTypeId).toDouble();
            double minCo = info->thing()->setting(coSensorSettingsMinCOParamTypeId).toDouble();
            double maxCo = info->thing()->setting(coSensorSettingsMaxCOParamTypeId).toDouble();
            double newValue = mapDoubleValue(value, minInput, maxInput, minCo, maxCo);
            double roundingFactor = qPow(10, info->thing()->setting(coSensorSettingsAccuracyParamTypeId).toInt());
            newValue = qRound(newValue * roundingFactor) / roundingFactor;
            thing->setStateValue(coSensorCoStateTypeId, newValue);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else {
            Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
        }
    } else if (thing->thingClassId() == co2SensorThingClassId) {
        if (action.actionTypeId() == co2SensorCo2ActionTypeId) {
            double value = info->action().param(co2SensorCo2ActionCo2ParamTypeId).value().toDouble();
            thing->setStateValue(co2SensorCo2StateTypeId, value);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else if (action.actionTypeId() == co2SensorInputActionTypeId) {
            double value = info->action().param(co2SensorInputActionInputParamTypeId).value().toDouble();
            thing->setStateValue(co2SensorInputStateTypeId, value);
            double minInput = info->thing()->setting(co2SensorSettingsMinInputParamTypeId).toDouble();
            double maxInput = info->thing()->setting(co2SensorSettingsMaxInputParamTypeId).toDouble();
            double minCo2 = info->thing()->setting(co2SensorSettingsMinCO2ParamTypeId).toDouble();
            double maxCo2 = info->thing()->setting(co2SensorSettingsMaxCO2ParamTypeId).toDouble();
            double newValue = mapDoubleValue(value, minInput, maxInput, minCo2, maxCo2);
            double roundingFactor = qPow(10, info->thing()->setting(co2SensorSettingsAccuracyParamTypeId).toInt());
            newValue = qRound(newValue * roundingFactor) / roundingFactor;
            thing->setStateValue(co2SensorCo2StateTypeId, newValue);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else {
            Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
        }
    } else if (thing->thingClassId() == gasSensorThingClassId) {
        if (action.actionTypeId() == gasSensorGasLevelActionTypeId) {
            double value = info->action().param(gasSensorGasLevelActionGasLevelParamTypeId).value().toDouble();
            thing->setStateValue(gasSensorGasLevelStateTypeId, value);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else if (action.actionTypeId() == gasSensorInputActionTypeId) {
            double value = info->action().param(gasSensorInputActionInputParamTypeId).value().toDouble();
            thing->setStateValue(gasSensorInputStateTypeId, value);
            double minInput = info->thing()->setting(gasSensorSettingsMinInputParamTypeId).toDouble();
            double maxInput = info->thing()->setting(gasSensorSettingsMaxInputParamTypeId).toDouble();
            double minValue = info->thing()->setting(gasSensorSettingsMinValueParamTypeId).toDouble();
            double maxValue = info->thing()->setting(gasSensorSettingsMaxValueParamTypeId).toDouble();
            double newValue = mapDoubleValue(value, minInput, maxInput, minValue, maxValue);
            double roundingFactor = qPow(10, info->thing()->setting(gasSensorSettingsAccuracyParamTypeId).toInt());
            newValue = qRound(newValue * roundingFactor) / roundingFactor;
            thing->setStateValue(gasSensorGasLevelStateTypeId, newValue);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else {
            Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
        }
    } else if (thing->thingClassId() == lightSensorThingClassId) {
        if (action.actionTypeId() == lightSensorLightIntensityStateTypeId) {
            double value = info->action().param(lightSensorLightIntensityStateTypeId).value().toDouble();
            thing->setStateValue(lightSensorLightIntensityStateTypeId, value);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else if (action.actionTypeId() == lightSensorInputActionTypeId) {
            double value = info->action().param(lightSensorInputActionInputParamTypeId).value().toDouble();
            thing->setStateValue(lightSensorInputStateTypeId, value);
            double minInput = info->thing()->setting(lightSensorSettingsMinInputParamTypeId).toDouble();
            double maxInput = info->thing()->setting(lightSensorSettingsMaxInputParamTypeId).toDouble();
            double minValue = info->thing()->setting(lightSensorSettingsMinLuxParamTypeId).toDouble();
            double maxValue = info->thing()->setting(lightSensorSettingsMaxLuxParamTypeId).toDouble();
            double newValue = mapDoubleValue(value, minInput, maxInput, minValue, maxValue);
            double roundingFactor = qPow(10, info->thing()->setting(lightSensorSettingsAccuracyParamTypeId).toInt());
            newValue = qRound(newValue * roundingFactor) / roundingFactor;
            thing->setStateValue(lightSensorLightIntensityStateTypeId, newValue);
            info->finish(Thing::ThingErrorNoError);
            return;
        } else {
            Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
        }
    } else if (thing->thingClassId() == doorSensorThingClassId) {
        if (action.actionTypeId() == doorSensorClosedActionTypeId) {
            bool closed = action.paramValue(doorSensorClosedActionClosedParamTypeId).toBool();
            qCDebug(dcGenericSensors()) << "Door sensor is now" << (closed ? "open" : "closed");
            thing->setStateValue(doorSensorClosedStateTypeId, closed);
            info->finish(Thing::ThingErrorNoError);
            return;
        }
    } else if (thing->thingClassId() == waterSensorThingClassId) {
        if (action.actionTypeId() == waterSensorWaterDetectedActionTypeId) {
            bool waterDetected = action.paramValue(waterSensorWaterDetectedActionWaterDetectedParamTypeId).toBool();
            qCDebug(dcGenericSensors()).nospace() << "Water sensor is " << (waterDetected ? "" : "not") << "detecting water";
            thing->setStateValue(waterSensorWaterDetectedStateTypeId, waterDetected);
            info->finish(Thing::ThingErrorNoError);
            return;
        }
    } else if (thing->thingClassId() == waterLevelSensorThingClassId) {
        if (action.actionTypeId() == waterLevelSensorInputActionTypeId) {
            double capacity = thing->setting(waterLevelSensorSettingsCapacityParamTypeId).toDouble();
            double input = action.paramValue(waterLevelSensorInputActionInputParamTypeId).toDouble();
            double minInputValue = thing->setting(waterLevelSensorSettingsMinInputValueParamTypeId).toDouble();
            double maxInputValue = thing->setting(waterLevelSensorSettingsMaxInputValueParamTypeId).toDouble();
            double normalizedInput = (input - minInputValue) / (maxInputValue - minInputValue);
            double waterLevel = normalizedInput * capacity;
            thing->setStateValue(waterLevelSensorWaterLevelStateTypeId, waterLevel);
            info->finish(Thing::ThingErrorNoError);
            return;
        }
    } else if (thing->thingClassId() == presenceSensorThingClassId) {
        if (action.actionTypeId() == presenceSensorIsPresentActionTypeId) {
            bool isPresent = action.paramValue(presenceSensorIsPresentActionIsPresentParamTypeId).toBool();
            qCDebug(dcGenericSensors()) << "Presence sensor is now" << (isPresent ? "present" : "not present");
            thing->setStateValue(presenceSensorIsPresentStateTypeId, isPresent);
            if (isPresent) {
                thing->setStateValue(presenceSensorLastSeenTimeStateTypeId, QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000);
            }
            info->finish(Thing::ThingErrorNoError);
            return;
        }
    } else if (thing->thingClassId() == fireSensorThingClassId) {
        if (action.actionTypeId() == fireSensorFireDetectedActionTypeId) {
            bool fire = action.paramValue(fireSensorFireDetectedActionFireDetectedParamTypeId).toBool();
            qCDebug(dcGenericSensors()) << "Fire sensor state:" << (fire ? "fire" : "no fire");
            thing->setStateValue(fireSensorFireDetectedStateTypeId, fire);
            info->finish(Thing::ThingErrorNoError);
            return;
        }
    } else if (thing->thingClassId() == vibrationSensorThingClassId) {
        if (action.actionTypeId() == vibrationSensorTriggerVibrationAlertActionTypeId) {
            thing->emitEvent(vibrationSensorVibrationDetectedEventTypeId);
        }
    } else {
        Q_ASSERT_X(false, "executeAction", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
    }
}

double IntegrationPluginGenericSensors::mapDoubleValue(double value, double fromMin, double fromMax, double toMin, double toMax)
{
    double percent = (value - fromMin) / (fromMax - fromMin);
    double toValue = toMin + (toMax - toMin) * percent;
    return toValue;
}
