Merge PR #498: Move simulation plugin to nymea-plugins-simulation repository
This commit is contained in:
commit
0479bb9ddb
16
debian/control
vendored
16
debian/control
vendored
@ -961,21 +961,6 @@ Description: nymea.io plugin for Shelly devices
|
||||
This package will install the nymea.io plugin for Shelly devices
|
||||
|
||||
|
||||
Package: nymea-plugin-simulation
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
nymea-plugins-translations,
|
||||
Description: nymea.io plugin for simulated devices
|
||||
The nymea daemon is a plugin based IoT (Internet of Things) server. The
|
||||
server works like a translator for devices, things and services and
|
||||
allows them to interact.
|
||||
With the powerful rule engine you are able to connect any device available
|
||||
in the system and create individual scenes and behaviors for your environment.
|
||||
.
|
||||
This package will install the nymea.io plugin for simulated devices
|
||||
|
||||
|
||||
Package: nymea-plugin-simpleheatpump
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
@ -1400,7 +1385,6 @@ Package: nymea-plugins-all
|
||||
Section: libs
|
||||
Architecture: all
|
||||
Depends: nymea-plugins,
|
||||
nymea-plugin-simulation,
|
||||
nymea-plugins-maker,
|
||||
nymea-plugins-merkurboard,
|
||||
Description: Plugins for nymea IoT server - Meta package for all plugins
|
||||
|
||||
1
debian/nymea-plugin-simulation.install.in
vendored
1
debian/nymea-plugin-simulation.install.in
vendored
@ -1 +0,0 @@
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginsimulation.so
|
||||
@ -55,7 +55,6 @@ PLUGIN_DIRS = \
|
||||
senic \
|
||||
serialportcommander \
|
||||
sgready \
|
||||
simulation \
|
||||
simpleheatpump \
|
||||
sma \
|
||||
somfytahoma \
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
# Simulation
|
||||
|
||||
This plugin contains many different simulated device in order to play with the system if you don't have any devices around or as a showcase for nymea.
|
||||
@ -1,923 +0,0 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "integrationpluginsimulation.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
#include <QtMath>
|
||||
#include <QColor>
|
||||
#include <QDateTime>
|
||||
#include <QSettings>
|
||||
|
||||
IntegrationPluginSimulation::IntegrationPluginSimulation()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
IntegrationPluginSimulation::~IntegrationPluginSimulation()
|
||||
{
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer20Seconds);
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer5Min);
|
||||
}
|
||||
|
||||
void IntegrationPluginSimulation::init()
|
||||
{
|
||||
// Seed the random generator with current time
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch() / 1000);
|
||||
|
||||
// Change some values every 20 seconds
|
||||
m_pluginTimer20Seconds = hardwareManager()->pluginTimerManager()->registerTimer(20);
|
||||
connect(m_pluginTimer20Seconds, &PluginTimer::timeout, this, &IntegrationPluginSimulation::onPluginTimer20Seconds);
|
||||
|
||||
// Change some values every 5 min
|
||||
m_pluginTimer5Min = hardwareManager()->pluginTimerManager()->registerTimer(300);
|
||||
connect(m_pluginTimer5Min, &PluginTimer::timeout, this, &IntegrationPluginSimulation::onPluginTimer5Minutes);
|
||||
}
|
||||
|
||||
void IntegrationPluginSimulation::setupThing(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
qCDebug(dcSimulation()) << "Set up thing" << thing->name();
|
||||
if (thing->thingClassId() == garageGateThingClassId ||
|
||||
thing->thingClassId() == extendedAwningThingClassId ||
|
||||
thing->thingClassId() == extendedBlindThingClassId ||
|
||||
thing->thingClassId() == venetianBlindThingClassId ||
|
||||
thing->thingClassId() == rollerShutterThingClassId ||
|
||||
thing->thingClassId() == fingerPrintSensorThingClassId ||
|
||||
thing->thingClassId() == barcodeScannerThingClassId ||
|
||||
thing->thingClassId() == contactSensorThingClassId ||
|
||||
thing->thingClassId() == waterSensorThingClassId ||
|
||||
thing->thingClassId() == cleaningRobotThingClassId) {
|
||||
m_simulationTimers.insert(thing, new QTimer(thing));
|
||||
connect(m_simulationTimers[thing], &QTimer::timeout, this, &IntegrationPluginSimulation::simulationTimerTimeout);
|
||||
}
|
||||
if (thing->thingClassId() == fingerPrintSensorThingClassId && thing->stateValue(fingerPrintSensorUsersStateTypeId).toStringList().count() > 0) {
|
||||
m_simulationTimers.value(thing)->start(10000);
|
||||
}
|
||||
if (thing->thingClassId() == barcodeScannerThingClassId) {
|
||||
m_simulationTimers.value(thing)->start(10000);
|
||||
}
|
||||
if (thing->thingClassId() == thermostatThingClassId) {
|
||||
QTimer *t = new QTimer(thing);
|
||||
connect(t, &QTimer::timeout, thing, [thing](){
|
||||
double targetTemp = thing->stateValue(thermostatTargetTemperatureStateTypeId).toDouble();
|
||||
double currentTemp = thing->stateValue(thermostatTemperatureStateTypeId).toDouble();
|
||||
bool heatingOn = thing->stateValue(thermostatHeatingOnStateTypeId).toBool();
|
||||
bool coolingOn = thing->stateValue(thermostatCoolingOnStateTypeId).toBool();
|
||||
bool boost = thing->stateValue(thermostatBoostStateTypeId).toBool();
|
||||
|
||||
// When we're heating, temp increases slowly until it's up on par with target temp
|
||||
if (heatingOn) {
|
||||
double diff = targetTemp - currentTemp;
|
||||
currentTemp += 0.005 + diff * (boost ? 0.2 : 0.1);
|
||||
if (currentTemp >= targetTemp) {
|
||||
thing->setStateValue(thermostatHeatingOnStateTypeId, false);
|
||||
}
|
||||
} else {
|
||||
// Decrease 1% per interval to simulate drop of temperature (assuming it's cold outside)
|
||||
currentTemp = currentTemp * 0.995;
|
||||
|
||||
// Start heating when we're more than 2 degrees lower than what we should be
|
||||
if (currentTemp < targetTemp - 2) {
|
||||
thing->setStateValue(thermostatHeatingOnStateTypeId, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (coolingOn) {
|
||||
double diff = targetTemp - currentTemp;
|
||||
currentTemp += diff * 0.1;
|
||||
if (currentTemp <= targetTemp) {
|
||||
thing->setStateValue(thermostatCoolingOnStateTypeId, false);
|
||||
}
|
||||
} else {
|
||||
if (currentTemp > targetTemp + 2) {
|
||||
thing->setStateValue(thermostatCoolingOnStateTypeId, true);
|
||||
}
|
||||
}
|
||||
|
||||
thing->setStateValue(thermostatTemperatureStateTypeId, currentTemp);
|
||||
});
|
||||
t->start(10000);
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == contactSensorThingClassId) {
|
||||
m_simulationTimers.value(thing)->start(10000);
|
||||
}
|
||||
if (thing->thingClassId() == waterSensorThingClassId) {
|
||||
m_simulationTimers.value(thing)->start(10000);
|
||||
}
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
void IntegrationPluginSimulation::thingRemoved(Thing *thing)
|
||||
{
|
||||
// Clean up any timers we may have for this thing
|
||||
if (m_simulationTimers.contains(thing)) {
|
||||
QTimer *t = m_simulationTimers.take(thing);
|
||||
t->stop();
|
||||
t->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginSimulation::executeAction(ThingActionInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
Action action = info->action();
|
||||
// Check the ThingClassId for "Simple Button"
|
||||
if (thing->thingClassId() == simpleButtonThingClassId ) {
|
||||
|
||||
// check if this is the "press" action
|
||||
if (action.actionTypeId() == simpleButtonTriggerActionTypeId) {
|
||||
|
||||
// Emit the "button pressed" event
|
||||
qCDebug(dcSimulation()) << "Emit button pressed event for" << thing->name();
|
||||
Event event(simpleButtonPressedEventTypeId, thing->id());
|
||||
emit emitEvent(event);
|
||||
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
return info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
|
||||
// Check the ThingClassId for "Alternative Button"
|
||||
if (thing->thingClassId() == alternativeButtonThingClassId) {
|
||||
|
||||
// check if this is the "set power" action
|
||||
if (action.actionTypeId() == alternativeButtonPowerActionTypeId) {
|
||||
|
||||
// get the param value
|
||||
Param powerParam = action.param(alternativeButtonPowerActionPowerParamTypeId);
|
||||
bool power = powerParam.value().toBool();
|
||||
|
||||
qCDebug(dcSimulation()) << "Set power" << power << "for button" << thing->name();
|
||||
|
||||
// Set the "power" state
|
||||
thing->setStateValue(alternativeButtonPowerStateTypeId, power);
|
||||
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
return info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == heatingThingClassId) {
|
||||
|
||||
// check if this is the "set power" action
|
||||
if (action.actionTypeId() == heatingPowerActionTypeId) {
|
||||
|
||||
// get the param value
|
||||
Param powerParam = action.param(heatingPowerActionPowerParamTypeId);
|
||||
bool power = powerParam.value().toBool();
|
||||
qCDebug(dcSimulation()) << "Set power" << power << "for heating device" << thing->name();
|
||||
thing->setStateValue(heatingPowerStateTypeId, power);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
} else if (action.actionTypeId() == heatingPercentageActionTypeId) {
|
||||
|
||||
// get the param value
|
||||
Param percentageParam = action.param(heatingPercentageActionPercentageParamTypeId);
|
||||
int percentage = percentageParam.value().toInt();
|
||||
|
||||
qCDebug(dcSimulation()) << "Set target temperature percentage" << percentage << "for heating device" << thing->name();
|
||||
|
||||
thing->setStateValue(heatingPercentageStateTypeId, percentage);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
return info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == thermostatThingClassId) {
|
||||
if (action.actionTypeId() == thermostatBoostActionTypeId) {
|
||||
bool boost = action.param(thermostatBoostActionBoostParamTypeId).value().toBool();
|
||||
qCDebug(dcSimulation()) << "Set boost" << boost << "for thermostat device" << thing->name();
|
||||
thing->setStateValue(thermostatBoostStateTypeId, boost);
|
||||
QTimer *t = new QTimer(thing);
|
||||
t->setInterval(5 * 60 * 1000);
|
||||
t->setSingleShot(true);
|
||||
connect(t, &QTimer::timeout, t, &QTimer::deleteLater);
|
||||
connect(t, &QTimer::timeout, thing, [thing](){
|
||||
thing->setStateValue(thermostatBoostStateTypeId, false);
|
||||
});
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == thermostatTargetTemperatureActionTypeId) {
|
||||
double targetTemp = action.param(thermostatTargetTemperatureActionTargetTemperatureParamTypeId).value().toDouble();
|
||||
qCDebug(dcSimulation()) << "Set targetTemp" << targetTemp << "for thermostat device" << thing->name();
|
||||
thing->setStateValue(thermostatTargetTemperatureStateTypeId, targetTemp);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == evChargerThingClassId){
|
||||
|
||||
if (action.actionTypeId() == evChargerPowerActionTypeId){
|
||||
// get the param value
|
||||
Param powerParam = action.param(evChargerPowerActionPowerParamTypeId);
|
||||
bool power = powerParam.value().toBool();
|
||||
|
||||
qCDebug(dcSimulation()) << "Set power" << power << "for heating device" << thing->name();
|
||||
|
||||
thing->setStateValue(evChargerPowerStateTypeId, power);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
} else if(action.actionTypeId() == evChargerMaxChargingCurrentActionTypeId){
|
||||
// get the param value
|
||||
Param maxChargeParam = action.param(evChargerMaxChargingCurrentActionMaxChargingCurrentParamTypeId);
|
||||
double maxCharge = maxChargeParam.value().toDouble();
|
||||
qCDebug(dcSimulation()) << "Set maximum charging current to" << maxCharge << "for EV Charger device" << thing->name();
|
||||
thing->setStateValue(evChargerMaxChargingCurrentStateTypeId, maxCharge);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
return info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
|
||||
if(thing->thingClassId() == socketThingClassId){
|
||||
|
||||
if(action.actionTypeId() == socketPowerActionTypeId){
|
||||
// get the param value
|
||||
Param powerParam = action.param(socketPowerActionPowerParamTypeId);
|
||||
bool power = powerParam.value().toBool();
|
||||
// Set the "power" state
|
||||
qCDebug(dcSimulation()) << "Set power" << power << "for socket device" << thing->name();
|
||||
thing->setStateValue(socketPowerStateTypeId, power);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
return info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
|
||||
if(thing->thingClassId() == colorBulbThingClassId){
|
||||
|
||||
if(action.actionTypeId() == colorBulbBrightnessActionTypeId){
|
||||
int brightness = action.param(colorBulbBrightnessActionBrightnessParamTypeId).value().toInt();
|
||||
qCDebug(dcSimulation()) << "Set brightness" << brightness << "for color bulb" << thing->name();
|
||||
thing->setStateValue(colorBulbBrightnessStateTypeId, brightness);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
} else if (action.actionTypeId() == colorBulbColorTemperatureActionTypeId){
|
||||
int temperature = action.param(colorBulbColorTemperatureActionColorTemperatureParamTypeId).value().toInt();
|
||||
qCDebug(dcSimulation()) << "Set color temperature" << temperature << "for color bulb" << thing->name();
|
||||
thing->setStateValue(colorBulbColorTemperatureStateTypeId, temperature);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
} else if (action.actionTypeId() == colorBulbColorActionTypeId) {
|
||||
QColor color = action.param(colorBulbColorActionColorParamTypeId).value().value<QColor>();
|
||||
qCDebug(dcSimulation()) << "Set color" << color << "for color bulb" << thing->name();
|
||||
thing->setStateValue(colorBulbColorStateTypeId, color);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
} else if (action.actionTypeId() == colorBulbPowerActionTypeId) {
|
||||
bool power = action.param(colorBulbPowerActionPowerParamTypeId).value().toBool();
|
||||
qCDebug(dcSimulation()) << "Set power" << power << "for color bulb" << thing->name();
|
||||
thing->setStateValue(colorBulbPowerStateTypeId, power);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
return info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == heatingRodThingClassId) {
|
||||
|
||||
if (action.actionTypeId() == heatingRodPowerActionTypeId) {
|
||||
bool power = action.param(heatingRodPowerActionPowerParamTypeId).value().toBool();
|
||||
qCDebug(dcSimulation()) << "Set power" << power << "for heating rod" << thing->name();
|
||||
thing->setStateValue(heatingRodPowerStateTypeId, power);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
} else if (action.actionTypeId() == heatingRodPercentageActionTypeId) {
|
||||
int percentage = action.param(heatingRodPercentageActionPercentageParamTypeId).value().toInt();
|
||||
qCDebug(dcSimulation()) << "Set percentage" << percentage << "for heating rod" << thing->name();
|
||||
thing->setStateValue(heatingRodPercentageStateTypeId, percentage);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
return info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == batteryThingClassId) {
|
||||
if (action.actionTypeId() == batteryMaxChargingActionTypeId) {
|
||||
int maxCharging = action.param(batteryMaxChargingActionMaxChargingParamTypeId).value().toInt();
|
||||
thing->setStateValue(batteryMaxChargingStateTypeId, maxCharging);
|
||||
qCDebug(dcSimulation()) << "Set max charging power" << maxCharging << "for battery" << thing->name();
|
||||
thing->setStateValue(batteryChargingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
return info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == waterValveThingClassId) {
|
||||
if (action.actionTypeId() == waterValvePowerActionTypeId) {
|
||||
bool power = action.param(waterValvePowerActionPowerParamTypeId).value().toBool();
|
||||
thing->setStateValue(waterValvePowerStateTypeId, power);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
return info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == garageGateThingClassId) {
|
||||
if (action.actionTypeId() == garageGateOpenActionTypeId) {
|
||||
if (thing->stateValue(garageGateStateStateTypeId).toString() == "opening") {
|
||||
qCDebug(dcSimulation()) << "Garage gate already opening.";
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (thing->stateValue(garageGateStateStateTypeId).toString() == "open" &&
|
||||
!thing->stateValue(garageGateIntermediatePositionStateTypeId).toBool()) {
|
||||
qCDebug(dcSimulation()) << "Garage gate already open.";
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
thing->setStateValue(garageGateStateStateTypeId, "opening");
|
||||
thing->setStateValue(garageGateIntermediatePositionStateTypeId, true);
|
||||
m_simulationTimers.value(thing)->start(5000);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == garageGateCloseActionTypeId) {
|
||||
if (thing->stateValue(garageGateStateStateTypeId).toString() == "closing") {
|
||||
qCDebug(dcSimulation()) << "Garage gate already closing.";
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (thing->stateValue(garageGateStateStateTypeId).toString() == "closed" &&
|
||||
!thing->stateValue(garageGateIntermediatePositionStateTypeId).toBool()) {
|
||||
qCDebug(dcSimulation()) << "Garage gate already closed.";
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
thing->setStateValue(garageGateStateStateTypeId, "closing");
|
||||
thing->setStateValue(garageGateIntermediatePositionStateTypeId, true);
|
||||
m_simulationTimers.value(thing)->start(5000);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == garageGateStopActionTypeId) {
|
||||
if (thing->stateValue(garageGateStateStateTypeId).toString() == "opening" ||
|
||||
thing->stateValue(garageGateStateStateTypeId).toString() == "closing") {
|
||||
thing->setStateValue(garageGateStateStateTypeId, "open");
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
qCDebug(dcSimulation()) << "Garage gate not moving";
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == garageGatePowerActionTypeId) {
|
||||
bool power = action.param(garageGatePowerActionPowerParamTypeId).value().toBool();
|
||||
thing->setStateValue(garageGatePowerStateTypeId, power);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == rollerShutterThingClassId) {
|
||||
if (action.actionTypeId() == rollerShutterOpenActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Opening roller shutter";
|
||||
m_simulationTimers.value(thing)->setProperty("targetValue", 0);
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(rollerShutterMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == rollerShutterCloseActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Closing roller shutter";
|
||||
m_simulationTimers.value(thing)->setProperty("targetValue", 100);
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(rollerShutterMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == rollerShutterStopActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Stopping roller shutter";
|
||||
m_simulationTimers.value(thing)->stop();
|
||||
thing->setStateValue(rollerShutterMovingStateTypeId, false);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == rollerShutterPercentageActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Setting awning to" << action.param(rollerShutterPercentageActionPercentageParamTypeId);
|
||||
m_simulationTimers.value(thing)->setProperty("targetValue", action.param(rollerShutterPercentageActionPercentageParamTypeId).value());
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(rollerShutterMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == extendedAwningThingClassId) {
|
||||
if (action.actionTypeId() == extendedAwningOpenActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Opening awning";
|
||||
m_simulationTimers.value(thing)->setProperty("targetValue", 100);
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(extendedAwningMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == extendedAwningCloseActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Closing awning";
|
||||
m_simulationTimers.value(thing)->setProperty("targetValue", 0);
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(extendedAwningMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == extendedAwningStopActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Stopping awning";
|
||||
m_simulationTimers.value(thing)->stop();
|
||||
thing->setStateValue(extendedAwningMovingStateTypeId, false);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == extendedAwningPercentageActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Setting awning to" << action.param(extendedAwningPercentageActionPercentageParamTypeId);
|
||||
m_simulationTimers.value(thing)->setProperty("targetValue", action.param(extendedAwningPercentageActionPercentageParamTypeId).value());
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(extendedAwningMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == fingerPrintSensorThingClassId) {
|
||||
if (action.actionTypeId() == fingerPrintSensorAddUserActionTypeId) {
|
||||
QStringList users = thing->stateValue(fingerPrintSensorUsersStateTypeId).toStringList();
|
||||
QString username = action.param(fingerPrintSensorAddUserActionUserIdParamTypeId).value().toString();
|
||||
QString finger = action.param(fingerPrintSensorAddUserActionFingerParamTypeId).value().toString();
|
||||
QSettings settings;
|
||||
settings.beginGroup(thing->id().toString());
|
||||
QStringList usedFingers = settings.value(username).toStringList();
|
||||
if (users.contains(username) && usedFingers.contains(finger)) {
|
||||
return info->finish(Thing::ThingErrorDuplicateUuid);
|
||||
}
|
||||
QTimer::singleShot(5000, info, [this, info, thing, username, finger]() {
|
||||
if (username.toLower().trimmed() == "john") {
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Fingerprint could not be scanned. Please try again."));
|
||||
} else {
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
QStringList users = thing->stateValue(fingerPrintSensorUsersStateTypeId).toStringList();
|
||||
if (!users.contains(username)) {
|
||||
users.append(username);
|
||||
thing->setStateValue(fingerPrintSensorUsersStateTypeId, users);
|
||||
m_simulationTimers.value(thing)->start(10000);
|
||||
}
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup(thing->id().toString());
|
||||
QStringList usedFingers = settings.value(username).toStringList();
|
||||
usedFingers.append(finger);
|
||||
settings.setValue(username, usedFingers);
|
||||
settings.endGroup();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (action.actionTypeId() == fingerPrintSensorRemoveUserActionTypeId) {
|
||||
QStringList users = thing->stateValue(fingerPrintSensorUsersStateTypeId).toStringList();
|
||||
QString username = action.params().first().value().toString();
|
||||
if (!users.contains(username)) {
|
||||
return info->finish(Thing::ThingErrorInvalidParameter);
|
||||
}
|
||||
users.removeAll(username);
|
||||
thing->setStateValue(fingerPrintSensorUsersStateTypeId, users);
|
||||
if (users.count() == 0) {
|
||||
m_simulationTimers.value(thing)->stop();
|
||||
}
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == simpleBlindThingClassId) {
|
||||
if (action.actionTypeId() == simpleBlindOpenActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Opening simple blind";
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == simpleBlindCloseActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Closing simple blind";
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == simpleBlindStopActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Stopping simple blind";
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == extendedBlindThingClassId) {
|
||||
if (action.actionTypeId() == extendedBlindOpenActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Opening extended blind";
|
||||
m_simulationTimers.value(thing)->setProperty("targetValue", 0);
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(extendedBlindMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == extendedBlindCloseActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Closing extended blind";
|
||||
m_simulationTimers.value(thing)->setProperty("targetValue", 100);
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(extendedBlindMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == extendedBlindStopActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Stopping extended blind";
|
||||
m_simulationTimers.value(thing)->stop();
|
||||
thing->setStateValue(extendedBlindMovingStateTypeId, false);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == extendedBlindPercentageActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Setting extended blind to" << action.param(extendedBlindPercentageActionPercentageParamTypeId);
|
||||
m_simulationTimers.value(thing)->setProperty("targetValue", action.param(extendedBlindPercentageActionPercentageParamTypeId).value());
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(extendedBlindMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == venetianBlindThingClassId) {
|
||||
if (action.actionTypeId() == venetianBlindOpenActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Opening venetian blind";
|
||||
m_simulationTimers.value(thing)->setProperty("targetPosition", 0);
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(venetianBlindMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == venetianBlindCloseActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Closing venetian blind";
|
||||
m_simulationTimers.value(thing)->setProperty("targetPosition", 100);
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(venetianBlindMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == venetianBlindStopActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Stopping venetian blind";
|
||||
m_simulationTimers.value(thing)->stop();
|
||||
m_simulationTimers.value(thing)->setProperty("targetPosition", thing->stateValue(venetianBlindPercentageStateTypeId).toInt());
|
||||
m_simulationTimers.value(thing)->setProperty("targetAngle", thing->stateValue(venetianBlindAngleStateTypeId).toInt());
|
||||
thing->setStateValue(venetianBlindMovingStateTypeId, false);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == venetianBlindPercentageActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Setting venetian blind position to" << action.param(venetianBlindPercentageActionPercentageParamTypeId);
|
||||
m_simulationTimers.value(thing)->setProperty("targetPosition", action.param(venetianBlindPercentageActionPercentageParamTypeId).value());
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(venetianBlindMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
if (action.actionTypeId() == venetianBlindAngleActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Setting venetian blind angle to" << action.param(venetianBlindAngleActionAngleParamTypeId);
|
||||
m_simulationTimers.value(thing)->setProperty("targetAngle", action.param(venetianBlindAngleActionAngleParamTypeId).value());
|
||||
m_simulationTimers.value(thing)->start(500);
|
||||
thing->setStateValue(venetianBlindMovingStateTypeId, true);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == cleaningRobotThingClassId) {
|
||||
if (action.actionTypeId() == cleaningRobotStartCleaningActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Starting to clean...";
|
||||
thing->setStateValue(cleaningRobotRobotStateStateTypeId, "cleaning");
|
||||
m_simulationTimers.value(thing)->stop();
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
if (action.actionTypeId() == cleaningRobotPauseCleaningActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Pausing...";
|
||||
if (thing->stateValue(cleaningRobotRobotStateStateTypeId).toString() == "paused") {
|
||||
thing->setStateValue(cleaningRobotRobotStateStateTypeId, "cleaning");
|
||||
} else if (thing->stateValue(cleaningRobotRobotStateStateTypeId).toString() == "cleaning"){
|
||||
thing->setStateValue(cleaningRobotRobotStateStateTypeId, "paused");
|
||||
}
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
if (action.actionTypeId() == cleaningRobotStopCleaningActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Stopping.";
|
||||
thing->setStateValue(cleaningRobotRobotStateStateTypeId, "stopped");
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
if (action.actionTypeId() == cleaningRobotReturnToBaseActionTypeId) {
|
||||
qCDebug(dcSimulation()) << "Returning to base...";
|
||||
QString robotState = thing->stateValue(cleaningRobotRobotStateStateTypeId).toString();
|
||||
if (robotState == "cleaning" || robotState == "paused" || robotState == "error") {
|
||||
thing->setStateValue(cleaningRobotRobotStateStateTypeId, "traveling");
|
||||
m_simulationTimers.value(thing)->start(5000);
|
||||
}
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
if (action.actionTypeId() == cleaningRobotSimulateErrorActionTypeId) {
|
||||
thing->setStateValue(cleaningRobotRobotStateStateTypeId, "error");
|
||||
thing->setStateValue(cleaningRobotErrorMessageStateTypeId, QT_TR_NOOP("Help me, I'm stuck!"));
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qCWarning(dcSimulation()) << "Unhandled thing class" << thing->thingClassId() << "for" << thing->name();
|
||||
}
|
||||
|
||||
int IntegrationPluginSimulation::generateRandomIntValue(int min, int max)
|
||||
{
|
||||
int value = ((qrand() % ((max + 1) - min)) + min);
|
||||
// qCDebug(dcSimulation()) << "Generateed random int value: [" << min << ", " << max << "] -->" << value;
|
||||
return value;
|
||||
}
|
||||
|
||||
double IntegrationPluginSimulation::generateRandomDoubleValue(double min, double max)
|
||||
{
|
||||
double value = generateRandomIntValue(static_cast<int>(min * 10), static_cast<int>(max * 10)) / 10.0;
|
||||
// qCDebug(dcSimulation()) << "Generated random double value: [" << min << ", " << max << "] -->" << value;
|
||||
return value;
|
||||
}
|
||||
|
||||
bool IntegrationPluginSimulation::generateRandomBoolValue()
|
||||
{
|
||||
bool value = static_cast<bool>(generateRandomIntValue(0, 1));
|
||||
// qCDebug(dcSimulation()) << "Generated random bool value:" << value;
|
||||
return value;
|
||||
}
|
||||
|
||||
qreal IntegrationPluginSimulation::generateSinValue(int min, int max, int hourOffset, int decimals)
|
||||
{
|
||||
// 00:00 : 23:99 = 0 : PI
|
||||
// seconds of day : (60 * 60 * 24) = x : 2*PI
|
||||
QDateTime d = QDateTime::currentDateTime();
|
||||
int secondsPerDay = 60 * 60 * 24;
|
||||
int offsetInSeconds = hourOffset * 60 * 60;
|
||||
int secondsOfDay = d.time().msecsSinceStartOfDay() / 1000;
|
||||
// add offset and wrap around
|
||||
secondsOfDay = (secondsOfDay - offsetInSeconds) % secondsPerDay;
|
||||
|
||||
qreal interval = secondsOfDay * 2*M_PI / secondsPerDay;
|
||||
qreal gain = 1.0 * (max - min) / 2;
|
||||
qreal temp = (gain * qSin(interval)) + min + gain;
|
||||
return QString::number(temp, 'f', decimals).toDouble();
|
||||
}
|
||||
|
||||
qreal IntegrationPluginSimulation::generateBatteryValue(int chargeStartHour, int chargeDurationInMinutes)
|
||||
{
|
||||
QDateTime d = QDateTime::currentDateTime();
|
||||
|
||||
int secondsPerDay = 24 * 60 * 60;
|
||||
int currentSecond = d.time().msecsSinceStartOfDay() / 1000;
|
||||
int chargeStartSecond = chargeStartHour * 60 * 60;
|
||||
int chargeEndSecond = chargeStartSecond + (chargeDurationInMinutes * 60);
|
||||
int chargeDurationInSeconds = chargeDurationInMinutes * 60;
|
||||
|
||||
// should we be charging?
|
||||
if (chargeStartSecond < currentSecond && currentSecond < chargeEndSecond) {
|
||||
// Yep, charging...
|
||||
int currentChargeSecond = currentSecond - chargeStartSecond;
|
||||
// x : 100 = currentChargeSecond : chargeDurationInSeconds
|
||||
return 100 * currentChargeSecond / chargeDurationInSeconds;
|
||||
}
|
||||
|
||||
int dischargeDurationInSecs = secondsPerDay - chargeDurationInSeconds;
|
||||
int currentDischargeSecond;
|
||||
if (currentSecond < chargeStartSecond) {
|
||||
currentDischargeSecond = currentSecond + (secondsPerDay - chargeEndSecond);
|
||||
} else {
|
||||
currentDischargeSecond = currentSecond - chargeEndSecond;
|
||||
}
|
||||
// 100 : x = dischargeDurationInSecs : currentDischargeSecond
|
||||
return 100 - (100 * currentDischargeSecond / dischargeDurationInSecs);
|
||||
}
|
||||
|
||||
qreal IntegrationPluginSimulation::generateNoisyRectangle(int min, int max, int maxNoise, int stablePeriodInMinutes, int &lastValue, QDateTime &lastChangeTimestamp)
|
||||
{
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
qCDebug(dcSimulation()) << "Generating noisy rect:" << min << "-" << max << "lastValue:" << lastValue << "lastUpdate" << lastChangeTimestamp << lastChangeTimestamp.secsTo(now) << lastChangeTimestamp.isValid();
|
||||
if (!lastChangeTimestamp.isValid() || lastChangeTimestamp.secsTo(now) / 60 > stablePeriodInMinutes) {
|
||||
lastChangeTimestamp.swap(now);
|
||||
lastValue = min + qrand() % (max - min);
|
||||
qCDebug(dcSimulation()) << "New last value:" << lastValue;
|
||||
}
|
||||
qreal noise = 0.1 * (qrand() % (maxNoise * 20) - maxNoise);
|
||||
qreal ret = 1.0 * lastValue + noise;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void IntegrationPluginSimulation::onPluginTimer20Seconds()
|
||||
{
|
||||
foreach (Thing *thing, myThings()) {
|
||||
if (thing->thingClassId() == temperatureSensorThingClassId) {
|
||||
// Temperature sensor
|
||||
thing->setStateValue(temperatureSensorTemperatureStateTypeId, generateSinValue(18, 23, 8));
|
||||
thing->setStateValue(temperatureSensorHumidityStateTypeId, generateSinValue(40, 55, 20));
|
||||
thing->setStateValue(temperatureSensorBatteryLevelStateTypeId, generateBatteryValue(8, 10));
|
||||
thing->setStateValue(temperatureSensorBatteryCriticalStateTypeId, thing->stateValue(temperatureSensorBatteryLevelStateTypeId).toInt() <= 25);
|
||||
thing->setStateValue(temperatureSensorConnectedStateTypeId, true);
|
||||
} else if (thing->thingClassId() == motionDetectorThingClassId) {
|
||||
// Motion detector
|
||||
thing->setStateValue(motionDetectorIsPresentStateTypeId, generateRandomBoolValue());
|
||||
thing->setStateValue(motionDetectorBatteryLevelStateTypeId, generateBatteryValue(13, 1));
|
||||
thing->setStateValue(motionDetectorBatteryCriticalStateTypeId, thing->stateValue(motionDetectorBatteryLevelStateTypeId).toInt() <= 30);
|
||||
thing->setStateValue(motionDetectorConnectedStateTypeId, true);
|
||||
} else if (thing->thingClassId() == waterSensorThingClassId) {
|
||||
thing->setStateValue(waterSensorWaterDetectedStateTypeId, generateRandomBoolValue());
|
||||
} else if (thing->thingClassId() == gardenSensorThingClassId) {
|
||||
// Garden sensor
|
||||
thing->setStateValue(gardenSensorTemperatureStateTypeId, generateSinValue(-4, 17, 5));
|
||||
thing->setStateValue(gardenSensorSoilMoistureStateTypeId, generateSinValue(40, 60, 13));
|
||||
thing->setStateValue(gardenSensorIlluminanceStateTypeId, generateSinValue(0, 80, 2));
|
||||
thing->setStateValue(gardenSensorBatteryLevelStateTypeId, generateBatteryValue(9, 20));
|
||||
thing->setStateValue(gardenSensorBatteryCriticalStateTypeId, thing->stateValue(gardenSensorBatteryLevelStateTypeId).toDouble() <= 30);
|
||||
thing->setStateValue(gardenSensorConnectedStateTypeId, true);
|
||||
} else if(thing->thingClassId() == netatmoIndoorThingClassId) {
|
||||
// Netatmo
|
||||
thing->setStateValue(netatmoIndoorUpdateTimeStateTypeId, QDateTime::currentDateTime().toTime_t());
|
||||
thing->setStateValue(netatmoIndoorHumidityStateTypeId, generateSinValue(35, 45, 13));
|
||||
thing->setStateValue(netatmoIndoorTemperatureStateTypeId, generateSinValue(20, 25, 3));
|
||||
thing->setStateValue(netatmoIndoorPressureStateTypeId, generateSinValue(1003, 1008, 8));
|
||||
thing->setStateValue(netatmoIndoorNoiseStateTypeId, generateRandomIntValue(40, 80));
|
||||
thing->setStateValue(netatmoIndoorWifiStrengthStateTypeId, generateRandomIntValue(85, 95));
|
||||
} else if (thing->thingClassId() == smartMeterThingClassId) {
|
||||
thing->setStateValue(smartMeterConnectedStateTypeId, true);
|
||||
int lastValue = thing->property("lastValue").toInt();
|
||||
QDateTime lastUpdate = thing->property("lastUpdate").toDateTime();
|
||||
qlonglong currentPower = generateNoisyRectangle(-2000, 100, 10, 5, lastValue, lastUpdate);
|
||||
thing->setStateValue(smartMeterCurrentPowerStateTypeId, currentPower);
|
||||
thing->setProperty("lastValue", lastValue);
|
||||
thing->setProperty("lastUpdate", lastUpdate);
|
||||
if (currentPower < 0) {
|
||||
qreal consumptionKWH = 1.0 * currentPower * (1.0 * m_pluginTimer20Seconds->interval() / 1000 / 60 / 60) / 1000;
|
||||
thing->setStateValue(smartMeterTotalEnergyConsumedStateTypeId, thing->stateValue(smartMeterTotalEnergyConsumedStateTypeId).toDouble() - consumptionKWH);
|
||||
}
|
||||
if (currentPower > 0) {
|
||||
qreal consumptionKWH = 1.0 * currentPower * (1.0 * m_pluginTimer20Seconds->interval() / 1000 / 60 / 60) / 1000;
|
||||
thing->setStateValue(smartMeterTotalEnergyProducedStateTypeId, thing->stateValue(smartMeterTotalEnergyProducedStateTypeId).toDouble() + consumptionKWH);
|
||||
}
|
||||
} else if (thing->thingClassId() == solarPanelThingClassId) {
|
||||
int lastValue = thing->property("lastValue").toInt();
|
||||
QDateTime lastUpdate = thing->property("lastUpdate").toDateTime();
|
||||
qlonglong currentPower = generateNoisyRectangle(0, 2000, 50, 5, lastValue, lastUpdate);
|
||||
thing->setStateValue(solarPanelCurrentPowerStateTypeId, currentPower);
|
||||
thing->setProperty("lastValue", lastValue);
|
||||
thing->setProperty("lastUpdate", lastUpdate);
|
||||
qreal consumptionKWH = 1.0 * currentPower * (1.0 * m_pluginTimer20Seconds->interval() / 1000 / 60 / 60) / 1000;
|
||||
thing->setStateValue(solarPanelTotalEnergyProducedStateTypeId, thing->stateValue(solarPanelTotalEnergyProducedStateTypeId).toDouble() + consumptionKWH);
|
||||
} else if (thing->thingClassId() == cleaningRobotThingClassId) {
|
||||
QString robotState = thing->stateValue(cleaningRobotRobotStateStateTypeId).toString();
|
||||
int batteryLevel = thing->stateValue(cleaningRobotBatteryLevelStateTypeId).toInt();
|
||||
bool charging = false;
|
||||
bool pluggedIn = false;
|
||||
if (robotState == "cleaning") {
|
||||
batteryLevel -= 1;
|
||||
if (batteryLevel < 5) {
|
||||
robotState = "traveling";
|
||||
m_simulationTimers.value(thing)->start(5000);
|
||||
}
|
||||
} else if (robotState == "docked") {
|
||||
batteryLevel = qMin(100, batteryLevel + 2);
|
||||
charging = batteryLevel < 100;
|
||||
pluggedIn = true;
|
||||
}
|
||||
thing->setStateValue(cleaningRobotRobotStateStateTypeId, robotState);
|
||||
thing->setStateValue(cleaningRobotBatteryLevelStateTypeId, batteryLevel);
|
||||
thing->setStateValue(cleaningRobotBatteryCriticalStateTypeId, batteryLevel < 10);
|
||||
thing->setStateValue(cleaningRobotChargingStateTypeId, charging);
|
||||
thing->setStateValue(cleaningRobotPluggedInStateTypeId, pluggedIn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginSimulation::onPluginTimer5Minutes()
|
||||
{
|
||||
foreach (Thing *thing, myThings()) {
|
||||
if(thing->thingClassId() == netatmoIndoorThingClassId) {
|
||||
// Note: should change between > 1000 co2 < 1000 for showcase, please do not change this behaviour
|
||||
int currentValue = thing->stateValue(netatmoIndoorCo2StateTypeId).toInt();
|
||||
if (currentValue < 1000) {
|
||||
thing->setStateValue(netatmoIndoorCo2StateTypeId, generateRandomIntValue(1001, 1010));
|
||||
} else {
|
||||
thing->setStateValue(netatmoIndoorCo2StateTypeId, generateRandomIntValue(950, 999));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginSimulation::simulationTimerTimeout()
|
||||
{
|
||||
QTimer *t = static_cast<QTimer*>(sender());
|
||||
Thing *thing = m_simulationTimers.key(t);
|
||||
if (thing->thingClassId() == garageGateThingClassId) {
|
||||
if (thing->stateValue(garageGateStateStateTypeId).toString() == "opening") {
|
||||
thing->setStateValue(garageGateIntermediatePositionStateTypeId, false);
|
||||
thing->setStateValue(garageGateStateStateTypeId, "open");
|
||||
}
|
||||
if (thing->stateValue(garageGateStateStateTypeId).toString() == "closing") {
|
||||
thing->setStateValue(garageGateIntermediatePositionStateTypeId, false);
|
||||
thing->setStateValue(garageGateStateStateTypeId, "closed");
|
||||
}
|
||||
} else if (thing->thingClassId() == extendedAwningThingClassId) {
|
||||
int currentValue = thing->stateValue(extendedAwningPercentageStateTypeId).toInt();
|
||||
int targetValue = t->property("targetValue").toInt();
|
||||
int newValue = targetValue > currentValue ? qMin(targetValue, currentValue + 5) : qMax(targetValue, currentValue - 5);
|
||||
thing->setStateValue(extendedAwningPercentageStateTypeId, newValue);
|
||||
if (newValue == targetValue) {
|
||||
t->stop();
|
||||
thing->setStateValue(extendedAwningMovingStateTypeId, false);
|
||||
}
|
||||
} else if (thing->thingClassId() == extendedBlindThingClassId) {
|
||||
int currentValue = thing->stateValue(extendedBlindPercentageStateTypeId).toInt();
|
||||
int targetValue = t->property("targetValue").toInt();
|
||||
int newValue = targetValue > currentValue ? qMin(targetValue, currentValue + 5) : qMax(targetValue, currentValue - 5);
|
||||
thing->setStateValue(extendedBlindPercentageStateTypeId, newValue);
|
||||
if (newValue == targetValue) {
|
||||
t->stop();
|
||||
thing->setStateValue(extendedBlindMovingStateTypeId, false);
|
||||
}
|
||||
} else if (thing->thingClassId() == venetianBlindThingClassId) {
|
||||
int targetPosition = t->property("targetPosition").toInt();
|
||||
int targetAngle = t->property("targetAngle").toInt();
|
||||
|
||||
int currentPosition = thing->stateValue(venetianBlindPercentageStateTypeId).toInt();
|
||||
int currentAngle = thing->stateValue(venetianBlindAngleStateTypeId).toInt();
|
||||
|
||||
int newPosition = targetPosition > currentPosition ? qMin(targetPosition, currentPosition + 5) : qMax(targetPosition, currentPosition - 5);
|
||||
thing->setStateValue(venetianBlindPercentageStateTypeId, newPosition);
|
||||
|
||||
int newAngle = targetAngle > currentAngle ? qMin(targetAngle, currentAngle + 5) : qMax(targetAngle, currentAngle - 5);
|
||||
thing->setStateValue(venetianBlindAngleStateTypeId, newAngle);
|
||||
|
||||
if (newPosition == targetPosition && newAngle == targetAngle) {
|
||||
t->stop();
|
||||
thing->setStateValue(venetianBlindMovingStateTypeId, false);
|
||||
}
|
||||
} else if (thing->thingClassId() == rollerShutterThingClassId) {
|
||||
int currentValue = thing->stateValue(rollerShutterPercentageStateTypeId).toInt();
|
||||
int targetValue = t->property("targetValue").toInt();
|
||||
int newValue = targetValue > currentValue ? qMin(targetValue, currentValue + 5) : qMax(targetValue, currentValue - 5);
|
||||
thing->setStateValue(rollerShutterPercentageStateTypeId, newValue);
|
||||
if (newValue == targetValue) {
|
||||
t->stop();
|
||||
thing->setStateValue(rollerShutterMovingStateTypeId, false);
|
||||
}
|
||||
} else if (thing->thingClassId() == fingerPrintSensorThingClassId) {
|
||||
EventTypeId evt = qrand() % 2 == 0 ? fingerPrintSensorAccessGrantedEventTypeId : fingerPrintSensorAccessDeniedEventTypeId;
|
||||
ParamList params;
|
||||
if (evt == fingerPrintSensorAccessGrantedEventTypeId) {
|
||||
QStringList users = thing->stateValue(fingerPrintSensorUsersStateTypeId).toStringList();
|
||||
QString user = users.at(qrand() % users.count());
|
||||
QSettings settings;
|
||||
settings.beginGroup(thing->id().toString());
|
||||
QStringList fingers = settings.value(user).toStringList();
|
||||
params.append(Param(fingerPrintSensorAccessGrantedEventUserIdParamTypeId, user));
|
||||
QString finger = fingers.at(qrand() % fingers.count());
|
||||
params.append(Param(fingerPrintSensorAccessGrantedEventFingerParamTypeId, finger));
|
||||
qCDebug(dcSimulation()) << "Emitting fingerprint accepted for user" << user << "and finger" << finger;
|
||||
} else {
|
||||
qCDebug(dcSimulation()) << "Emitting fingerprint denied";
|
||||
}
|
||||
Event event(evt, thing->id(), params);
|
||||
emitEvent(event);
|
||||
} else if (thing->thingClassId() == thermostatThingClassId) {
|
||||
thing->setStateValue(thermostatBoostStateTypeId, false);
|
||||
t->stop();
|
||||
} else if (thing->thingClassId() == barcodeScannerThingClassId) {
|
||||
QString code;
|
||||
int codeIndex = thing->property("codeIndex").toInt();
|
||||
switch (codeIndex) {
|
||||
case 0:
|
||||
code = "12345";
|
||||
thing->setProperty("codeIndex", 1);
|
||||
break;
|
||||
case 1:
|
||||
code = "23456";
|
||||
thing->setProperty("codeIndex", 2);
|
||||
break;
|
||||
default:
|
||||
code = "34567";
|
||||
thing->setProperty("codeIndex", 0);
|
||||
break;
|
||||
}
|
||||
|
||||
ParamList params = ParamList() << Param(barcodeScannerCodeScannedEventContentParamTypeId, code);
|
||||
Event event(barcodeScannerCodeScannedEventTypeId, thing->id(), params);
|
||||
emit emitEvent(event);
|
||||
} else if (thing->thingClassId() == contactSensorThingClassId) {
|
||||
thing->setStateValue(contactSensorClosedStateTypeId, !thing->stateValue(contactSensorClosedStateTypeId).toBool());
|
||||
thing->setStateValue(contactSensorBatteryLevelStateTypeId, thing->stateValue(contactSensorBatteryLevelStateTypeId).toInt()-1);
|
||||
|
||||
if (thing->stateValue(contactSensorBatteryLevelStateTypeId).toInt() == 0) {
|
||||
thing->setStateValue(contactSensorBatteryLevelStateTypeId, 100);
|
||||
thing->setStateValue(contactSensorBatteryCriticalStateTypeId, false);
|
||||
} else if (thing->stateValue(contactSensorBatteryLevelStateTypeId).toInt() <= 20) {
|
||||
thing->setStateValue(contactSensorBatteryCriticalStateTypeId, true);
|
||||
} else {
|
||||
thing->setStateValue(contactSensorBatteryCriticalStateTypeId, false);
|
||||
}
|
||||
} else if (thing->thingClassId() == waterSensorThingClassId) {
|
||||
bool wet = qrand() > (RAND_MAX / 2);
|
||||
thing->setStateValue(waterSensorWaterDetectedStateTypeId, wet);
|
||||
} else if (thing->thingClassId() == cleaningRobotThingClassId) {
|
||||
thing->setStateValue(cleaningRobotRobotStateStateTypeId, "docked");
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,79 +0,0 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef INTEGRATIONPLUGINSIMMULATION_H
|
||||
#define INTEGRATIONPLUGINSIMMULATION_H
|
||||
|
||||
#include "integrations/integrationplugin.h"
|
||||
#include "plugintimer.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
class IntegrationPluginSimulation : public IntegrationPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginsimulation.json")
|
||||
Q_INTERFACES(IntegrationPlugin)
|
||||
|
||||
|
||||
public:
|
||||
explicit IntegrationPluginSimulation();
|
||||
~IntegrationPluginSimulation();
|
||||
|
||||
void init() override;
|
||||
void setupThing(ThingSetupInfo *info) override;
|
||||
void thingRemoved(Thing *thing) override;
|
||||
void executeAction(ThingActionInfo *info) override;
|
||||
|
||||
private:
|
||||
PluginTimer *m_pluginTimer20Seconds = nullptr;
|
||||
PluginTimer *m_pluginTimer5Min = nullptr;
|
||||
|
||||
int generateRandomIntValue(int min, int max);
|
||||
double generateRandomDoubleValue(double min, double max);
|
||||
bool generateRandomBoolValue();
|
||||
|
||||
// Generates values in a sin curve from min to max, moving the start by hourOffset from midnight
|
||||
qreal generateSinValue(int min, int max, int hourOffset, int decimals = 2);
|
||||
qreal generateBatteryValue(int chargeStartHour, int chargeDurationInMinutes);
|
||||
qreal generateNoisyRectangle(int min, int max, int noise, int stablePeriodInMinutes, int &lastValue, QDateTime &lastChangeTimestamp);
|
||||
|
||||
QHash<Thing*, QTimer*> m_simulationTimers;
|
||||
private slots:
|
||||
void onPluginTimer20Seconds();
|
||||
void onPluginTimer5Minutes();
|
||||
void simulationTimerTimeout();
|
||||
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINSIMMULATION_H
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,12 +0,0 @@
|
||||
{
|
||||
"title": "Simulation",
|
||||
"tagline": "Simulate different thing types.",
|
||||
"icon": "simulation.svg",
|
||||
"stability": "consumer",
|
||||
"offline": false,
|
||||
"technologies": [
|
||||
],
|
||||
"categories": [
|
||||
"tool"
|
||||
]
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
include(../plugins.pri)
|
||||
|
||||
TARGET = $$qtLibraryTarget(nymea_integrationpluginsimulation)
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginsimulation.cpp \
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginsimulation.h \
|
||||
@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M447.1,256.2C401.8,204,339.2,144,256,144c-33.6,0-64.4,9.5-96.9,29.8C131.7,191,103.6,215.2,65,255l-1,1l6.7,6.9
|
||||
C125.8,319.3,173.4,368,256,368c36.5,0,71.9-11.9,108.2-36.4c30.9-20.9,57.2-47.4,78.3-68.8l5.5-5.5L447.1,256.2z M256,160
|
||||
c33.1,0,64.9,9.5,97.2,30.6c23.9,15.6,47.4,36.7,73.7,66.1C388.6,295.4,331.1,352,256,352c-34.2,0-64.2-8.4-94.2-28.2
|
||||
c-27.5-18.1-52.3-43.3-76.2-67.8C144.7,196.3,194,160,256,160z"/>
|
||||
<path d="M256,336c44.1,0,80-35.9,80-80c0-44.1-35.9-80-80-80c-44.1,0-80,35.9-80,80C176,300.1,211.9,336,256,336z M256,192.3
|
||||
c35.2,0,64,28.6,64,63.7c0,35.1-28.8,63.7-64,63.7c-35.2,0-63.9-28.6-63.9-63.7C192.1,220.9,220.8,192.3,256,192.3z"/>
|
||||
</g>
|
||||
<path d="M288,256L288,256c0,17.5-14.4,32-31.8,32S224,272.8,224,255.3s15.8-31.3,32-31.3l0-16c-26.5,0-47.9,21.6-47.9,48.2
|
||||
c0,26.6,21.5,48.1,47.9,48.1s48-21.6,48-48.1V256H288z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user