Skip to content

Commit

Permalink
Merge PR #104: New plugin: Senseair S8
Browse files Browse the repository at this point in the history
  • Loading branch information
jenkins committed Feb 4, 2023
2 parents 9096691 + 346d5e2 commit 1bee033
Show file tree
Hide file tree
Showing 13 changed files with 395 additions and 0 deletions.
8 changes: 8 additions & 0 deletions debian/control
Expand Up @@ -185,6 +185,14 @@ Description: nymea integration plugin for Schrack wallboxes
This package contains the nymea integration plugin for Schrack wallboxes.


Package: nymea-plugin-senseair
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
Description: nymea integration plugin for Senseair sensors
This package contains the nymea integration plugin for Senseair sensors.


Package: nymea-plugin-sma
Architecture: any
Depends: ${shlibs:Depends},
Expand Down
2 changes: 2 additions & 0 deletions debian/nymea-plugin-senseair.install.in
@@ -0,0 +1,2 @@
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginsenseair.so
senseair/translations/*qm usr/share/nymea/translations/
1 change: 1 addition & 0 deletions nymea-plugins-modbus.pro
Expand Up @@ -18,6 +18,7 @@ PLUGIN_DIRS = \
mypv \
phoenixconnect \
schrack \
senseair \
sma \
stiebeleltron \
sunspec \
Expand Down
10 changes: 10 additions & 0 deletions senseair/README.md
@@ -0,0 +1,10 @@
# Senseair

Connects Senseair sensors to nymea. Currently supported sensors are:

* S8 CO2 sensor

# Requirements

* A working RS485 (Modbus RTU) connection to the sensor.
* A Modbus RTU master configured in nymea with baudrate 9600, 8 data bits, 1 stop bits and no parity.
Binary file added senseair/ba-senseair-s8-modbus.pdf
Binary file not shown.
147 changes: 147 additions & 0 deletions senseair/integrationpluginsenseair.cpp
@@ -0,0 +1,147 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, 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 "integrationpluginsenseair.h"
#include "plugininfo.h"
#include "hardwaremanager.h"
#include "hardware/modbus/modbusrtuhardwareresource.h"

IntegrationPluginSenseAir::IntegrationPluginSenseAir()
{

}

void IntegrationPluginSenseAir::discoverThings(ThingDiscoveryInfo *info)
{
foreach (ModbusRtuMaster *modbusMaster, hardwareManager()->modbusRtuResource()->modbusRtuMasters()) {
qCDebug(dcSenseAir()) << "Found RTU master resource" << modbusMaster;
if (modbusMaster->connected() && modbusMaster->baudrate() == 9600 && modbusMaster->dataBits() == 8 && modbusMaster->stopBits() == 1 && modbusMaster->parity() == QSerialPort::NoParity) {
ParamList parameters;
ThingDescriptor thingDescriptor(s8ThingClassId, "Modbus RTU master", modbusMaster->serialPort());
parameters.append(Param(s8ThingRtuMasterParamTypeId, modbusMaster->modbusUuid()));
thingDescriptor.setParams(parameters);
info->addThingDescriptor(thingDescriptor);
} else {
qCWarning(dcSenseAir()) << "Found configured resource" << modbusMaster << "but it is not connected. Skipping.";
}
}

QString displayMessage;
if (info->thingDescriptors().count() == 0) {
displayMessage = QT_TR_NOOP("Please set up a Modbus RTU master with baudrate 9600, 8 data bits, 1 stop bits and no parity first.");
}

info->finish(Thing::ThingErrorNoError, displayMessage);
}

void IntegrationPluginSenseAir::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
qCDebug(dcSenseAir()) << "Setup" << thing << thing->params();


if (m_s8Connections.contains(thing)) {
qCDebug(dcSenseAir()) << "Reconfiguring existing thing" << thing->name();
m_s8Connections.take(thing)->deleteLater();
}

ModbusRtuMaster *master = hardwareManager()->modbusRtuResource()->getModbusRtuMaster(thing->paramValue(s8ThingRtuMasterParamTypeId).toUuid());
if (!master) {
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The Modbus RTU master is not available."));
return;
}

SenseAirS8ModbusRtuConnection *s8Connection = new SenseAirS8ModbusRtuConnection(master, 0xfe, this);
connect(info, &ThingSetupInfo::aborted, s8Connection, &SenseAirS8ModbusRtuConnection::deleteLater);

connect(s8Connection, &SenseAirS8ModbusRtuConnection::reachableChanged, thing, [s8Connection, thing](bool reachable){
qCDebug(dcSenseAir()) << "Reachable state changed" << reachable;
if (reachable) {
s8Connection->initialize();
} else {
thing->setStateValue(s8ConnectedStateTypeId, false);
}
});
connect(s8Connection, &SenseAirS8ModbusRtuConnection::initializationFinished, info, [=](bool success){
qCDebug(dcSenseAir()) << "Initialisation finished" << success;
if (success) {
qCDebug(dcSenseAir()) << "Meter status:" << s8Connection->meterStatus();
m_s8Connections.insert(thing, s8Connection);
info->finish(Thing::ThingErrorNoError);
} else {
delete s8Connection;
info->finish(Thing::ThingErrorHardwareNotAvailable);
}
});
connect(s8Connection, &SenseAirS8ModbusRtuConnection::initializationFinished, thing, [=](bool success){
if (success) {
thing->setStateValue(s8ConnectedStateTypeId, true);
}
});

connect(s8Connection, &SenseAirS8ModbusRtuConnection::spaceCo2Changed, thing, [=](quint16 spaceCo2){
qCDebug(dcSenseAir()) << "CO2 changed:" << spaceCo2;
thing->setStateValue(s8Co2StateTypeId, spaceCo2);
});

s8Connection->update();
}

void IntegrationPluginSenseAir::postSetupThing(Thing *thing)
{
Q_UNUSED(thing)
if (!m_pluginTimer) {
qCDebug(dcSenseAir()) << "Starting plugin timer...";
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(5);
connect(m_pluginTimer, &PluginTimer::timeout, this, [this] {
foreach(SenseAirS8ModbusRtuConnection *connection, m_s8Connections) {
qCDebug(dcSenseAir()) << "Updating...";
if (connection->reachable()) {
connection->update();
}
}
});

m_pluginTimer->start();

}
}

void IntegrationPluginSenseAir::thingRemoved(Thing *thing)
{
SenseAirS8ModbusRtuConnection *connection = m_s8Connections.take(thing);
delete connection;


if (myThings().isEmpty() && m_pluginTimer) {
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
m_pluginTimer = nullptr;
}
}
64 changes: 64 additions & 0 deletions senseair/integrationpluginsenseair.h
@@ -0,0 +1,64 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, 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 INTEGRATIONPLUGINSENSEAIR_H
#define INTEGRATIONPLUGINSENSEAIR_H

#include <plugintimer.h>
#include <integrations/integrationplugin.h>

#include "extern-plugininfo.h"
#include "senseairs8modbusrtuconnection.h"

class IntegrationPluginSenseAir: public IntegrationPlugin
{
Q_OBJECT

Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginsenseair.json")
Q_INTERFACES(IntegrationPlugin)

public:
explicit IntegrationPluginSenseAir();

void discoverThings(ThingDiscoveryInfo *info) override;
void setupThing(ThingSetupInfo *info) override;
void postSetupThing(Thing *thing) override;
void thingRemoved(Thing *thing) override;

private:

PluginTimer *m_pluginTimer = nullptr;
QHash<Thing *, SenseAirS8ModbusRtuConnection *> m_s8Connections;

};

#endif // INTEGRATIONPLUGINSENSEAIR_H


50 changes: 50 additions & 0 deletions senseair/integrationpluginsenseair.json
@@ -0,0 +1,50 @@
{
"name": "SenseAir",
"displayName": "Senseair",
"id": "c7c3c65c-a0cc-4ab1-90d8-4ad05bfcdc38",
"vendors": [
{
"name": "senseAir",
"displayName": "SenseAir",
"id": "f8427109-ada2-4428-9cfe-61631f763f5a",
"thingClasses": [
{
"name": "s8",
"displayName": "SenseAir S8",
"id": "b7c80f1c-b583-4e81-a161-c7e1a0b13ea0",
"createMethods": ["discovery"],
"interfaces": ["co2sensor", "connectable"],
"paramTypes": [
{
"id": "5de7968f-e433-4143-bd0f-b8c3776782b7",
"name":"rtuMaster",
"displayName": "Modbus RTU master UUID",
"type": "QString",
"defaultValue": ""
}
],
"stateTypes": [
{
"id": "71d6ba02-ca41-41f6-af87-f6e60d371591",
"name": "connected",
"displayName": "Connected",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "7506d7c3-ebf7-408b-9625-03be99736a00",
"name": "co2",
"displayName": "CO2",
"type": "double",
"unit": "PartsPerMillion",
"defaultValue": 0,
"minValue": 0,
"maxValue": 2000
}
]
}
]
}
]
}
13 changes: 13 additions & 0 deletions senseair/meta.json
@@ -0,0 +1,13 @@
{
"title": "Senseair",
"tagline": "Connect Senseair sensors nymea.",
"icon": "senseair.png",
"stability": "consumer",
"offline": true,
"technologies": [
"modbus"
],
"categories": [
"sensor"
]
}
31 changes: 31 additions & 0 deletions senseair/s8-registers.json
@@ -0,0 +1,31 @@
{
"className": "SenseAirS8",
"protocol": "RTU",
"errorLimitUntilNotReachable": 20,
"endianness": "BigEndian",
"checkReachableRegister": "spaceCo2",
"registers": [
{
"id": "meterStatus",
"address": 0,
"size": 1,
"type": "uint16",
"readSchedule": "init",
"registerType": "inputRegister",
"description": "Meter status",
"defaultValue": 0,
"access": "RO"
},
{
"id": "spaceCo2",
"address": 3,
"size": 1,
"type": "uint16",
"readSchedule": "update",
"registerType": "inputRegister",
"description": "Space CO2",
"defaultValue": 0,
"access": "RO"
}
]
}
Binary file added senseair/senseair.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions senseair/senseair.pro
@@ -0,0 +1,13 @@
include(../plugins.pri)

# Generate modbus connection
MODBUS_CONNECTIONS += s8-registers.json
MODBUS_TOOLS_CONFIG += VERBOSE

include(../modbus.pri)

HEADERS += \
integrationpluginsenseair.h

SOURCES += \
integrationpluginsenseair.cpp

0 comments on commit 1bee033

Please sign in to comment.