// 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-energy-plugin-nymea.
*
* nymea-energy-plugin-nymea.s 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-energy-plugin-nymea.s 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-energy-plugin-nymea. If not, see .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "integrationpluginenergymocks.h"
#include "plugininfo.h"
#include "mockcontroller.h"
#include
#include
IntegrationPluginEnergyMocks::IntegrationPluginEnergyMocks(QObject *parent) : IntegrationPlugin(parent)
{
}
void IntegrationPluginEnergyMocks::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
qCDebug(dcEnergyMocks()) << "Setting up" << thing << thing->params();
if (thing->thingClassId() == chargerThingClassId) {
EnergyMockController *controller = new EnergyMockController(thing, this);
ParamType paramType = thing->thingClass().paramTypes().findByName("port");
quint16 port = thing->paramValue(paramType.id()).toUInt();
if (!controller->listen(QHostAddress::Any, port)) {
qCWarning(dcEnergyMocks()) << "Failed to start mock controller on port" << controller->errorString();
delete controller;
info->finish(Thing::ThingErrorThingInUse);
return;
}
connect(controller, &EnergyMockController::updateStateRequestReceived, thing, [=](const QUrlQuery &query){
if (query.hasQueryItem("connected"))
thing->setStateValue("connected", QVariant(query.queryItemValue("connected")).toBool());
if (query.hasQueryItem("power"))
thing->setStateValue("power", QVariant(query.queryItemValue("power")).toBool());
if (query.hasQueryItem("pluggedIn"))
thing->setStateValue("pluggedIn", QVariant(query.queryItemValue("pluggedIn")).toBool());
if (query.hasQueryItem("usedPhases")) {
thing->setStateValue("usedPhases", query.queryItemValue("usedPhases"));
thing->setStateValue("phaseCount", Electricity::getPhaseCount(Electricity::convertPhasesFromString(query.queryItemValue("usedPhases"))));
}
if (query.hasQueryItem("maxChargingCurrent"))
thing->setStateValue("maxChargingCurrent", QVariant(query.queryItemValue("maxChargingCurrent")).toInt());
if (query.hasQueryItem("maxChargingCurrentMaxValue"))
thing->setStateMaxValue("maxChargingCurrent", QVariant(query.queryItemValue("maxChargingCurrentMaxValue")).toInt());
updateChargerMeter(thing);
qCDebug(dcEnergyMocks()) << "--> States" << thing->name();
foreach (const State &state, thing->states())
qCDebug(dcEnergyMocks()) << " -" << thing->thingClass().stateTypes().findById(state.stateTypeId()).displayName() << ":" << state.value();
});
qCDebug(dcEnergyMocks()) << "Setting up charger" << thing->name() << "finished successfully";
m_controllers.insert(thing, controller);
info->finish(Thing::ThingErrorNoError);
thing->setStateValue("usedPhases", thing->paramValue("phases").toString());
thing->setStateValue("phaseCount", Electricity::getPhaseCount(Electricity::convertPhasesFromString(thing->stateValue("usedPhases").toString())));
thing->setStateMaxValue("maxChargingCurrent", QVariant(thing->paramValue("maxChargingCurrentUpperLimit")).toInt());
updateChargerMeter(thing);
return;
} else if (thing->thingClassId() == chargerPhaseSwitchingThingClassId) {
EnergyMockController *controller = new EnergyMockController(thing, this);
ParamType paramType = thing->thingClass().paramTypes().findByName("port");
quint16 port = thing->paramValue(paramType.id()).toUInt();
if (!controller->listen(QHostAddress::Any, port)) {
qCWarning(dcEnergyMocks()) << "Failed to start mock controller on port" << controller->errorString();
delete controller;
info->finish(Thing::ThingErrorThingInUse);
return;
}
connect(controller, &EnergyMockController::updateStateRequestReceived, thing, [=](const QUrlQuery &query){
if (query.hasQueryItem("connected"))
thing->setStateValue("connected", QVariant(query.queryItemValue("connected")).toBool());
if (query.hasQueryItem("power"))
thing->setStateValue("power", QVariant(query.queryItemValue("power")).toBool());
if (query.hasQueryItem("pluggedIn"))
thing->setStateValue("pluggedIn", QVariant(query.queryItemValue("pluggedIn")).toBool());
if (query.hasQueryItem("usedPhases")) {
thing->setStateValue("usedPhases", query.queryItemValue("usedPhases"));
thing->setStateValue("phaseCount", Electricity::getPhaseCount(Electricity::convertPhasesFromString(query.queryItemValue("usedPhases"))));
}
if (query.hasQueryItem("maxChargingCurrent"))
thing->setStateValue("maxChargingCurrent", QVariant(query.queryItemValue("maxChargingCurrent")).toInt());
if (query.hasQueryItem("maxChargingCurrentMaxValue"))
thing->setStateMaxValue("maxChargingCurrent", QVariant(query.queryItemValue("maxChargingCurrentMaxValue")).toInt());
if (query.hasQueryItem("desiredPhaseCount"))
thing->setStateValue("desiredPhaseCount", QVariant(query.queryItemValue("desiredPhaseCount")).toUInt());
updateChargerMeter(thing);
qCDebug(dcEnergyMocks()) << "--> States" << thing->name();
foreach (const State &state, thing->states())
qCDebug(dcEnergyMocks()) << " -" << thing->thingClass().stateTypes().findById(state.stateTypeId()).displayName() << ":" << state.value();
});
qCDebug(dcEnergyMocks()) << "Setting up charger" << thing->name() << "finished successfully";
m_controllers.insert(thing, controller);
info->finish(Thing::ThingErrorNoError);
thing->setStateValue("usedPhases", thing->paramValue("phases").toString());
thing->setStateValue("phaseCount", Electricity::getPhaseCount(Electricity::convertPhasesFromString(thing->stateValue("usedPhases").toString())));
thing->setStateMaxValue("maxChargingCurrent", QVariant(thing->paramValue("maxChargingCurrentUpperLimit")).toInt());
updateChargerMeter(thing);
return;
} else if (thing->thingClassId() == simpleChargerThingClassId) {
EnergyMockController *controller = new EnergyMockController(thing, this);
ParamType paramType = thing->thingClass().paramTypes().findByName("port");
quint16 port = thing->paramValue(paramType.id()).toUInt();
if (!controller->listen(QHostAddress::Any, port)) {
qCWarning(dcEnergyMocks()) << "Failed to start mock controller on port" << controller->errorString();
delete controller;
info->finish(Thing::ThingErrorThingInUse);
return;
}
connect(controller, &EnergyMockController::updateStateRequestReceived, thing, [=](const QUrlQuery &query){
if (query.hasQueryItem("connected"))
thing->setStateValue("connected", QVariant(query.queryItemValue("connected")).toBool());
if (query.hasQueryItem("power"))
thing->setStateValue("power", QVariant(query.queryItemValue("power")).toBool());
if (query.hasQueryItem("pluggedIn"))
thing->setStateValue("pluggedIn", QVariant(query.queryItemValue("pluggedIn")).toBool());
if (query.hasQueryItem("phaseCount"))
thing->setStateValue("phaseCount", QVariant(query.queryItemValue("phaseCount")).toInt());
if (query.hasQueryItem("maxChargingCurrent"))
thing->setStateValue("maxChargingCurrent", QVariant(query.queryItemValue("maxChargingCurrent")).toInt());
if (query.hasQueryItem("maxChargingCurrentMaxValue"))
thing->setStateMaxValue("maxChargingCurrent", QVariant(query.queryItemValue("maxChargingCurrentMaxValue")).toInt());
qCDebug(dcEnergyMocks()) << "--> States" << thing->name();
foreach (const State &state, thing->states())
qCDebug(dcEnergyMocks()) << " -" << thing->thingClass().stateTypes().findById(state.stateTypeId()).displayName() << ":" << state.value();
});
m_controllers.insert(thing, controller);
qCDebug(dcEnergyMocks()) << "Setting up simple charger" << thing->name() << "finished successfully";
info->finish(Thing::ThingErrorNoError);
return;
} else if (thing->thingClassId() == meterThingClassId) {
EnergyMockController *controller = new EnergyMockController(thing, this);
ParamType paramType = thing->thingClass().paramTypes().findByName("port");
quint16 port = thing->paramValue(paramType.id()).toUInt();
if (!controller->listen(QHostAddress::Any, port)) {
qCWarning(dcEnergyMocks()) << "Failed to start mock controller on port" << controller->errorString();
delete controller;
info->finish(Thing::ThingErrorThingInUse);
return;
}
connect(controller, &EnergyMockController::updateStateRequestReceived, thing, [=](const QUrlQuery &query){
if (query.hasQueryItem("connected"))
thing->setStateValue("connected", QVariant(query.queryItemValue("connected")).toBool());
if (query.hasQueryItem("originalPower")) {
// Split the original power on the 3 phases and add the current power (depending on the phases) from the charger
// double originalPhasePower = query.queryItemValue("originalPower").toInt() / 3.0;
// double phaseA = originalPhasePower;
// double phaseB = originalPhasePower;
// double phaseC = originalPhasePower;
// // Get charger phases, set each phase and then the sum
// foreach (Thing *chargerThing, myThings().filterByThingClassId(chargerThingClassId)) {
// Electricity::Phases phases = Electricity::convertPhasesFromString(thing->paramValue("phases").toString());
// //
// }
}
if (query.hasQueryItem("currentPowerPhaseA") && query.hasQueryItem("currentPowerPhaseB") && query.hasQueryItem("currentPowerPhaseC")) {
// We directly set the current power on the 3 phases, no need to perform calculations (for tests to emulate a certain situation)
int currentPowerPhaseA = query.queryItemValue("currentPowerPhaseA").toInt();
int currentPowerPhaseB = query.queryItemValue("currentPowerPhaseB").toInt();
int currentPowerPhaseC = query.queryItemValue("currentPowerPhaseC").toInt();
thing->setStateValue("currentPhaseA", currentPowerPhaseA / 230.0);
thing->setStateValue("currentPhaseB", currentPowerPhaseB / 230.0);
thing->setStateValue("currentPhaseC", currentPowerPhaseC / 230.0);
thing->setStateValue("currentPowerPhaseA", currentPowerPhaseA);
thing->setStateValue("currentPowerPhaseB", currentPowerPhaseB);
thing->setStateValue("currentPowerPhaseC", currentPowerPhaseC);
// Note: set the current power at the end since that triggers caclulations....
thing->setStateValue("currentPower", currentPowerPhaseA + currentPowerPhaseB + currentPowerPhaseC);
}
qCDebug(dcEnergyMocks()) << "--> States" << thing->name();
foreach (const State &state, thing->states())
qCDebug(dcEnergyMocks()) << " -" << thing->thingClass().stateTypes().findById(state.stateTypeId()).displayName() << ":" << state.value();
});
m_controllers.insert(thing, controller);
qCDebug(dcEnergyMocks()) << "Setting up meter" << thing->name() << "finished successfully";
info->finish(Thing::ThingErrorNoError);
return;
} else if (thing->thingClassId() == carThingClassId) {
EnergyMockController *controller = new EnergyMockController(thing, this);
ParamType paramType = thing->thingClass().paramTypes().findByName("port");
quint16 port = thing->paramValue(paramType.id()).toUInt();
if (!controller->listen(QHostAddress::Any, port)) {
qCWarning(dcEnergyMocks()) << "Failed to start mock controller on port" << controller->errorString();
delete controller;
info->finish(Thing::ThingErrorThingInUse);
return;
}
connect(controller, &EnergyMockController::updateStateRequestReceived, thing, [=](const QUrlQuery &query){
if (query.hasQueryItem("batteryLevel"))
thing->setStateValue("batteryLevel", QVariant(query.queryItemValue("batteryLevel")).toInt());
if (query.hasQueryItem("capacity"))
thing->setStateValue("capacity", QVariant(query.queryItemValue("capacity")).toInt());
if (query.hasQueryItem("minChargingCurrent"))
thing->setStateValue("minChargingCurrent", QVariant(query.queryItemValue("minChargingCurrent")).toInt());
if (query.hasQueryItem("phaseCount")) {
thing->setSettingValue(carSettingsPhaseCountParamTypeId, QVariant(query.queryItemValue("phaseCount")).toInt());
thing->setStateValue("phaseCount", QVariant(query.queryItemValue("phaseCount")).toInt());
}
});
// Set the min charging current state if the settings value changed
connect(thing, &Thing::settingChanged, controller, [thing](const ParamTypeId ¶mTypeId, const QVariant &value){
if (paramTypeId == carSettingsCapacityParamTypeId) {
qCDebug(dcEnergyMocks()) << "Car capacity settings changed" << value << "kWh";
thing->setStateValue(carCapacityStateTypeId, value);
} else if (paramTypeId == carSettingsMinChargingCurrentParamTypeId) {
qCDebug(dcEnergyMocks()) << "Car minimum charging current settings changed" << value.toUInt() << "A";
thing->setStateValue(carMinChargingCurrentStateTypeId, value);
} else if (paramTypeId == carSettingsPhaseCountParamTypeId) {
qCDebug(dcEnergyMocks()) << "Car phase count settings changed" << value.toUInt();
thing->setStateValue(carPhaseCountStateTypeId, value);
}
qCDebug(dcEnergyMocks()) << "--> States" << thing->name();
foreach (const State &state, thing->states())
qCDebug(dcEnergyMocks()) << " -" << thing->thingClass().stateTypes().findById(state.stateTypeId()).displayName() << ":" << state.value();
});
qCDebug(dcEnergyMocks()) << "Setting car" << thing->name() << "finished successfully";
m_controllers.insert(thing, controller);
info->finish(Thing::ThingErrorNoError);
thing->setStateValue(carMinChargingCurrentStateTypeId, thing->setting(carSettingsMinChargingCurrentParamTypeId));
return;
} else if (thing->thingClassId() == notificationThingClassId) {
EnergyMockController *controller = new EnergyMockController(thing, this);
ParamType paramType = thing->thingClass().paramTypes().findByName("port");
quint16 port = thing->paramValue(paramType.id()).toUInt();
if (!controller->listen(QHostAddress::Any, port)) {
qCWarning(dcEnergyMocks()) << "Failed to start mock controller on port" << controller->errorString();
delete controller;
info->finish(Thing::ThingErrorThingInUse);
return;
}
connect(controller, &EnergyMockController::updateStateRequestReceived, thing, [=](const QUrlQuery &query){
Q_UNUSED(query)
});
m_controllers.insert(thing, controller);
qCDebug(dcEnergyMocks()) << "Setting up notification" << thing->name() << "finished successfully";
info->finish(Thing::ThingErrorNoError);
return;
} else if (thing->thingClassId() == energyStorageThingClassId) {
EnergyMockController * controller = new EnergyMockController(thing, this);
ParamType paramType = thing->thingClass().paramTypes().findByName("port");
quint16 port = thing->paramValue(paramType.id()).toUInt();
if (!controller->listen(QHostAddress::Any, port)) {
qWarning(dcEnergyMocks()) << "Failed to start mock controller on port" << controller->errorString();
delete controller;
info->finish(Thing::ThingErrorThingInUse);
return;
}
connect(controller, &EnergyMockController::updateStateRequestReceived, thing, [=](const QUrlQuery &query){
if (query.hasQueryItem("batteryLevel")) {
thing->setStateValue("batteryLevel", QVariant(query.queryItemValue("batteryLevel")).toInt());
thing->setStateValue("batteryCritical", thing->stateValue("batteryLevel").toInt() < 10);
}
if (query.hasQueryItem("currentPower"))
thing->setStateValue("currentPower", QVariant(query.queryItemValue("currentPower")).toInt());
});
qCDebug(dcEnergyMocks()) << "Setting battery storage" << thing->name() << "finished successfully";
m_controllers.insert(thing, controller);
info->finish(Thing::ThingErrorNoError);
thing->setStateValue("capacity", thing->paramValue(energyStorageThingCapacityParamTypeId));
return;
}
}
void IntegrationPluginEnergyMocks::thingRemoved(Thing *thing)
{
qCDebug(dcEnergyMocks()) << "Removing" << thing;
if (m_controllers.contains(thing)) {
delete m_controllers.take(thing);
}
}
void IntegrationPluginEnergyMocks::executeAction(ThingActionInfo *info)
{
Thing *thing = info->thing();
ThingClass thingClass = info->thing()->thingClass();
Action action = info->action();
ActionType actionType = thingClass.actionTypes().findById(action.actionTypeId());
qCDebug(dcEnergyMocks()) << "Executing action" << actionType.displayName() << "on" << thing->name() << action.params();
m_controllers.value(thing)->logActionExecuted(actionType, action);
// Update states
if (thing->thingClassId() == chargerThingClassId) {
// Note do this before every action execution since it might hase changed directly within the simulation
Electricity::Phases usedPhases = Electricity::convertPhasesFromString(thing->stateValue("usedPhases").toString());
uint phaseCount = Electricity::getPhaseCount(usedPhases);
thing->setStateValue("phaseCount", phaseCount);
if (actionType.name() == "maxChargingCurrent") {
uint maxChargingCurrent = action.paramValue(actionType.paramTypes().findByName("maxChargingCurrent").id()).toUInt();
thing->setStateValue("maxChargingCurrent", maxChargingCurrent);
updateChargerMeter(thing);
}
if (actionType.name() == "power") {
bool power = action.paramValue(actionType.paramTypes().findByName("power").id()).toBool();
qCDebug(dcEnergyMocks()) << "Setting charger power to" << power;
thing->setStateValue("power", power);
updateChargerMeter(thing);
}
if (actionType.name() == "update") {
updateChargerMeter(thing);
}
}
if (thing->thingClassId() == chargerPhaseSwitchingThingClassId) {
// Note do this before every action execution since it might has changed directly within the simulation
Electricity::Phases usedPhases = Electricity::convertPhasesFromString(thing->stateValue("usedPhases").toString());
uint phaseCount = Electricity::getPhaseCount(usedPhases);
thing->setStateValue("phaseCount", phaseCount);
if (actionType.name() == "maxChargingCurrent") {
uint maxChargingCurrent = action.paramValue(actionType.paramTypes().findByName("maxChargingCurrent").id()).toUInt();
thing->setStateValue("maxChargingCurrent", maxChargingCurrent);
updateChargerMeter(thing);
}
if (actionType.name() == "connected") {
bool connected = action.paramValue(actionType.paramTypes().findByName("connected").id()).toBool();
thing->setStateValue("connected", connected);
updateChargerMeter(thing);
}
if (actionType.name() == "pluggedIn") {
bool pluggedIn = action.paramValue(actionType.paramTypes().findByName("pluggedIn").id()).toBool();
thing->setStateValue("pluggedIn", pluggedIn);
updateChargerMeter(thing);
}
if (actionType.name() == "desiredPhaseCount") {
uint desiredPhaseCount = action.paramValue(actionType.paramTypes().findByName("desiredPhaseCount").id()).toUInt();
thing->setStateValue("desiredPhaseCount", desiredPhaseCount);
updateChargerMeter(thing);
}
if (actionType.name() == "power") {
bool power = action.paramValue(actionType.paramTypes().findByName("power").id()).toBool();
qCDebug(dcEnergyMocks()) << "Setting charger power to" << power;
thing->setStateValue("power", power);
updateChargerMeter(thing);
}
if (actionType.name() == "update") {
updateChargerMeter(thing);
}
}
if (thing->thingClassId() == simpleChargerThingClassId) {
if (actionType.name() == "maxChargingCurrent") {
uint maxChargingCurrent = action.paramValue(actionType.paramTypes().findByName("maxChargingCurrent").id()).toUInt();
thing->setStateValue("maxChargingCurrent", action.paramValue(actionType.paramTypes().findByName("maxChargingCurrent").id()).toUInt());
qCDebug(dcEnergyMocks()) << "Setting max charging current to" << maxChargingCurrent << "A";
}
if (actionType.name() == "power") {
bool power = action.paramValue(actionType.paramTypes().findByName("power").id()).toBool();
qCDebug(dcEnergyMocks()) << "Setting charger power to" << power;
thing->setStateValue("power", power);
}
}
if (thing->thingClassId() == carThingClassId) {
if (action.actionTypeId() == carBatteryLevelActionTypeId) {
thing->setStateValue(carBatteryLevelStateTypeId, action.paramValue(carBatteryLevelActionBatteryLevelParamTypeId));
thing->setStateValue(carBatteryCriticalStateTypeId, action.paramValue(carBatteryLevelActionBatteryLevelParamTypeId).toInt() < 10);
info->finish(Thing::ThingErrorNoError);
return;
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8());
}
}
info->finish(Thing::ThingErrorNoError);
}
void IntegrationPluginEnergyMocks::updateChargerMeter(Thing *thing)
{
Electricity::Phases connectedPhases = Electricity::convertPhasesFromString(thing->paramValue("phases").toString());
bool canSwitchPhases = thing->hasState("desiredPhaseCount");
Electricity::Phases usedPhases = Electricity::PhaseNone;
if (canSwitchPhases) {
uint desiredPhaseCount = thing->stateValue("desiredPhaseCount").toUInt();
if (desiredPhaseCount == 1) {
usedPhases = Electricity::PhaseA;
} else {
usedPhases = Electricity::PhaseAll;
}
thing->setStateValue("usedPhases", Electricity::convertPhasesToString(usedPhases));
} else {
usedPhases = Electricity::convertPhasesFromString(thing->stateValue("usedPhases").toString());
}
uint phaseCount = Electricity::getPhaseCount(usedPhases);
thing->setStateValue("phaseCount", phaseCount);
if (connectedPhases.testFlag(Electricity::PhaseA)) {
thing->setStateValue("voltagePhaseA", 230);
} else {
thing->setStateValue("voltagePhaseA", 0);
}
if (connectedPhases.testFlag(Electricity::PhaseB)) {
thing->setStateValue("voltagePhaseB", 230);
} else {
thing->setStateValue("voltagePhaseB", 0);
}
if (connectedPhases.testFlag(Electricity::PhaseC)) {
thing->setStateValue("voltagePhaseC", 230);
} else {
thing->setStateValue("voltagePhaseC", 0);
}
double maxChargingCurrent = thing->stateValue("maxChargingCurrent").toDouble();
if (thing->stateValue("power").toBool() && thing->stateValue("connected").toBool() && thing->stateValue("pluggedIn").toBool()) {
double currentPower = maxChargingCurrent * 230 * phaseCount;
double phasePower = currentPower / phaseCount;
double phaseAmpere = maxChargingCurrent;
if (usedPhases.testFlag(Electricity::PhaseA)) {
thing->setStateValue("currentPhaseA", phaseAmpere);
thing->setStateValue("currentPowerPhaseA", phasePower);
} else {
thing->setStateValue("currentPhaseA", 0);
thing->setStateValue("currentPowerPhaseA", 0);
}
if (usedPhases.testFlag(Electricity::PhaseB)) {
thing->setStateValue("currentPhaseB", phaseAmpere);
thing->setStateValue("currentPowerPhaseB", phasePower);
} else {
thing->setStateValue("currentPhaseB", 0);
thing->setStateValue("currentPowerPhaseB", 0);
}
if (usedPhases.testFlag(Electricity::PhaseC)) {
thing->setStateValue("currentPhaseC", phaseAmpere);
thing->setStateValue("currentPowerPhaseC", phasePower);
} else {
thing->setStateValue("currentPhaseC", 0);
thing->setStateValue("currentPowerPhaseC", 0);
}
thing->setStateValue("currentPower", currentPower);
thing->setStateValue("charging", true);
} else {
thing->setStateValue("currentPhaseA", 0);
thing->setStateValue("currentPowerPhaseA", 0);
thing->setStateValue("currentPhaseB", 0);
thing->setStateValue("currentPowerPhaseB", 0);
thing->setStateValue("currentPhaseC", 0);
thing->setStateValue("currentPowerPhaseC", 0);
thing->setStateValue("currentPower", 0);
thing->setStateValue("charging", false);
}
}