Merge PR #538: Shelly: Add support for Shelly EM

This commit is contained in:
Jenkins nymea 2022-03-27 19:50:14 +02:00
commit 3674be276e
5 changed files with 2008 additions and 1292 deletions

View File

@ -12,6 +12,7 @@ The currently supported devices are:
* Shelly RGBW2
* Shelly Dimmer / Dimmer 2
* Shelly Button 1
* Shelly EM
* Shelly 3EM
* Shelly H+T
* Shelly i3

View File

@ -44,6 +44,8 @@
#include "plugintimer.h"
#include "qmath.h"
#include "network/zeroconf/zeroconfservicebrowser.h"
#include "platform/platformzeroconfcontroller.h"
@ -65,6 +67,7 @@ static QHash<ThingClassId, ParamTypeId> idParamTypeMap = {
{shelly2ThingClassId, shelly2ThingIdParamTypeId},
{shelly25ThingClassId, shelly25ThingIdParamTypeId},
{shellyButton1ThingClassId, shellyButton1ThingIdParamTypeId},
{shellyEmThingClassId, shellyEmThingIdParamTypeId},
{shellyEm3ThingClassId, shellyEm3ThingIdParamTypeId},
{shellyHTThingClassId, shellyHTThingIdParamTypeId},
{shellyI3ThingClassId, shellyI3ThingIdParamTypeId},
@ -81,6 +84,7 @@ static QHash<ThingClassId, ParamTypeId> usernameParamTypeMap = {
{shelly2ThingClassId, shelly2ThingUsernameParamTypeId},
{shelly25ThingClassId, shelly25ThingUsernameParamTypeId},
{shellyButton1ThingClassId, shellyButton1ThingUsernameParamTypeId},
{shellyEmThingClassId, shellyEmThingUsernameParamTypeId},
{shellyEm3ThingClassId, shellyEm3ThingUsernameParamTypeId},
{shellyHTThingClassId, shellyHTThingUsernameParamTypeId},
{shellyI3ThingClassId, shellyI3ThingUsernameParamTypeId},
@ -97,6 +101,7 @@ static QHash<ThingClassId, ParamTypeId> passwordParamTypeMap = {
{shelly2ThingClassId, shelly2ThingPasswordParamTypeId},
{shelly25ThingClassId, shelly25ThingPasswordParamTypeId},
{shellyButton1ThingClassId, shellyButton1ThingPasswordParamTypeId},
{shellyEmThingClassId, shellyEmThingPasswordParamTypeId},
{shellyEm3ThingClassId, shellyEm3ThingPasswordParamTypeId},
{shellyHTThingClassId, shellyHTThingPasswordParamTypeId},
{shellyI3ThingClassId, shellyI3ThingPasswordParamTypeId},
@ -121,6 +126,7 @@ static QHash<ThingClassId, ParamTypeId> channelParamTypeMap = {
{shellyLightPMThingClassId, shellyLightPMThingChannelParamTypeId},
{shellySocketPMThingClassId, shellySocketPMThingChannelParamTypeId},
{shellyRollerThingClassId, shellyRollerThingChannelParamTypeId},
{shellyEmChannelThingClassId, shellyEmChannelThingChannelParamTypeId},
};
static QHash<ThingClassId, StateTypeId> connectedStateTypesMap = {
@ -133,6 +139,8 @@ static QHash<ThingClassId, StateTypeId> connectedStateTypesMap = {
{shellyRgbw2ThingClassId, shellyRgbw2ConnectedStateTypeId},
{shellyDimmerThingClassId, shellyDimmerConnectedStateTypeId},
{shellyButton1ThingClassId, shellyButton1ConnectedStateTypeId},
{shellyEmThingClassId, shellyEmConnectedStateTypeId},
{shellyEmChannelThingClassId, shellyEmChannelConnectedStateTypeId},
{shellyEm3ThingClassId, shellyEm3ConnectedStateTypeId},
{shellyHTThingClassId, shellyHTConnectedStateTypeId},
{shellySwitchThingClassId, shellySwitchConnectedStateTypeId},
@ -157,6 +165,8 @@ static QHash<ThingClassId, StateTypeId> signalStrengthStateTypesMap = {
{shellyRgbw2ThingClassId, shellyRgbw2SignalStrengthStateTypeId},
{shellyDimmerThingClassId, shellyDimmerSignalStrengthStateTypeId},
{shellyButton1ThingClassId, shellyButton1SignalStrengthStateTypeId},
{shellyEmThingClassId, shellyEmSignalStrengthStateTypeId},
{shellyEmChannelThingClassId, shellyEmChannelSignalStrengthStateTypeId},
{shellyEm3ThingClassId, shellyEm3SignalStrengthStateTypeId},
{shellyHTThingClassId, shellyHTSignalStrengthStateTypeId},
{shellySwitchThingClassId, shellySwitchSignalStrengthStateTypeId},
@ -184,6 +194,7 @@ static QHash<ThingClassId, StateTypeId> powerStateTypeMap = {
{shellyGenericPMThingClassId, shellyGenericPMPowerStateTypeId},
{shellyLightPMThingClassId, shellyLightPMPowerStateTypeId},
{shellySocketPMThingClassId, shellySocketPMPowerStateTypeId},
{shellyEmThingClassId, shellyEmPowerStateTypeId},
{shellyEm3ThingClassId, shellyEm3PowerStateTypeId},
};
@ -197,6 +208,7 @@ static QHash<ThingClassId, StateTypeId> currentPowerStateTypeMap = {
{shellyLightPMThingClassId, shellyLightPMCurrentPowerStateTypeId},
{shellySocketPMThingClassId, shellySocketPMCurrentPowerStateTypeId},
{shellyRollerThingClassId, shellyRollerCurrentPowerStateTypeId},
{shellyEmChannelThingClassId, shellyEmChannelCurrentPowerStateTypeId},
{shellyEm3ThingClassId, shellyEm3CurrentPowerStateTypeId},
};
@ -207,6 +219,7 @@ static QHash<ThingClassId, StateTypeId> totalEnergyConsumedStateTypeMap = {
{shellyLightPMThingClassId, shellyLightPMTotalEnergyConsumedStateTypeId},
{shellySocketPMThingClassId, shellySocketPMTotalEnergyConsumedStateTypeId},
{shellyRollerThingClassId, shellyRollerTotalEnergyConsumedStateTypeId},
{shellyEmChannelThingClassId, shellyEmChannelTotalEnergyConsumedStateTypeId},
{shellyEm3ThingClassId, shellyEm3TotalEnergyConsumedStateTypeId},
};
@ -233,6 +246,7 @@ static QHash<ThingClassId, StateTypeId> updateStatusStateTypesMap = {
{shellyRgbw2ThingClassId, shellyRgbw2UpdateStatusStateTypeId},
{shellyDimmerThingClassId, shellyDimmerUpdateStatusStateTypeId},
{shellyButton1ThingClassId, shellyButton1UpdateStatusStateTypeId},
{shellyEmThingClassId, shellyEmUpdateStatusStateTypeId},
{shellyEm3ThingClassId, shellyEm3UpdateStatusStateTypeId},
{shellyHTThingClassId, shellyHTUpdateStatusStateTypeId},
{shellyI3ThingClassId, shellyI3UpdateStatusStateTypeId},
@ -249,6 +263,7 @@ static QHash<ThingClassId, StateTypeId> currentVersionStateTypesMap = {
{shellyRgbw2ThingClassId, shellyRgbw2CurrentVersionStateTypeId},
{shellyDimmerThingClassId, shellyDimmerCurrentVersionStateTypeId},
{shellyButton1ThingClassId, shellyButton1CurrentVersionStateTypeId},
{shellyEmThingClassId, shellyEmCurrentVersionStateTypeId},
{shellyEm3ThingClassId, shellyEm3CurrentVersionStateTypeId},
{shellyHTThingClassId, shellyHTCurrentVersionStateTypeId},
{shellyI3ThingClassId, shellyI3CurrentVersionStateTypeId},
@ -265,6 +280,7 @@ static QHash<ThingClassId, StateTypeId> availableVersionStateTypesMap = {
{shellyRgbw2ThingClassId, shellyRgbw2AvailableVersionStateTypeId},
{shellyDimmerThingClassId, shellyDimmerAvailableVersionStateTypeId},
{shellyButton1ThingClassId, shellyButton1AvailableVersionStateTypeId},
{shellyEmThingClassId, shellyEmAvailableVersionStateTypeId},
{shellyEm3ThingClassId, shellyEm3AvailableVersionStateTypeId},
{shellyHTThingClassId, shellyHTAvailableVersionStateTypeId},
{shellyI3ThingClassId, shellyI3AvailableVersionStateTypeId},
@ -319,6 +335,7 @@ static QHash<ActionTypeId, ThingClassId> powerActionTypesMap = {
{shellyGenericPMPowerActionTypeId, shellyGenericPMThingClassId},
{shellyLightPMPowerActionTypeId, shellyLightPMThingClassId},
{shellySocketPMPowerActionTypeId, shellySocketPMThingClassId},
{shellyEmPowerActionTypeId, shellyEmThingClassId},
{shellyEm3PowerActionTypeId, shellyEm3ThingClassId},
{shelly25Channel1ActionTypeId, shelly25ThingClassId},
{shelly25Channel2ActionTypeId, shelly25ThingClassId}
@ -335,6 +352,7 @@ static QHash<ActionTypeId, ThingClassId> powerActionParamTypesMap = {
{shellyGenericPMPowerActionTypeId, shellyGenericPMPowerActionPowerParamTypeId},
{shellyLightPMPowerActionTypeId, shellyLightPMPowerActionPowerParamTypeId},
{shellySocketPMPowerActionTypeId, shellySocketPMPowerActionPowerParamTypeId},
{shellyEmPowerActionTypeId, shellyEmPowerActionPowerParamTypeId},
{shellyEm3PowerActionTypeId, shellyEm3PowerActionPowerParamTypeId},
{shelly25Channel1ActionTypeId, shelly25Channel1ActionChannel1ParamTypeId},
{shelly25Channel2ActionTypeId, shelly25Channel2ActionChannel2ParamTypeId}
@ -410,6 +428,7 @@ static QHash<ActionTypeId, ThingClassId> updateActionTypesMap = {
{shellyRgbw2PerformUpdateActionTypeId, shellyRgbw2ThingClassId},
{shellyDimmerPerformUpdateActionTypeId, shellyDimmerThingClassId},
{shellyButton1PerformUpdateActionTypeId, shellyButton1ThingClassId},
{shellyEmPerformUpdateActionTypeId, shellyEmThingClassId},
{shellyEm3PerformUpdateActionTypeId, shellyEm3ThingClassId},
{shellyHTPerformUpdateActionTypeId, shellyHTThingClassId},
{shellyI3PerformUpdateActionTypeId, shellyI3ThingClassId},
@ -465,6 +484,8 @@ void IntegrationPluginShelly::discoverThings(ThingDiscoveryInfo *info)
namePattern = QRegExp("^shellyswitch25-[0-9A-Z]+$");
} else if (info->thingClassId() == shellyButton1ThingClassId) {
namePattern = QRegExp("^shellybutton1-[0-9-A-Z]+$");
} else if (info->thingClassId() == shellyEmThingClassId) {
namePattern = QRegExp("^shellyem-[0-9A-Z]+$");
} else if (info->thingClassId() == shellyEm3ThingClassId) {
namePattern = QRegExp("^shellyem3-[0-9A-Z]+$");
} else if (info->thingClassId() == shellyHTThingClassId) {
@ -707,6 +728,14 @@ void IntegrationPluginShelly::executeAction(ThingActionInfo *info)
return;
}
if (action.actionTypeId() == shellyEmResetActionTypeId) {
MqttChannel *channel = m_mqttChannels.value(thing);
QString shellyId = thing->paramValue(shellyEmThingIdParamTypeId).toString();
channel->publish("shellies/" + shellyId + "/command", "reset_data");
info->finish(Thing::ThingErrorNoError);
return;
}
if (action.actionTypeId() == shellyEm3ResetActionTypeId) {
MqttChannel *channel = m_mqttChannels.value(thing);
QString shellyId = thing->paramValue(shellyEm3ThingIdParamTypeId).toString();
@ -1131,6 +1160,63 @@ void IntegrationPluginShelly::onPublishReceived(MqttChannel *channel, const QStr
}
}
if (topicMatcher.exactMatch(topic) && thing->thingClassId() == shellyEmThingClassId) {
int channel = topic.split('/').at(3).toInt();
QString stateName = topic.split('/').at(4);
QVariant value = payload;
QHash<QString, StateTypeId> stateTypeIdMap;
stateTypeIdMap["power"] = shellyEmChannelCurrentPowerStateTypeId;
stateTypeIdMap["pf"] = shellyEmChannelPowerFactorPhaseAStateTypeId;
stateTypeIdMap["reactive_power"] = shellyEmChannelReactivePowerPhaseAStateTypeId;
stateTypeIdMap["voltage"] = shellyEmChannelVoltagePhaseAStateTypeId;
stateTypeIdMap["total"] = shellyEmChannelTotalEnergyConsumedStateTypeId;
stateTypeIdMap["total_returned"] = shellyEmChannelTotalEnergyProducedStateTypeId;
StateTypeId stateTypeId = stateTypeIdMap.value(stateName);
if (stateTypeId.isNull()) {
qCWarning(dcShelly()) << "Unhandled emeter value for channel" << channel << stateName;
return;
}
double factor = 1;
if (stateName == "total" || stateName == "total_returned") {
factor = 0.001;
}
// For multi-channel devices, power measurements are per-channel, so, find the child thing
foreach (Thing *child, myThings().filterByParentId(thing->id()).filterByInterface("energymeter")) {
ParamTypeId channelParamTypeId = channelParamTypeMap.value(child->thingClassId());
if (child->paramValue(channelParamTypeId).toInt() == channel + 1) {
child->setStateValue(stateTypeId, value.toDouble() * factor);
}
}
// Some optimization specific to the EM: We calculate totals, current & power factor ourselves.
// In order to not produce intermediate totals for each incoming message,
// we'll only do the calculations when we get the total_returned (i.e. the last message) for the channel.
if (stateName == "total_returned") {
foreach (Thing *child, myThings().filterByParentId(thing->id()).filterByInterface("energymeter")) {
ParamTypeId channelParamTypeId = channelParamTypeMap.value(child->thingClassId());
if (child->paramValue(channelParamTypeId).toInt() == channel + 1) {
double power = child->stateValue(shellyEmChannelCurrentPowerStateTypeId).toDouble();
double voltage = child->stateValue(shellyEmChannelVoltagePhaseAStateTypeId).toDouble();
if (qFuzzyCompare(voltage, 0) == false) {
double calcCurrent = power/voltage;
child->setStateValue(shellyEmChannelCurrentPhaseAStateTypeId, calcCurrent);
} else {
child->setStateValue(shellyEmChannelCurrentPhaseAStateTypeId, 0);
}
/*double reactivePower = child->stateValue(shellyEmChannelReactivePowerPhaseAStateTypeId).toDouble();
double root = qSqrt(power*power + reactivePower*reactivePower);
if (qFuzzyCompare(root, 0) == false) {
double calcPf = power/root;
child->setStateValue(shellyEmChannelPowerFactorPhaseAStateTypeId, calcPf);
} else {
child->setStateValue(shellyEmChannelPowerFactorPhaseAStateTypeId, 0);
}*/
}
}
}
}
if (topic == "shellies/" + shellyId + "/status") {
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error);
@ -1369,6 +1455,16 @@ void IntegrationPluginShelly::setupShellyGateway(ThingSetupInfo *info)
autoChilds.append(switch2Child);
}
// Create 2 measurement channels for shelly em
if (info->thing()->thingClassId() == shellyEmThingClassId) {
ThingDescriptor channelChild(shellyEmChannelThingClassId, info->thing()->name() + " channel 1", QString(), info->thing()->id());
channelChild.setParams(ParamList() << Param(shellyEmChannelThingChannelParamTypeId, 1));
autoChilds.append(channelChild);
ThingDescriptor channel2Child(shellyEmChannelThingClassId, info->thing()->name() + " channel 2", QString(), info->thing()->id());
channel2Child.setParams(ParamList() << Param(shellyEmChannelThingChannelParamTypeId, 2));
autoChilds.append(channel2Child);
}
// Add connected devices as configured in params
// No PM devices for shelly 1 and 2
if (info->thing()->thingClassId() == shelly1ThingClassId

View File

@ -1814,6 +1814,229 @@
}
]
},
{
"id": "bcc7326d-555a-4763-80ce-7354e67cc700",
"name": "shellyEm",
"displayName": "Shelly EM",
"createMethods": ["discovery"],
"interfaces": ["gateway", "wirelessconnectable", "power", "update"],
"providedInterfaces": ["energymeter"],
"paramTypes": [
{
"id": "e44d6880-4e54-44b0-85f5-4e035179402e",
"name":"id",
"displayName": "Shelly ID",
"type": "QString",
"readOnly": true
},
{
"id": "61957e60-4b77-4aa7-893f-e94559cac15c",
"name": "username",
"displayName": "Username (optional)",
"type": "QString"
},
{
"id": "66c1cd5d-c1c4-4d23-8e83-d5f5563abb8e",
"name": "password",
"displayName": "Password (optional)",
"type": "QString"
}
],
"stateTypes": [
{
"id": "75f1a571-b21c-43c2-b4a3-ab8e9d7ef08c",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected or disconnected",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "87664175-4b84-4cfe-9bee-5d1bcf2566f8",
"name": "signalStrength",
"displayName": "Signal strength",
"displayNameEvent": "Signal strength changed",
"type": "uint",
"minValue": 0,
"maxValue": 100,
"unit": "Percentage",
"defaultValue": 0,
"cached": false
},
{
"id": "ad5d523e-9a4d-47d2-912c-c8ec5081f9ff",
"name": "updateStatus",
"displayName": "Update status",
"displayNameEvent": "Update status changed",
"type": "QString",
"possibleValues": ["idle", "available", "updating"],
"defaultValue": "idle",
"cached": false
},
{
"id": "f5f03ab2-fec6-4502-8a47-100211878a8a",
"name": "currentVersion",
"displayName": "Current firmware version",
"displayNameEvent": "Current firmware version changed",
"type": "QString",
"defaultValue": "",
"cached": false
},
{
"id": "97ab36bb-355b-4559-838a-fe49a9cbd13e",
"name": "availableVersion",
"displayName": "Available firmware version",
"displayNameEvent": "Available firmware version changed",
"type": "QString",
"defaultValue": "",
"cached": false
},
{
"id": "9a2c6304-91d6-45fc-8ef7-75355457eca5",
"name": "power",
"displayName": "Powered",
"displayNameEvent": "Turned on or off",
"displayNameAction": "Turn on or off",
"type": "bool",
"defaultValue": false,
"writable": true,
"ioType": "digitalOutput",
"cached": false
}
],
"actionTypes": [
{
"id": "6a30f435-2b35-4df5-8a20-ef3dbec817c9",
"name": "performUpdate",
"displayName": "Start firmware update"
},
{
"id": "09f6d675-4c22-4a9f-b9f2-3349ab947529",
"name": "reset",
"displayName": "Reset data"
}
]
},
{
"id": "67ccc046-c8b5-4584-8f7f-6fe0a0c6a860",
"name": "shellyEmChannel",
"displayName": "Shelly EM Channel",
"createMethods": ["auto"],
"interfaces": ["wirelessconnectable", "energymeter"],
"paramTypes": [
{
"id": "b9b85416-0d48-4e71-9471-03385f8fc619",
"name":"id",
"displayName": "Shelly ID",
"type": "QString",
"readOnly": true
},
{
"id": "e8865f9d-2601-4e02-9ff1-780332f1f18f",
"name": "channel",
"displayName": "Channel",
"type": "uint",
"defaultValue": 1
}
],
"stateTypes": [
{
"id": "d96d7505-f270-49ad-abb2-4f29ac11fb84",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected or disconnected",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "d1928e72-73e6-4009-846e-149f80ad5899",
"name": "signalStrength",
"displayName": "Signal strength",
"displayNameEvent": "Signal strength changed",
"type": "uint",
"unit": "Percentage",
"minValue": 0,
"maxValue": 100,
"defaultValue": 0,
"cached": false
},
{
"id": "4ce53fa0-d6b7-4c1b-87d9-edcaeedb640e",
"name": "totalEnergyConsumed",
"displayName": "Total consumed energy",
"displayNameEvent": "Total consumed energy changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0,
"cached": true
},
{
"id": "7fe88e8f-a1c4-4e8d-a1de-9135b80bc7e3",
"name": "totalEnergyProduced",
"displayName": "Total returned energy",
"displayNameEvent": "Total returned energy changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0,
"cached": true
},
{
"id": "a85041e8-a19e-4695-8404-3e3a06b1e92a",
"name": "currentPower",
"displayName": "Current power",
"displayNameEvent": "Current power changed",
"type": "double",
"unit": "Watt",
"defaultValue": 0,
"cached": false
},
{
"id": "fd5898ce-c8c9-422d-a32a-996d4004ca15",
"name": "powerFactorPhaseA",
"displayName": "Power factor",
"displayNameEvent": "Power factor changed",
"type": "double",
"defaultValue": 0,
"cached": false
},
{
"id": "abdb5b38-05d3-4c12-aed2-f7c560d6b4e8",
"name": "reactivePowerPhaseA",
"displayName": "Reactive power",
"displayNameEvent": "Reactive power changed",
"type": "double",
"unit": "Watt",
"defaultValue": 0,
"cached": false
},
{
"id": "1d457f50-0951-4ba5-8d8e-b79ea5a75535",
"name": "currentPhaseA",
"displayName": "Current",
"displayNameEvent": "Current changed",
"type": "double",
"unit": "Ampere",
"defaultValue": 0,
"filter": "adaptive",
"cached": false
},
{
"id": "d6cb777f-c9af-46d8-845a-883ac05c206a",
"name": "voltagePhaseA",
"displayName": "Voltage",
"displayNameEvent": "Voltage changed",
"type": "double",
"unit": "Volt",
"defaultValue": 0,
"filter": "adaptive",
"cached": false
}
],
"actionTypes": [
]
},
{
"id": "a82737bb-f2d6-442e-a468-5acc0a2e4cd7",
"name": "shellyMotion",

File diff suppressed because it is too large Load Diff