More work on Shelly. Pretty complete already for the Shelly1

This commit is contained in:
Michael Zanetti 2019-09-26 00:51:10 +02:00
parent 0d4d6cd4f6
commit b744b3b245
4 changed files with 437 additions and 114 deletions

View File

@ -1,20 +1,29 @@
# Tasmota # Shelly
This plugin allows to make use of Sonoff-Tasmota devices via the nymea internal MQTT broker. There is no external MQTT broker needed. The Shelly plugin adds support for Shelly devices (https://shelly.cloud).
Note that Sonoff devices must be flashed with the Tasmota sofware and connected to the WiFi network in order to work with this plugin. Currently the Shelly1 and Shelly1PM are supported.
See the [Sonoff-Tasmota wiki](https://github.com/arendst/Sonoff-Tasmota/wiki) for a list of all supported devices and instructions on how to ## Requirements
install Tasmota on those. Shelly devices communicate with via MQTT. This means, in order to add Shelly devices to nymea, the nymea instance is required
to have the MQTT broker enabled in the nymea settings and the Shelly device needs to be connected to the same WiFi as nymea is
in. New Shelly devices will open a WiFi named with their name as SSID. For instance, a Shelly 1 would appear as "shelly1-XXXXXX".
Connect to this WiFi and open the webpage that will pop up. From there, it can be configured it to connect to the same
network where the nymea system is located. No other options need to be set as they can be configured using nymea later on.
After flashing Tasmota to a Sonoff device and connecting it to WiFi, it can be added to nymea. The only required
thing is the IP address to the device. This plugin will create a new isoloated MQTT channel on the nymea internal ## Setting up devices
MQTT broker and provision login details to the Tasmota device via HTTP. Once that is successful, the Tasmota device Once the Shelly is connected to the WiFi, a device discovery in nymea can be performed and will list the Shelly device.
will connect to the MQTT broker and appear as connected in nymea. During setup, the connected device can be configured. If the Shelly is connected to e.g. a light bulb, choose "Light" here.
Optionally, a username and password can be set. If the Shelly device is already configured to require authentication,
the username and password here must match the ones set on the Shelly. NOTE: If the Shelly is not configured to require a
login yet, but credentials are entered during setup, the Shelly device will be configured to require authentication from
now on.
## Plugin properties ## Plugin properties
When adding a Tasmota device it will add a new Gateway type device representing the Tasmota device itself. In addition When adding a Shelly device it will add a new Gateway type device representing the Shelly device itself. It will allow
to that a power switch device will appear which can be used to control the switches in the Tasmota device. Upon basic monitoring (such as the connected state) and interaction (e.g. reboot the Shelly device). In addition to that, a
device setup, the user can optionally select the type of the connected hardware, (e.g. a light) which causes this power switch device will appear which will reflect presses on the Shelly's SW input. This power switch device also
plugin to create a light device in the system which also controls the switches inside the Tasmota device and nicely offers the possiblity to configure the used switch (e.g. toggle, momentary, edge or detached from the Shelly's output).
integrates with the nymea:ux for the given device type. If a connected device has been selected during setup, an additional device, e.g. the light will appear in the system and
can be used to control the power output of the Shelly, e.g. turning on or off the connected light.

View File

@ -38,6 +38,18 @@
DevicePluginShelly::DevicePluginShelly() DevicePluginShelly::DevicePluginShelly()
{ {
m_connectedStateTypesMap[shellySwitchDeviceClassId] = shellySwitchConnectedStateTypeId;
m_connectedStateTypesMap[shellyGenericDeviceClassId] = shellyGenericConnectedStateTypeId;
m_connectedStateTypesMap[shellyLightDeviceClassId] = shellyLightConnectedStateTypeId;
m_powerActionTypesMap[shellyGenericPowerActionTypeId] = shellyGenericDeviceClassId;
m_powerActionTypesMap[shellyLightPowerActionTypeId] = shellyLightDeviceClassId;
m_powerActionParamTypesMap[shellyGenericPowerActionTypeId] = shellyGenericPowerActionPowerParamTypeId;
m_powerActionParamTypesMap[shellyLightPowerActionTypeId] = shellyLightPowerActionPowerParamTypeId;
m_powerStateTypeMap[shellyGenericDeviceClassId] = shellyGenericPowerStateTypeId;
m_powerStateTypeMap[shellyLightDeviceClassId] = shellyLightPowerStateTypeId;
} }
DevicePluginShelly::~DevicePluginShelly() DevicePluginShelly::~DevicePluginShelly()
@ -53,14 +65,17 @@ void DevicePluginShelly::discoverDevices(DeviceDiscoveryInfo *info)
{ {
foreach (const ZeroConfServiceEntry &entry, m_zeroconfBrowser->serviceEntries()) { foreach (const ZeroConfServiceEntry &entry, m_zeroconfBrowser->serviceEntries()) {
// qCDebug(dcShelly()) << "Have entry" << entry; // qCDebug(dcShelly()) << "Have entry" << entry;
QRegExp namePattern("^shelly[1-2]-[0-9A-Z]+$"); QRegExp namePattern;
if (info->deviceClassId() == shelly1DeviceClassId) {
namePattern = QRegExp("^shelly(1|1pm|plug|plug-s)-[0-9A-Z]+$");
}
if (!entry.name().contains(namePattern)) { if (!entry.name().contains(namePattern)) {
continue; continue;
} }
DeviceDescriptor descriptor(shellyOneDeviceClassId, entry.name(), entry.hostAddress().toString()); DeviceDescriptor descriptor(shelly1DeviceClassId, entry.name(), entry.hostAddress().toString());
ParamList params; ParamList params;
params << Param(shellyOneDeviceIdParamTypeId, entry.name()); params << Param(shelly1DeviceIdParamTypeId, entry.name());
descriptor.setParams(params); descriptor.setParams(params);
Device *existingDevice = myDevices().findByParams(params); Device *existingDevice = myDevices().findByParams(params);
@ -80,8 +95,122 @@ void DevicePluginShelly::setupDevice(DeviceSetupInfo *info)
{ {
Device *device = info->device(); Device *device = info->device();
if (device->deviceClassId() == shellyOneDeviceClassId) { if (device->deviceClassId() == shelly1DeviceClassId) {
QString shellyId = device->paramValue(shellyOneDeviceIdParamTypeId).toString(); setupShellyGateway(info);
return;
}
setupShellyChild(info);
}
void DevicePluginShelly::deviceRemoved(Device *device)
{
if (m_mqttChannels.contains(device)) {
hardwareManager()->mqttProvider()->releaseChannel(m_mqttChannels.take(device));
}
qCDebug(dcShelly()) << "Device removed" << device->name();
}
void DevicePluginShelly::executeAction(DeviceActionInfo *info)
{
Device *device = info->device();
Action action = info->action();
if (action.actionTypeId() == shelly1RebootActionTypeId) {
QUrl url;
url.setScheme("http");
url.setHost(getIP(info->device()));
url.setPath("/reboot");
url.setUserName(device->paramValue(shelly1DeviceUsernameParamTypeId).toString());
url.setPassword(device->paramValue(shelly1DevicePasswordParamTypeId).toString());
QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url));
connect(reply, &QNetworkReply::finished, &QNetworkReply::deleteLater);
connect(reply, &QNetworkReply::finished, info, [info, reply](){
info->finish(reply->error() == QNetworkReply::NoError ? Device::DeviceErrorNoError : Device::DeviceErrorHardwareFailure);
});
return;
}
if (m_powerActionTypesMap.contains(action.actionTypeId())) {
Device *parentDevice = myDevices().findById(device->parentId());
MqttChannel *channel = m_mqttChannels.value(parentDevice);
QString shellyId = parentDevice->paramValue(shelly1DeviceIdParamTypeId).toString();
ParamTypeId powerParamTypeId = m_powerActionParamTypesMap.value(action.actionTypeId());
bool on = action.param(powerParamTypeId).value().toBool();
channel->publish("shellies/" + shellyId + "/relay/0/command", on ? "on" : "off");
info->finish(Device::DeviceErrorNoError);
return;
}
qCWarning(dcShelly()) << "Unhandled execute action call for device" << device;
}
void DevicePluginShelly::onClientConnected(MqttChannel *channel)
{
Device *device = m_mqttChannels.key(channel);
if (!device) {
qCWarning(dcShelly()) << "Received a client connect for a device we don't know!";
return;
}
device->setStateValue(shelly1ConnectedStateTypeId, true);
foreach (Device *child, myDevices().filterByParentDeviceId(device->id())) {
child->setStateValue(m_connectedStateTypesMap[child->deviceClassId()], true);
}
}
void DevicePluginShelly::onClientDisconnected(MqttChannel *channel)
{
Device *device = m_mqttChannels.key(channel);
if (!device) {
qCWarning(dcShelly()) << "Received a client disconnect for a device we don't know!";
return;
}
device->setStateValue(shelly1ConnectedStateTypeId, false);
foreach (Device *child, myDevices().filterByParentDeviceId(device->id())) {
child->setStateValue(m_connectedStateTypesMap[child->deviceClassId()], false);
}
}
void DevicePluginShelly::onPublishReceived(MqttChannel *channel, const QString &topic, const QByteArray &payload)
{
Device *device = m_mqttChannels.key(channel);
if (!device) {
qCWarning(dcShelly()) << "Received a publish message for a device we don't know!";
return;
}
QString shellyId = device->paramValue(shelly1DeviceIdParamTypeId).toString();
if (topic == "shellies/" + shellyId + "/input/0") {
// "1" or "0"
// Emit event button pressed
bool on = payload == "1";
foreach (Device *child, myDevices().filterByParentDeviceId(device->id())) {
if (child->deviceClassId() == shellySwitchDeviceClassId) {
if (child->stateValue(shellySwitchPowerStateTypeId).toBool() != on) {
child->setStateValue(shellySwitchPowerStateTypeId, on);
emit emitEvent(Event(shellySwitchPressedEventTypeId, child->id()));
}
}
}
}
if (topic == "shellies/" + shellyId + "/relay/0") {
bool on = payload == "on";
foreach (Device *child, myDevices().filterByParentDeviceId(device->id())) {
if (m_powerStateTypeMap.contains(child->deviceClassId())) {
child->setStateValue(m_powerStateTypeMap.value(child->deviceClassId()), on);
}
}
}
qCDebug(dcShelly()) << "Publish received from" << device->name() << topic << payload;
}
void DevicePluginShelly::setupShellyGateway(DeviceSetupInfo *info)
{
QString shellyId = info->device()->paramValue(shelly1DeviceIdParamTypeId).toString();
ZeroConfServiceEntry zeroConfEntry; ZeroConfServiceEntry zeroConfEntry;
foreach (const ZeroConfServiceEntry &entry, m_zeroconfBrowser->serviceEntries()) { foreach (const ZeroConfServiceEntry &entry, m_zeroconfBrowser->serviceEntries()) {
if (entry.name() == shellyId) { if (entry.name() == shellyId) {
@ -89,7 +218,7 @@ void DevicePluginShelly::setupDevice(DeviceSetupInfo *info)
} }
} }
QHostAddress address; QHostAddress address;
pluginStorage()->beginGroup(device->id().toString()); pluginStorage()->beginGroup(info->device()->id().toString());
if (zeroConfEntry.isValid()) { if (zeroConfEntry.isValid()) {
address = zeroConfEntry.hostAddress().toString(); address = zeroConfEntry.hostAddress().toString();
pluginStorage()->setValue("cachedAddress", address.toString()); pluginStorage()->setValue("cachedAddress", address.toString());
@ -116,6 +245,8 @@ void DevicePluginShelly::setupDevice(DeviceSetupInfo *info)
url.setHost(address.toString()); url.setHost(address.toString());
url.setPort(80); url.setPort(80);
url.setPath("/settings"); url.setPath("/settings");
url.setUserName(info->device()->paramValue(shelly1DeviceUsernameParamTypeId).toString());
url.setPassword(info->device()->paramValue(shelly1DevicePasswordParamTypeId).toString());
QUrlQuery query; QUrlQuery query;
query.addQueryItem("mqtt_server", channel->serverAddress().toString() + ":" + QString::number(channel->serverPort())); query.addQueryItem("mqtt_server", channel->serverAddress().toString() + ":" + QString::number(channel->serverPort()));
@ -124,19 +255,23 @@ void DevicePluginShelly::setupDevice(DeviceSetupInfo *info)
query.addQueryItem("mqtt_enable", "true"); query.addQueryItem("mqtt_enable", "true");
url.setQuery(query); url.setQuery(query);
QNetworkRequest request(url); QNetworkRequest request(url);
qCDebug(dcShelly()) << "Connecting to" << url.toString(); qCDebug(dcShelly()) << "Connecting to" << url.toString();
QNetworkReply *reply = hardwareManager()->networkManager()->get(request); QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
connect(info, &DeviceSetupInfo::aborted, channel, [this, channel](){ connect(info, &DeviceSetupInfo::aborted, channel, [this, channel](){
hardwareManager()->mqttProvider()->releaseChannel(channel); hardwareManager()->mqttProvider()->releaseChannel(channel);
}); });
connect(reply, &QNetworkReply::finished, info, [this, info, reply, channel](){ connect(reply, &QNetworkReply::finished, info, [this, info, reply, channel, address](){
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
qCWarning(dcShelly()) << "Error fetching device settings" << reply->error() << reply->errorString();
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("Error connecting to Shelly device."));
hardwareManager()->mqttProvider()->releaseChannel(channel); hardwareManager()->mqttProvider()->releaseChannel(channel);
qCWarning(dcShelly()) << "Error fetching device settings" << reply->error() << reply->errorString();
if (reply->error() == QNetworkReply::AuthenticationRequiredError) {
info->finish(Device::DeviceErrorAuthenticationFailure, QT_TR_NOOP("Username and password not set correctly."));
} else {
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("Error connecting to Shelly device."));
}
return; return;
} }
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
@ -155,77 +290,106 @@ void DevicePluginShelly::setupDevice(DeviceSetupInfo *info)
connect(channel, &MqttChannel::clientDisconnected, this, &DevicePluginShelly::onClientDisconnected); connect(channel, &MqttChannel::clientDisconnected, this, &DevicePluginShelly::onClientDisconnected);
connect(channel, &MqttChannel::publishReceived, this, &DevicePluginShelly::onPublishReceived); connect(channel, &MqttChannel::publishReceived, this, &DevicePluginShelly::onPublishReceived);
DeviceDescriptors autoChilds;
// Always create the switch device if we don't have one yet
if (myDevices().filterByParentDeviceId(info->device()->id()).filterByDeviceClassId(shellySwitchDeviceClassId).isEmpty()) {
DeviceDescriptor switchChild(shellySwitchDeviceClassId, "Shelly switch", QString(), info->device()->id());
autoChilds.append(switchChild);
}
// Add connected devices as configured in params
if (info->device()->paramValue(shelly1DeviceConnectedDeviceParamTypeId).toString() == "Generic") {
if (myDevices().filterByParentDeviceId(info->device()->id()).filterByDeviceClassId(shellyGenericDeviceClassId).isEmpty()) {
DeviceDescriptor genericChild(shellyGenericDeviceClassId, "Shelly connected device", QString(), info->device()->id());
autoChilds.append(genericChild);
}
}
if (info->device()->paramValue(shelly1DeviceConnectedDeviceParamTypeId).toString() == "Light") {
if (myDevices().filterByParentDeviceId(info->device()->id()).filterByDeviceClassId(shellyLightDeviceClassId).isEmpty()) {
DeviceDescriptor genericChild(shellyLightDeviceClassId, "Shelly connected light", QString(), info->device()->id());
autoChilds.append(genericChild);
}
}
info->finish(Device::DeviceErrorNoError); info->finish(Device::DeviceErrorNoError);
emit autoDevicesAppeared(autoChilds);
// Make sure authentication is enalbed if the user wants it
QString username = info->device()->paramValue(shelly1DeviceUsernameParamTypeId).toString();
QString password = info->device()->paramValue(shelly1DevicePasswordParamTypeId).toString();
if (!username.isEmpty()) {
QUrl url;
url.setScheme("http");
url.setHost(address.toString());
url.setPort(80);
url.setPath("/settings/login");
url.setUserName(username);
url.setPassword(password);
QUrlQuery query;
query.addQueryItem("username", username);
query.addQueryItem("password", password);
query.addQueryItem("enabled", "true");
url.setQuery(query);
QNetworkRequest request(url);
qCDebug(dcShelly()) << "Enabling auth" << username << password;
QNetworkReply *reply = hardwareManager()->networkManager()->get(request);
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
}
}); });
return;
}
qCWarning(dcShelly) << "Unhandled DeviceClass in setupDevice" << device->deviceClassId();
} }
void DevicePluginShelly::deviceRemoved(Device *device) void DevicePluginShelly::setupShellyChild(DeviceSetupInfo *info)
{
if (m_mqttChannels.contains(device)) {
hardwareManager()->mqttProvider()->releaseChannel(m_mqttChannels.take(device));
}
qCDebug(dcShelly()) << "Device removed" << device->name();
}
void DevicePluginShelly::executeAction(DeviceActionInfo *info)
{ {
Device *device = info->device(); Device *device = info->device();
Action action = info->action();
if (action.actionTypeId() == shellyOnePowerActionTypeId) { // Connect to settings changes to store them to the device
MqttChannel *channel = m_mqttChannels.value(device); connect(info->device(), &Device::settingChanged, this, [this, device](const ParamTypeId &paramTypeId, const QVariant &value){
QString shellyId = device->paramValue(shellyOneDeviceIdParamTypeId).toString(); Device *parentDevice = myDevices().findById(device->parentId());
bool on = action.param(shellyOnePowerActionPowerParamTypeId).value().toBool(); pluginStorage()->beginGroup(parentDevice->id().toString());
channel->publish("shellies/" + shellyId + "/relay/0/command", on ? "on" : "off"); QString address = pluginStorage()->value("cachedAddress").toString();
pluginStorage()->endGroup();
QUrl url;
url.setScheme("http");
url.setHost(address);
url.setPort(80);
url.setPath("/settings/relay/0");
url.setUserName(parentDevice->paramValue(shelly1DeviceUsernameParamTypeId).toString());
url.setPassword(parentDevice->paramValue(shelly1DevicePasswordParamTypeId).toString());
QUrlQuery query;
if (paramTypeId == shellySwitchSettingsButtonTypeParamTypeId) {
query.addQueryItem("btn_type", value.toString());
}
if (paramTypeId == shellySwitchSettingsInvertButtonParamTypeId) {
query.addQueryItem("btn_reverse", value.toBool() ? "1" : "0");
}
if (paramTypeId == shellyGenericSettingsDefaultStateParamTypeId || paramTypeId == shellyLightSettingsDefaultStateParamTypeId) {
query.addQueryItem("default_state", value.toString());
}
url.setQuery(query);
QNetworkReply *reply = hardwareManager()->networkManager()->get(QNetworkRequest(url));
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
});
info->finish(Device::DeviceErrorNoError); info->finish(Device::DeviceErrorNoError);
return;
}
qCWarning(dcShelly()) << "Unhandled execute action call for device" << device;
} }
void DevicePluginShelly::onClientConnected(MqttChannel *channel) QString DevicePluginShelly::getIP(Device *device) const
{ {
Device *device = m_mqttChannels.key(channel); Device *d = device;
if (!device) { if (!device->parentId().isNull()) {
qCWarning(dcShelly()) << "Received a client connect for a device we don't know!"; d = myDevices().findById(device->parentId());
return;
} }
device->setStateValue(shellyOneConnectedStateTypeId, true); pluginStorage()->beginGroup(d->id().toString());
} QString ip = pluginStorage()->value("cachedAddress").toString();
pluginStorage()->endGroup();
void DevicePluginShelly::onClientDisconnected(MqttChannel *channel) return ip;
{
Device *device = m_mqttChannels.key(channel);
if (!device) {
qCWarning(dcShelly()) << "Received a client disconnect for a device we don't know!";
return;
}
device->setStateValue(shellyOneConnectedStateTypeId, false);
}
void DevicePluginShelly::onPublishReceived(MqttChannel *channel, const QString &topic, const QByteArray &payload)
{
Device *device = m_mqttChannels.key(channel);
if (!device) {
qCWarning(dcShelly()) << "Received a publish message for a device we don't know!";
return;
}
QString shellyId = device->paramValue(shellyOneDeviceIdParamTypeId).toString();
if (topic == "shellies/" + shellyId + "/input/0") {
// "1" or "0"
// Emit event button pressed
}
if (topic == "shellies/" + shellyId + "/relay/0") {
bool on = payload == "on";
device->setStateValue(shellyOnePowerStateTypeId, on);
}
qCDebug(dcShelly()) << "Publish received from" << device->name() << topic << payload;
} }

View File

@ -50,10 +50,21 @@ private slots:
void onClientDisconnected(MqttChannel* channel); void onClientDisconnected(MqttChannel* channel);
void onPublishReceived(MqttChannel* channel, const QString &topic, const QByteArray &payload); void onPublishReceived(MqttChannel* channel, const QString &topic, const QByteArray &payload);
private:
void setupShellyGateway(DeviceSetupInfo *info);
void setupShellyChild(DeviceSetupInfo *info);
QString getIP(Device *device) const;
private: private:
ZeroConfServiceBrowser *m_zeroconfBrowser = nullptr; ZeroConfServiceBrowser *m_zeroconfBrowser = nullptr;
QHash<Device*, MqttChannel*> m_mqttChannels; QHash<Device*, MqttChannel*> m_mqttChannels;
QHash<DeviceClassId, StateTypeId> m_connectedStateTypesMap;
QHash<DeviceClassId, StateTypeId> m_powerStateTypeMap;
QHash<ActionTypeId, DeviceClassId> m_powerActionTypesMap;
QHash<ActionTypeId, ParamTypeId> m_powerActionParamTypesMap;
}; };
#endif // DEVICEPLUGINSHELLY_H #endif // DEVICEPLUGINSHELLY_H

View File

@ -10,15 +10,36 @@
"deviceClasses": [ "deviceClasses": [
{ {
"id": "f810b66a-7177-4397-9771-4229abaabbb6", "id": "f810b66a-7177-4397-9771-4229abaabbb6",
"name": "shellyOne", "name": "shelly1",
"displayName": "Shelly One", "displayName": "Shelly1 / Shelly1PM",
"createMethods": ["discovery"], "createMethods": ["discovery"],
"interfaces": [ "powerswitch", "connectable" ], "interfaces": [ "gateway" ],
"paramTypes": [ "paramTypes": [
{ {
"id": "1d301dc0-5e48-473f-a611-8e407289e545", "id": "1d301dc0-5e48-473f-a611-8e407289e545",
"name":"id", "name":"id",
"displayName": "ID", "displayName": "Shelly ID",
"type": "QString",
"readOnly": true
},
{
"id": "d0e0499e-faa0-432a-a760-c295b0aefed0",
"name": "connectedDevice",
"displayName": "Connected device",
"type": "QString",
"allowedValues": ["None", "Generic", "Light"],
"defaultValue": "Generic"
},
{
"id": "fa1aa0f6-93b2-410d-a2c5-7b2f45eae679",
"name": "username",
"displayName": "Username (optional)",
"type": "QString"
},
{
"id": "d29b8399-bfa6-4146-921d-a1d43ca4e184",
"name": "password",
"displayName": "Password (optional)",
"type": "QString" "type": "QString"
} }
], ],
@ -31,9 +52,95 @@
"type": "bool", "type": "bool",
"defaultValue": false, "defaultValue": false,
"cached": false "cached": false
}
],
"actionTypes": [
{
"id": "b4067d54-36c5-4d30-bbc3-c8c712d6fd32",
"name": "reboot",
"displayName": "Reboot"
}
]
}, },
{ {
"id": "0f6df838-7fc4-4fc0-9247-b9b8fa4ec924", "id": "6de35a17-0f54-4397-894d-4321b64c53d1",
"name": "shellySwitch",
"displayName": "Shelly switch",
"createMethods": ["auto"],
"interfaces": [ "powerswitch", "connectable"],
"settingsTypes": [
{
"id": "ce9f1650-5e12-40f4-97de-27af86afa40b",
"name": "buttonType",
"displayName": "Button type",
"allowedValues": ["momentary", "toggle", "edge", "detached"],
"type": "QString",
"defaultValue": "toggle"
},
{
"id": "f31eb52b-9aaf-409d-8bba-badda7c1a249",
"name": "invertButton",
"displayName": "Invert button",
"type": "bool",
"defaultValue": false
}
],
"stateTypes": [
{
"id": "0c233312-7b8f-4ca3-880d-523cab9b3ccb",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "20f74d88-0683-4d3a-9513-6b29b5112b7b",
"name": "power",
"displayName": "On/Off",
"displayNameEvent": "On/Off toggled",
"type": "bool",
"defaultValue": false
}
],
"eventTypes": [
{
"id": "41498655-1943-4b46-ac36-adea7bafab87",
"name": "pressed",
"displayName": "Pressed"
}
]
},
{
"id": "512c3c7d-d6a6-4d2a-bccd-83147e5f9a25",
"name": "shellyGeneric",
"displayName": "Shelly connected device",
"createMethods": ["auto"],
"interfaces": ["power", "connectable"],
"settingsTypes": [
{
"id": "7d35aea3-1444-48c8-9732-a41bfc3b9d75",
"name": "defaultState",
"displayName": "Default state",
"allowedValues": ["on", "off", "last", "switch"],
"defaultValue": "off",
"type": "QString"
}
],
"stateTypes": [
{
"id": "4a141674-faa6-4953-8272-5b4a4da84d31",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "72d7dbba-757c-4b03-a092-1d3f374fa961",
"name": "power", "name": "power",
"displayName": "Power", "displayName": "Power",
"displayNameEvent": "Power changed", "displayNameEvent": "Power changed",
@ -42,12 +149,44 @@
"defaultValue": false, "defaultValue": false,
"writable": true "writable": true
} }
],
"eventTypes": [ ]
},
{ {
"id": "172e6aa3-13d3-4c71-8a4d-112605460863", "id": "62a2d6b8-d70d-45fc-ba8c-1c680282a399",
"name": "pressed", "name": "shellyLight",
"displayName": "Pressed" "displayName": "Shelly connected light",
"createMethods": ["auto"],
"interfaces": ["light", "connectable"],
"settingsTypes": [
{
"id": "4fe9ae31-3657-41bf-bd40-a219d58465d3",
"name": "defaultState",
"displayName": "Default state",
"allowedValues": ["on", "off", "last", "switch"],
"defaultValue": "off",
"type": "QString"
}
],
"stateTypes": [
{
"id": "61b7d8ac-d229-4268-8143-6edb2eca978d",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "2ee5bfab-271e-4b95-9464-122a5208f1a5",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"displayNameAction": "Set power",
"type": "bool",
"defaultValue": false,
"writable": true
} }
] ]
} }