Merge PR #545: New plugin: USB-RLY82 relay with 2 relays and 8 analog/digital inputs.

This commit is contained in:
jenkins 2022-05-08 19:14:28 +02:00
commit 29650429f2
15 changed files with 1916 additions and 0 deletions

12
debian/control vendored
View File

@ -846,6 +846,18 @@ Description: nymea.io plugin for USB relay
This package will install the nymea.io plugin for USB relay
Package: nymea-plugin-usbrly82
Architecture: any
Multi-Arch: same
Section: libs
Depends: ${shlibs:Depends},
${misc:Depends},
libudev1,
Description: nymea integration plugin for USB-RLY82 relay
This package will install the nymea integration plugin for USB-RLY82 relay
with 2 relays and 8 analog/digital inputs.
Package: nymea-plugin-wakeonlan
Architecture: any
Depends: ${shlibs:Depends},

View File

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

View File

@ -72,6 +72,7 @@ PLUGIN_DIRS = \
udpcommander \
unifi \
usbrelay \
usbrly82 \
wakeonlan \
wemo \
ws2812fx \

23
usbrly82/README.md Normal file
View File

@ -0,0 +1,23 @@
# USB-RLY82 - 2 channel USB relay
--------------------------------
This plugin allows you to control USB relays and receive digital and analog input values from the controller.
In order to set up the the controller in nymea, you only need to plug in the device. All digital and analog channels will be set up automatically.
Each relay board has a unique serial number and therefore it can be clearly identified.
## Supported things
* USB-RLY82
This relay supports:
* 2 Digital outputs (Relays)
* 8 Digital and analog inputs
* the Resolution of the ADC can be configured
## More
Information about the USB relay hardware can be found [here](https://www.robot-electronics.co.uk/usb-rly82.html).
The datasheet can be downloaded from [here](https://www.robot-electronics.co.uk/files/usb-rly82.pdf)

View File

@ -0,0 +1,258 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "plugininfo.h"
#include "integrationpluginusbrly82.h"
IntegrationPluginUsbRly82::IntegrationPluginUsbRly82()
{
}
void IntegrationPluginUsbRly82::init()
{
m_monitor = new SerialPortMonitor(this);
}
void IntegrationPluginUsbRly82::discoverThings(ThingDiscoveryInfo *info)
{
info->finish(Thing::ThingErrorNoError);
}
void IntegrationPluginUsbRly82::startMonitoringAutoThings()
{
// Start seaching for devices which can be discovered and added automatically
connect(m_monitor, &SerialPortMonitor::serialPortAdded, this, &IntegrationPluginUsbRly82::onSerialPortAdded);
connect(m_monitor, &SerialPortMonitor::serialPortRemoved, this, &IntegrationPluginUsbRly82::onSerialPortRemoved);
// Check the initial list of
foreach (const SerialPortMonitor::SerialPortInfo &serialPortInfo, m_monitor->serialPortInfos()) {
onSerialPortAdded(serialPortInfo);
}
}
void IntegrationPluginUsbRly82::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
qCDebug(dcUsbRly82()) << "Setup thing" << thing;
// Relay
if (info->thing()->thingClassId() == usbRelayThingClassId) {
// Search for the serial port with the given serialnumber
foreach (const SerialPortMonitor::SerialPortInfo &serialPortInfo, m_monitor->serialPortInfos()) {
if (serialPortInfo.serialNumber == thing->paramValue(usbRelayThingSerialNumberParamTypeId).toString()) {
qCDebug(dcUsbRly82()) << "Found serial port for" << thing << serialPortInfo;
// Handle reconfigure
if (m_relays.contains(thing)) {
m_relays.take(thing)->deleteLater();
}
UsbRly82 *relay = new UsbRly82(this);
relay->setAnalogRefreshRate(thing->setting(usbRelaySettingsAnalogRefreshRateParamTypeId).toUInt());
connect(relay, &UsbRly82::availableChanged, thing, [=](bool available){
qCDebug(dcUsbRly82()) << thing << "available changed" << available;
thing->setStateValue("connected", available);
if (available) {
// Set the already fetched information
thing->setStateValue(usbRelayPowerRelay1StateTypeId, relay->powerRelay1());
thing->setStateValue(usbRelayPowerRelay2StateTypeId, relay->powerRelay2());
updateDigitalInputs(thing);
thing->setStateValue(usbRelayVersionStateTypeId, relay->softwareVersion());
}
});
connect(relay, &UsbRly82::powerRelay1Changed, thing, [=](bool power){
qCDebug(dcUsbRly82()) << thing << "relay 1 power changed" << power;
thing->setStateValue(usbRelayPowerRelay1StateTypeId, power);
});
connect(relay, &UsbRly82::powerRelay2Changed, thing, [=](bool power){
qCDebug(dcUsbRly82()) << thing << "relay 2 power changed" << power;
thing->setStateValue(usbRelayPowerRelay2StateTypeId, power);
});
connect(relay, &UsbRly82::digitalInputsChanged, thing, [=](){
updateDigitalInputs(thing);
});
if (!relay->connectRelay(serialPortInfo.systemLocation)) {
qCWarning(dcUsbRly82()) << "Setup failed. Could not connect to relay" << thing;
info->finish(Thing::ThingErrorHardwareFailure);
relay->deleteLater();
return;
}
m_relays.insert(thing, relay);
info->finish(Thing::ThingErrorNoError);
connect(thing, &Thing::settingChanged, this, [=](const ParamTypeId &paramTypeId, const QVariant &value){
if (paramTypeId == usbRelaySettingsAnalogRefreshRateParamTypeId) {
qCDebug(dcUsbRly82()) << "Refrsh rat changed for" << thing << value.toUInt() << "ms";
relay->setAnalogRefreshRate(value.toUInt());
}
});
return;
}
}
// Does not seem to be available
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
info->finish(Thing::ThingErrorSetupFailed);
}
void IntegrationPluginUsbRly82::postSetupThing(Thing *thing)
{
qCDebug(dcUsbRly82()) << "Post setup thing" << thing;
}
void IntegrationPluginUsbRly82::thingRemoved(Thing *thing)
{
qCDebug(dcUsbRly82()) << "Remove thing" << thing;
if (thing->thingClassId() == usbRelayThingClassId) {
UsbRly82 *relay = m_relays.take(thing);
if (relay) {
delete relay;
}
}
}
void IntegrationPluginUsbRly82::executeAction(ThingActionInfo *info)
{
qCDebug(dcUsbRly82()) << "Executing action for thing" << info->thing() << info->action().actionTypeId().toString() << info->action().params();
if (info->thing()->thingClassId() == usbRelayThingClassId) {
Thing *thing = info->thing();
UsbRly82 *relay = m_relays.value(thing);
if (!relay) {
qCWarning(dcUsbRly82()) << "Could execute action because could not find USB relay for" << thing;
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
if (!relay->available()) {
qCWarning(dcUsbRly82()) << "Cannot execute action. Relay is not available" << thing;
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
if (info->action().actionTypeId() == usbRelayPowerRelay1ActionTypeId) {
bool power = info->action().paramValue(usbRelayPowerRelay1ActionPowerRelay1ParamTypeId).toBool();
UsbRly82Reply *reply = relay->setRelay1Power(power);
connect(reply, &UsbRly82Reply::finished, info, [=](){
if (reply->error() != UsbRly82Reply::ErrorNoError) {
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
info->finish(Thing::ThingErrorNoError);
});
return;
} else if (info->action().actionTypeId() == usbRelayPowerRelay2ActionTypeId) {
bool power = info->action().paramValue(usbRelayPowerRelay2ActionPowerRelay2ParamTypeId).toBool();
UsbRly82Reply *reply = relay->setRelay2Power(power);
connect(reply, &UsbRly82Reply::finished, info, [=](){
if (reply->error() != UsbRly82Reply::ErrorNoError) {
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
info->finish(Thing::ThingErrorNoError);
});
return;
}
info->finish(Thing::ThingErrorActionTypeNotFound);
}
info->finish(Thing::ThingErrorThingClassNotFound);
}
void IntegrationPluginUsbRly82::onSerialPortAdded(const SerialPortMonitor::SerialPortInfo &serialPortInfo)
{
if (serialPortInfo.vendorId == 0x04d8 && serialPortInfo.productId == 0xffee) {
qCDebug(dcUsbRly82()) << "[+] Added" << serialPortInfo;
Things alreadyAddedThings = myThings().filterByThingClassId(usbRelayThingClassId).filterByParam(usbRelayThingSerialNumberParamTypeId, serialPortInfo.serialNumber);
if (alreadyAddedThings.isEmpty()) {
qCDebug(dcUsbRly82()) << "New" << serialPortInfo.product << serialPortInfo.serialNumber << "showed up. Setting up a new thing for this.";
ThingDescriptor descriptor(usbRelayThingClassId, "USB-RLY82");
ParamList params;
params.append(Param(usbRelayThingSerialNumberParamTypeId, serialPortInfo.serialNumber));
descriptor.setParams(params);
emit autoThingsAppeared(ThingDescriptors() << descriptor);
} else {
Thing *relayThing = alreadyAddedThings.first();
if (relayThing) {
qCDebug(dcUsbRly82()) << "Thing already set up for this controller" << relayThing;
UsbRly82 *relay = m_relays.value(relayThing);
if (relay) {
relay->connectRelay(serialPortInfo.systemLocation);
}
}
}
}
}
void IntegrationPluginUsbRly82::onSerialPortRemoved(const SerialPortMonitor::SerialPortInfo &serialPortInfo)
{
if (serialPortInfo.vendorId == 0x04d8 && serialPortInfo.productId == 0xffee) {
qCDebug(dcUsbRly82()) << "[-] Removed" << serialPortInfo;
}
}
void IntegrationPluginUsbRly82::updateDigitalInputs(Thing *thing)
{
UsbRly82 *relay = m_relays.value(thing);
if (!relay)
return;
qCDebug(dcUsbRly82()) << thing << "digital inputs changed:" << QString("%1").arg(relay->digitalInputs(), 8, 2, QChar('0'));
thing->setStateValue(usbRelayDigitalInputChannel1StateTypeId, UsbRly82::checkBit(relay->digitalInputs(), 0));
thing->setStateValue(usbRelayDigitalInputChannel2StateTypeId, UsbRly82::checkBit(relay->digitalInputs(), 1));
thing->setStateValue(usbRelayDigitalInputChannel3StateTypeId, UsbRly82::checkBit(relay->digitalInputs(), 2));
thing->setStateValue(usbRelayDigitalInputChannel4StateTypeId, UsbRly82::checkBit(relay->digitalInputs(), 3));
thing->setStateValue(usbRelayDigitalInputChannel5StateTypeId, UsbRly82::checkBit(relay->digitalInputs(), 4));
thing->setStateValue(usbRelayDigitalInputChannel6StateTypeId, UsbRly82::checkBit(relay->digitalInputs(), 5));
thing->setStateValue(usbRelayDigitalInputChannel7StateTypeId, UsbRly82::checkBit(relay->digitalInputs(), 6));
thing->setStateValue(usbRelayDigitalInputChannel8StateTypeId, UsbRly82::checkBit(relay->digitalInputs(), 7));
}

View File

@ -0,0 +1,68 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 INTEGRATIONPLUGINUSBRLY82_H
#define INTEGRATIONPLUGINUSBRLY82_H
#include "integrations/integrationplugin.h"
#include "serialportmonitor.h"
#include "usbrly82.h"
class IntegrationPluginUsbRly82: public IntegrationPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginusbrly82.json")
Q_INTERFACES(IntegrationPlugin)
public:
explicit IntegrationPluginUsbRly82();
void init() override;
void startMonitoringAutoThings() override;
void discoverThings(ThingDiscoveryInfo *info) override;
void setupThing(ThingSetupInfo *info) override;
void postSetupThing(Thing *thing) override;
void thingRemoved(Thing *thing) override;
void executeAction(ThingActionInfo *info) override;
private:
SerialPortMonitor *m_monitor = nullptr;
QHash<Thing *, UsbRly82 *> m_relays;
private slots:
void onSerialPortAdded(const SerialPortMonitor::SerialPortInfo &serialPortInfo);
void onSerialPortRemoved(const SerialPortMonitor::SerialPortInfo &serialPortInfo);
void updateDigitalInputs(Thing *thing);
};
#endif // INTEGRATIONPLUGINUSBRLY82_H

View File

@ -0,0 +1,255 @@
{
"name": "UsbRly82",
"displayName": "USB-RLY82",
"id": "33d4ebad-104b-4f24-8089-6edd35ba2c95",
"vendors": [
{
"displayName": "Robot electronics",
"name": "robotElectronics",
"id": "c3b972d1-85e6-436c-ae07-a04cbfae604e",
"thingClasses": [
{
"name": "usbRelay",
"displayName": "USB-RLY82",
"id": "0ae5cd75-d42e-4993-8ce8-ced5ba315688",
"setupMethod": "JustAdd",
"createMethods": ["discovery", "auto"],
"interfaces": [ "gateway" ],
"paramTypes": [
{
"id": "5ac5b15f-34cf-4d42-8b9d-6e9866d8f2ed",
"name": "serialNumber",
"displayName": "Serial number",
"type": "QString",
"defaultValue": ""
}
],
"settingsTypes": [
{
"id": "e426f3b1-e9bf-4bef-bc01-2aba6326d265",
"name": "analogRefreshRate",
"displayName": "Analog refresh interval",
"type": "uint",
"unit": "MilliSeconds",
"defaultValue": 1000
}
],
"stateTypes": [
{
"id": "b482b0ea-1901-4437-a309-be43833a1ad5",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "9935ecba-bb56-4c6c-8be1-60bacc40abb7",
"name": "powerRelay1",
"displayName": "Power relay 1",
"displayNameEvent": "Power relay 1 changed",
"displayNameAction": "Set power relay 1",
"type": "bool",
"ioType": "digitalOutput",
"defaultValue": false,
"writable": true,
"cached": true
},
{
"id": "d4b84a73-44dd-4bb9-a0d0-e45e6d2aaf7d",
"name": "powerRelay2",
"displayName": "Power relay 2",
"displayNameEvent": "Power relay 2 changed",
"displayNameAction": "Set power relay 2",
"type": "bool",
"ioType": "digitalOutput",
"defaultValue": false,
"writable": true,
"cached": true
},
{
"id": "ad60f859-11e0-4a80-86fc-1810a5054fb6",
"name": "digitalInputChannel1",
"displayName": "Channel 1",
"displayNameEvent": "Channel 1 input changed",
"type": "bool",
"ioType": "digitalInput",
"defaultValue": false,
"cached": true
},
{
"id": "5038008d-68c7-4cde-845d-335758a05a15",
"name": "digitalInputChannel2",
"displayName": "Channel 2",
"displayNameEvent": "Channel 2 input changed",
"type": "bool",
"ioType": "digitalInput",
"defaultValue": false,
"cached": true
},
{
"id": "73a8c051-5ce8-4909-a75f-f0e88f1ceb61",
"name": "digitalInputChannel3",
"displayName": "Channel 3",
"displayNameEvent": "Channel 3 input changed",
"type": "bool",
"ioType": "digitalInput",
"defaultValue": false,
"cached": true
},
{
"id": "11a3e692-cccc-435b-a9fd-721ae332c90c",
"name": "digitalInputChannel4",
"displayName": "Channel 4",
"displayNameEvent": "Channel 4 input changed",
"type": "bool",
"ioType": "digitalInput",
"defaultValue": false,
"cached": true
},
{
"id": "932cb171-5ba2-4420-bee3-9016dca6498a",
"name": "digitalInputChannel5",
"displayName": "Channel 5",
"displayNameEvent": "Channel 5 input changed",
"type": "bool",
"ioType": "digitalInput",
"defaultValue": false,
"cached": true
},
{
"id": "e10074d7-68cb-4f74-87ea-f0cdf193d207",
"name": "digitalInputChannel6",
"displayName": "Channel 6",
"displayNameEvent": "Channel 6 input changed",
"type": "bool",
"ioType": "digitalInput",
"defaultValue": false,
"cached": true
},
{
"id": "f37aa320-ecfb-4896-bb31-8109606cc5b3",
"name": "digitalInputChannel7",
"displayName": "Channel 7",
"displayNameEvent": "Channel 7 input changed",
"type": "bool",
"ioType": "digitalInput",
"defaultValue": false,
"cached": true
},
{
"id": "32aa1fbd-c128-4258-bb41-22b5b5e150de",
"name": "digitalInputChannel8",
"displayName": "Channel 8",
"displayNameEvent": "Channel 8 input changed",
"type": "bool",
"ioType": "digitalInput",
"defaultValue": false,
"cached": true
},
{
"id": "0dfda321-7da5-4e4c-880e-5b45098398e8",
"name": "analogInputChannel1",
"displayName": "Analog Channel 1",
"displayNameEvent": "Analog channel 1 value changed",
"ioType": "analogInput",
"type": "double",
"minValue": 0,
"maxValue": 1,
"defaultValue": 0
},
{
"id": "02882e2a-f0a6-4042-b91e-c7618c15399e",
"name": "analogInputChannel2",
"displayName": "Analog Channel 2",
"displayNameEvent": "Analog channel 2 value changed",
"ioType": "analogInput",
"type": "double",
"minValue": 0,
"maxValue": 1,
"defaultValue": 0
},
{
"id": "de9172e5-b1c1-4a7e-aebe-819ef22c2c5a",
"name": "analogInputChannel3",
"displayName": "Analog Channel 3",
"displayNameEvent": "Analog channel 3 value changed",
"ioType": "analogInput",
"type": "double",
"minValue": 0,
"maxValue": 1,
"defaultValue": 0
},
{
"id": "875ffbf1-3f11-4935-bd16-de983585baad",
"name": "analogInputChannel4",
"displayName": "Analog Channel 4",
"displayNameEvent": "Analog channel 4 value changed",
"ioType": "analogInput",
"type": "double",
"minValue": 0,
"maxValue": 1,
"defaultValue": 0
},
{
"id": "fa97d343-544d-4322-aacd-4f9f7c21224b",
"name": "analogInputChannel5",
"displayName": "Analog Channel 5",
"displayNameEvent": "Analog channel 5 value changed",
"ioType": "analogInput",
"type": "double",
"minValue": 0,
"maxValue": 1,
"defaultValue": 0
},
{
"id": "51d50bf9-6d40-4d63-a3a2-a0a7c0b5850d",
"name": "analogInputChannel6",
"displayName": "Analog Channel 6",
"displayNameEvent": "Analog channel 6 value changed",
"ioType": "analogInput",
"type": "double",
"minValue": 0,
"maxValue": 1,
"defaultValue": 0
},
{
"id": "25ecf6a9-2007-4768-93a4-28ec81bac179",
"name": "analogInputChannel7",
"displayName": "Analog Channel 7",
"displayNameEvent": "Analog channel 7 value changed",
"ioType": "analogInput",
"type": "double",
"minValue": 0,
"maxValue": 1,
"defaultValue": 0
},
{
"id": "93198a69-4ab6-4fd5-90ab-7f26a67b3b4a",
"name": "analogInputChannel8",
"displayName": "Analog Channel 8",
"displayNameEvent": "Analog channel 8 value changed",
"ioType": "analogInput",
"type": "double",
"minValue": 0,
"maxValue": 1,
"defaultValue": 0
},
{
"id": "e06c88f4-38d9-42cd-bab9-2a07a9a83ce0",
"name": "version",
"displayName": "Version",
"displayNameEvent": "Version changed",
"type": "QString",
"defaultValue": ""
}
]
}
]
}
]
}

13
usbrly82/meta.json Normal file
View File

@ -0,0 +1,13 @@
{
"title": "USB-RLY82",
"tagline": "Control relays connected over USB and read digital and analog inputs.",
"icon": "robotelectronics.png",
"stability": "consumer",
"offline": true,
"technologies": [
"usb"
],
"categories": [
"diy"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,220 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "serialportmonitor.h"
#include "extern-plugininfo.h"
#include <QSerialPortInfo>
SerialPortMonitor::SerialPortMonitor(QObject *parent) : QObject(parent)
{
m_udev = udev_new();
if (!m_udev) {
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Could not initialize udev";
return;
}
// Read initially all tty devices
struct udev_enumerate *enumerate = udev_enumerate_new(m_udev);
if (!enumerate) {
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Could not create udev enumerate for initial device reading.";
udev_unref(m_udev);
m_udev = nullptr;
return;
}
// We are only interested in FTDI devices
udev_enumerate_add_match_subsystem(enumerate, "tty");
// udev_enumerate_add_match_property(enumerate, "ID_VENDOR_ID", "04d8");
// udev_enumerate_add_match_property(enumerate, "ID_MODEL_ID", "ffee");
if (udev_enumerate_scan_devices(enumerate) < 0) {
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Failed to scan devices from udev enumerate.";
udev_enumerate_unref(enumerate);
enumerate = nullptr;
udev_unref(m_udev);
m_udev = nullptr;
return;
}
qCDebug(dcUsbRly82()) << "SerialPortMonitor: Load initial list of available serial ports...";
struct udev_list_entry *devices = nullptr;
devices = udev_enumerate_get_list_entry(enumerate);
struct udev_list_entry *dev_list_entry = nullptr;
udev_list_entry_foreach(dev_list_entry, devices) {
struct udev_device *device = nullptr;
const char *path;
path = udev_list_entry_get_name(dev_list_entry);
device = udev_device_new_from_syspath(m_udev, path);
// Print properties
struct udev_list_entry *properties = udev_device_get_properties_list_entry(device);
struct udev_list_entry *property_list_entry = nullptr;
udev_list_entry_foreach(property_list_entry, properties) {
qCDebug(dcUsbRly82()) << "SerialPortMonitor: - Property" << udev_list_entry_get_name(property_list_entry) << udev_list_entry_get_value(property_list_entry);
}
QString vendorIdString = QString::fromLatin1(udev_device_get_property_value(device, "ID_VENDOR_ID"));
QString productIdString = QString::fromLatin1(udev_device_get_property_value(device, "ID_MODEL_ID"));
SerialPortInfo info;
info.systemLocation = QString::fromLatin1(udev_device_get_property_value(device,"DEVNAME"));
info.manufacturer = QString::fromLatin1(udev_device_get_property_value(device,"ID_VENDOR_ENC"));
info.product = QString::fromLatin1(udev_device_get_property_value(device,"ID_MODEL_ENC"));
info.serialNumber = QString::fromLatin1(udev_device_get_property_value(device, "ID_SERIAL_SHORT"));
info.vendorId = static_cast<quint16>(vendorIdString.toUInt(nullptr, 16));
info.productId = static_cast<quint16>(productIdString.toUInt(nullptr, 16));
// Clean up this device since we have all information
udev_device_unref(device);
qCDebug(dcUsbRly82()) << "SerialPortMonitor: [+]" << info;
m_serialPortInfos.insert(info.systemLocation, info);
emit serialPortAdded(info);
}
udev_enumerate_unref(enumerate);
enumerate = nullptr;
// Create udev monitor
m_monitor = udev_monitor_new_from_netlink(m_udev, "udev");
if (!m_monitor) {
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Could not initialize udev monitor.";
udev_unref(m_udev);
m_udev = nullptr;
return;
}
// Set monitor filter to tty subsystem
if (udev_monitor_filter_add_match_subsystem_devtype(m_monitor, "tty", nullptr) < 0) {
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Could not set subsystem device type filter to tty.";
udev_monitor_unref(m_monitor);
m_monitor = nullptr;
udev_unref(m_udev);
m_udev = nullptr;
return;
}
// Enable the monitor
if (udev_monitor_enable_receiving(m_monitor) < 0) {
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Could not enable udev monitor.";
udev_monitor_unref(m_monitor);
m_monitor = nullptr;
udev_unref(m_udev);
m_udev = nullptr;
return;
}
// Create socket notifier for read
int socketDescriptor = udev_monitor_get_fd(m_monitor);
m_notifier = new QSocketNotifier(socketDescriptor, QSocketNotifier::Read, this);
connect(m_notifier, &QSocketNotifier::activated, this, [this, socketDescriptor](int socket){
if (socketDescriptor != socket) {
qCWarning(dcUsbRly82()) << "SerialPortMonitor: socket != socketdescriptor";
return;
}
// Create udev device
udev_device *device = udev_monitor_receive_device(m_monitor);
if (!device) {
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Got socket sotification but could not read device information.";
return;
}
QString actionString = QString::fromUtf8(udev_device_get_action(device));
QString vendorIdString = QString::fromUtf8(udev_device_get_property_value(device, "ID_VENDOR_ID"));
QString productIdString = QString::fromUtf8(udev_device_get_property_value(device, "ID_MODEL_ID"));
SerialPortInfo info;
info.systemLocation = QString::fromUtf8(udev_device_get_property_value(device,"DEVNAME"));
info.manufacturer = QString::fromUtf8(udev_device_get_property_value(device,"ID_VENDOR_ENC"));
info.product = QString::fromUtf8(udev_device_get_property_value(device,"ID_MODEL_ENC"));
info.serialNumber = QString::fromUtf8(udev_device_get_property_value(device, "ID_SERIAL_SHORT"));
info.vendorId = static_cast<quint16>(vendorIdString.toUInt(nullptr, 16));
info.productId = static_cast<quint16>(productIdString.toUInt(nullptr, 16));
// Clean udev device
udev_device_unref(device);
// Make sure we know the action
if (actionString.isEmpty())
return;
if (actionString == "add") {
qCDebug(dcUsbRly82()) << "SerialPortMonitor: [+]" << info;
if (!m_serialPortInfos.contains(info.systemLocation)) {
m_serialPortInfos.insert(info.systemLocation, info);
emit serialPortAdded(info);
}
}
if (actionString == "remove") {
qCDebug(dcUsbRly82()) << "SerialPortMonitor: [-]" << info;
if (m_serialPortInfos.contains(info.systemLocation)) {
m_serialPortInfos.remove(info.systemLocation);
emit serialPortRemoved(info);
}
}
});
m_notifier->setEnabled(true);
}
SerialPortMonitor::~SerialPortMonitor()
{
if (m_notifier)
delete m_notifier;
if (m_monitor)
udev_monitor_unref(m_monitor);
if (m_udev)
udev_unref(m_udev);
}
QList<SerialPortMonitor::SerialPortInfo> SerialPortMonitor::serialPortInfos() const
{
return m_serialPortInfos.values();
}
QDebug operator<<(QDebug dbg, const SerialPortMonitor::SerialPortInfo &serialPortInfo)
{
dbg.nospace().noquote() << "SerialPort(" << QString("%1:%2")
.arg(serialPortInfo.vendorId, 4, 16, QLatin1Char('0'))
.arg(serialPortInfo.productId, 4, 16, QLatin1Char('0'))
<< ", " << serialPortInfo.systemLocation
<< ", " << serialPortInfo.manufacturer
<< ", " << serialPortInfo.product << ") ";
return dbg.maybeSpace().maybeQuote();
}

View File

@ -0,0 +1,75 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 SERIALPORTMONITOR_H
#define SERIALPORTMONITOR_H
#include <QHash>
#include <QObject>
#include <QSerialPortInfo>
#include <QSocketNotifier>
#include <libudev.h>
class SerialPortMonitor : public QObject
{
Q_OBJECT
public:
typedef struct SerialPortInfo {
QString manufacturer;
QString product;
QString serialNumber;
QString systemLocation;
quint16 vendorId;
quint16 productId;
} SerialPortInfo;
explicit SerialPortMonitor(QObject *parent = nullptr);
~SerialPortMonitor();
QList<SerialPortInfo> serialPortInfos() const;
signals:
void serialPortAdded(const SerialPortInfo &serialPortInfo);
void serialPortRemoved(const SerialPortInfo &serialPortInfo);
private:
struct udev *m_udev = nullptr;
struct udev_monitor *m_monitor = nullptr;
QSocketNotifier *m_notifier = nullptr;
QHash<QString, SerialPortInfo> m_serialPortInfos;
};
QDebug operator<< (QDebug dbg, const SerialPortMonitor::SerialPortInfo &serialPortInfo);
#endif // SERIALPORTMONITOR_H

View File

@ -0,0 +1,352 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>UsbRly82</name>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="94"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="97"/>
<source>Analog Channel 1</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: analogInputChannel1, ID: {0dfda321-7da5-4e4c-880e-5b45098398e8})
----------
The name of the StateType ({0dfda321-7da5-4e4c-880e-5b45098398e8}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="100"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="103"/>
<source>Analog Channel 2</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: analogInputChannel2, ID: {02882e2a-f0a6-4042-b91e-c7618c15399e})
----------
The name of the StateType ({02882e2a-f0a6-4042-b91e-c7618c15399e}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="106"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="109"/>
<source>Analog Channel 3</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: analogInputChannel3, ID: {de9172e5-b1c1-4a7e-aebe-819ef22c2c5a})
----------
The name of the StateType ({de9172e5-b1c1-4a7e-aebe-819ef22c2c5a}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="112"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="115"/>
<source>Analog Channel 4</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: analogInputChannel4, ID: {875ffbf1-3f11-4935-bd16-de983585baad})
----------
The name of the StateType ({875ffbf1-3f11-4935-bd16-de983585baad}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="118"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="121"/>
<source>Analog Channel 5</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: analogInputChannel5, ID: {fa97d343-544d-4322-aacd-4f9f7c21224b})
----------
The name of the StateType ({fa97d343-544d-4322-aacd-4f9f7c21224b}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="124"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="127"/>
<source>Analog Channel 6</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: analogInputChannel6, ID: {51d50bf9-6d40-4d63-a3a2-a0a7c0b5850d})
----------
The name of the StateType ({51d50bf9-6d40-4d63-a3a2-a0a7c0b5850d}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="130"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="133"/>
<source>Analog Channel 7</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: analogInputChannel7, ID: {25ecf6a9-2007-4768-93a4-28ec81bac179})
----------
The name of the StateType ({25ecf6a9-2007-4768-93a4-28ec81bac179}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="136"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="139"/>
<source>Analog Channel 8</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: analogInputChannel8, ID: {93198a69-4ab6-4fd5-90ab-7f26a67b3b4a})
----------
The name of the StateType ({93198a69-4ab6-4fd5-90ab-7f26a67b3b4a}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="142"/>
<source>Analog channel 1 value changed</source>
<extracomment>The name of the EventType ({0dfda321-7da5-4e4c-880e-5b45098398e8}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="145"/>
<source>Analog channel 2 value changed</source>
<extracomment>The name of the EventType ({02882e2a-f0a6-4042-b91e-c7618c15399e}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="148"/>
<source>Analog channel 3 value changed</source>
<extracomment>The name of the EventType ({de9172e5-b1c1-4a7e-aebe-819ef22c2c5a}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="151"/>
<source>Analog channel 4 value changed</source>
<extracomment>The name of the EventType ({875ffbf1-3f11-4935-bd16-de983585baad}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="154"/>
<source>Analog channel 5 value changed</source>
<extracomment>The name of the EventType ({fa97d343-544d-4322-aacd-4f9f7c21224b}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="157"/>
<source>Analog channel 6 value changed</source>
<extracomment>The name of the EventType ({51d50bf9-6d40-4d63-a3a2-a0a7c0b5850d}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="160"/>
<source>Analog channel 7 value changed</source>
<extracomment>The name of the EventType ({25ecf6a9-2007-4768-93a4-28ec81bac179}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="163"/>
<source>Analog channel 8 value changed</source>
<extracomment>The name of the EventType ({93198a69-4ab6-4fd5-90ab-7f26a67b3b4a}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="166"/>
<source>Analog refresh interval</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, Type: settings, ID: {e426f3b1-e9bf-4bef-bc01-2aba6326d265})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="169"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="172"/>
<source>Channel 1</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: digitalInputChannel1, ID: {ad60f859-11e0-4a80-86fc-1810a5054fb6})
----------
The name of the StateType ({ad60f859-11e0-4a80-86fc-1810a5054fb6}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="175"/>
<source>Channel 1 input changed</source>
<extracomment>The name of the EventType ({ad60f859-11e0-4a80-86fc-1810a5054fb6}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="178"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="181"/>
<source>Channel 2</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: digitalInputChannel2, ID: {5038008d-68c7-4cde-845d-335758a05a15})
----------
The name of the StateType ({5038008d-68c7-4cde-845d-335758a05a15}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="184"/>
<source>Channel 2 input changed</source>
<extracomment>The name of the EventType ({5038008d-68c7-4cde-845d-335758a05a15}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="187"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="190"/>
<source>Channel 3</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: digitalInputChannel3, ID: {73a8c051-5ce8-4909-a75f-f0e88f1ceb61})
----------
The name of the StateType ({73a8c051-5ce8-4909-a75f-f0e88f1ceb61}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="193"/>
<source>Channel 3 input changed</source>
<extracomment>The name of the EventType ({73a8c051-5ce8-4909-a75f-f0e88f1ceb61}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="196"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="199"/>
<source>Channel 4</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: digitalInputChannel4, ID: {11a3e692-cccc-435b-a9fd-721ae332c90c})
----------
The name of the StateType ({11a3e692-cccc-435b-a9fd-721ae332c90c}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="202"/>
<source>Channel 4 input changed</source>
<extracomment>The name of the EventType ({11a3e692-cccc-435b-a9fd-721ae332c90c}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="205"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="208"/>
<source>Channel 5</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: digitalInputChannel5, ID: {932cb171-5ba2-4420-bee3-9016dca6498a})
----------
The name of the StateType ({932cb171-5ba2-4420-bee3-9016dca6498a}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="211"/>
<source>Channel 5 input changed</source>
<extracomment>The name of the EventType ({932cb171-5ba2-4420-bee3-9016dca6498a}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="214"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="217"/>
<source>Channel 6</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: digitalInputChannel6, ID: {e10074d7-68cb-4f74-87ea-f0cdf193d207})
----------
The name of the StateType ({e10074d7-68cb-4f74-87ea-f0cdf193d207}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="220"/>
<source>Channel 6 input changed</source>
<extracomment>The name of the EventType ({e10074d7-68cb-4f74-87ea-f0cdf193d207}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="223"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="226"/>
<source>Channel 7</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: digitalInputChannel7, ID: {f37aa320-ecfb-4896-bb31-8109606cc5b3})
----------
The name of the StateType ({f37aa320-ecfb-4896-bb31-8109606cc5b3}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="229"/>
<source>Channel 7 input changed</source>
<extracomment>The name of the EventType ({f37aa320-ecfb-4896-bb31-8109606cc5b3}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="232"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="235"/>
<source>Channel 8</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: digitalInputChannel8, ID: {32aa1fbd-c128-4258-bb41-22b5b5e150de})
----------
The name of the StateType ({32aa1fbd-c128-4258-bb41-22b5b5e150de}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="238"/>
<source>Channel 8 input changed</source>
<extracomment>The name of the EventType ({32aa1fbd-c128-4258-bb41-22b5b5e150de}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="241"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="244"/>
<source>Connected</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: connected, ID: {b482b0ea-1901-4437-a309-be43833a1ad5})
----------
The name of the StateType ({b482b0ea-1901-4437-a309-be43833a1ad5}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="247"/>
<source>Connected changed</source>
<extracomment>The name of the EventType ({b482b0ea-1901-4437-a309-be43833a1ad5}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="250"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="253"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="256"/>
<source>Power relay 1</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, ActionType: powerRelay1, ID: {9935ecba-bb56-4c6c-8be1-60bacc40abb7})
----------
The name of the ParamType (ThingClass: usbRelay, EventType: powerRelay1, ID: {9935ecba-bb56-4c6c-8be1-60bacc40abb7})
----------
The name of the StateType ({9935ecba-bb56-4c6c-8be1-60bacc40abb7}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="259"/>
<source>Power relay 1 changed</source>
<extracomment>The name of the EventType ({9935ecba-bb56-4c6c-8be1-60bacc40abb7}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="262"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="265"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="268"/>
<source>Power relay 2</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, ActionType: powerRelay2, ID: {d4b84a73-44dd-4bb9-a0d0-e45e6d2aaf7d})
----------
The name of the ParamType (ThingClass: usbRelay, EventType: powerRelay2, ID: {d4b84a73-44dd-4bb9-a0d0-e45e6d2aaf7d})
----------
The name of the StateType ({d4b84a73-44dd-4bb9-a0d0-e45e6d2aaf7d}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="271"/>
<source>Power relay 2 changed</source>
<extracomment>The name of the EventType ({d4b84a73-44dd-4bb9-a0d0-e45e6d2aaf7d}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="274"/>
<source>Robot electronics</source>
<extracomment>The name of the vendor ({c3b972d1-85e6-436c-ae07-a04cbfae604e})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="277"/>
<source>Serial number</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, Type: thing, ID: {5ac5b15f-34cf-4d42-8b9d-6e9866d8f2ed})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="280"/>
<source>Set power relay 1</source>
<extracomment>The name of the ActionType ({9935ecba-bb56-4c6c-8be1-60bacc40abb7}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="283"/>
<source>Set power relay 2</source>
<extracomment>The name of the ActionType ({d4b84a73-44dd-4bb9-a0d0-e45e6d2aaf7d}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="286"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="289"/>
<source>USB-RLY82</source>
<extracomment>The name of the ThingClass ({0ae5cd75-d42e-4993-8ce8-ced5ba315688})
----------
The name of the plugin UsbRly82 ({33d4ebad-104b-4f24-8089-6edd35ba2c95})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="292"/>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="295"/>
<source>Version</source>
<extracomment>The name of the ParamType (ThingClass: usbRelay, EventType: version, ID: {e06c88f4-38d9-42cd-bab9-2a07a9a83ce0})
----------
The name of the StateType ({e06c88f4-38d9-42cd-bab9-2a07a9a83ce0}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-Desktop-Debug/usbrly82/plugininfo.h" line="298"/>
<source>Version changed</source>
<extracomment>The name of the EventType ({e06c88f4-38d9-42cd-bab9-2a07a9a83ce0}) of ThingClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

471
usbrly82/usbrly82.cpp Normal file
View File

@ -0,0 +1,471 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "usbrly82.h"
#include "extern-plugininfo.h"
#include <QDataStream>
UsbRly82Reply::Error UsbRly82Reply::error() const
{
return m_error;
}
QByteArray UsbRly82Reply::requestData() const
{
return m_requestData;
}
QByteArray UsbRly82Reply::responseData() const
{
return m_responseData;
}
UsbRly82Reply::UsbRly82Reply(QObject *parent) : QObject(parent)
{
m_timer.setSingleShot(true);
m_timer.setInterval(1000);
connect(&m_timer, &QTimer::timeout, this, [this](){
m_error = ErrorTimeout;
emit finished();
});
}
UsbRly82::UsbRly82(QObject *parent) : QObject(parent)
{
qRegisterMetaType<QSerialPort::SerialPortError>();
m_digitalRefreshTimer.setInterval(50);
m_digitalRefreshTimer.setSingleShot(false);
connect(&m_digitalRefreshTimer, &QTimer::timeout, this, &UsbRly82::updateDigitalInputs);
m_analogRefreshTimer.setInterval(m_analogRefreshRate);
m_analogRefreshTimer.setSingleShot(false);
connect(&m_analogRefreshTimer, &QTimer::timeout, this, &UsbRly82::updateAnalogInputs);
}
bool UsbRly82::available() const
{
return m_available;
}
QString UsbRly82::serialNumber() const
{
return m_serialNumber;
}
QString UsbRly82::softwareVersion() const
{
return m_softwareVersion;
}
bool UsbRly82::powerRelay1() const
{
return m_powerRelay1;
}
UsbRly82Reply *UsbRly82::setRelay1Power(bool power)
{
UsbRly82Reply *reply;
if (power) {
reply = createReply(QByteArray::fromHex("65"), false);
connect(reply, &UsbRly82Reply::finished, this, [=](){
if (reply->error() == UsbRly82Reply::ErrorNoError) {
emit powerRelay1Changed(true);
}
});
} else {
reply = createReply(QByteArray::fromHex("6F"), false);
connect(reply, &UsbRly82Reply::finished, this, [=](){
if (reply->error() == UsbRly82Reply::ErrorNoError) {
emit powerRelay1Changed(false);
}
});
}
sendNextRequest();
return reply;
}
bool UsbRly82::powerRelay2() const
{
return m_powerRelay2;
}
UsbRly82Reply *UsbRly82::setRelay2Power(bool power)
{
UsbRly82Reply *reply;
if (power) {
reply = createReply(QByteArray::fromHex("66"), false);
connect(reply, &UsbRly82Reply::finished, this, [=](){
if (reply->error() == UsbRly82Reply::ErrorNoError) {
emit powerRelay2Changed(true);
}
});
} else {
reply = createReply(QByteArray::fromHex("70"), false);
connect(reply, &UsbRly82Reply::finished, this, [=](){
if (reply->error() == UsbRly82Reply::ErrorNoError) {
emit powerRelay2Changed(false);
}
});
}
sendNextRequest();
return reply;
}
uint UsbRly82::analogRefreshRate() const
{
return m_analogRefreshRate;
}
void UsbRly82::setAnalogRefreshRate(uint analogRefreshRate)
{
m_analogRefreshRate = analogRefreshRate;
if (m_analogRefreshRate == 0) {
qCDebug(dcUsbRly82()) << "Refresh rate set to 0. Auto refreshing analog inputs disabled.";
m_analogRefreshTimer.stop();
} else {
m_analogRefreshTimer.setInterval(m_analogRefreshRate);
if (m_available) {
m_analogRefreshTimer.start();
}
}
}
quint8 UsbRly82::digitalInputs() const
{
return m_digitalInputs;
}
bool UsbRly82::connectRelay(const QString &serialPort)
{
qCDebug(dcUsbRly82()) << "Connecting to" << serialPort;
if (m_serialPort) {
m_serialPort->close();
delete m_serialPort;
m_serialPort = nullptr;
}
m_available = false;
m_serialPort = new QSerialPort(serialPort, this);
m_serialPort->setBaudRate(19200);
m_serialPort->setStopBits(QSerialPort::OneStop);
m_serialPort->setParity(QSerialPort::NoParity);
if (!m_serialPort->open(QIODevice::ReadWrite)) {
qCWarning(dcUsbRly82()) << "Could not open serial port" << serialPort << m_serialPort->errorString();
m_serialPort->deleteLater();
m_serialPort = nullptr;
emit availableChanged(m_available);
return false;
}
connect(m_serialPort, &QSerialPort::readyRead, this, &UsbRly82::onReadyRead);
connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onError(QSerialPort::SerialPortError)), Qt::QueuedConnection);
// Get serial number
UsbRly82Reply *reply = getSerialNumber();
connect(reply, &UsbRly82Reply::finished, this, [=](){
if (reply->error() != UsbRly82Reply::ErrorNoError) {
qCWarning(dcUsbRly82()) << "Reading serial number finished with error" << reply->error();
return;
}
m_serialNumber = QString::fromUtf8(reply->responseData());
qCDebug(dcUsbRly82()) << "Get serial number finished successfully." << m_serialNumber;
// Get software version
UsbRly82Reply *reply = getSoftwareVersion();
connect(reply, &UsbRly82Reply::finished, this, [=](){
if (reply->error() != UsbRly82Reply::ErrorNoError) {
qCWarning(dcUsbRly82()) << "Reading software version finished with error" << reply->error();
return;
}
m_softwareVersion = QString::fromUtf8(reply->responseData().toHex());
qCDebug(dcUsbRly82()) << "Get software version finished successfully." << m_softwareVersion;
UsbRly82Reply *reply = getRelayStates();
connect(reply, &UsbRly82Reply::finished, this, [=](){
if (reply->error() != UsbRly82Reply::ErrorNoError) {
qCWarning(dcUsbRly82()) << "Reading relay states finished with error" << reply->error();
return;
}
qCDebug(dcUsbRly82()) << "Reading relay states finished successfully." << reply->responseData().toHex();
bool power = checkBit(reply->responseData().at(0), 0);
if (m_powerRelay1 != power) {
m_powerRelay1 = power;
emit powerRelay1Changed(m_powerRelay1);
}
power = checkBit(reply->responseData().at(0), 1);
if (m_powerRelay2 != power) {
m_powerRelay2 = power;
emit powerRelay2Changed(m_powerRelay2);
}
qCDebug(dcUsbRly82()) << "Relay 1:" << m_powerRelay1;
qCDebug(dcUsbRly82()) << "Relay 2:" << m_powerRelay2;
UsbRly82Reply *reply = getDigitalInputs();
connect(reply, &UsbRly82Reply::finished, this, [=](){
if (reply->error() != UsbRly82Reply::ErrorNoError) {
qCWarning(dcUsbRly82()) << "Reading digital inputs finished with error" << reply->error();
return;
}
if (reply->responseData().isEmpty())
return;
quint8 digitalInputs = reply->responseData().at(0);
if (m_digitalInputs != digitalInputs) {
qCDebug(dcUsbRly82()) << "Digital inputs changed";
m_digitalInputs = digitalInputs;
emit digitalInputsChanged();
}
m_available = true;
emit availableChanged(m_available);
m_digitalRefreshTimer.start();
if (m_analogRefreshRate != 0) {
m_analogRefreshTimer.start(m_analogRefreshRate);
} else {
qCDebug(dcUsbRly82()) << "Refresh rate set to 0. Auto refreshing analog inputs disabled.";
}
});
});
});
});
return true;
}
void UsbRly82::disconnectRelay()
{
if (m_serialPort) {
qCDebug(dcUsbRly82()) << "Disconnecting from" << m_serialPort->portName();
m_serialPort->close();
delete m_serialPort;
m_serialPort = nullptr;
}
m_digitalRefreshTimer.stop();
m_analogRefreshTimer.stop();
m_available = false;
emit availableChanged(m_available);
}
UsbRly82Reply *UsbRly82::getSerialNumber()
{
UsbRly82Reply *reply = createReply(QByteArray::fromHex("38"));
sendNextRequest();
return reply;
}
UsbRly82Reply *UsbRly82::getSoftwareVersion()
{
UsbRly82Reply *reply = createReply(QByteArray::fromHex("5A"));
sendNextRequest();
return reply;
}
UsbRly82Reply *UsbRly82::getRelayStates()
{
UsbRly82Reply *reply = createReply(QByteArray::fromHex("5B"));
sendNextRequest();
return reply;
}
UsbRly82Reply *UsbRly82::getDigitalInputs()
{
UsbRly82Reply *reply = createReply(QByteArray::fromHex("5E"));
sendNextRequest();
return reply;
}
UsbRly82Reply *UsbRly82::getAdcValues()
{
UsbRly82Reply *reply = createReply(QByteArray::fromHex("80"));
sendNextRequest();
return reply;
}
UsbRly82Reply *UsbRly82::getAdcReference()
{
UsbRly82Reply *reply = createReply(QByteArray::fromHex("82"));
sendNextRequest();
return reply;
}
UsbRly82Reply *UsbRly82::createReply(const QByteArray &requestData, bool expectsResponse)
{
UsbRly82Reply *reply = new UsbRly82Reply(this);
reply->m_expectsResponse = expectsResponse;
reply->m_requestData = requestData;
connect(reply, &UsbRly82Reply::finished, this, [=](){
if (m_currentReply == reply) {
m_currentReply = nullptr;
sendNextRequest();
}
reply->deleteLater();
});
if (!expectsResponse) {
m_replyQueue.enqueue(reply);
} else {
// Prioritize requests without response (like switching the relay)
m_replyQueue.prepend(reply);
}
return reply;
}
void UsbRly82::sendNextRequest()
{
if (m_currentReply)
return;
if (m_replyQueue.isEmpty())
return;
m_currentReply = m_replyQueue.dequeue();
//qCDebug(dcUsbRly82()) << "-->" << m_currentReply->requestData().toHex();
m_serialPort->write(m_currentReply->requestData());
if (m_currentReply->m_expectsResponse) {
m_currentReply->m_timer.start(1000);
} else {
// Finish the reply on next event loop
QTimer::singleShot(0, m_currentReply, &UsbRly82Reply::finished);
}
}
bool UsbRly82::checkBit(quint8 byte, uint bitNumber)
{
return ((byte >> bitNumber) & 0x01) == 1;
}
void UsbRly82::onReadyRead()
{
QByteArray data = m_serialPort->readAll();
//qCDebug(dcUsbRly82()) << "<--" << data.toHex();
if (m_currentReply) {
m_currentReply->m_responseData = data;
m_currentReply->m_timer.stop();
emit m_currentReply->finished();
} else {
qCWarning(dcUsbRly82()) << "Unexpected data received" << data.toHex();
}
}
void UsbRly82::onError(QSerialPort::SerialPortError error)
{
if (error != QSerialPort::NoError && error != QSerialPort::OpenError && m_serialPort && m_serialPort->isOpen()) {
qCWarning(dcUsbRly82()) << "Serial port error occurred:" << error << m_serialPort->errorString() << "(Is open:" << m_serialPort->isOpen() << ")";
m_available = false;
emit availableChanged(available());
disconnectRelay();
}
}
void UsbRly82::updateDigitalInputs()
{
// Make sure the queue does not overflow
if (m_updateDigitalInputsReply)
return;
m_updateDigitalInputsReply = getDigitalInputs();
connect(m_updateDigitalInputsReply, &UsbRly82Reply::finished, this, [=](){
if (m_updateDigitalInputsReply->error() != UsbRly82Reply::ErrorNoError) {
qCWarning(dcUsbRly82()) << "Reading digital inputs finished with error" << m_updateDigitalInputsReply->error();
m_updateDigitalInputsReply = nullptr;
return;
}
if (m_updateDigitalInputsReply->responseData().isEmpty()) {
m_updateDigitalInputsReply = nullptr;
return;
}
quint8 digitalInputs = m_updateDigitalInputsReply->responseData().at(0);
if (m_digitalInputs != digitalInputs) {
m_digitalInputs = digitalInputs;
emit digitalInputsChanged();
}
m_updateDigitalInputsReply = nullptr;
});
}
void UsbRly82::updateAnalogInputs()
{
// Make sure the queue does not overflow
if (m_updateAnalogInputsReply)
return;
m_updateAnalogInputsReply = getAdcValues();
connect(m_updateAnalogInputsReply, &UsbRly82Reply::finished, this, [=](){
if (m_updateAnalogInputsReply->error() != UsbRly82Reply::ErrorNoError) {
qCWarning(dcUsbRly82()) << "Reading analog inputs finished with error" << m_updateAnalogInputsReply->error();
m_updateAnalogInputsReply = nullptr;
return;
}
if (m_updateAnalogInputsReply->responseData().count() != 16) {
qCWarning(dcUsbRly82()) << "Reading analog inputs response returned invalid size" << m_updateAnalogInputsReply->responseData().count() << "(should be 16)";
m_updateAnalogInputsReply = nullptr;
return;
}
//qCDebug(dcUsbRly82()) << "Analog inputs" << m_updateAnalogInputsReply->responseData().toHex();
QDataStream stream(m_updateAnalogInputsReply->responseData());
quint16 value = 0;
for (int i = 0; i < 8; i++) {
stream >> value;
m_analogValues.insert(i, value);
//qCDebug(dcUsbRly82()) << "Channel" << i << ":" << value;
}
m_updateAnalogInputsReply = nullptr;
});
}

150
usbrly82/usbrly82.h Normal file
View File

@ -0,0 +1,150 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 USBRLY82_H
#define USBRLY82_H
#include <QHash>
#include <QTimer>
#include <QQueue>
#include <QObject>
#include <QSerialPort>
class UsbRly82Reply : public QObject
{
Q_OBJECT
friend class UsbRly82;
public:
enum Error {
ErrorNoError,
ErrorTimeout
};
Q_ENUM(Error)
Error error() const;
QByteArray requestData() const;
QByteArray responseData() const;
signals:
void finished();
private:
explicit UsbRly82Reply(QObject *parent = nullptr);
Error m_error = ErrorNoError;
QTimer m_timer;
bool m_expectsResponse = true;
QByteArray m_requestData;
QByteArray m_responseData;
};
class UsbRly82 : public QObject
{
Q_OBJECT
public:
explicit UsbRly82(QObject *parent = nullptr);
bool available() const;
QString serialNumber() const;
QString softwareVersion() const;
bool powerRelay1() const;
UsbRly82Reply *setRelay1Power(bool power);
bool powerRelay2() const;
UsbRly82Reply *setRelay2Power(bool power);
uint analogRefreshRate() const;
void setAnalogRefreshRate(uint analogRefreshRate);
quint8 digitalInputs() const;
bool connectRelay(const QString &serialPort);
void disconnectRelay();
UsbRly82Reply *getSerialNumber();
UsbRly82Reply *getSoftwareVersion();
UsbRly82Reply *getRelayStates();
UsbRly82Reply *getDigitalInputs();
UsbRly82Reply *getAdcValues();
UsbRly82Reply *getAdcReference();
static bool checkBit(quint8 byte, uint bitNumber);
signals:
void availableChanged(bool available);
void powerRelay1Changed(bool powerRelay1);
void powerRelay2Changed(bool powerRelay2);
void digitalInputsChanged();
private:
QTimer m_digitalRefreshTimer;
QTimer m_analogRefreshTimer;
QSerialPort *m_serialPort = nullptr;
bool m_available = false;
QString m_serialNumber;
QString m_softwareVersion;
uint m_analogRefreshRate = 1000;
bool m_powerRelay1 = false;
bool m_powerRelay2 = false;
UsbRly82Reply *m_currentReply = nullptr;
QQueue<UsbRly82Reply *> m_replyQueue;
UsbRly82Reply *m_updateDigitalInputsReply = nullptr;
UsbRly82Reply *m_updateAnalogInputsReply = nullptr;
UsbRly82Reply *createReply(const QByteArray &requestData, bool expectsResponse = true);
void sendNextRequest();
quint8 m_digitalInputs = 0x00;
QHash<int, quint16> m_analogValues;
private slots:
void onReadyRead();
void onError(QSerialPort::SerialPortError error);
void updateDigitalInputs();
void updateAnalogInputs();
};
Q_DECLARE_METATYPE(QSerialPort::SerialPortError)
#endif // USBRLY82_H

16
usbrly82/usbrly82.pro Normal file
View File

@ -0,0 +1,16 @@
include(../plugins.pri)
QT *= serialport
PKGCONFIG += libudev
HEADERS += \
integrationpluginusbrly82.h \
serialportmonitor.h \
usbrly82.h
SOURCES += \
integrationpluginusbrly82.cpp \
serialportmonitor.cpp \
usbrly82.cpp