added drexel und weiss

This commit is contained in:
Boernsman 2020-01-22 16:03:12 +01:00
commit 28542347a2
20 changed files with 2327 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.pro.user
builddir
doc/html

296
debian/changelog vendored Normal file
View File

@ -0,0 +1,296 @@
nymea-plugins (0.17.0) xenial; urgency=medium
[ Michael Zanetti ]
* FlowerCare: Add a refresh rate setting
* Packaging: Fix dpkg clean target
* Simulation: Add a simulated barcode scanner device
[ Bernhard Trinnes ]
* Http commander: Add http server
[ Michael Zanetti ]
* DaylightSensor: Update README.md
* dweet.io: Update README
* Kodi: Automatically redetect Kodi when its IP address changes
* Various Plugins: Spelling fixes in README files
* Tasmota: Add Blinds support
[ Federico Leoni ]
* Update Tasmota readme
[ Michael Zanetti ]
* PhilipsHue: Fix the timeout setting
-- Jenkins <jenkins@nymea.io> Mon, 09 Dec 2019 10:09:05 +0100
nymea-plugins (0.16.0) xenial; urgency=medium
[ Bernhard Trinnes ]
* New Plugin: CoinMarketCap
* New plugin: Sonos
[ Michael Zanetti ]
* All Plugins: Devicemanager api rework
* Fix hertz wording
* New plugin: Shelly
* EQ-3: Some fixes for Eqiva Bluetooth Smart Radiator Thermostats
* PhilipsHue: Fix light intensity conversion to lux for the motion
sensor
* Anel: Add anel plugin to main plugin package
* NetworkDetector: Improve packaging and error handling
* Nuimo: Make it work with Bluez 5.48 (except battery service)
* Kodi: Fix active player not updating properly in some circumstances
-- Jenkins <jenkins@nymea.io> Tue, 22 Oct 2019 01:09:57 +0200
nymea-plugins (0.15.2) xenial; urgency=medium
[ Bernhard Trinnes ]
* Denon Plug-In: Add heos devices
[ George Yatsev ]
* Tasmota: Add support for 3ch devices
[ Bernhard Trinnes ]
* TcpCommander: Fixed input and output device class
* New plug-in: systemmonitor
[ Michael Zanetti ]
* PhilipsHue: Make use of the new alert interface
[ Bernhard Trinnes ]
* New plugin: One-Wire
* GPIO Plug-In: add counter device
* Senic: Fixed device clean up on a failed device setup
[ Michael Zanetti ]
* Remove Plugin: UniPi
-- Jenkins <jenkins@nymea.io> Thu, 19 Sep 2019 12:28:02 +0200
nymea-plugins (0.15.1) xenial; urgency=medium
[ Bernhard Trinnes ]
* New Plugin: WS2812FX
[ Michael Zanetti ]
* Tasmota: Use new powerswitch interface
[ Bernhard Trinnes ]
* Add plug-in bose soundtouch
* New plugin: generic interfaces
[ Michael Zanetti ]
* Kodi: Add support for media browsing
[ Bernhard Trinnes ]
* Fix senic
[ Michael Zanetti ]
* Fix hue outdoorsensor
-- Jenkins <jenkins@nymea.io> Mon, 02 Sep 2019 18:02:07 +0200
nymea-plugins (0.15.0) xenial; urgency=medium
[ Simon Stürz ]
* All Plugins: Update docs mechanism
[ Michael Zanetti ]
* NetworkDetector: Change grace period to be a setting instead of a
param
* Philips Hue: Add support for the hue indoor motion sensor
* All Plugins: Update according to new DeviceManager API
* Use the new nymea-plugininfocompiler for building
* GenericElements: Fix toggle button writable state
-- Jenkins <jenkins@nymea.io> Mon, 22 Jul 2019 12:17:16 +0200
nymea-plugins (0.12.3) xenial; urgency=medium
[ Michael Zanetti ]
* FlowerCare: Drop unneeded name parameter
* PhilipsHue: Improve discovery and fix reconfigure
* PhilipsHue: Drop deprecated old-style Hue Remote button events
* WeMo: Fix discovery if device has been renamed
* TexasInstruments: Fix Package dependencies
* PhilipsHue: Work around a bug in Osram LIGHTIFY RGBW LED stripes
-- Jenkins <jenkins@nymea.io> Fri, 28 Jun 2019 12:45:50 +0200
nymea-plugins (0.12.2) xenial; urgency=medium
[ Bernhard Trinnes ]
* Removed plugins: Orderbutton, Plantcare and ws2812
* renamed HTTP commander vendor, fixed segfault on device removed
* Fix serialportcommander
[ Michael Zanetti ]
* OpenWeatherMap: Add daylightsensor and allow loading custom API keys
[ Simon Stürz ]
* Update mail notifications plugin
[ Michael Zanetti ]
* Depend on pkg-config
* Kodi, AvahiMonitor: Update to new ZeroConf API
-- Jenkins <jenkins@nymea.io> Wed, 19 Jun 2019 23:52:37 +0200
nymea-plugins (0.12.1) xenial; urgency=medium
[ Michael Zanetti ]
* Rename vendor "guh GmbH" to "nymea"
* Don't use dpkg specific tools to set install path
-- Jenkins <jenkins@nymea.io> Thu, 02 May 2019 11:56:17 +0200
nymea-plugins (0.12.0) xenial; urgency=medium
[ Michael Zanetti ]
* aWATTar: Cleanup old unused code and use connectable interface
[ Simon Stürz ]
* PhilipsHue plugin: Add Hue Outdoor sensor
[ Michael Zanetti ]
* All Plugins: Update to API changes in core.
* Boblight: Build using internal plugins.pri
* New plugin: Pushbullet
* New Plugin: TexasInstruments, replaces MultiSensor plugin
-- Jenkins <jenkins@nymea.io> Fri, 12 Apr 2019 13:42:46 +0200
nymea-plugins (0.10.3) xenial; urgency=medium
[ Simon Stürz ]
* Netatmo: Update API changes and improve refresh behavior
[ Michael Zanetti ]
* Simulation: Update ev charger to new interface spec
* SonoffTasmota: Fix wrong IP settings in device setup
* NetworkDetector: Don't fail the entire discovery if one of the scan
targets fails
* PhilipsHue: Enable reconfiguring Hue bridges (Re-pairing)
* Elgato: Fix reading of color state
* Simulation: Add missing unit to heating plugins
* FlowerCare: Add unit to conductivity sensor
* Simulation: Make use of thermostat interface
* NetworkDetector: Improve monitoring, add a grace period parameter
* EQ-3: Add support for the Eqiva Bluetooth thermostat
* Update Plugins: Set deviceId in Descriptors on discovery
* Flower Care: Fix temperature reading for negative values
[ Bernhard Trinnes ]
* New Plugin: Serialport commander
-- Jenkins <jenkins@nymea.io> Wed, 06 Mar 2019 21:13:53 +0100
nymea-plugins (0.10.2) xenial; urgency=medium
[ Michael Zanetti ]
* Simulation: Make use of powersocket interface
* New Plugin: Boblight
[ Simon Stürz ]
* LG smart TV: General update (use of interfaces, improve connectivity
with TV)
[ Michael Zanetti ]
* New Plugin: ANEL Elektronik NET-PwrCtrl
* Networkdetector: Implement the presencesensor interface
* New Plugin: Daylight sensor which works offline and without any
hardware
* Sumulation: Fix action simulation for simulated blinds
* PhilipsHue: Fix hue tap button 4 param value typo
* Kodi: Implement new media interfaces
-- Jenkins <jenkins@nymea.io> Thu, 17 Jan 2019 15:37:27 +0100
nymea-plugins (0.10.1) xenial; urgency=medium
[ Michael Zanetti ]
* Elgato/Avea: Fix handling white channel in set color action
* New plugin: Generic MQTT client plugin (mqttclient)
* PhilipsHue: Improve naming of hue devices using nymea's system name
and syncing to bridge
* New plugin: Sonoff-Tasmota devices plugin (tasmota)
* Netatmo: fix after upstream API change, make use of new interfaces
[ Simon Stürz ]
* New plugin: UniPi
-- Jenkins <jenkins@nymea.io> Wed, 12 Dec 2018 14:44:58 +0100
nymea-plugins (0.10.0) xenial; urgency=medium
[ Michael Zanetti ]
* Elgato/Avea: Fix handling white channel in set color action
* New plugin: Generic MQTT client plugin (mqttclient)
* PhilipsHue: Improve naming of hue devices using nymea's system name
and syncing to bridge
* New plugin: Sonoff-Tasmota devices plugin (tasmota)
* Netatmo: fix after upstream API change, make use of new interfaces
-- Jenkins <jenkins@nymea.io> Sun, 09 Dec 2018 18:56:19 +0100
nymea-plugins (0.9.39) xenial; urgency=medium
[ Jenkins ]
* Prepare for release
[ Michael Zanetti ]
* use system interface in snapdcontrol
* update hue plugin
* change networkdetector discovery to do parallel scans...
* Make use of new heating/evcharger interfaces in the simulated
devices plugin
-- Jenkins <jenkins@nymea.io> Tue, 20 Nov 2018 14:32:38 +0100
nymea-plugins (0.9.38) xenial; urgency=medium
[ Jenkins ]
* Prepare for release
[ Michael Zanetti ]
* add some nicer curves to simulated values
* gateway now inherits connectable
* add simple blind simulation
* implement fingerprint interface in simulation plugin
-- Jenkins <jenkins@nymea.io> Mon, 29 Oct 2018 14:19:53 +0100
nymea-plugins (0.9.37) xenial; urgency=medium
[ Jenkins ]
* Prepare for release
[ Michael Zanetti ]
* Update all plugins using the new nymea-generateplugininfo
-- Jenkins <jenkins@nymea.io> Wed, 10 Oct 2018 04:38:16 +0200
nymea-plugins (0.9.36) bionic; urgency=medium
* Bump version to be in line with core
-- Michael Zanetti <michael.zanetti@nymea.io> Wed, 10 Oct 2018 04:00:57 +0200
nymea-plugins (0.2.0) bionic; urgency=medium
* rename to nymea
-- Michael Zanetti <michael.zanetti@nymea.io> Wed, 10 Oct 2018 04:00:00 +0200
guh-plugins (0.1.0) xenial; urgency=medium
* Add metapackages
-- Simon Stürz <simon.stuerz@guh.io> Tue, 03 Oct 2017 17:07:31 +0200
guh-plugins (0.1) UNRELEASED; urgency=medium
* Initial release. (Closes: #XXXXXX)
-- Michael Zanetti <michael.zanetti@guh.io> Tue, 11 Jul 2017 15:23:34 +0200

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
9

63
debian/control vendored Normal file
View File

@ -0,0 +1,63 @@
Source: nymea-plugins-modbus
Section: utils
Priority: options
Maintainer: Michael Zanetti <michael.zanetti@guh.io>
Build-depends: libboblight-dev,
debhelper (>= 0.0.0),
libnymea1-dev (>= 0.17),
libnymea-mqtt-dev,
libqt5serialport5-dev,
libqt5websockets5-dev,
nymea-dev-tools:native,
pkg-config,
python:any,
qtbase5-dev,
qtconnectivity5-dev,
libow-dev,
Standards-Version: 3.9.3
Package: nymea-plugin-drexelundweiss
Architecture: any
Section: libs
Depends: ${shlibs:Depends},
${misc:Depends},
nymea-plugins-translations,
Description: nymea.io plugin for Drexel & Weiss heat pumps
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 Drexel & Weiss heat pumps
Package: nymea-plugins-translations
Section: misc
Architecture: all
Depends: ${misc:Depends}
Replaces: guh-plugins-translations
Description: Translation files for nymea plugins - translations
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 provides the translation files for all nymea plugins.
Package: nymea-plugins-modbus
Section: libs
Architecture: all
Depends: nymea-plugin-drexelundweiss,
Description: Plugins for nymea IoT server - the modbus plugin collection
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 modbus plugins.

77
debian/copyright vendored Normal file
View File

@ -0,0 +1,77 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: nymea-plugins
Upstream-Contact: Simon Stürz <simon.stuerz@guh.io>
Copyright: 2014-2017, guh GmbH
Download: http://www.github.com/guh/guh-plugins
Source: https://github.com/guh/guh-plugins.git
License: GPL-2+
On Debian systems, the complete text of the GNU General
Public License can be found in `/usr/share/common-licenses/GPL-2'.
License: LGPL-2.1
On Debian systems, the complete text of the GNU Lesser General
Public License can be found in `/usr/share/common-licenses/LGPL-2.1'.
License: LGPL-3
On Debian systems, the complete text of the GNU Lesser General
Public License can be found in `/usr/share/common-licenses/LGPL-3'.
Files: translations/*
License: LGPL-2.1
Copyright: 2016-2017, Simon Stürz <simon.stuerz@guh.io>
Files: debian/*
License: GPL-2+
Copyright: 2017, Michael Zanetti <michael.zanetti@guh.io>
2017, Simon Stürz <simon.stuerz@guh.io>
Files: denon/*
orderbutton/*
ws2812/*
tcpcommander/*
License: LGPL-2.1
Copyright: 2016, Bernhard Trinnes <bernhard.trinnes@guh.io>
Files: plantcare/*
unipi/*
License: LGPL-2.1
Copyright: 2016-2018, Simon Stürz <simon.stuerz@guh.io>
2016-2018, Bernhard Trinnes <bernhard.trinnes@guh.io>
Files: avahimonitor/*
awattar/*
dollhouse/*
elgato/*
elro/*
eq-3/*
genericelements/*
gpio/*
leynew/*
lgsmarttv/*
mailnotification/*
netatmo/*
networkdetector/*
osdomotics/*
senic/*
udpcommander/*
unitec/*
wemo/*
License: LGPL-2.1
Copyright: 2014-2017, Simon Stürz <simon.stuerz@guh.io>
Files: wakeonlan/*
commandlauncher/*
conrad/*
datetime/*
intertechno/*
kodi/*
lircd/*
openweathermap/*
philipshue/*
License: LGPL-2.1
Copyright: 2014-2017, Simon Stürz <simon.stuerz@guh.io>
2014-2017, Michael Zanetti <michael.zanetti@guh.io>

View File

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

25
debian/rules vendored Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/make -f
DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
PREPROCESS_FILES := $(wildcard debian/*.in)
$(PREPROCESS_FILES:.in=): %: %.in
sed 's,/@DEB_HOST_MULTIARCH@,$(DEB_HOST_MULTIARCH:%=/%),g' $< > $@
%:
dh $@ --parallel
override_dh_auto_build:
dh_auto_build
make lrelease
override_dh_install: $(PREPROCESS_FILES:.in=)
dh_install --fail-missing
override_dh_auto_clean:
dh_auto_clean
find -name *plugininfo.h -exec rm {} \;
find -name *.qm -exec rm {} \;
rm -rf $(PREPROCESS_FILES:.in=)

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (native)

0
drexelundweiss/README.md Normal file
View File

View File

@ -0,0 +1,635 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, 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 "deviceplugindrexelundweiss.h"
#include "plugininfo.h"
#include "modbusdegisterdefinition.h"
DevicePluginDrexelUndWeiss::DevicePluginDrexelUndWeiss()
{
}
DevicePluginDrexelUndWeiss::~DevicePluginDrexelUndWeiss()
{
}
void DevicePluginDrexelUndWeiss::init()
{
connect(this, &DevicePluginDrexelUndWeiss::configValueChanged, this, &DevicePluginDrexelUndWeiss::onPluginConfigurationChanged);
}
void DevicePluginDrexelUndWeiss::discoverDevices(DeviceDiscoveryInfo *info)
{
// Create the list of available serial interfaces
QList<DeviceDescriptor> deviceDescriptors;
if (info->deviceClassId() == modbusConnectionDeviceClassId) {
Q_FOREACH(QSerialPortInfo port, QSerialPortInfo::availablePorts()) {
if (m_usedSerialPorts.contains(port.systemLocation())){
//device already in use
qCDebug(dcDrexelUndWeiss()) << "Found serial port that is already used:" << port.portName();
} else {
//Serial port is not yet used, create now a new one
qCDebug(dcDrexelUndWeiss()) << "Found serial port:" << port.portName();
QString description = port.manufacturer() + " " + port.description();
DeviceDescriptor descriptor(info->deviceClassId(), port.portName(), description);
ParamList parameters;
parameters.append(Param(modbusConnectionDeviceSerialPortParamTypeId, port.systemLocation()));
descriptor.setParams(parameters);
info->addDeviceDescriptor(descriptor);
}
}
info->finish(Device::DeviceErrorNoError);
return;
}
if (info->deviceClassId() == x2luDeviceClassId) {
info->finish(Device::DeviceErrorNoError);
return;
}
if (info->deviceClassId() == x2wpDeviceClassId) {
info->finish(Device::DeviceErrorNoError);
return;
}
info->finish(Device::DeviceErrorDeviceClassNotFound);
return;
}
void DevicePluginDrexelUndWeiss::setupDevice(DeviceSetupInfo *info)
{
Device *device = info->device();
if (device->deviceClassId() == modbusConnectionDeviceClassId) {
QString serialPort = device->paramValue(modbusConnectionDeviceSerialPortParamTypeId).toString();
int baudRate = device->paramValue(modbusConnectionDeviceBaudRateParamTypeId).toInt();
ModbusRTUMaster *modbus = new ModbusRTUMaster(serialPort, baudRate, QSerialPort::Parity::NoParity, 8, 1, this);
connect(modbus, &ModbusRTUMaster::connectionStateChanged, this, &DevicePluginDrexelUndWeiss::onConnectionStateChanged);
connect(modbus, &ModbusRTUMaster::receivedCoil, this, &DevicePluginDrexelUndWeiss::onReceivedCoil);
connect(modbus, &ModbusRTUMaster::receivedDiscreteInput, this, &DevicePluginDrexelUndWeiss::onReceivedDiscreteInput);
connect(modbus, &ModbusRTUMaster::receivedHoldingRegister, this, &DevicePluginDrexelUndWeiss::onReceivedHoldingRegister);
connect(modbus, &ModbusRTUMaster::receivedInputRegister, this, &DevicePluginDrexelUndWeiss::onReceivedInputRegister);
m_modbusRTUMasters.insert(device, modbus);
m_usedSerialPorts.append(serialPort);
info->finish(Device::DeviceErrorNoError);
return;
}
if (device->deviceClassId() == x2luDeviceClassId) {
info->finish(Device::DeviceErrorNoError);
return;
}
if (device->deviceClassId() == x2wpDeviceClassId) {
info->finish(Device::DeviceErrorNoError);
return;
}
info->finish(Device::DeviceErrorDeviceClassNotFound);
return;
}
void DevicePluginDrexelUndWeiss::postSetupDevice(Device *device)
{
if (!m_refreshTimer) {
// Refresh timer for TCP read
int refreshTime = configValue(drexelUndWeissPluginUpdateIntervalParamTypeId).toInt();
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(refreshTime);
connect(m_refreshTimer, &PluginTimer::timeout, this, &DevicePluginDrexelUndWeiss::onRefreshTimer);
}
if (device->deviceClassId() == modbusConnectionDeviceClassId) {
// read Register 5000 and emit auto-device
ModbusRTUMaster *modbus = m_modbusRTUMasters.value(device);
if (!modbus){
qCWarning(dcDrexelUndWeiss()) << "No modbus master available";
}
device->setStateValue(modbusConnectionConnectedStateTypeId, true);
}
if ((device->deviceClassId() == x2luDeviceClassId) || (device->deviceClassId() == x2wpDeviceClassId)) {
Device *parentDevice = myDevices().findById(device->parentId());
if (!parentDevice) {
qWarning(dcDrexelUndWeiss()) << "Could not find the parent device";
return;
}
ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parentDevice);
if (!modbus){
qCWarning(dcDrexelUndWeiss()) << "No modbus interface available";
}
updateStates(device);
// Update states
}
}
void DevicePluginDrexelUndWeiss::executeAction(DeviceActionInfo *info)
{
Device *device = info->device();
Action action = info->action();
if (device->deviceClassId() == modbusConnectionDeviceClassId) {
ModbusRTUMaster *modbus = m_modbusRTUMasters.value(device);
if (!modbus){
qCWarning(dcDrexelUndWeiss()) << "No modbus interface available";
info->finish(Device::DeviceErrorHardwareFailure);
return;
}
if (action.actionTypeId() == modbusConnectionDiscoverDevicesActionTypeId) {
int slave = action.param(modbusConnectionDiscoverDevicesActionSlaveAddressParamTypeId).value().toInt();
discoverModbusSlaves(modbus, slave);
info->finish(Device::DeviceErrorNoError);
return;
}
info->finish(Device::DeviceErrorActionTypeNotFound);
return;
}
if (device->deviceClassId() == x2luDeviceClassId) {
Device *parentDevice = myDevices().findById(device->parentId());
if (!parentDevice) {
qWarning(dcDrexelUndWeiss()) << "Could not find the parent device";
info->finish(Device::DeviceErrorHardwareFailure);
return;
}
ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parentDevice);
int slave = device->paramValue(x2luDeviceSlaveAddressParamTypeId).toInt();
if (!modbus){
qCWarning(dcDrexelUndWeiss()) << "No modbus interface available";
info->finish(Device::DeviceErrorHardwareFailure);
return;
}
if (action.actionTypeId() == x2luVentilationModeActionTypeId) {
QString mode = action.param(x2luVentilationModeActionVentilationModeParamTypeId).value().toString();
int data = 0;
if (mode == "Manual level 0") {
data = VentialtionMode::ManuellStufe0;
} else if(mode == "Manual level 1") {
data = VentialtionMode::ManuellStufe1;
} else if(mode == "Manual level 2") {
data = VentialtionMode::ManuellStufe2;
}else if(mode == "Manual level 3") {
data = VentialtionMode::ManuellStufe3;
} else if(mode == "Automatic") {
data = VentialtionMode::Automatikbetrieb;
} else if(mode == "Party") {
data = VentialtionMode::Party;
}
m_pendingActions.insert(modbus->writeHoldingRegister(slave, ModbusRegisterX2::Betriebsart, data), info);
return;
}
info->finish(Device::DeviceErrorActionTypeNotFound);
return;
}
if (device->deviceClassId() == x2wpDeviceClassId) {
Device *parentDevice = myDevices().findById(device->parentId());
if (!parentDevice) {
qWarning(dcDrexelUndWeiss()) << "Could not find modbus interface";
info->finish(Device::DeviceErrorHardwareFailure);
return;
}
ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parentDevice);
int slave = device->paramValue(x2wpDeviceSlaveAddressParamTypeId).toInt();
if (!modbus){
qCWarning(dcDrexelUndWeiss()) << "No modbus master available";
info->finish(Device::DeviceErrorHardwareFailure);
return;
}
if (action.actionTypeId() == x2wpTargetTemperatureActionTypeId) {
qreal targetTemp = (action.param(x2wpTargetTemperatureActionTargetTemperatureParamTypeId).value().toDouble());
int data = static_cast<int>(qRound(targetTemp * 1000));
m_pendingActions.insert(modbus->writeHoldingRegister(slave,ModbusRegisterX2::RaumSoll, data), info);
return;
}
if (action.actionTypeId() == x2wpTargetWaterTemperatureActionTypeId) {
qreal targetWaterTemp = action.param(x2wpTargetWaterTemperatureActionTargetWaterTemperatureParamTypeId).value().toDouble();
int data = static_cast<int>(qRound(targetWaterTemp * 1000));
m_pendingActions.insert(modbus->writeHoldingRegister(slave, ModbusRegisterX2::BrauchwasserSolltermperatur, data), info);
return;
}
info->finish(Device::DeviceErrorActionTypeNotFound);
return;
}
info->finish(Device::DeviceErrorDeviceClassNotFound);
return;
}
void DevicePluginDrexelUndWeiss::deviceRemoved(Device *device)
{
if (device->deviceClassId() == modbusConnectionDeviceClassId) {
ModbusRTUMaster *modbus = m_modbusRTUMasters.take(device);
if (!modbus){
qCWarning(dcDrexelUndWeiss()) << "No modbus interface available";
return;
}
m_usedSerialPorts.removeAll(modbus->serialPort());
modbus->deleteLater();
}
}
void DevicePluginDrexelUndWeiss::onRefreshTimer()
{
foreach (Device *device, myDevices()) {
if (device->deviceClassId() == modbusConnectionDeviceClassId) {
ModbusRTUMaster *modbus = m_modbusRTUMasters.value(device);
if (!modbus) {
qCWarning(dcDrexelUndWeiss()) << "No modbus master available";
return;
}
} else if (device->deviceClassId() == x2luDeviceClassId || device->deviceClassId() == x2wpDeviceClassId){
updateStates(device);
}
}
}
void DevicePluginDrexelUndWeiss::updateStates(Device *device)
{
if (device->deviceClassId() == x2luDeviceClassId) {
Device *parent = myDevices().findById(device->parentId());
ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parent);
int slave = device->paramValue(x2luDeviceSlaveAddressParamTypeId).toInt();
modbus->readHoldingRegister(slave, ModbusRegisterX2::AktiveLuefterstufe);
modbus->readHoldingRegister(slave, ModbusRegisterX2::Betriebsart); // Ventilation mode
modbus->readHoldingRegister(slave, ModbusRegisterX2::CO2);
}
if (device->deviceClassId() == x2wpDeviceClassId) {
Device *parent = myDevices().findById(device->parentId());
ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parent);
int slave = device->paramValue(x2wpDeviceSlaveAddressParamTypeId).toInt();
modbus->readHoldingRegister(slave, ModbusRegisterX2::Waermepumpe);
modbus->readHoldingRegister(slave, ModbusRegisterX2::RaumSoll);
modbus->readHoldingRegister(slave, ModbusRegisterX2::Raum);
modbus->readHoldingRegister(slave, ModbusRegisterX2::TemperaturWarmwasserspeicherUnten);
modbus->readHoldingRegister(slave, ModbusRegisterX2::BrauchwasserSolltermperatur);
modbus->readHoldingRegister(slave, ModbusRegisterX2::Auszenluft);
modbus->readHoldingRegister(slave, ModbusRegisterX2::Summenstoerung);
modbus->readHoldingRegister(slave, ModbusRegisterX2::LeistungKompressor);
modbus->readHoldingRegister(slave, ModbusRegisterX2::LeistungWarmwasser);
modbus->readHoldingRegister(slave, ModbusRegisterX2::LeistungRaumheizung);
modbus->readHoldingRegister(slave, ModbusRegisterX2::LeistungLuftvorwaermung);
modbus->readHoldingRegister(slave, ModbusRegisterX2::EnergieKompressor);
modbus->readHoldingRegister(slave, ModbusRegisterX2::EnergieWarmwasser);
modbus->readHoldingRegister(slave, ModbusRegisterX2::EnergieRaumheizung);
modbus->readHoldingRegister(slave, ModbusRegisterX2::EnergieLuftvorerwarrmung);
}
}
void DevicePluginDrexelUndWeiss::onPluginConfigurationChanged(const ParamTypeId &paramTypeId, const QVariant &value)
{
// Check refresh schedule
if (paramTypeId == drexelUndWeissPluginUpdateIntervalParamTypeId) {
if (m_refreshTimer) {
int refreshTime = value.toInt();
m_refreshTimer->stop();
m_refreshTimer->startTimer(refreshTime);
}
}
}
void DevicePluginDrexelUndWeiss::onConnectionStateChanged(bool status)
{
Q_UNUSED(status)
}
void DevicePluginDrexelUndWeiss::onReceivedCoil(int slaveAddress, int modbusRegister, bool value)
{
Q_UNUSED(slaveAddress)
Q_UNUSED(modbusRegister)
Q_UNUSED(value)
}
void DevicePluginDrexelUndWeiss::onReceivedDiscreteInput(int slaveAddress, int modbusRegister, bool value)
{
Q_UNUSED(slaveAddress)
Q_UNUSED(modbusRegister)
Q_UNUSED(value)
}
void DevicePluginDrexelUndWeiss::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, int value)
{
ModbusRTUMaster *modbus = static_cast<ModbusRTUMaster *>(sender());
if (m_modbusRTUMasters.values().contains(modbus) ){
Device *parentDevice = m_modbusRTUMasters.key(static_cast<ModbusRTUMaster *>(modbus));
foreach(Device *device, myDevices().filterByParentDeviceId(parentDevice->id())) {
if (device->deviceClassId() == x2luDeviceClassId && device->paramValue(x2luDeviceSlaveAddressParamTypeId) == slaveAddress) {
switch (modbusRegister) {
case ModbusRegisterX2::Waermepumpe:
device->setStateValue(x2wpPowerStateTypeId, value);
break;
case ModbusRegisterX2::RaumSoll:
device->setStateValue(x2wpTargetTemperatureStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::Raum:
device->setStateValue(x2wpTemperatureStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::TemperaturWarmwasserspeicherUnten:
device->setStateValue(x2wpWaterTemperatureStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::BrauchwasserSolltermperatur:
device->setStateValue(x2wpTargetWaterTemperatureStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::Auszenluft:
device->setStateValue(x2wpOutsideAirTemperatureStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::Summenstoerung:
if (value != 0) {
//get actual error
} else {
device->setStateValue(x2wpErrorStateTypeId, "No Error");
}
break;
case ModbusRegisterX2::StoerungAbluftventilator:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Exhaust fan");
break;
case ModbusRegisterX2::StoerungBoilerfuehlerElektroheizstab:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Boiler sensor electric heating element");
break;
case ModbusRegisterX2::StoerungBoilerfuehlerSolar:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Boiler sensor solar");
break;
case ModbusRegisterX2::StoerungBoilerfuehlerWaermepumpe:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Boiler sensor heat pump");
break;
case ModbusRegisterX2::StoerungBoileruebertemperatur:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Boiler overtemperature");
break;
case ModbusRegisterX2::StoerungCO2Sensor:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "CO2-Sensor");
break;
case ModbusRegisterX2::StoerungDruckverlustAbluftZuGrosz:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Pressure loss exhaust air too big");
break;
case ModbusRegisterX2::StoerungDruckverlustZuluftZuGrosz:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Pressure loss supply air too large");
break;
case ModbusRegisterX2::StoerungDurchflussmengeHeizgkreis:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Flow rate of heating circuit");
break;
case ModbusRegisterX2::StoerungDurchflussmengeSolekreis:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Flow rate brine circuit");
break;
case ModbusRegisterX2::StoerungTeilnehmerNichtErreichbar:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Participant not available");
break;
case ModbusRegisterX2::StoerungTemperaturfuehlerAuszenluft:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor outside air");
break;
case ModbusRegisterX2::StoerungTemperaturfuehlerHeizkreisVorlauf:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor heating circuit flow");
break;
case ModbusRegisterX2::StoerungTemperaturfuehlerRaum:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor room");
break;
case ModbusRegisterX2::StoerungTemperaturfuehlerSolarkollektor:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor solar collector");
break;
case ModbusRegisterX2::StoerungTemperaturfuehlerSole:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor brine");
break;
case ModbusRegisterX2::StoerungTemperaturfuehlerSoleAuszenluft:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Temperature sensor brine outside air");
break;
case ModbusRegisterX2::StoerungWaermepumpeHochdruck:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Heat pump high pressure");
break;
case ModbusRegisterX2::StoerungWaermepumpeNiederdruck:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Heat pump low pressure");
break;
case ModbusRegisterX2::StoerungWertNichtZulaessig:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Value not allowed");
break;
case ModbusRegisterX2::StoerungZuluftventilator:
if (value != 0)
device->setStateValue(x2wpErrorStateTypeId, "Supply air fan");
break;
case ModbusRegisterX2::LeistungKompressor:
device->setStateValue(x2wpPowerCompressorStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::LeistungWarmwasser:
device->setStateValue(x2wpPowerWaterHeatingStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::LeistungRaumheizung:
device->setStateValue(x2wpPowerRoomHeatingStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::LeistungLuftvorwaermung:
device->setStateValue(x2wpPowerAirPreheatingStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::EnergieKompressor:
device->setStateValue(x2wpEnergyCompressorStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::EnergieWarmwasser:
device->setStateValue(x2wpEnergyWaterHeatingStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::EnergieRaumheizung:
device->setStateValue(x2wpEnergyRoomHeatingStateTypeId, value/1000.00);
break;
case ModbusRegisterX2::EnergieLuftvorerwarrmung:
device->setStateValue(x2wpEnergyAirPreheatingStateTypeId, value/1000.00);
break;
default:
break;
}
} else if (device->deviceClassId() == x2wpDeviceClassId && device->paramValue(x2wpDeviceSlaveAddressParamTypeId) == slaveAddress) {
switch (modbusRegister) {
case ModbusRegisterX2::Betriebsart:
if (value == VentialtionMode::ManuellStufe0) {
device->setStateValue(x2luVentilationModeStateTypeId, "Manual level 0");
} else if (value == VentialtionMode::ManuellStufe1) {
device->setStateValue(x2luVentilationModeStateTypeId, "Manual level 1");
} else if (value == VentialtionMode::ManuellStufe2) {
device->setStateValue(x2luVentilationModeStateTypeId, "Manual level 2");
} else if (value == VentialtionMode::ManuellStufe3) {
device->setStateValue(x2luVentilationModeStateTypeId, "Manual level 3");
} else if (value == VentialtionMode::Automatikbetrieb) {
device->setStateValue(x2luVentilationModeStateTypeId, "Automatic");
} else if (value == VentialtionMode::Party) {
device->setStateValue(x2luVentilationModeStateTypeId, "Party");
}
break;
case ModbusRegisterX2::AktiveLuefterstufe:
device->setStateValue(x2luActiveVentilationLevelStateTypeId, value);
break;
case ModbusRegisterX2::CO2:
device->setStateValue(x2luCo2StateTypeId, value);
break;
}
}
}
if (modbusRegister == ModbusRegisterX2::Geraetetyp) {
switch (value) {
case DeviceType::X2_WP: {
qDebug(dcDrexelUndWeiss()) << "Discovered X2 heat pump";
QList<DeviceDescriptor> deviceDescriptors;
DeviceDescriptor descriptor(x2wpDeviceClassId, "X2 WP", "Drexel und Weiss", parentDevice->id());
ParamList params;
//modbus->readHoldingRegister(slaveAddress, ModbusRegisterX2::SoftwareVersion);
//params.append(Param(x2wpDeviceSofwareVersionParamTypeId, data));
params.append(Param(x2wpDeviceSlaveAddressParamTypeId, slaveAddress));
descriptor.setParams(params);
deviceDescriptors.append(descriptor);
emit autoDevicesAppeared(deviceDescriptors);
break;
}
case DeviceType::X2_LU: {
qDebug(dcDrexelUndWeiss()) << "Discovered X2 ventilation unit";
QList<DeviceDescriptor> deviceDescriptors;
DeviceDescriptor descriptor(x2luDeviceClassId, "X2 LU", "Drexel und Weiss", parentDevice->id());
ParamList params;
//modbus->readHoldingRegister(slaveAddress, ModbusRegisterX2::SoftwareVersion);
//params.append(Param(x2luDeviceSofwareVersionParamTypeId, data));
params.append(Param(x2luDeviceSlaveAddressParamTypeId, slaveAddress));
descriptor.setParams(params);
deviceDescriptors.append(descriptor);
emit autoDevicesAppeared(deviceDescriptors);
break;
}
case DeviceType::AerosilentBianco:
//Just a test
qDebug(dcDrexelUndWeiss()) << "Discovered Aerosilent Bianco";
break;
default:
qDebug(dcDrexelUndWeiss()) << "Unkown Devicetype" << value;
}
}
}
}
void DevicePluginDrexelUndWeiss::onReceivedInputRegister(int slaveAddress, int modbusRegister, int value)
{
Q_UNUSED(slaveAddress)
Q_UNUSED(modbusRegister)
Q_UNUSED(value)
}
void DevicePluginDrexelUndWeiss::onWriteRequestFinished(QUuid requestId, bool success)
{
DeviceActionInfo *info = m_pendingActions.take(requestId);
if (!info)
return;
if (success) {
info->finish(Device::DeviceErrorNoError);
} else {
info->finish(Device::DeviceErrorHardwareFailure);
}
}
void DevicePluginDrexelUndWeiss::discoverModbusSlaves(ModbusRTUMaster *modbus, int slaveAddress)
{
foreach (Device *device, myDevices()) {
if (device->deviceClassId() == x2luDeviceClassId) {
if (device->paramValue(x2luDeviceSlaveAddressParamTypeId).toInt() == slaveAddress) {
qWarning(dcDrexelUndWeiss()) << "Device with slave address" << slaveAddress << "already added";
return;
}
}
if (device->deviceClassId() == x2wpDeviceClassId) {
if (device->paramValue(x2wpDeviceSlaveAddressParamTypeId).toInt() == slaveAddress) {
qWarning(dcDrexelUndWeiss()) << "Device with slave address" << slaveAddress << "already added";
return;
}
}
}
modbus->readHoldingRegister(slaveAddress, ModbusRegisterX2::Geraetetyp);
}

View File

@ -0,0 +1,80 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, 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 DEVICEPLUGINDREXELUNDWEISS_H
#define DEVICEPLUGINDREXELUNDWEISS_H
#include "devices/devicemanager.h"
#include "plugintimer.h"
#include <QDateTime>
#include "modbusrtumaster.h"
#include <QSerialPortInfo>
class DevicePluginDrexelUndWeiss : public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugindrexelundweiss.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginDrexelUndWeiss();
~DevicePluginDrexelUndWeiss() override;
void init() override;
void discoverDevices(DeviceDiscoveryInfo *info) override;
void setupDevice(DeviceSetupInfo *info) override;
void postSetupDevice(Device *device) override;
void deviceRemoved(Device *device) override;
public slots:
void executeAction(DeviceActionInfo *info) override;
private:
QList<QString> m_usedSerialPorts;
QHash<Device *, ModbusRTUMaster *> m_modbusRTUMasters;
PluginTimer *m_refreshTimer = nullptr;
QHash<QUuid, DeviceActionInfo *> m_pendingActions;
void updateStates(Device *device);
void discoverModbusSlaves(ModbusRTUMaster *modbus, int slaveAddress);
private slots:
void onRefreshTimer();
void onPluginConfigurationChanged(const ParamTypeId &paramTypeId, const QVariant &value);
void onConnectionStateChanged(bool status);
void onReceivedCoil(int slaveAddress, int modbusRegister, bool value);
void onReceivedDiscreteInput(int slaveAddress, int modbusRegister, bool value);
void onReceivedHoldingRegister(int slaveAddress, int modbusRegister, int value);
void onReceivedInputRegister(int slaveAddress, int modbusRegister, int value);
void onWriteRequestFinished(QUuid requestId, bool success);
};
#endif // DEVICEPLUGINDREXELUNDWEISS_H

View File

@ -0,0 +1,374 @@
{
"name": "DrexelUndWeiss",
"displayName": "Drexel und Weiss",
"id": "68d78ce6-82d0-4a5b-b901-7c3b843ef63c",
"paramTypes":[
{
"id": "ecc8e0f1-5fac-4ea9-b5ef-459d75c4fe78",
"name": "updateInterval",
"displayName": "Update interval",
"type": "int",
"unit": "Seconds",
"defaultValue": 15
}
],
"vendors": [
{
"name": "DrexelUndWeiss",
"displayName": "Drexel und Weiss",
"id": "9f476e8b-7e95-448e-b03b-874747e8fb1f",
"deviceClasses": [
{
"name": "modbusConnection",
"displayName": "Modbus Connection",
"id": "06d04eec-ab5d-479a-b9e6-8c89efc18a8b",
"createMethods": ["discovery", "user"],
"interfaces": ["gateway"],
"paramTypes": [
{
"id": "ed49f7d8-ab18-4c37-9b80-1004b75dcb91",
"name": "serialPort",
"displayName": "Serial port",
"type": "QString",
"inputType": "TextLine",
"defaultValue": "ttyAMA0"
},
{
"id": "d0c04612-cc3e-4d38-b4c9-708e28dc4eb3",
"name": "baudRate",
"displayName": "Baudrate",
"type": "int",
"defaultValue": 9600
}
],
"stateTypes": [
{
"id": "181ce6e2-9c55-45c6-b329-adf379679e07a",
"name": "connected",
"displayName": "connected",
"displayNameEvent": "connection status changed",
"type": "bool",
"defaultValue": false
}
],
"actionTypes": [
{
"id": "b9a24ecc-4433-4f31-99ba-596033bda421",
"name": "discoverDevices",
"displayName": "Discover devices",
"paramTypes": [
{
"id": "22413a22-31d4-4b8c-b855-8a29da5946bc",
"name": "slaveAddress",
"displayName": "Slave address",
"type": "int",
"minValue": 0,
"maxValue": 250,
"defaultValue": 0
}
]
}
]
},
{
"name": "x2lu",
"displayName": "X2 LU",
"id": "0de8e21e-392a-4790-a78a-b1a7eaa7571b",
"createMethods": ["auto"],
"interfaces": ["co2sensor", "connectable"],
"paramTypes": [
{
"id": "22413a22-31d4-4b8c-b855-8a29da5946bc",
"name": "slaveAddress",
"displayName": "Slave address",
"type": "int",
"minValue": 0,
"maxValue": 250,
"defaultValue": 0
},
{
"id": "91ef76cf-6c53-4a8a-a278-6f6e2ef68cc6",
"name": "sofwareVersion",
"displayName": "Software version",
"type": "QString",
"inputType": "TextLine",
"defaultValue": "-"
}
],
"stateTypes":[
{
"id": "181ce6e2-9c55-45c6-b329-adf379679e07a",
"name": "connected",
"displayName": "connected",
"displayNameEvent": "connection status changed",
"type": "bool",
"defaultValue": false
},
{
"id": "0a6b44c8-e7af-4148-92ff-682ae717f3a8",
"name": "co2",
"displayName": "CO2",
"displayNameEvent": "CO2 changed",
"unit": "PartsPerMillion",
"type": "double",
"defaultValue": 350
},
{
"id": "4269d9d0-ddff-4e7a-9d7a-bf9a7db50f98",
"name": "ventilationMode",
"displayName": "Ventilation mode",
"displayNameEvent": "Ventilation mode changed",
"displayNameAction": "Change ventilation mode",
"type": "QString",
"defaultValue": "Manual level 0",
"possibleValues": [
"Manual level 0",
"Manual level 1",
"Manual level 2",
"Manual level 3",
"Automatic",
"Party"
],
"writable": true
},
{
"id": "1f26a013-7836-4f3e-b369-7ce07310fc59",
"name": "activeVentilationLevel",
"displayName": "Ventilation level",
"displayNameEvent": "Ventilation level changed",
"type": "int",
"defaultValue": 0
}
]
},
{
"name": "x2wp",
"displayName": "X2 WP",
"id": "e548f962-92db-4110-8279-10fbcde35f93",
"createMethods": ["auto"],
"interfaces": ["thermostat", "heating", "temperaturesensor", "connectable"],
"paramTypes": [
{
"id": "22413a22-31d4-4b8c-b855-8a29da5946bc",
"name": "slaveAddress",
"displayName": "Slave address",
"type": "int",
"minValue": 0,
"maxValue": 250,
"defaultValue": 0
},
{
"id": "91ef76cf-6c53-4a8a-a278-6f6e2ef68cc6",
"name": "sofwareVersion",
"displayName": "Software version",
"type": "QString",
"inputType": "TextLine",
"defaultValue": "-"
}
],
"stateTypes":[
{
"id": "181ce6e2-9c55-45c6-b329-adf379679e07a",
"name": "connected",
"displayName": "connected",
"displayNameEvent": "connection status changed",
"type": "bool",
"defaultValue": false
},
{
"id": "f2ce8389-c33f-4f10-8484-f2e993841762",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"displayNameAction": "Change power",
"type": "bool",
"defaultValue": 0,
"writable": true
},
{
"id": "3ab2d609-1686-4fd7-84e3-580c8e0537d0",
"name": "temperature",
"displayName": "Room temperature",
"displayNameEvent": "Room temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0
},
{
"id": "77a96b57-fa0a-4946-af5b-39c3b66d9422",
"name": "waterTemperature",
"displayName": "Water temperature",
"displayNameEvent": "Water temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0
},
{
"id": "32378843-5478-4b86-9c0e-ccbf978c02be",
"name": "outsideAirTemperature",
"displayName": "Outside air temperature",
"displayNameEvent": "Outside air temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0
},
{
"id": "fb98754d-0fba-4163-9b74-3e5a07d71421",
"name": "targetTemperature",
"displayName": "Target room temperature",
"displayNameEvent": "Target room temperature changed",
"displayNameAction": "Change room target temperature",
"type": "double",
"unit": "DegreeCelsius",
"minValue": 14.00,
"maxValue": 26.00,
"defaultValue": 22.00,
"writable": true
},
{
"id": "fb021cac-1236-4324-a45c-8d89ad069052",
"name": "targetWaterTemperature",
"displayName": "Target water temperature",
"displayNameEvent": "Target water temperature changed",
"displayNameAction": "Change water target temperature",
"type": "double",
"unit": "DegreeCelsius",
"minValue": 20.00,
"maxValue": 55.00,
"defaultValue": 46.00,
"writable": true
},
{
"id": "5c125ddd-a0db-40fe-9998-2afea6c727f1",
"name": "heatPumpMode",
"displayName": "Heat pump mode",
"displayNameEvent": "Heat pump mode changed",
"type": "QString",
"defaultValue": "Heat pump off",
"possibleValues": [
"Restart interlock" ,
"Heat pump off",
"Lead time brine pump",
"Fan run-up time",
"Open hot gas valve",
"Open LPG valve",
"Start compressor",
"Minimum runtime heat pump",
"Heat pump on",
"Draw off refrigerant",
"Vacuum during defrosting",
"Defrost",
"Drain after defrosting"
]
},
{
"id": "8d6e52ef-992d-47ac-90a8-9dba95ab200e",
"name": "error",
"displayName": "Error",
"displayNameEvent": "Error occured",
"type": "QString",
"defaultValue": "No error",
"possibleValues": [
"No error",
"Exhaust fan" ,
"Boiler sensor electric heating element",
"Boiler sensor solar",
"Boiler sensor heat pump",
"Boiler overtemperature",
"CO2-Sensor",
"Pressure loss exhaust air too big",
"Pressure loss supply air too large",
"Flow rate of heating circuit",
"Flow rate brine circuit",
"Participant not available",
"Temperature sensor outside air",
"Temperature sensor heating circuit flow",
"Temperature sensor room",
"Temperature sensor solar collector",
"Temperature sensor brine",
"Temperature sensor brine outside air",
"Heat pump high pressure",
"Heat pump low pressure",
"Value not allowed",
"Supply air fan"
]
},
{
"id": "7287943a-ea6d-4c92-abbd-f55f6c7ee9e5",
"name": "powerCompressor",
"displayName": "Power consumption compressor",
"displayNameEvent": "Power consumption compressor changed",
"type": "double",
"unit": "Watt",
"defaultValue": 0
},
{
"id": "59beeff5-89c1-4996-9e07-48d53d74684d",
"name": "powerAirPreheating",
"displayName": "Power consumption air preheating",
"displayNameEvent": "Power consumption air preheating changed",
"type": "double",
"unit": "Watt",
"defaultValue": 0
},
{
"id": "c4237da0-0ead-42b8-b192-6a681509dc90",
"name": "powerRoomHeating",
"displayName": "Power consumption room heating",
"displayNameEvent": "Power consumption room heating changed",
"type": "double",
"unit": "Watt",
"defaultValue": 0
},
{
"id": "21dfc736-f35c-469f-be57-afc1976d8328",
"name": "powerWaterHeating",
"displayName": "Power consumption water heating",
"displayNameEvent": "Power consumption water heating changed",
"type": "double",
"unit": "Watt",
"defaultValue": 0
},
{
"id": "b423657b-4e59-41cd-89a3-4f5cb1c3a271",
"name": "energyCompressor",
"displayName": "Energy compressor",
"displayNameEvent": "Energy compressor changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0
},
{
"id": "0816ca6d-a178-4a2a-8183-c26a794fb0ca",
"name": "energyAirPreheating",
"displayName": "Energy air preheating",
"displayNameEvent": "Energy air preheating changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0
},
{
"id": "cb189b75-3634-4674-a847-f29ca322d4be",
"name": "energyWaterHeating",
"displayName": "Energy water heating",
"displayNameEvent": "Energy water heating changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0
},
{
"id": "4495618e-5a43-46ac-9f76-32aae3f3e954",
"name": "energyRoomHeating",
"displayName": "Energy room heating",
"displayNameEvent": "Energy room heating changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0
}
]
}
]
}
]
}

View File

@ -0,0 +1,16 @@
include(../plugins.pri)
QT += \
serialport \
serialbus \
SOURCES += \
deviceplugindrexelundweiss.cpp \
modbusrtumaster.cpp \
HEADERS += \
deviceplugindrexelundweiss.h \
modbusrtumaster.h \
modbusdegisterdefinition.h

View File

@ -0,0 +1,248 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, 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 MODBUSDEGISTERDEFINITION
#define MODBUSDEGISTERDEFINITION
#endif // MODBUSDEGISTERDEFINITION
enum ModbusRegisterX2 {
AbsenkungderLuefterstufe1 = 5328,
AktiveLuefterstufe = 1066,
AnforderungDerVentilatorenDurchWaermepumpe = 1292,
AnforderungDerVentilatorenDurchZonenregelung = 1336,
AnforderungDisbalance = 1338,
AnforderungBeschattung = 1218,
AnforderungBrauchwasserheizungElektoheizstab = 1038,
AnforderungBrauchwasserheizungWaermepumpe = 1036,
AnforderungBypassklappe = 1216,
AnforderungFrostschutz = 1146,
AnforderungLST3EXT = 228,
AnforderungRaumHeizstufe1 = 1032,
AnforderungRaumHeizstufe2 = 1034,
AnforderungSoleFuerAuszenlufterwaermung = 1198,
AnforderungSoleFuerWaermepumpe = 1196,
AnforderungSoleKuehlung = 1320,
Auszenluft = 202,
AuszenluftfuehlerVorhanden = 5304 ,
Beschattungsfunktion = 5336,
Betriebsart = 5002,
BetriebsstundenAbluftventilator = 902,
BetriebsstundenBeschattung = 964,
BetriebsstundenBypassklappe = 932,
BetriebsstundenFeinstaub = 928,
BetriebsstundenFrostschutzeinrichtung = 940,
BetriebsstundenGrobstaub = 926,
BetriebsstundenHeizstufe1 = 912,
BetriebsstundenHeizstufe2 = 914,
BetriebsstundenHeizung = 938,
BetriebsstundenLuefterstufe0 = 962,
BetriebsstundenLuefterstufe1 = 904,
BetriebsstundenLuefterstufe2 = 906,
BetriebsstundenLuefterstufe3 = 908,
BetriebsstundenReduzierteLuefterstufe = 944,
BetriebsstundenSoleKreis = 934,
BetriebsstundenSoleKreisAuszenluft = 970,
BetriebsstundenZuluftventilator = 900,
Brandmeldealarm = 838,
BrandmeldeanlageVorhanden = 5068,
CO2 = 230,
CO2Messung = 1048,
CO2SensorVorhanden = 5054,
Datum = 5210,
DrehzahlAbluftventilator = 1094,
DrehzahlZuluftventilator = 1092,
ErhoehungDerLuefterstufe3 = 5330,
FeinstaubfilterVorhanden = 5034,
FeinstaubfilterWechseln = 7004,
FeinstaubfilterStandzeit = 5032,
FunktionHeizungPlus = 5492,
Geraetetyp = 5000,
GesamtBefoerderteKubikmeter = 946,
GesamtBefoerderteLuftmengeSeitFilterwechsel = 960,
GrobstaubfilterVorhanden = 5154,
GrobstaubfilterWechseln = 7002,
GrobstaubfilterBetriebsartFiltereberwachung = 5164,
GrobstaubfilterMaximalesFoerdervolumen = 5166,
GrobstaubfilterStandzeit = 5030,
IstDrehzahlAbluftventilator = 1186,
IstDrehzahlZuluftventilator = 1184,
KontaktBrandmeldealarm = 238,
KuehlungVorhanden = 5192,
MaximalZulaessigeDrehzahlAbluftventilator = 5270,
MaximalZulaessigeDrehzahlZuluftventilator = 5268,
PelletofenVorhanden = 5168,
PelletofenAnlaufverzoegerung = 5282,
PelletofenMindestlaufzeit = 5284,
Raum = 200,
RaumSoll = 5016,
RaumtemperaturBeschattung = 5338,
RelaiskontaktEXT = 252,
Revisionstuere = 226,
SollVolumenstromAbluft = 1084,
SollVolumenstromLuefterstufe = 5060,
SollVolumenstromZuluft = 1082,
SollwertErhoehungFunktionHeizungPlus = 5496,
StoerungAbluftventilator = 826,
StoerungBoilerfuehlerElektroheizstab = 828,
StoerungBoilerfuehlerSolar = 844,
StoerungBoilerfuehlerWaermepumpe = 830,
StoerungBoileruebertemperatur = 810,
StoerungCO2Sensor = 832,
StoerungDruckverlustAbluftZuGrosz = 854,
StoerungDruckverlustZuluftZuGrosz = 852,
StoerungDurchflussmengeHeizgkreis = 848,
StoerungDurchflussmengeSolekreis = 846,
StoerungTeilnehmerNichtErreichbar = 856,
StoerungTemperaturfuehlerAuszenluft = 806,
StoerungTemperaturfuehlerHeizkreisVorlauf = 850,
StoerungTemperaturfuehlerRaum = 804,
StoerungTemperaturfuehlerSolarkollektor = 842,
StoerungTemperaturfuehlerSole = 812,
StoerungTemperaturfuehlerSoleAuszenluft = 7504,
StoerungWaermepumpeHochdruck = 818,
StoerungWaermepumpeNiederdruck = 820,
StoerungWertNichtZulaessig = 840,
StoerungZuluftventilator = 824,
Summenstoerung = 800,
Summenstoerung2 = 7500,
SoftwareVersion = 1156,
Tag = 1174,
TemperaturAuszenluftBeschattung = 5340,
TemperaturAuszenluftBypassKuehlungAus = 5452,
TemperaturAuszenluftBypassKuehlungEin = 5450,
TemperaturAuszenluftBypassOeffnen = 5084,
TemperaturAuszenluftBypassSchlieszen = 5086,
TemperaturAuszenluftFrostschutz= 5206,
TemperaturAuszenluftFrostschutzAus = 5090,
TemperaturAuszenluftFrostschutzEin = 5088,
TemperaturAuszenluftKuehlungAus = 5200,
TemperaturAuszenluftKuelungEin = 5198,
TemperaturAuszenluftReduktionLuftmenge10Prozent = 5422,
TemperaturAuszenluftReduktionLuftmenge20Prozent = 5424,
TemperaturSole = 216,
TemperaturSoleNachAuszenluftvorwaermung = 206,
TemperaturschwelleAuszenluftHeizenKuehlen = 5186,
TimeOutSolltemperaturRaum = 7508,
TimeOutTemperaturfuehlerAuszenluft = 7510,
TimeoutTemperaturfuehlerHeizkreisVorlauf = 7512,
TimeoutTemperaturfuehlerRaum = 7506,
Uhrzeit = 5212,
UhrzeitundDatumVerschicken = 5442,
VolumenstrombalanceZuluftAbluft = 5026,
VorlaufNiedertemperaturHeizkreis = 240,
ZeitspanneFunktionHeizung = 5494,
ZeitspanneFunktionParty = 5038,
HochdruckWaermepumpe = 222,
NiederdruckWaermepumpe = 224, //nicht vorhanden, vorhanden
HystereseSolareRaumheizungAus = 5302,
KontaktEVU = 232,
MaximalZulaessigeVorlauftemperaturHeizung = 5202,
MinimalZulaessigeRuecklauftemperaturPassiveKuehlung = 5204,
MinimaleBrauchwassertemperaturSolareRaumheizungEin = 5300,
RaumtemperaturPassiveKuehlungEin = 5182,
RelaiskontaktExt = 262,
SolaranlageVorhanden = 5190,
SolaranlageAusschaltschwelleSolarpumpe = 5180,
SolaranlageEinschaltschwelleSolarpumpe =5178,
SolaranlageMaximaleLadetemperatur = 5176,
SolareRaumheizungVorhanden = 5306,
StatusWaermepumpe = 1314,
StatusWaermepumpeRestzeit = 1316, // in seconds
EVUAnlageVorhanden = 5146,
EVUSperreBrauchwasserAktiv = 1270,
EVUSperreElektroheizstabAktiv = 1274,
EVUSperreRaumheizungAktiv = 1272,
BrauchwasserSolltermperatur = 5064,
BrauchwassertemperaturRaumheizungssperre = 5130,
FunktionBadPlus = 5036,
ElektroheizstabVorhanden = 5126,
EnergieKompressor = 4500,
EnergieLuftvorerwarrmung = 4506,
EnergieRaumheizung = 4502,
EnergieWarmwasser = 4504,
LeistungKompressor = 4000,
LeistungLuftvorwaermung = 4006 ,
LeistungRaumheizung = 4002,
LeistungWarmwasser = 4004,
TemperaturBoilerSolar = 244,
TemperaturSolarkollektor = 242,
TemperaturWarmwasserspeicherOben = 212,
TemperaturWarmwasserspeicherUnten = 214,
Waermepumpe = 1044,
ZentralgeraetAdresse = 5436
};
enum DeviceType {
NotDefined,
AerosilentPrimus = 1,
AerosilentTopo,
AerosilentMicro,
AerosmartS,
AerosmartM,
AerosmartL,
AerosmartXls,
AerosilentCentro,
TermosmartSc,
X2_LU = 25,
X2_WP = 26,
AerosmartMono,
Vbox120,
AerosilentBianco,
X2Plus,
AerosilentBusiness,
CentralDevice,
AerosilentStratos,
ZoneControl,
Vbox300
};
enum HeatPumpStatus {
Wiedereinschaltsperre,
Waermepumpeaus,
VorlaufzeitSolepumpe,
VorlaufzeitVentilatoren,
OeffnenHeissgasventil,
OeffnenFluessiggasventil,
StartKompressor,
MindestlaufzeitWaermepumpe,
WaermepumpeEin,
KaeltemittleAbsaugen,
BeimAbtauenAbsaugen,
AbtauenEin,
NachAbtauenAbtropfen
};
enum VentialtionMode {
ManuellStufe0 = 0,
ManuellStufe1,
ManuellStufe2,
ManuellStufe3,
Automatikbetrieb,
Party
};

View File

@ -0,0 +1,362 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, 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 "modbusrtumaster.h"
#include "extern-plugininfo.h"
#include <QSerialPortInfo>
ModbusRTUMaster::ModbusRTUMaster(QString serialPort, int baudrate, QSerialPort::Parity parity, int dataBits, int stopBits, QObject *parent) :
QObject(parent)
{
m_modbusRtuSerialMaster = new QModbusRtuSerialMaster(this);
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialPortNameParameter, serialPort);
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, baudrate);
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, dataBits);
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, stopBits);
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialParityParameter, parity);
//m_modbusRtuSerialMaster->setTimeout(100);
//m_modbusRtuSerialMaster->setNumberOfRetries(1);
connect(m_modbusRtuSerialMaster, &QModbusTcpClient::stateChanged, this, &ModbusRTUMaster::onModbusStateChanged);
connect(m_modbusRtuSerialMaster, &QModbusRtuSerialMaster::errorOccurred, this, &ModbusRTUMaster::onModbusErrorOccurred);
m_reconnectTimer = new QTimer(this);
m_reconnectTimer->setSingleShot(true);
connect(m_reconnectTimer, &QTimer::timeout, this, &ModbusRTUMaster::onReconnectTimer);
}
ModbusRTUMaster::~ModbusRTUMaster()
{
if (!m_modbusRtuSerialMaster) {
m_modbusRtuSerialMaster->disconnectDevice();
m_modbusRtuSerialMaster->deleteLater();
}
if (!m_reconnectTimer) {
m_reconnectTimer->stop();
m_reconnectTimer->deleteLater();
}
}
bool ModbusRTUMaster::connectDevice()
{
qDebug(dcDrexelUndWeiss()) << "Setting up TCP connecion";
if (!m_modbusRtuSerialMaster)
return false;
return m_modbusRtuSerialMaster->connectDevice();
}
QString ModbusRTUMaster::serialPort()
{
return m_modbusRtuSerialMaster->connectionParameter(QModbusDevice::SerialPortNameParameter).toString();
}
void ModbusRTUMaster::onReplyFinished()
{
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
if (!reply)
return;
reply->deleteLater();
int modbusAddress = 0;
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
for (int i = 0; i < static_cast<int>(unit.valueCount()); i++) {
//qCDebug(dcUniPi()) << "Start Address:" << unit.startAddress() << "Register Type:" << unit.registerType() << "Value:" << unit.value(i);
modbusAddress = unit.startAddress() + i;
switch (unit.registerType()) {
case QModbusDataUnit::RegisterType::Coils:
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::DiscreteInputs:
emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::InputRegisters:
emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::HoldingRegisters:
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::Invalid:
break;
}
}
} else if (reply->error() == QModbusDevice::ProtocolError) {
qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->errorString() << reply->rawResult().exceptionCode();
} else {
qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->error();
}
}
void ModbusRTUMaster::onReplyErrorOccured(QModbusDevice::Error error)
{
qCWarning(dcDrexelUndWeiss()) << "Modbus replay error:" << error;
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
if (!reply)
return;
reply->finished(); //to make sure it will be deleted
}
void ModbusRTUMaster::onReconnectTimer()
{
if(!m_modbusRtuSerialMaster->connectDevice()) {
m_reconnectTimer->start(10000);
}
}
bool ModbusRTUMaster::readCoil(int slaveAddress, int registerAddress)
{
if (!m_modbusRtuSerialMaster)
return false;
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1);
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, &ModbusRTUMaster::onReplyFinished);
connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured);
QTimer::singleShot(200, reply, SLOT(deleteLater()));
} else {
delete reply; // broadcast replies return immediately
}
} else {
qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
}
return true;
}
QUuid ModbusRTUMaster::writeCoil(int slaveAddress, int registerAddress, bool value)
{
QUuid requestId = QUuid::createUuid();
if (!m_modbusRtuSerialMaster)
return requestId;
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1);
request.setValue(0, static_cast<uint16_t>(value));
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [requestId, this] {
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
if (!reply)
return;
reply->deleteLater();
int modbusAddress = 0;
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
emit writeRequestFinished(requestId, true);
for (int i = 0; i < static_cast<int>(unit.valueCount()); i++) {
//qCDebug(dcUniPi()) << "Start Address:" << unit.startAddress() << "Register Type:" << unit.registerType() << "Value:" << unit.value(i);
modbusAddress = unit.startAddress() + i;
switch (unit.registerType()) {
case QModbusDataUnit::RegisterType::Coils:
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::DiscreteInputs:
emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::InputRegisters:
emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::HoldingRegisters:
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::Invalid:
break;
}
}
} else if (reply->error() == QModbusDevice::ProtocolError) {
emit writeRequestFinished(requestId, false);
qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->errorString() << reply->rawResult().exceptionCode();
} else {
emit writeRequestFinished(requestId, false);
qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->error();
}
});
connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured);
QTimer::singleShot(200, reply, SLOT(deleteLater()));
} else {
delete reply; // broadcast replies return immediately
}
} else {
qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
}
return requestId;
}
QUuid ModbusRTUMaster::writeHoldingRegister(int slaveAddress, int registerAddress, int value)
{
QUuid requestId = QUuid::createUuid();
if (!m_modbusRtuSerialMaster){
qCWarning(dcDrexelUndWeiss()) << "Modbus RTU interface not available";
return requestId;
}
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1);
request.setValue(0, static_cast<uint16_t>(value));
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [requestId, this] {
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
if (!reply)
return;
reply->deleteLater();
int modbusAddress = 0;
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
emit writeRequestFinished(requestId, true);
for (int i = 0; i < static_cast<int>(unit.valueCount()); i++) {
//qCDebug(dcUniPi()) << "Start Address:" << unit.startAddress() << "Register Type:" << unit.registerType() << "Value:" << unit.value(i);
modbusAddress = unit.startAddress() + i;
switch (unit.registerType()) {
case QModbusDataUnit::RegisterType::Coils:
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::DiscreteInputs:
emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::InputRegisters:
emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::HoldingRegisters:
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(i));
break;
case QModbusDataUnit::RegisterType::Invalid:
break;
}
}
} else if (reply->error() == QModbusDevice::ProtocolError) {
emit writeRequestFinished(requestId, false);
qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->errorString() << reply->rawResult().exceptionCode();
} else {
emit writeRequestFinished(requestId, false);
qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->error();
}
});
connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured);
QTimer::singleShot(200, reply, SLOT(deleteLater()));
} else {
delete reply; // broadcast replies return immediately
}
} else {
qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
}
return requestId;
}
bool ModbusRTUMaster::readDiscreteInput(int slaveAddress, int registerAddress)
{
if (!m_modbusRtuSerialMaster)
return false;
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, registerAddress, 1);
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, &ModbusRTUMaster::onReplyFinished);
connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured);
QTimer::singleShot(200, reply, SLOT(deleteLater()));
} else {
delete reply; // broadcast replies return immediately
}
} else {
qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
}
return true;
}
bool ModbusRTUMaster::readInputRegister(int slaveAddress, int registerAddress)
{
if (!m_modbusRtuSerialMaster)
return false;
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, registerAddress, 1);
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, &ModbusRTUMaster::onReplyFinished);
connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured);
QTimer::singleShot(200, reply, SLOT(deleteLater()));
} else {
delete reply; // broadcast replies return immediately
}
} else {
qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
}
return true;
}
bool ModbusRTUMaster::readHoldingRegister(int slaveAddress, int registerAddress)
{
if (!m_modbusRtuSerialMaster)
return false;
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1);
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, &ModbusRTUMaster::onReplyFinished);
connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured);
QTimer::singleShot(200, reply, SLOT(deleteLater()));
} else {
delete reply; // broadcast replies return immediately
}
} else {
qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
}
return true;
}
void ModbusRTUMaster::onModbusErrorOccurred(QModbusDevice::Error error)
{
qCWarning(dcDrexelUndWeiss()) << "An error occured" << error;
}
void ModbusRTUMaster::onModbusStateChanged(QModbusDevice::State state)
{
bool connected = (state != QModbusDevice::UnconnectedState);
if (!connected) {
//try to reconnect in 10 seconds
m_reconnectTimer->start(10000);
}
emit connectionStateChanged(connected);
}

View File

@ -0,0 +1,78 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, 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 MODBUSRTUMASTER_H
#define MODBUSRTUMASTER_H
#include <QObject>
#include <QtSerialBus>
#include <QSerialPort>
#include <QTimer>
#include <QUuid>
class ModbusRTUMaster : public QObject
{
Q_OBJECT
public:
explicit ModbusRTUMaster(QString serialPort, int baudrate, QSerialPort::Parity parity, int dataBits, int stopBits, QObject *parent = nullptr);
~ModbusRTUMaster();
bool connectDevice();
bool readCoil(int slaveAddress, int registerAddress);
bool readDiscreteInput(int slaveAddress, int registerAddress);
bool readInputRegister(int slaveAddress, int registerAddress);
bool readHoldingRegister(int slaveAddress, int registerAddress);
QUuid writeCoil(int slaveAddress, int registerAddress, bool status);
QUuid writeHoldingRegister(int slaveAddress, int registerAddress, int data);
QString serialPort();
private:
QModbusRtuSerialMaster *m_modbusRtuSerialMaster;
QTimer *m_reconnectTimer = nullptr;
private slots:
void onReplyFinished();
void onReplyErrorOccured(QModbusDevice::Error error);
void onReconnectTimer();
void onModbusErrorOccurred(QModbusDevice::Error error);
void onModbusStateChanged(QModbusDevice::State state);
signals:
void connectionStateChanged(bool status);
void receivedCoil(int slaveAddress, int modbusRegister, bool value);
void receivedDiscreteInput(int slaveAddress, int modbusRegister, bool value);
void receivedHoldingRegister(int slaveAddress, int modbusRegister, int value);
void receivedInputRegister(int slaveAddress, int modbusRegister, int value);
void writeRequestFinished(QUuid requestId, bool success);
};
#endif // MODBUSRTUMASTER_H

View File

@ -0,0 +1 @@
<クd<>箆!ソ`。スン

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
</TS>

45
nymea-plugins-modbus.pro Normal file
View File

@ -0,0 +1,45 @@
TEMPLATE = subdirs
PLUGIN_DIRS = \
drexelundweiss \
message(============================================)
message("Qt version:" $$[QT_VERSION])
plugininfo.depends = FORCE
for (entry, PLUGIN_DIRS):plugininfo.commands += test -d $${entry} || mkdir -p $${entry}; cd $${entry} && qmake -o Makefile $$PWD/$${entry}/$${entry}.pro && cd ..;
for (entry, PLUGIN_DIRS):plugininfo.commands += make -C $${entry} plugininfo.h;
QMAKE_EXTRA_TARGETS += plugininfo
# Translations:
# make lupdate to update .ts files
lupdate.depends = FORCE plugininfo
for (entry, PLUGIN_DIRS):lupdate.commands += make -C $${entry} lupdate;
QMAKE_EXTRA_TARGETS += lupdate
# make lrelease to build .qm from .ts
lrelease.depends = FORCE
for (entry, PLUGIN_DIRS):lrelease.commands += lrelease $$files($$PWD/$${entry}/translations/*.ts, true);
for (entry, PLUGIN_DIRS):lrelease.commands += rsync -a $$PWD/$${entry}/translations/*.qm $$OUT_PWD/translations/;
QMAKE_EXTRA_TARGETS += lrelease
# For Qt-Creator's code model: Add CPATH to INCLUDEPATH explicitly
INCLUDEPATH += $$(CPATH)
# Verify if building only a selection of plugins
contains(CONFIG, selection) {
# Check each plugin if the subdir exists
for(plugin, PLUGINS) {
contains(PLUGIN_DIRS, $${plugin}) {
SUBDIRS*= $${plugin}
} else {
error("Invalid plugin passed. There is no subdirectory with the name $${plugin}.")
}
}
message("Building plugin selection: $${SUBDIRS}")
} else {
SUBDIRS *= $${PLUGIN_DIRS}
message("Building all plugins")
}

17
plugins.pri Normal file
View File

@ -0,0 +1,17 @@
isEmpty(PLUGIN_PRI) {
exists($$[QT_INSTALL_PREFIX]/include/nymea/plugin.pri) {
include($$[QT_INSTALL_PREFIX]/include/nymea/plugin.pri)
} else {
message("plugin.pri not found. Either install libnymea1-dev or use the PLUGIN_PRI argument to point to it.")
message("For building this project without nymea installed system-wide, you will want to export those variables in addition:")
message("PKG_CONFIG_PATH=/path/to/build-nymea/libnymea/pkgconfig/")
message("CPATH=/path/to/nymea/libnymea/")
message("LIBRARY_PATH=/path/to/build-nymea/libnymea/")
message("PATH=/path/to/build-nymea/tools/nymea-plugininfocompiler:$PATH")
message("LD_LIBRARY_PATH=/path/to/build-nymea/libnymea/")
error("plugin.pri not found. Cannot continue")
}
} else {
# message("Using $$PLUGIN_PRI")
include($$PLUGIN_PRI)
}