diff --git a/evbox/README.md b/evbox/README.md index fed95b89..1ece872d 100644 --- a/evbox/README.md +++ b/evbox/README.md @@ -7,6 +7,7 @@ This integration allows nymea to control EVBox wallboxes supporting the Protocol Generally, all EVBox wallboxes supporting the Protocol Max v4 are supported. We've tested it with * Elvi +* Elvi + MID meter ## Requirements diff --git a/evbox/integrationpluginevbox.cpp b/evbox/integrationpluginevbox.cpp index 9dba178f..f3de66fc 100644 --- a/evbox/integrationpluginevbox.cpp +++ b/evbox/integrationpluginevbox.cpp @@ -83,10 +83,12 @@ void IntegrationPluginEVBox::discoverThings(ThingDiscoveryInfo *info) return; } - ThingDescriptor thingDescriptor(info->thingClassId(), "EVBox Elvi", serial); + ThingDescriptor thingDescriptor(info->thingClassId(), thingClass(info->thingClassId()).displayName(), serial); + ParamTypeId serialPortParamTypeId = thingClass(info->thingClassId()).paramTypes().findByName("serialPort").id(); + ParamTypeId serialNumberParamTypeId = thingClass(info->thingClassId()).paramTypes().findByName("serialNumber").id(); ParamList params{ - {evboxThingSerialPortParamTypeId, portInfo.portName()}, - {evboxThingSerialNumberParamTypeId, serial} + {serialPortParamTypeId, portInfo.portName()}, + {serialNumberParamTypeId, serial} }; thingDescriptor.setParams(params); Thing *existingThing = myThings().findByParams(params); @@ -117,8 +119,8 @@ void IntegrationPluginEVBox::discoverThings(ThingDiscoveryInfo *info) void IntegrationPluginEVBox::setupThing(ThingSetupInfo *info) { Thing *thing = info->thing(); - QString portName = thing->paramValue(evboxThingSerialPortParamTypeId).toString(); - QString serialNumber = thing->paramValue(evboxThingSerialNumberParamTypeId).toString(); + QString portName = thing->paramValue("serialPort").toString(); + QString serialNumber = thing->paramValue("serialNumber").toString(); // Opening the port, sharing with others if already opened. EVBoxPort *port = m_ports.value(portName); @@ -154,7 +156,7 @@ void IntegrationPluginEVBox::setupThing(ThingSetupInfo *info) // And connect the signals to the thing for when the setup went well connect(port, &EVBoxPort::closed, thing, [thing, portName](){ qCInfo(dcEVBox()) << "Port" << portName << "closed. Marking thing as offline:" << thing->name(); - thing->setStateValue(evboxConnectedStateTypeId, false); + thing->setStateValue("connected", false); }); connect(port, &EVBoxPort::opened, thing, [portName](){ qCInfo(dcEVBox()) << "Port" << portName << "opened."; @@ -164,7 +166,7 @@ void IntegrationPluginEVBox::setupThing(ThingSetupInfo *info) if (serial != serialNumber) { return; } - thing->setStateValue(evboxConnectedStateTypeId, true); + thing->setStateValue("connected", true); finishPendingAction(thing); m_waitingForResponses[thing] = false; }); @@ -173,31 +175,37 @@ void IntegrationPluginEVBox::setupThing(ThingSetupInfo *info) if (serial != serialNumber) { return; } - thing->setStateValue(evboxConnectedStateTypeId, true); + thing->setStateValue("connected", true); finishPendingAction(thing); m_waitingForResponses[thing] = false; - double currentPower = (chargingCurrentL1 + chargingCurrentL2 + chargingCurrentL3) * 23; - thing->setStateValue(evboxCurrentPowerStateTypeId, currentPower); - thing->setStateMinMaxValues(evboxMaxChargingCurrentStateTypeId, minChargingCurrent / 10, maxChargingCurrent / 10); - thing->setStateValue(evboxTotalEnergyConsumedStateTypeId, totalEnergyConsumed / 1000.0); - thing->setStateValue(evboxChargingStateTypeId, currentPower > 0); + thing->setStateMinMaxValues("maxChargingCurrent", minChargingCurrent / 10, maxChargingCurrent / 10); - int phaseCount = 0; - if (chargingCurrentL1 > 0) { - phaseCount++; - } - if (chargingCurrentL2 > 0) { - phaseCount++; - } - if (chargingCurrentL3 > 0) { - phaseCount++; - } - // If all phases are on 0, we aren't charging and don't know how many phases are available... - // Only updating the count if we actually do know that at least one is charging. - if (phaseCount > 0) { - thing->setStateValue(evboxPhaseCountStateTypeId, phaseCount); + if (thing->thingClassId() == elviMidThingClassId) { + double currentPower = (chargingCurrentL1 + chargingCurrentL2 + chargingCurrentL3) * 23; + thing->setStateValue("currentPower", currentPower); + thing->setStateValue("totalEnergyConsumed", totalEnergyConsumed / 1000.0); + thing->setStateValue("charging", currentPower > 0); + int phaseCount = 0; + if (chargingCurrentL1 > 0) { + phaseCount++; + } + if (chargingCurrentL2 > 0) { + phaseCount++; + } + if (chargingCurrentL3 > 0) { + phaseCount++; + } + // If all phases are on 0, we aren't charging and don't know how many phases are available... + // Only updating the count if we actually do know that at least one is charging. + if (phaseCount > 0) { + thing->setStateValue("phaseCount", phaseCount); + } + } else { + thing->setStateValue("charging", thing->stateValue("maxChargingCurrent").toUInt() > 0 && thing->stateValue("power").toBool()); + thing->setStateValue("phaseCount", thing->setting("phaseCount").toUInt()); } + }); } @@ -207,19 +215,19 @@ void IntegrationPluginEVBox::postSetupThing(Thing */*thing*/) m_timer = hardwareManager()->pluginTimerManager()->registerTimer(5); connect(m_timer, &PluginTimer::timeout, this, [this](){ foreach (Thing *t, myThings()) { - QString portName = t->paramValue(evboxThingSerialPortParamTypeId).toString(); - QString serial = t->paramValue(evboxThingSerialNumberParamTypeId).toString(); + QString portName = t->paramValue("serialPort").toString(); + QString serial = t->paramValue("serialNumber").toString(); if (m_waitingForResponses.value(t)) { qCInfo(dcEVBox()) << "Wallbox" << t->name() << "did not respond to last command. Marking offline."; - t->setStateValue(evboxConnectedStateTypeId, false); + t->setStateValue("connected", false); } EVBoxPort *port = m_ports.value(portName); if (port->isOpen()) { quint16 maxChargingCurrent = 0; - if (t->stateValue(evboxPowerStateTypeId).toBool()) { - maxChargingCurrent = t->stateValue(evboxMaxChargingCurrentStateTypeId).toUInt(); + if (t->stateValue("power").toBool()) { + maxChargingCurrent = t->stateValue("maxChargingCurrent").toUInt(); } port->sendCommand(EVBoxPort::Command68, 60, maxChargingCurrent, serial); m_waitingForResponses[t] = true; @@ -231,8 +239,9 @@ void IntegrationPluginEVBox::postSetupThing(Thing */*thing*/) void IntegrationPluginEVBox::thingRemoved(Thing *thing) { - QString portName = thing->paramValue(evboxThingSerialPortParamTypeId).toString(); - if (myThings().filterByParam(evboxThingSerialPortParamTypeId, portName).isEmpty()) { + QString portName = thing->paramValue("serialPort").toString(); + ParamTypeId serialPortParamTypeId = thing->thingClass().paramTypes().findByName("serialPort").id(); + if (myThings().filterByParam(serialPortParamTypeId, portName).isEmpty()) { qCInfo(dcEVBox()).nospace() << "No more EVBox devices using port " << portName << ". Destroying port."; delete m_ports.take(portName); } @@ -247,17 +256,18 @@ void IntegrationPluginEVBox::executeAction(ThingActionInfo *info) { Thing *thing = info->thing(); - QString portName = thing->paramValue(evboxThingSerialPortParamTypeId).toString(); - QString serial = thing->paramValue(evboxThingSerialNumberParamTypeId).toString(); + QString portName = thing->paramValue("serialPort").toString(); + QString serial = thing->paramValue("serialNumber").toString(); EVBoxPort *port = m_ports.value(portName); qCDebug(dcEVBox()) << "Executing action" << info->action().actionTypeId().toString(); - if (info->action().actionTypeId() == evboxPowerActionTypeId) { - bool power = info->action().paramValue(evboxPowerActionPowerParamTypeId).toBool(); - quint16 maxChargingCurrent = thing->stateValue(evboxMaxChargingCurrentStateTypeId).toUInt(); + ActionType actionType = thing->thingClass().actionTypes().findById(info->action().actionTypeId()); + if (actionType.name() == "power") { + bool power = info->action().paramValue(actionType.id()).toBool(); + quint16 maxChargingCurrent = thing->stateValue("maxChargingCurrent").toUInt(); port->sendCommand(EVBoxPort::Command68, 60, power ? maxChargingCurrent : 0, serial); - } else if (info->action().actionTypeId() == evboxMaxChargingCurrentActionTypeId) { - int maxChargingCurrent = info->action().paramValue(evboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toInt(); + } else if (actionType.name() == "maxChargingCurrent") { + int maxChargingCurrent = info->action().paramValue(actionType.id()).toInt(); port->sendCommand(EVBoxPort::Command68, 60, maxChargingCurrent, serial); } @@ -273,10 +283,11 @@ void IntegrationPluginEVBox::finishPendingAction(Thing *thing) if (!m_pendingActions.value(thing).isEmpty()) { ThingActionInfo *info = m_pendingActions.value(thing).first(); qCDebug(dcEVBox()) << "Finishing action:" << info->action().actionTypeId().toString(); - if (info->action().actionTypeId() == evboxPowerActionTypeId) { - thing->setStateValue(evboxPowerStateTypeId, info->action().paramValue(evboxPowerActionPowerParamTypeId)); - } else if (info->action().actionTypeId() == evboxMaxChargingCurrentActionTypeId) { - thing->setStateValue(evboxMaxChargingCurrentStateTypeId, info->action().paramValue(evboxMaxChargingCurrentActionMaxChargingCurrentParamTypeId)); + ActionType actionType = thing->thingClass().actionTypes().findById(info->action().actionTypeId()); + if (actionType.name() == "power") { + thing->setStateValue("power", info->action().paramValue(actionType.id())); + } else if (actionType.name() == "maxChargingCurrent") { + thing->setStateValue("maxChargingCurrent", info->action().paramValue(actionType.id())); } info->finish(Thing::ThingErrorNoError); } diff --git a/evbox/integrationpluginevbox.json b/evbox/integrationpluginevbox.json index 8952e7b2..b596f4ca 100644 --- a/evbox/integrationpluginevbox.json +++ b/evbox/integrationpluginevbox.json @@ -10,11 +10,12 @@ "thingClasses": [ { "id": "d73a14e3-10af-47bc-9bc7-a5ff6e52f72c", - "name": "evbox", - "displayName": "Elvi", + "name": "elviMid", + "displayName": "Elvi + MID meter", "createMethods": ["discovery"], + "discoveryType": "weak", "setupMethod": "justadd", - "interfaces": [ "evcharger", "connectable" ], + "interfaces": [ "evcharger", "smartmeterconsumer", "connectable" ], "paramTypes": [ { "id": "bce7c412-c19a-4e60-a11f-fe8308408abf", @@ -94,6 +95,88 @@ "defaultValue": 1 } ] + }, + { + "id": "81df1344-3861-4554-972c-6cd4b2ec97a7", + "name": "elvi", + "displayName": "Elvi", + "createMethods": ["discovery"], + "discoveryType": "weak", + "setupMethod": "justadd", + "interfaces": [ "evcharger", "connectable" ], + "paramTypes": [ + { + "id": "159fec55-e73d-4a9d-bbde-b2b44fe7f363", + "name":"serialPort", + "displayName": "Serial port", + "type": "QString" + }, + { + "id": "987ddd33-cbcd-4716-bdaa-6824a5295d22", + "name":"serialNumber", + "displayName": "Serial number", + "type": "QString" + } + ], + "settingsTypes": [ + { + "id": "3b44ed38-5cfe-4f61-8ad4-a4fe282944ac", + "name": "phaseCount", + "displayName": "Connected phases", + "type": "uint", + "minValue": 1, + "maxValue": 3, + "defaultValue": 3 + } + ], + "stateTypes": [ + { + "id": "e763b450-e180-4afb-9fcf-c75ad2f3d7c7", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + }, + { + "id": "d9875cbf-3736-484c-bf07-08a1d7f9cc89", + "name": "power", + "displayName": "Charging enabled", + "displayNameAction": "Enable/disable charging", + "type": "bool", + "defaultValue": false, + "writable": true + }, + { + "id": "186d792a-5bab-4144-837a-cbf5027de4fb", + "name": "maxChargingCurrent", + "displayName": "Maximum charging current", + "displayNameAction": "Set maximum charging current", + "type": "uint", + "writable": true, + "unit": "Ampere", + "minValue": "6", + "maxValue": "32", + "defaultValue": 6 + }, + { + "id": "91093627-ddae-4ba2-83f1-0241ea01aa01", + "name": "charging", + "displayName": "Charging", + "type": "bool", + "defaultValue": false + }, + { + "id": "46781438-2a31-48d3-a140-11593888b268", + "name": "phaseCount", + "displayName": "Used phases", + "type": "uint", + "minValue": 1, + "maxValue": 3, + "defaultValue": 1 + } + ] } ] } diff --git a/evbox/translations/3362ac5c-5e2f-43c0-b3fc-70a98773e119-en_US.ts b/evbox/translations/3362ac5c-5e2f-43c0-b3fc-70a98773e119-en_US.ts index 0d41216c..aaf338ab 100644 --- a/evbox/translations/3362ac5c-5e2f-43c0-b3fc-70a98773e119-en_US.ts +++ b/evbox/translations/3362ac5c-5e2f-43c0-b3fc-70a98773e119-en_US.ts @@ -4,35 +4,53 @@ EVBox - + + Charging - The name of the StateType ({6439fdba-dc03-454f-bc33-0f6e2619d2ab}) of ThingClass evbox - - - - - - Charging enabled - The name of the ParamType (ThingClass: evbox, ActionType: power, ID: {e3ed9334-68bf-47eb-bd9a-d9a800529bce}) + The name of the StateType ({91093627-ddae-4ba2-83f1-0241ea01aa01}) of ThingClass elvi ---------- -The name of the StateType ({e3ed9334-68bf-47eb-bd9a-d9a800529bce}) of ThingClass evbox +The name of the StateType ({6439fdba-dc03-454f-bc33-0f6e2619d2ab}) of ThingClass elviMid - + + + + + Charging enabled + The name of the ParamType (ThingClass: elvi, ActionType: power, ID: {d9875cbf-3736-484c-bf07-08a1d7f9cc89}) +---------- +The name of the StateType ({d9875cbf-3736-484c-bf07-08a1d7f9cc89}) of ThingClass elvi +---------- +The name of the ParamType (ThingClass: elviMid, ActionType: power, ID: {e3ed9334-68bf-47eb-bd9a-d9a800529bce}) +---------- +The name of the StateType ({e3ed9334-68bf-47eb-bd9a-d9a800529bce}) of ThingClass elviMid + + + + + Connected - The name of the StateType ({5ef06038-9fa9-4d5d-8d9b-0375b8aa343a}) of ThingClass evbox + The name of the StateType ({e763b450-e180-4afb-9fcf-c75ad2f3d7c7}) of ThingClass elvi +---------- +The name of the StateType ({5ef06038-9fa9-4d5d-8d9b-0375b8aa343a}) of ThingClass elviMid - + + Connected phases + The name of the ParamType (ThingClass: elvi, Type: settings, ID: {3b44ed38-5cfe-4f61-8ad4-a4fe282944ac}) + + + + Current power consumption - The name of the StateType ({8d3c80b7-f1f1-48de-8b7a-f99b9bc688b7}) of ThingClass evbox + The name of the StateType ({8d3c80b7-f1f1-48de-8b7a-f99b9bc688b7}) of ThingClass elviMid - - + + EVBox The name of the vendor ({435d8843-887a-4642-b2f5-cd27d18bdb95}) ---------- @@ -40,60 +58,104 @@ The name of the plugin EVBox ({3362ac5c-5e2f-43c0-b3fc-70a98773e119}) - + Elvi + The name of the ThingClass ({81df1344-3861-4554-972c-6cd4b2ec97a7}) + + + + + Elvi + MID meter The name of the ThingClass ({d73a14e3-10af-47bc-9bc7-a5ff6e52f72c}) - + + Enable/disable charging - The name of the ActionType ({e3ed9334-68bf-47eb-bd9a-d9a800529bce}) of ThingClass evbox - - - - - - Maximum charging current - The name of the ParamType (ThingClass: evbox, ActionType: maxChargingCurrent, ID: {cc9ae86d-fc86-473f-ae90-d9eb20d7a011}) + The name of the ActionType ({d9875cbf-3736-484c-bf07-08a1d7f9cc89}) of ThingClass elvi ---------- -The name of the StateType ({cc9ae86d-fc86-473f-ae90-d9eb20d7a011}) of ThingClass evbox +The name of the ActionType ({e3ed9334-68bf-47eb-bd9a-d9a800529bce}) of ThingClass elviMid - + + + + + Maximum charging current + The name of the ParamType (ThingClass: elvi, ActionType: maxChargingCurrent, ID: {186d792a-5bab-4144-837a-cbf5027de4fb}) +---------- +The name of the StateType ({186d792a-5bab-4144-837a-cbf5027de4fb}) of ThingClass elvi +---------- +The name of the ParamType (ThingClass: elviMid, ActionType: maxChargingCurrent, ID: {cc9ae86d-fc86-473f-ae90-d9eb20d7a011}) +---------- +The name of the StateType ({cc9ae86d-fc86-473f-ae90-d9eb20d7a011}) of ThingClass elviMid + + + + + + Serial number + The name of the ParamType (ThingClass: elvi, Type: thing, ID: {987ddd33-cbcd-4716-bdaa-6824a5295d22}) +---------- +The name of the ParamType (ThingClass: elviMid, Type: thing, ID: {abc607a7-7dc5-48d4-b3d0-1545ddc63592}) + + + + + Serial port - The name of the ParamType (ThingClass: evbox, Type: thing, ID: {bce7c412-c19a-4e60-a11f-fe8308408abf}) + The name of the ParamType (ThingClass: elvi, Type: thing, ID: {159fec55-e73d-4a9d-bbde-b2b44fe7f363}) +---------- +The name of the ParamType (ThingClass: elviMid, Type: thing, ID: {bce7c412-c19a-4e60-a11f-fe8308408abf}) - + + Set maximum charging current - The name of the ActionType ({cc9ae86d-fc86-473f-ae90-d9eb20d7a011}) of ThingClass evbox + The name of the ActionType ({186d792a-5bab-4144-837a-cbf5027de4fb}) of ThingClass elvi +---------- +The name of the ActionType ({cc9ae86d-fc86-473f-ae90-d9eb20d7a011}) of ThingClass elviMid - + Total consumed energy - The name of the StateType ({9fd15d14-c228-4af6-85af-4cb171d6f9f0}) of ThingClass evbox + The name of the StateType ({9fd15d14-c228-4af6-85af-4cb171d6f9f0}) of ThingClass elviMid - + + Used phases - The name of the StateType ({1120abe3-1878-4301-a701-014b24fd1e41}) of ThingClass evbox + The name of the StateType ({46781438-2a31-48d3-a140-11593888b268}) of ThingClass elvi +---------- +The name of the StateType ({1120abe3-1878-4301-a701-014b24fd1e41}) of ThingClass elviMid IntegrationPluginEVBox - + + No serial ports are available on this system. Please connect a RS485 adapter first. + + + + + Unable to open the RS485 port. Please make sure the RS485 adapter is connected properly and not in use by anything else. + + + + Unable to open the RS485 port. Please make sure the RS485 adapter is connected properly. - + + The EVBox is not responding.