diff --git a/mennekes/amtron-hcc3-registers.json b/mennekes/amtron-hcc3-registers.json index c17a910..32d3988 100644 --- a/mennekes/amtron-hcc3-registers.json +++ b/mennekes/amtron-hcc3-registers.json @@ -2,6 +2,7 @@ "className": "AmtronHCC3", "protocol": "TCP", "endianness": "LittleEndian", + "stringEndianness": "LittleEndian", "errorLimitUntilNotReachable": 20, "checkReachableRegister": "customerCurrentLimitation", "enums": [ @@ -299,7 +300,7 @@ "address": 781, "size": 2, "type": "uint32", - "unut": "Wh", + "unit": "Wh", "registerType": "inputRegister", "description": "Charging session meter count", "defaultValue": "0", @@ -317,6 +318,34 @@ "access": "RO" } ] + }, + { + "id": "limits", + "readSchedule": "update", + "registers": [ + { + "id": "plannedMinimumCurrent", + "address": 805, + "size": 1, + "type": "uint16", + "readSchedule": "update", + "registerType": "inputRegister", + "description": "Planned Minimum Current per Phase", + "unit": "A", + "access": "RO" + }, + { + "id": "plannedMaximumCurrent", + "address": 806, + "size": 1, + "type": "uint16", + "readSchedule": "update", + "registerType": "inputRegister", + "description": "Planned Maximum Current per Phase", + "unit": "A", + "access": "RO" + } + ] } ], "registers": [ diff --git a/mennekes/integrationpluginmennekes.cpp b/mennekes/integrationpluginmennekes.cpp index 5a37e01..20b871a 100644 --- a/mennekes/integrationpluginmennekes.cpp +++ b/mennekes/integrationpluginmennekes.cpp @@ -280,6 +280,50 @@ void IntegrationPluginMennekes::executeAction(ThingActionInfo *info) }); } } + + if (info->thing()->thingClassId() == amtronHCC3ThingClassId) { + AmtronHCC3ModbusTcpConnection *amtronHCC3Connection = m_amtronHCC3Connections.value(info->thing()); + + if (info->action().actionTypeId() == amtronHCC3PowerActionTypeId) { + bool power = info->action().paramValue(amtronHCC3PowerActionPowerParamTypeId).toBool(); + + AmtronHCC3ModbusTcpConnection::ChargeState chargeState; + if (power) { + // When turning on, we'll need to either start or resume, depending on the current state + if (amtronHCC3Connection->amtronState() == AmtronHCC3ModbusTcpConnection::AmtronStatePaused) { + chargeState = AmtronHCC3ModbusTcpConnection::ChargeStateContinue; + } else { + chargeState = AmtronHCC3ModbusTcpConnection::ChargeStateStart; + } + } else { + // We'll just use Pause as a Terminate command would requre an EV re-plug in order to resume. + chargeState = AmtronHCC3ModbusTcpConnection::ChargeStatePause; + } + + QModbusReply *reply = amtronHCC3Connection->setChangeChargeState(chargeState); + connect(reply, &QModbusReply::finished, info, [info, reply, power](){ + if (reply->error() == QModbusDevice::NoError) { + info->thing()->setStateValue(amtronHCC3PowerStateTypeId, power); + info->finish(Thing::ThingErrorNoError); + } else { + qCWarning(dcMennekes()) << "Error setting charge state:" << reply->error() << reply->errorString(); + info->finish(Thing::ThingErrorHardwareFailure); + } + }); + } + if (info->action().actionTypeId() == amtronHCC3MaxChargingCurrentActionTypeId) { + int maxChargingCurrent = info->action().paramValue(amtronHCC3MaxChargingCurrentActionMaxChargingCurrentParamTypeId).toInt(); + QModbusReply *reply = amtronHCC3Connection->setCustomerCurrentLimitation(maxChargingCurrent); + connect(reply, &QModbusReply::finished, info, [info, reply, maxChargingCurrent](){ + if (reply->error() == QModbusDevice::NoError) { + info->thing()->setStateValue(amtronHCC3MaxChargingCurrentStateTypeId, maxChargingCurrent); + info->finish(Thing::ThingErrorNoError); + } else { + info->finish(Thing::ThingErrorHardwareFailure); + } + }); + } + } } void IntegrationPluginMennekes::thingRemoved(Thing *thing) @@ -534,10 +578,59 @@ void IntegrationPluginMennekes::setupAmtronHCC3Connection(ThingSetupInfo *info) info->finish(Thing::ThingErrorNoError); thing->setStateValue(amtronHCC3ConnectedStateTypeId, true); - amtronHCC3Connection->update(); }); + connect(amtronHCC3Connection, &AmtronHCC3ModbusTcpConnection::updateFinished, thing, [amtronHCC3Connection, thing](){ + qCDebug(dcMennekes()) << "Amtron HCC3 update finished:" << thing->name() << amtronHCC3Connection; + thing->setStateMaxValue(amtronHCC3MaxChargingCurrentStateTypeId, amtronHCC3Connection->installationCurrent()); + }); + + connect(amtronHCC3Connection, &AmtronHCC3ModbusTcpConnection::cpSignalStateChanged, thing, [thing](AmtronHCC3ModbusTcpConnection::CPSignalState cpSignalState) { + qCInfo(dcMennekes()) << "CP signal state changed" << cpSignalState; + thing->setStateValue(amtronHCC3PluggedInStateTypeId, cpSignalState >= AmtronHCC3ModbusTcpConnection::CPSignalStateB1); + }); + + connect(amtronHCC3Connection, &AmtronHCC3ModbusTcpConnection::phaseCountChanged, thing, [thing](quint16 phaseCount) { + qCInfo(dcMennekes()) << "Phase count changed:" << phaseCount; + if (phaseCount > 0) { + thing->setStateValue(amtronHCC3PhaseCountStateTypeId, phaseCount); + } + }); + + connect(amtronHCC3Connection, &AmtronHCC3ModbusTcpConnection::amtronStateChanged, thing, [thing](AmtronHCC3ModbusTcpConnection::AmtronState amtronState) { + qCInfo(dcMennekes()) << "Amtron state changed:" << amtronState; + switch (amtronState) { + case AmtronHCC3ModbusTcpConnection::AmtronStateIdle: + case AmtronHCC3ModbusTcpConnection::AmtronStateStandByAuthorize: + case AmtronHCC3ModbusTcpConnection::AmtronStateStandbyConnect: + case AmtronHCC3ModbusTcpConnection::AmtronStatePaused: + case AmtronHCC3ModbusTcpConnection::AmtronStateTerminated: + case AmtronHCC3ModbusTcpConnection::AmtronStateError: + thing->setStateValue(amtronHCC3ChargingStateTypeId, false); + break; + case AmtronHCC3ModbusTcpConnection::AmtronStateCharging: + thing->setStateValue(amtronHCC3ChargingStateTypeId, true); + break; + } + }); + + connect(amtronHCC3Connection, &AmtronHCC3ModbusTcpConnection::actualPowerConsumptionChanged, thing, [thing](quint32 actualPowerConsumption) { + qCInfo(dcMennekes()) << "Actual power consumption changed:" << actualPowerConsumption; + thing->setStateValue(amtronHCC3CurrentPowerStateTypeId, actualPowerConsumption); + }); + + connect(amtronHCC3Connection, &AmtronHCC3ModbusTcpConnection::chargingSessionMeterChanged, thing, [thing](quint32 chargingSessionMeter) { + thing->setStateValue(amtronHCC3SessionEnergyStateTypeId, chargingSessionMeter / 1000.0); + // Don't have a total... still providing it for the interface, the energy experience will deal with the value resetting frequently + thing->setStateValue(amtronHCC3TotalEnergyConsumedStateTypeId, chargingSessionMeter / 1000.0); + }); + + connect(amtronHCC3Connection, &AmtronHCC3ModbusTcpConnection::customerCurrentLimitationChanged, thing, [thing](quint16 customerCurrentLimitation) { + thing->setStateValue(amtronHCC3MaxChargingCurrentStateTypeId, customerCurrentLimitation); + }); + + amtronHCC3Connection->connectDevice(); } diff --git a/mennekes/integrationpluginmennekes.json b/mennekes/integrationpluginmennekes.json index 1092280..dc3be8a 100644 --- a/mennekes/integrationpluginmennekes.json +++ b/mennekes/integrationpluginmennekes.json @@ -133,6 +133,31 @@ "defaultValue": false, "cached": false }, + { + "id": "fed5a1ba-48f5-41ef-98f7-9356bcae7612", + "name": "pluggedIn", + "displayName": "Plugged in", + "type": "bool", + "defaultValue": false, + "cached": false + }, + { + "id": "982af18d-dbfb-4dd8-b5b3-4d22c64642a6", + "name": "charging", + "displayName": "Charging", + "type": "bool", + "defaultValue": false, + "cached": false + }, + { + "id": "05c095b6-2395-4bbf-8d14-a77576ce7717", + "name": "phaseCount", + "displayName": "Connected phases", + "type": "uint", + "minValue": 1, + "maxValue": 3, + "defaultValue": 1 + }, { "id": "96b9c121-3caf-44fe-8380-483b9b40dbd9", "name": "currentPower", @@ -147,7 +172,15 @@ "id": "3d1384fc-8b46-42b0-b043-23279d8c7665", "name": "totalEnergyConsumed", "displayName": "Total consumed energy", - "displayNameEvent": "Total consumed energy changed", + "type": "double", + "unit": "KiloWattHour", + "defaultValue": 0.0, + "cached": true + }, + { + "id": "c5882da3-963b-4cb3-ba1a-44d16d219e7e", + "name": "sessionEnergy", + "displayName": "Session energy", "type": "double", "unit": "KiloWattHour", "defaultValue": 0.0,