New plugin: Senseair S8

master
Michael Zanetti 2022-12-29 02:51:59 +01:00
parent 015e25ee13
commit 346d5e207d
13 changed files with 395 additions and 0 deletions

8
debian/control vendored
View File

@ -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},

View File

@ -0,0 +1,2 @@
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginsenseair.so
senseair/translations/*qm usr/share/nymea/translations/

View File

@ -18,6 +18,7 @@ PLUGIN_DIRS = \
mypv \
phoenixconnect \
schrack \
senseair \
sma \
stiebeleltron \
sunspec \

10
senseair/README.md Normal file
View File

@ -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 not shown.

View File

@ -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;
}
}

View File

@ -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

View File

@ -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
senseair/meta.json Normal file
View File

@ -0,0 +1,13 @@
{
"title": "Senseair",
"tagline": "Connect Senseair sensors nymea.",
"icon": "senseair.png",
"stability": "consumer",
"offline": true,
"technologies": [
"modbus"
],
"categories": [
"sensor"
]
}

View File

@ -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"
}
]
}

BIN
senseair/senseair.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

13
senseair/senseair.pro Normal file
View File

@ -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

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>IntegrationPluginSenseAir</name>
<message>
<location filename="../integrationpluginsenseair.cpp" line="58"/>
<source>Please set up a Modbus RTU master with baudrate 9600, 8 data bits, 1 stop bits and no parity first.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginsenseair.cpp" line="77"/>
<source>The Modbus RTU master is not available.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SenseAir</name>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/senseair/plugininfo.h" line="31"/>
<source>CO2</source>
<extracomment>The name of the StateType ({7506d7c3-ebf7-408b-9625-03be99736a00}) of ThingClass s8</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/senseair/plugininfo.h" line="34"/>
<source>Connected</source>
<extracomment>The name of the StateType ({71d6ba02-ca41-41f6-af87-f6e60d371591}) of ThingClass s8</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/senseair/plugininfo.h" line="37"/>
<source>Modbus RTU master UUID</source>
<extracomment>The name of the ParamType (ThingClass: s8, Type: thing, ID: {5de7968f-e433-4143-bd0f-b8c3776782b7})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/senseair/plugininfo.h" line="40"/>
<source>SenseAir</source>
<extracomment>The name of the vendor ({f8427109-ada2-4428-9cfe-61631f763f5a})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/senseair/plugininfo.h" line="43"/>
<source>SenseAir S8</source>
<extracomment>The name of the ThingClass ({b7c80f1c-b583-4e81-a161-c7e1a0b13ea0})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build/nymea-plugins-modbus-Desktop-Debug/senseair/plugininfo.h" line="46"/>
<source>Senseair</source>
<extracomment>The name of the plugin SenseAir ({c7c3c65c-a0cc-4ab1-90d8-4ad05bfcdc38})</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
</TS>