New Plugin: SG-Ready heat pump based on GPIOs

pull/488/head
Simon Stürz 2021-10-12 14:09:58 +02:00
parent e7eae3711f
commit aef9cba99e
12 changed files with 608 additions and 0 deletions

17
debian/control vendored
View File

@ -930,6 +930,22 @@ Description: nymea.io plugin for senic
This package will install the nymea.io plugin for senic
Package: nymea-plugin-sgready
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
libnymea-gpio,
nymea-plugins-translations,
Description: nymea.io plugin for SG-Ready heat pump interface based on GPIOs
The nymea daemon is a plugin based IoT (Internet of Things) server. The
server works like a translator for devices, things and services and
allows them to interact.
With the powerful rule engine you are able to connect any device available
in the system and create individual scenes and behaviors for your environment.
.
This package will install the nymea.io plugin for SG-Ready heat pump interface based on GPIOs
Package: nymea-plugin-shelly
Architecture: any
Depends: ${shlibs:Depends},
@ -1333,6 +1349,7 @@ Depends: nymea-plugin-boblight,
nymea-plugin-avahimonitor,
nymea-plugin-anel,
nymea-plugin-gpio,
nymea-plugin-sgready,
nymea-plugin-i2cdevices,
nymea-plugin-mqttclient,
nymea-plugin-mcp3008,

View File

@ -0,0 +1 @@
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginsgready.so

View File

@ -54,6 +54,7 @@ PLUGIN_DIRS = \
reversessh \
senic \
serialportcommander \
sgready \
simulation \
sma \
somfytahoma \

14
sgready/README.md Normal file
View File

@ -0,0 +1,14 @@
# SG-Ready
Theis plugin allowes to create an SG ready interface based on 2 GPIO pins connected to a SG-Ready heat pump.
The GPIO need to be connected to the SG ready interface of the heat pump.
> Note: take care to configure the correct pins for relay 1 and 2. Connecting them over cross will lead to missbehaviour of your heat pump.
More information about the SG ready interface can be found on following website:
https://www.waermepumpe.de/normen-technik/sg-ready/

View File

@ -0,0 +1,157 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "integrationpluginsgready.h"
#include "plugininfo.h"
IntegrationPluginSgReady::IntegrationPluginSgReady()
{
}
void IntegrationPluginSgReady::init()
{
// Load possible system configurations for gpio pairs depending on well knwon platforms
}
void IntegrationPluginSgReady::discoverThings(ThingDiscoveryInfo *info)
{
// Check if GPIOs are available on this platform
if (!Gpio::isAvailable()) {
qCWarning(dcSgReady()) << "There are no GPIOs available on this plattform";
//: Error discovering SG ready GPIOs
return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("No GPIOs available on this system."));
}
// FIXME: provide once system configurations are loaded
info->finish(Thing::ThingErrorNoError);
}
void IntegrationPluginSgReady::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
qCDebug(dcSgReady()) << "Setup" << thing->name() << thing->params();
// Check if GPIOs are available on this platform
if (!Gpio::isAvailable()) {
qCWarning(dcSgReady()) << "There are no GPIOs available on this plattform";
//: Error discovering SG ready GPIOs
return info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("No GPIOs found on this system."));
}
if (thing->thingClassId() == sgReadyInterfaceThingClassId) {
if (m_sgReadyInterfaces.contains(thing)) {
// Rreconfigure...
SgReadyInterface *interface = m_sgReadyInterfaces.take(thing);
delete interface;
// Continue with normal setup...
}
int gpioNumber1 = thing->paramValue(sgReadyInterfaceThingGpioNumber1ParamTypeId).toUInt();
int gpioNumber2 = thing->paramValue(sgReadyInterfaceThingGpioNumber2ParamTypeId).toUInt();
bool gpioEnabled1 = thing->stateValue(sgReadyInterfaceGpio1StateStateTypeId).toBool();
bool gpioEnabled2 = thing->stateValue(sgReadyInterfaceGpio2StateStateTypeId).toBool();
SgReadyInterface *sgReadyInterface = new SgReadyInterface(gpioNumber1, gpioNumber2, this);
if (!sgReadyInterface->setup(gpioEnabled1, gpioEnabled2)) {
qCWarning(dcSgReady()) << "Setup" << thing << "failed because the GPIO could not be set up correctly.";
//: Error message if SG ready GPIOs setup failed
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Failed to set up the GPIO hardware interface."));
return;
}
// Reflect the SG states
connect(sgReadyInterface, &SgReadyInterface::sgReadyModeChanged, this, [thing, sgReadyInterface](SgReadyInterface::SgReadyMode mode){
Q_UNUSED(mode)
thing->setSettingValue(sgReadyInterfaceGpio1StateStateTypeId, sgReadyInterface->gpio1()->value() == Gpio::ValueHigh);
thing->setSettingValue(sgReadyInterfaceGpio2StateStateTypeId, sgReadyInterface->gpio2()->value() == Gpio::ValueHigh);
});
m_sgReadyInterfaces.insert(thing, sgReadyInterface);
info->finish(Thing::ThingErrorNoError);
return;
}
}
void IntegrationPluginSgReady::postSetupThing(Thing *thing)
{
Q_UNUSED(thing)
}
void IntegrationPluginSgReady::thingRemoved(Thing *thing)
{
if (m_sgReadyInterfaces.contains(thing)) {
SgReadyInterface *sgReadyInterface = m_sgReadyInterfaces.take(thing);
delete sgReadyInterface;
}
}
void IntegrationPluginSgReady::executeAction(ThingActionInfo *info)
{
Thing *thing = info->thing();
if (thing->thingClassId() == sgReadyInterfaceThingClassId) {
SgReadyInterface *sgReadyInterface = m_sgReadyInterfaces.value(thing);
if (!sgReadyInterface || !sgReadyInterface->isValid()) {
qCWarning(dcSgReady()) << "Failed to execute action. There is no interface available for" << thing;
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
// FIXME: the modes have timeing constrains we need to take care off.
if (info->action().actionTypeId() == sgReadyInterfaceSgReadyModeActionTypeId) {
QString sgReadyModeString = info->action().paramValue(sgReadyInterfaceSgReadyModeActionSgReadyModeParamTypeId).toString();
qCDebug(dcSgReady()) << "Set SG ready mode from" << thing << "to" << sgReadyModeString;
SgReadyInterface::SgReadyMode mode;
if (sgReadyModeString == "Off") {
mode = SgReadyInterface::SgReadyModeOff;
} else if (sgReadyModeString == "Low") {
mode = SgReadyInterface::SgReadyModeLow;
} else if (sgReadyModeString == "High") {
mode = SgReadyInterface::SgReadyModeHigh;
} else {
mode = SgReadyInterface::SgReadyModeStandard;
}
if (!sgReadyInterface->setSgReadyMode(mode)) {
qCWarning(dcSgReady()) << "Failed to set the sg ready mode on" << thing << "to" << sgReadyModeString;
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
info->finish(Thing::ThingErrorNoError);
}
}
}

View File

@ -0,0 +1,59 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef INTEGRATIONPLUGINSGREADY_H
#define INTEGRATIONPLUGINSGREADY_H
#include "integrations/integrationplugin.h"
#include "sgreadyinterface.h"
class IntegrationPluginSgReady : public IntegrationPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginsgready.json")
Q_INTERFACES(IntegrationPlugin)
public:
explicit IntegrationPluginSgReady();
void init() override;
void discoverThings(ThingDiscoveryInfo *info) override;
void setupThing(ThingSetupInfo *info) override;
void postSetupThing(Thing *thing) override;
void thingRemoved(Thing *thing) override;
void executeAction(ThingActionInfo *info) override;
private:
QHash<Thing *, SgReadyInterface *> m_sgReadyInterfaces;
};
#endif // INTEGRATIONPLUGINSGREADY_H

View File

@ -0,0 +1,77 @@
{
"name": "SgReady",
"displayName": "SG-Ready",
"id": "7923e2f3-b2f7-445d-ab14-033000485bd8",
"vendors": [
{
"name": "nymea",
"displayName": "nymea",
"id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6",
"thingClasses": [
{
"id": "5a5f3aca-a958-4bca-b8fc-1168fe3371ec",
"name": "sgReadyInterface",
"displayName": "SG Ready interface",
"createMethods": ["user"],
"interfaces": [ "smartgridheatpump" ],
"settingsTypes": [
],
"paramTypes": [
{
"id": "8de25aef-d868-4410-abd3-3bec9c46aba3",
"name": "gpioNumber1",
"displayName": "GPIO number 1",
"type": "int",
"defaultValue": -1
},
{
"id": "113ba124-a392-4bc3-861a-f7feaab82fc4",
"name": "gpioNumber2",
"displayName": "GPIO number 2",
"type": "int",
"defaultValue": -1
}
],
"stateTypes": [
{
"id": "91008e09-2702-4bd5-942e-398513ae3d15",
"name": "sgReadyMode",
"displayName": "Smart grid mode",
"displayNameEvent": "Smart grid mode changed",
"displayNameAction": "Set smart grid mode",
"type": "QString",
"possibleValues": [
"Off",
"Low",
"Standard",
"High"
],
"writable": true,
"defaultValue": "Standard",
"cached": true,
"suggestLogging": true
},
{
"id": "d2c83778-693f-4146-a656-a8ed901c1a7f",
"name": "gpio1State",
"displayName": "Relay 1 enabled",
"displayNameEvent": "Relay 1 enabled changed",
"type": "bool",
"cached": true,
"defaultValue": false
},
{
"id": "15608d58-b1d4-4696-ac1a-62aec5ec44ab",
"name": "gpio2State",
"displayName": "Relay 2 enabled",
"displayNameEvent": "Relay 2 enabled changed",
"type": "bool",
"cached": true,
"defaultValue": false
}
]
}
]
}
]
}

13
sgready/meta.json Normal file
View File

@ -0,0 +1,13 @@
{
"title": "SG-Ready",
"tagline": "Control heat pumps offering the SG-Ready interface using GPIOs.",
"icon": "smart-grid-logo.png",
"stability": "community",
"offline": true,
"technologies": [
],
"categories": [
"heating"
]
}

13
sgready/sgready.pro Normal file
View File

@ -0,0 +1,13 @@
include(../plugins.pri)
PKGCONFIG += nymea-gpio
SOURCES += \
integrationpluginsgready.cpp \
sgreadyinterface.cpp
HEADERS += \
integrationpluginsgready.h \
sgreadyinterface.h

View File

@ -0,0 +1,181 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "sgreadyinterface.h"
#include "extern-plugininfo.h"
SgReadyInterface::SgReadyInterface(int gpioNumber1, int gpioNumber2, QObject *parent) :
QObject(parent),
m_gpioNumber1(gpioNumber1),
m_gpioNumber2(gpioNumber2)
{
}
SgReadyInterface::SgReadyMode SgReadyInterface::sgReadyMode() const
{
return m_sgReadyMode;
}
bool SgReadyInterface::setSgReadyMode(SgReadyMode sgReadyMode)
{
if (!isValid())
return false;
/* https://www.waermepumpe.de/normen-technik/sg-ready/
*
* Off: 1,0
* Low: 0,0
* Standard: 0,1
* High: 1,1
*/
QPair<bool, bool> gpioSettings;
switch (sgReadyMode) {
case SgReadyModeOff:
gpioSettings.first = true;
gpioSettings.second = false;
break;
case SgReadyModeLow:
gpioSettings.first = false;
gpioSettings.second = false;
break;
case SgReadyModeStandard:
gpioSettings.first = false;
gpioSettings.second = true;
break;
case SgReadyModeHigh:
gpioSettings.first = true;
gpioSettings.second = true;
break;
}
if (!m_gpio1->setValue(gpioSettings.first ? Gpio::ValueHigh : Gpio::ValueLow)) {
qCWarning(dcSgReady()) << "Could not switch GPIO 1 for setting" << sgReadyMode;
return false;
}
if (!m_gpio2->setValue(gpioSettings.second ? Gpio::ValueHigh : Gpio::ValueLow)) {
qCWarning(dcSgReady()) << "Could not switch GPIO 2 for setting" << sgReadyMode;
return false;
}
if (m_sgReadyMode != sgReadyMode) {
m_sgReadyMode = sgReadyMode;
emit sgReadyModeChanged(m_sgReadyMode);
}
return true;
}
bool SgReadyInterface::setup(bool gpio1Enabled, bool gpio2Enabled)
{
m_gpio1 = setupGpio(m_gpioNumber1, gpio1Enabled);
if (!m_gpio1) {
qCWarning(dcSgReady()) << "Failed to set up SG ready interface gpio 1" << m_gpioNumber1;
return false;
}
m_gpio2 = setupGpio(m_gpioNumber2, gpio2Enabled);
if (!m_gpio2) {
delete m_gpio1;
m_gpio1 = nullptr;
qCWarning(dcSgReady()) << "Failed to set up SG ready interface gpio 2" << m_gpioNumber1;
return false;
}
/* https://www.waermepumpe.de/normen-technik/sg-ready/
*
* Off: 1,0
* Low: 0,0
* Standard: 0,1
* High: 1,1
*/
if (gpio1Enabled && !gpio2Enabled) {
m_sgReadyMode = SgReadyModeOff;
} else if (!gpio1Enabled && !gpio2Enabled) {
m_sgReadyMode = SgReadyModeLow;
} else if (!gpio1Enabled && gpio2Enabled) {
m_sgReadyMode = SgReadyModeStandard;
} else {
m_sgReadyMode = SgReadyModeHigh;
}
emit sgReadyModeChanged(m_sgReadyMode);
return true;
}
bool SgReadyInterface::isValid() const
{
return m_gpioNumber1 >= 0 && m_gpioNumber2 >= 0 && m_gpio1 && m_gpio2;
}
Gpio *SgReadyInterface::gpio1() const
{
return m_gpio1;
}
Gpio *SgReadyInterface::gpio2() const
{
return m_gpio2;
}
Gpio *SgReadyInterface::setupGpio(int gpioNumber, bool initialValue)
{
if (gpioNumber < 0) {
qCWarning(dcSgReady()) << "Invalid gpio number for setting up gpio" << gpioNumber;
return nullptr;
}
// Create and configure gpio
Gpio *gpio = new Gpio(gpioNumber, this);
if (!gpio->exportGpio()) {
qCWarning(dcSgReady()) << "Could not export gpio" << gpioNumber;
gpio->deleteLater();
return nullptr;
}
if (!gpio->setDirection(Gpio::DirectionOutput)) {
qCWarning(dcSgReady()) << "Failed to configure gpio" << gpioNumber << "as output";
gpio->deleteLater();
return nullptr;
}
// Set the pin to the initial value
if (!gpio->setValue(initialValue ? Gpio::ValueHigh : Gpio::ValueLow)) {
qCWarning(dcSgReady()) << "Failed to set initially value" << initialValue << "for gpio" << gpioNumber;
gpio->deleteLater();
return nullptr;
}
return gpio;
}

View File

@ -0,0 +1,75 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2021, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef SGREADYINTERFACE_H
#define SGREADYINTERFACE_H
#include <QObject>
#include "gpio.h"
class SgReadyInterface : public QObject
{
Q_OBJECT
public:
enum SgReadyMode {
SgReadyModeOff,
SgReadyModeLow,
SgReadyModeStandard,
SgReadyModeHigh
};
Q_ENUM(SgReadyMode)
explicit SgReadyInterface(int gpioNumber1, int gpioNumber2, QObject *parent = nullptr);
SgReadyMode sgReadyMode() const;
bool setSgReadyMode(SgReadyMode sgReadyMode);
bool setup(bool gpio1Enabled, bool gpio2Enabled);
bool isValid() const;
Gpio *gpio1() const;
Gpio *gpio2() const;
signals:
void sgReadyModeChanged(SgReadyMode sgReadyMode);
private:
SgReadyMode m_sgReadyMode = SgReadyModeStandard;
int m_gpioNumber1 = -1;
int m_gpioNumber2 = -1;
Gpio *m_gpio1 = nullptr;
Gpio *m_gpio2 = nullptr;
Gpio *setupGpio(int gpioNumber, bool initialValue);
};
#endif // SGREADYINTERFACE_H

BIN
sgready/smart-grid-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB