Merge PR #219: New plugin: USB relay

This commit is contained in:
Jenkins nymea 2020-01-18 23:58:23 +01:00
commit 01adff2cd9
14 changed files with 1067 additions and 0 deletions

20
debian/control vendored
View File

@ -15,6 +15,8 @@ Build-depends: libboblight-dev,
qtconnectivity5-dev,
libow-dev,
libsodium-dev,
libudev-dev,
libhidapi-dev,
Standards-Version: 3.9.3
@ -687,6 +689,23 @@ Description: nymea.io plugin for unitec
This package will install the nymea.io plugin for unitec
Package: nymea-plugin-usbrelay
Architecture: any
Multi-Arch: same
Section: libs
Depends: ${shlibs:Depends},
${misc:Depends},
libudev1,
libhidapi-hidraw0,
Description: nymea.io plugin for USB relay
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 USB relay
Package: nymea-plugin-wakeonlan
Architecture: any
@ -970,6 +989,7 @@ Depends: nymea-plugin-anel,
nymea-plugin-tado,
nymea-plugin-keba,
nymea-plugin-unifi,
nymea-plugin-usbrelay,
Replaces: guh-plugins
Description: Plugins for nymea IoT server - the default plugin collection
The nymea daemon is a plugin based IoT (Internet of Things) server. The

1
debian/copyright vendored
View File

@ -59,6 +59,7 @@ Files: avahimonitor/*
senic/*
udpcommander/*
unitec/*
usbrelay/*
wemo/*
License: LGPL-2.1
Copyright: 2014-2017, Simon Stürz <simon.stuerz@guh.io>

View File

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

View File

@ -55,6 +55,7 @@ PLUGIN_DIRS = \
udpcommander \
unifi \
unitec \
usbrelay \
wakeonlan \
wemo \
ws2812fx \

5
usbrelay/README.md Normal file
View File

@ -0,0 +1,5 @@
# usbrelay
--------------------------------
This plugin eneblas you to control low-cost USB HID relays. The plugin creates a power device for each relay and can be controlled using the `power?? interface.
Examples of USB relay hardware can be found [here](http://vusb.wikidot.com/project:driver-less-usb-relays-hid-interface).

View File

@ -0,0 +1,298 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "plugininfo.h"
#include "devicepluginusbrelay.h"
#include <QTimer>
#include <hidapi/hidapi.h>
DevicePluginUsbRelay::DevicePluginUsbRelay()
{
}
void DevicePluginUsbRelay::init()
{
// Initialize/create objects
}
void DevicePluginUsbRelay::startMonitoringAutoDevices()
{
// Start seaching for devices which can be discovered and added automatically
}
void DevicePluginUsbRelay::postSetupDevice(Device *device)
{
qCDebug(dcUsbRelay()) << "Post setup device" << device;
if (device->deviceClassId() == usbRelayConnectorDeviceClassId) {
// Initialize the states
UsbRelay *relay = m_relayDevices.key(device);
if (!relay) {
qCWarning(dcUsbRelay()) << "Could not find relay in post setup.";
return;
}
device->setStateValue(usbRelayConnectorConnectedStateTypeId, relay->connected());
// Check if we have to create child devices (relays)
if (myDevices().filterByParentDeviceId(device->id()).isEmpty()) {
DeviceDescriptors descriptors;
for (int i = 0; i < relay->relayCount(); i++) {
int relayNumber = i + 1;
DeviceDescriptor descriptor(usbRelayDeviceClassId, QString("Relay %1").arg(relayNumber), QString(), device->id());
ParamList params;
params.append(Param(usbRelayDeviceRelayNumberParamTypeId, relayNumber));
descriptor.setParams(params);
descriptors.append(descriptor);
}
emit autoDevicesAppeared(descriptors);
}
} else if (device->deviceClassId() == usbRelayDeviceClassId) {
UsbRelay *relay = getRelayForDevice(device);
if (!relay) return;
// Set the current states
int relayNumber = device->paramValue(usbRelayDeviceRelayNumberParamTypeId).toInt();
device->setStateValue(usbRelayConnectedStateTypeId, relay->connected());
device->setStateValue(usbRelayPowerStateTypeId, relay->relayPower(relayNumber));
}
}
void DevicePluginUsbRelay::deviceRemoved(Device *device)
{
qCDebug(dcUsbRelay()) << "Remove device" << device;
if (device->deviceClassId() == usbRelayConnectorDeviceClassId) {
UsbRelay *relay = m_relayDevices.key(device);
if (!relay) return;
m_relayDevices.remove(relay);
delete relay;
}
}
void DevicePluginUsbRelay::discoverDevices(DeviceDiscoveryInfo *info)
{
// Init
if (hid_init() < 0) {
qCWarning(dcUsbRelay()) << "Could not initialize HID.";
info->finish(Device::DeviceErrorHardwareFailure, QT_TR_NOOP("Cannot discover USB devices. HID initialisation failed on this system."));
return;
}
// Enumerate hid devices
struct hid_device_info *devices = nullptr;
struct hid_device_info *currentDevice = nullptr;
devices = hid_enumerate(0x16c0, 0x05df);
int relayCount = 0;
currentDevice = devices;
for (relayCount = 0; currentDevice != nullptr; relayCount++) {
currentDevice = currentDevice->next;
}
qCDebug(dcUsbRelay()) << "Found" << relayCount << "usb relay devices";
currentDevice = devices;
for (int i = 0; i < relayCount; i++) {
QString path = QString::fromLatin1(currentDevice->path);
QString manufacturer = QString::fromWCharArray(currentDevice->manufacturer_string);
QString product = QString::fromWCharArray(currentDevice->product_string);
QString serialnumber = QString::fromWCharArray(currentDevice->serial_number);
quint16 releaseNumber = static_cast<quint16>(currentDevice->release_number);
quint16 productId = static_cast<quint16>(currentDevice->product_id);
quint16 vendorId = static_cast<quint16>(currentDevice->vendor_id);
int relayCount = QString(product.at(product.count() -1)).toInt();
qCDebug(dcUsbRelay()) << path << manufacturer << product << serialnumber << "Relay count" << relayCount << QString("%1:%2").arg(QString::number(vendorId, 16)).arg(QString::number(productId, 16)) << releaseNumber;
// Open it to get more details
hid_device *hidDevice = nullptr;
hidDevice = hid_open_path(currentDevice->path);
if (!hidDevice) {
qCWarning(dcUsbRelay()) << "Could not create HID device for" << path;
continue;
}
unsigned char buf[9];
buf[0] = 0x01;
int ret = hid_get_feature_report(hidDevice, buf, sizeof(buf));
if (ret < 0) {
qCWarning(dcUsbRelay()) << "Could not create HID device hidDevice for" << path;
continue;
}
quint8 relayStatus = static_cast<quint8>(buf[7]);
for (int i = 0; i < relayCount; i++) {
int relayNumber = i + 1;
bool power = static_cast<bool>(relayStatus & 1 << i);
qCDebug(dcUsbRelay()) << "--> Relay" << relayNumber << ":" << power;
}
hid_close(hidDevice);
DeviceDescriptor descriptor(usbRelayConnectorDeviceClassId, "USB Relay Connector", QString("%1 (%2)").arg(product).arg(path));
ParamList params;
params.append(Param(usbRelayConnectorDevicePathParamTypeId, path));
params.append(Param(usbRelayConnectorDeviceRelayCountParamTypeId, relayCount));
descriptor.setParams(params);
// Set the current device id if we already have a device on this path
foreach (Device *existingDevice, myDevices()) {
if (existingDevice->paramValue(usbRelayConnectorDevicePathParamTypeId).toString() == path &&
existingDevice->paramValue(usbRelayConnectorDeviceRelayCountParamTypeId).toInt() == relayCount) {
descriptor.setDeviceId(existingDevice->id());
break;
}
}
info->addDeviceDescriptor(descriptor);
}
hid_free_enumeration(devices);
hid_exit();
info->finish(Device::DeviceErrorNoError);
}
void DevicePluginUsbRelay::setupDevice(DeviceSetupInfo *info)
{
qCDebug(dcUsbRelay()) << "Setup device" << info->device();
// Relay connector
if (info->device()->deviceClassId() == usbRelayConnectorDeviceClassId) {
Device *device = info->device();
QString path = device->paramValue(usbRelayConnectorDevicePathParamTypeId).toString();
int relayCount = device->paramValue(usbRelayConnectorDeviceRelayCountParamTypeId).toInt();
UsbRelay *relay = new UsbRelay(path, relayCount, this);
m_relayDevices.insert(relay, device);
connect(relay, &UsbRelay::connectedChanged, this, [this, device, relay](bool connected) {
qCDebug(dcUsbRelay()) << "Device" << device->name() << (connected ? "connected" : "disconnected");
device->setStateValue(usbRelayConnectorConnectedStateTypeId, connected);
// Set the connected state of all child devices
foreach (Device *childDevice, myDevices().filterByParentDeviceId(device->id())) {
if (childDevice->deviceClassId() == usbRelayDeviceClassId && childDevice->setupComplete()) {
childDevice->setStateValue(usbRelayConnectedStateTypeId, connected);
if (connected) {
childDevice->setStateValue(usbRelayPowerStateTypeId, relay->relayPower(childDevice->paramValue(usbRelayDeviceRelayNumberParamTypeId).toInt()));
}
}
}
});
connect(relay, &UsbRelay::relayPowerChanged, this, [this, device](int relayNumber, bool power) {
Device *relayDevice = getRelayDevice(device, relayNumber);
if (!relayDevice) {
// Note: probably not set up yet
qCWarning(dcUsbRelay()) << "Could not find USB relay child device for" << device << relayNumber;
return;
}
relayDevice->setStateValue(usbRelayPowerStateTypeId, power);
});
info->finish(Device::DeviceErrorNoError);
return;
}
// Relay
if (info->device()->deviceClassId() == usbRelayDeviceClassId) {
info->finish(Device::DeviceErrorNoError);
return;
}
info->finish(Device::DeviceErrorSetupFailed);
}
void DevicePluginUsbRelay::executeAction(DeviceActionInfo *info)
{
qCDebug(dcUsbRelay()) << "Executing action for device" << info->device() << info->action().actionTypeId().toString() << info->action().params();
if (info->device()->deviceClassId() == usbRelayDeviceClassId) {
Device *device = info->device();
UsbRelay *relay = getRelayForDevice(device);
if (!relay) {
qCWarning(dcUsbRelay()) << "Could execute action because could not find USB relay for" << device;
info->finish(Device::DeviceErrorHardwareNotAvailable);
return;
}
if (!relay->connected()) {
qCWarning(dcUsbRelay()) << "Relay is not connected";
info->finish(Device::DeviceErrorHardwareNotAvailable);
return;
}
int relayNumber = device->paramValue(usbRelayDeviceRelayNumberParamTypeId).toInt();
if (info->action().actionTypeId() == usbRelayPowerActionTypeId) {
bool power = info->action().param(usbRelayPowerActionPowerParamTypeId).value().toBool();
if (!relay->setRelayPower(relayNumber, power)) {
info->finish(Device::DeviceErrorHardwareFailure);
return;
}
info->finish(Device::DeviceErrorNoError);
return;
}
info->finish(Device::DeviceErrorActionTypeNotFound);
}
info->finish(Device::DeviceErrorDeviceClassNotFound);
}
Device *DevicePluginUsbRelay::getRelayDevice(Device *parentDevice, int relayNumber)
{
foreach (Device *childDevice, myDevices().filterByParentDeviceId(parentDevice->id())) {
if (childDevice->deviceClassId() == usbRelayDeviceClassId) {
if (childDevice->paramValue(usbRelayDeviceRelayNumberParamTypeId).toInt() == relayNumber) {
return childDevice;
}
}
}
return nullptr;
}
UsbRelay *DevicePluginUsbRelay::getRelayForDevice(Device *relayDevice)
{
Device *parentDevice = myDevices().findById(relayDevice->parentId());
if (!parentDevice) {
qCWarning(dcUsbRelay()) << "Could not find the parent device for" << relayDevice;
return nullptr;
}
UsbRelay *relay = m_relayDevices.key(parentDevice);
if (!relay) {
qCWarning(dcUsbRelay()) << "Could not find USB relay for" << relayDevice;
return nullptr;
}
return relay;
}

View File

@ -0,0 +1,58 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DEVICEPLUGINUSBRELAY_H
#define DEVICEPLUGINUSBRELAY_H
#include "usbrelay.h"
#include "devices/deviceplugin.h"
class DevicePluginUsbRelay: public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginusbrelay.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginUsbRelay();
void init() override;
void startMonitoringAutoDevices() override;
void postSetupDevice(Device *device) override;
void deviceRemoved(Device *device) override;
void discoverDevices(DeviceDiscoveryInfo *info) override;
void setupDevice(DeviceSetupInfo *info) override;
void executeAction(DeviceActionInfo *info) override;
private:
QHash<UsbRelay *, Device *> m_relayDevices;
Device *getRelayDevice(Device *parentDevice, int relayNumber);
UsbRelay *getRelayForDevice(Device *relayDevice);
private slots:
};
#endif // DEVICEPLUGINUSBRELAY_H

View File

@ -0,0 +1,88 @@
{
"name": "UsbRelay",
"displayName": "USB relay",
"id": "ed0035d3-561c-498e-bdb2-2b574cbd0a2f",
"vendors": [
{
"displayName": "USB Relay",
"name": "usbrelay",
"id": "d699b81a-621a-4db6-a137-6adbb5c9c091",
"deviceClasses": [
{
"name": "usbRelayConnector",
"displayName": "USB Relay Connector",
"id": "f48ff4db-d207-4221-963a-3fcb635102d8",
"setupMethod": "JustAdd",
"createMethods": ["Discovery"],
"interfaces": [ "gateway" ],
"paramTypes": [
{
"id": "a9fffbb9-f1c3-439c-9168-8c3fdfab29c8",
"name": "path",
"displayName": "System path",
"type": "QString",
"defaultValue": ""
},
{
"id": "406e1396-e3a3-4200-9045-2146381041b3",
"name": "relayCount",
"displayName": "Relay count",
"type": "uint",
"defaultValue": 2
}
],
"stateTypes": [
{
"id": "4cb1cf1f-76fd-4a9c-9397-b5f07b123050",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
}
]
},
{
"name": "usbRelay",
"displayName": "USB Relay",
"id": "6e01190d-ed9e-4dab-97d1-dc357c740db8",
"setupMethod": "JustAdd",
"createMethods": ["Auto"],
"interfaces": [ "power", "connectable" ],
"paramTypes": [
{
"id": "48d6e667-643f-4f9f-ae12-5308d23a36d7",
"name": "relayNumber",
"displayName": "Relay number",
"type": "uint",
"defaultValue": 1
}
],
"stateTypes": [
{
"id": "fd5338f8-f02b-4f99-8677-365cfc98221d",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
},
{
"id": "b22ec763-cb41-4d1b-846d-7b2cbe5e433d",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power",
"displayNameAction": "Set power",
"type": "bool",
"defaultValue": false,
"writable": true
}
]
}
]
}
]
}

View File

@ -0,0 +1,163 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "rawhiddevicewatcher.h"
#include "extern-plugininfo.h"
RawHidDeviceWatcher::RawHidDeviceWatcher(QObject *parent) : QObject(parent)
{
m_udev = udev_new();
if (!m_udev) {
qCWarning(dcUsbRelay()) << "Could not initialize udev";
return;
}
// Create udev monitor
m_monitor = udev_monitor_new_from_netlink(m_udev, "udev");
if (!m_monitor) {
qCWarning(dcUsbRelay()) << "Could not initialize udev monitor.";
udev_unref(m_udev);
m_udev = nullptr;
return;
}
// Set monitor filter to USB subsystem
if (udev_monitor_filter_add_match_subsystem_devtype(m_monitor, "hidraw", nullptr) < 0) {
qCWarning(dcUsbRelay()) << "Could not set seubsystem device type filter to usb_device.";
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(dcUsbRelay()) << "Could not enable udev monitor.";
udev_monitor_unref(m_monitor);
m_monitor = nullptr;
udev_unref(m_udev);
m_udev = nullptr;
return;
}
// Read initially all devices
struct udev_enumerate *enumerate;
struct udev_list_entry *devices, *dev_list_entry;
enumerate = udev_enumerate_new(m_udev);
if (!enumerate) {
qCWarning(dcUsbRelay()) << "Could not create udev enumerate for initial device reading.";
udev_monitor_unref(m_monitor);
m_monitor = nullptr;
udev_unref(m_udev);
m_udev = nullptr;
return;
}
udev_enumerate_add_match_subsystem(enumerate, "hidraw");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
if (!devices) {
qCWarning(dcUsbRelay()) << "Failed to get hidraw devices from udev enumerate.";
udev_monitor_unref(m_monitor);
m_monitor = nullptr;
udev_unref(m_udev);
m_udev = nullptr;
return;
}
udev_list_entry_foreach(dev_list_entry, devices) {
struct udev_device *device;
const char *path;
path = udev_list_entry_get_name(dev_list_entry);
device = udev_device_new_from_syspath(m_udev, path);
QString devicePath = QString::fromLatin1(udev_device_get_property_value(device,"DEVNAME"));
udev_device_unref(device);
qCDebug(dcUsbRelay()) << "[+]" << devicePath;
m_devicePaths.append(devicePath);
emit deviceAdded(devicePath);
}
udev_enumerate_unref(enumerate);
// Create socket notifier for read
m_notifier = new QSocketNotifier(udev_monitor_get_fd(m_monitor), QSocketNotifier::Read, this );
connect(m_notifier, &QSocketNotifier::activated, this, [this](int socket){
Q_UNUSED(socket)
// Create udev device
udev_device *device = udev_monitor_receive_device(m_monitor);
if (!device) {
qCWarning(dcUsbRelay()) << "Got socket sotification but could not read device information.";
return;
}
QString actionString = QString::fromLatin1(udev_device_get_action(device));
QString devicePath = QString::fromLatin1(udev_device_get_property_value(device,"DEVNAME"));
// Clean udev device
udev_device_unref(device);
if (actionString.isEmpty())
return;
if (actionString == "add") {
qCDebug(dcUsbRelay()) << "[+]" << devicePath;
if (!m_devicePaths.contains(devicePath)) {
m_devicePaths.append(devicePath);
emit deviceAdded(devicePath);
}
}
if (actionString == "remove") {
qCDebug(dcUsbRelay()) << "[-]" << devicePath;
if (m_devicePaths.contains(devicePath)) {
m_devicePaths.removeAll(devicePath);
emit deviceRemoved(devicePath);
}
}
});
m_notifier->setEnabled(true);
qCDebug(dcUsbRelay()) << "Usb device watcher initialized successfully.";
}
RawHidDeviceWatcher::~RawHidDeviceWatcher()
{
if (m_monitor)
udev_monitor_unref(m_monitor);
if (m_udev)
udev_unref(m_udev);
}
QStringList RawHidDeviceWatcher::devicePaths() const
{
return m_devicePaths;
}

View File

@ -0,0 +1,53 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef USBDEVICEWATCHER_H
#define USBDEVICEWATCHER_H
#include <QDebug>
#include <QObject>
#include <QSocketNotifier>
#include <libudev.h>
class RawHidDeviceWatcher : public QObject
{
Q_OBJECT
public:
explicit RawHidDeviceWatcher(QObject *parent = nullptr);
~RawHidDeviceWatcher();
QStringList devicePaths() const;
private:
struct udev *m_udev = nullptr;
struct udev_monitor *m_monitor = nullptr;
QSocketNotifier *m_notifier = nullptr;
QStringList m_devicePaths;
signals:
void deviceAdded(const QString &devicePath);
void deviceRemoved(const QString &devicePath);
};
#endif // USBDEVICEWATCHER_H

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>MaveoUsbRelay</name>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="39"/>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="42"/>
<source>Connected</source>
<extracomment>The name of the ParamType (DeviceClass: usbRelay, EventType: connected, ID: {ccb0ab2e-5507-4742-b2e0-0cd2aec95df6})
----------
The name of the StateType ({ccb0ab2e-5507-4742-b2e0-0cd2aec95df6}) of DeviceClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="45"/>
<source>Connected changed</source>
<extracomment>The name of the EventType ({ccb0ab2e-5507-4742-b2e0-0cd2aec95df6}) of DeviceClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="48"/>
<source>Impulse relay 1</source>
<extracomment>The name of the ActionType ({35590728-12ff-4afe-a6be-7ba69ce81b63}) of DeviceClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="51"/>
<source>Impulse relay 2</source>
<extracomment>The name of the ActionType ({63bfdae2-1c0f-4548-81cd-f64be5303f20}) of DeviceClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="54"/>
<source>Maveo USB Relay</source>
<extracomment>The name of the DeviceClass ({942a187f-bdec-4527-8696-45d4891e7d6b})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="57"/>
<source>Maveo USB relay</source>
<extracomment>The name of the plugin MaveoUsbRelay ({ddb4823d-0918-498e-87bc-6ae1a652538c})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="60"/>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="63"/>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="66"/>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="69"/>
<source>Power relay 1</source>
<extracomment>The name of the ParamType (DeviceClass: usbRelay, ActionType: powerOne, ID: {39d85190-739f-474f-b04c-e0627d681bb2})
----------
The name of the ParamType (DeviceClass: usbRelay, EventType: powerOne, ID: {39d85190-739f-474f-b04c-e0627d681bb2})
----------
The name of the EventType ({39d85190-739f-474f-b04c-e0627d681bb2}) of DeviceClass usbRelay
----------
The name of the StateType ({39d85190-739f-474f-b04c-e0627d681bb2}) of DeviceClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="72"/>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="75"/>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="78"/>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="81"/>
<source>Power relay 2</source>
<extracomment>The name of the ParamType (DeviceClass: usbRelay, ActionType: powerTwo, ID: {294376f7-7d21-4989-8803-ad5bffe08c48})
----------
The name of the ParamType (DeviceClass: usbRelay, EventType: powerTwo, ID: {294376f7-7d21-4989-8803-ad5bffe08c48})
----------
The name of the EventType ({294376f7-7d21-4989-8803-ad5bffe08c48}) of DeviceClass usbRelay
----------
The name of the StateType ({294376f7-7d21-4989-8803-ad5bffe08c48}) of DeviceClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="84"/>
<source>Set power relay 1</source>
<extracomment>The name of the ActionType ({39d85190-739f-474f-b04c-e0627d681bb2}) of DeviceClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="87"/>
<source>Set power relay 2</source>
<extracomment>The name of the ActionType ({294376f7-7d21-4989-8803-ad5bffe08c48}) of DeviceClass usbRelay</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="90"/>
<source>System path</source>
<extracomment>The name of the ParamType (DeviceClass: usbRelay, Type: device, ID: {a3644b93-1c37-472e-9303-a116feb33067})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-maveousbrelay-Desktop-Debug/plugininfo.h" line="93"/>
<source>maveo</source>
<extracomment>The name of the vendor ({e77e72ae-1228-42a3-85cd-34c6f3a8d494})</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

188
usbrelay/usbrelay.cpp Normal file
View File

@ -0,0 +1,188 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "usbrelay.h"
#include "extern-plugininfo.h"
UsbRelay::UsbRelay(const QString &path, int relayCount, QObject *parent) :
QObject(parent),
m_path(path),
m_relayCount(relayCount)
{
m_deviceWatcher = new RawHidDeviceWatcher(this);
connect(m_deviceWatcher, &RawHidDeviceWatcher::deviceAdded, this, &UsbRelay::onDeviceAdded);
connect(m_deviceWatcher, &RawHidDeviceWatcher::deviceRemoved, this, &UsbRelay::onDeviceRemoved);
if (m_deviceWatcher->devicePaths().contains(m_path)) {
setConnected(true);
}
// Create initial relay map
for (int i = 0; i < m_relayCount; i++) {
m_relayMap.insert(i + 1, false);
}
}
UsbRelay::~UsbRelay()
{
if (m_hidDevice)
hid_close(m_hidDevice);
hid_exit();
}
QString UsbRelay::path() const
{
return m_path;
}
int UsbRelay::relayCount() const
{
return m_relayCount;
}
bool UsbRelay::connected() const
{
return m_connected;
}
bool UsbRelay::relayPower(int relayNumber) const
{
return m_relayMap[relayNumber];
}
bool UsbRelay::setRelayPower(int relayNumber, bool power)
{
if (!m_connected) {
qCWarning(dcUsbRelay()) << "Could not set power of relay" << relayNumber << "because the device is not connected.";
return false;
}
if (relayNumber > m_relayCount) {
qCWarning(dcUsbRelay()) << "Could not set power of relay power because the relay number is invalid" << relayNumber << ">" << m_relayCount;
return false;
}
return switchRelay(relayNumber, power);
}
void UsbRelay::setConnected(bool connected)
{
if (m_connected == connected)
return;
qCDebug(dcUsbRelay()) << m_path << (connected ? "connected" : "disconnected");
if (connected) {
// Initialize the device
m_hidDevice = hid_open_path(m_path.toLatin1().data());
if (!m_hidDevice) {
qCWarning(dcUsbRelay()) << "Could nor open HID device for" << m_path;
m_connected = false;
emit connectedChanged(m_connected);
}
readStatus();
} else {
// Deinitialize
if (m_hidDevice) {
hid_close(m_hidDevice);
m_hidDevice = nullptr;
hid_exit();
}
}
m_connected = connected;
emit connectedChanged(m_connected);
}
void UsbRelay::setRelayPowerInternally(int relayNumber, bool power)
{
if (m_relayMap[relayNumber] == power)
return;
qCDebug(dcUsbRelay()) << "Relay power changed" << relayNumber << power;
m_relayMap[relayNumber] = power;
emit relayPowerChanged(relayNumber, power);
}
void UsbRelay::readStatus()
{
qCDebug(dcUsbRelay()) << "Read relay status of" << m_path;
unsigned char buf[9];
buf[0] = 0x01;
int ret = hid_get_feature_report(m_hidDevice, buf, sizeof(buf));
if (ret < 0) {
qCWarning(dcUsbRelay()) << "Could not create HID device for reading" << m_path;
hid_close(m_hidDevice);
m_hidDevice = nullptr;
m_connected = false;
emit connectedChanged(m_connected);
return;
}
quint8 relayStatus = static_cast<quint8>(buf[7]);
for (int i = 0; i < m_relayCount; i++) {
int relayNumber = i + 1;
bool power = static_cast<bool>(relayStatus & 1 << i);
qCDebug(dcUsbRelay()) << "--> Relay" << relayNumber << power;
setRelayPowerInternally(relayNumber, power);
}
}
bool UsbRelay::switchRelay(int relayNumber, bool power)
{
if (!m_hidDevice) {
qCWarning(dcUsbRelay()) << "Cannot switch power for" << m_path << "because there is no HID device.";
return false;
}
unsigned char buf[9]; // 1 extra byte for the report ID
memset(buf, 0x00, sizeof (buf));
buf[1] = power ? 0xff : 0xfd;
buf[2] = static_cast<unsigned char>(relayNumber); // Relay number
if (hid_write(m_hidDevice, buf, sizeof(buf)) <= 0) {
qCWarning(dcUsbRelay()) << "Cannot switch power for" << m_path << "because could not write to HID device.";
return false;
}
readStatus();
return true;
}
void UsbRelay::onDeviceAdded(const QString &devicePath)
{
if (devicePath == m_path) {
setConnected(true);
}
}
void UsbRelay::onDeviceRemoved(const QString &devicePath)
{
if (devicePath == m_path) {
setConnected(false);
}
}

77
usbrelay/usbrelay.h Normal file
View File

@ -0,0 +1,77 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* This file is part of nymea. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 library; If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef USBRELAY_H
#define USBRELAY_H
#include <QObject>
#include <hidapi/hidapi.h>
#include "rawhiddevicewatcher.h"
class UsbRelay : public QObject
{
Q_OBJECT
public:
explicit UsbRelay(const QString &path, int relayCount, QObject *parent = nullptr);
~UsbRelay();
QString path() const;
int relayCount() const;
bool connected() const;
bool relayPower(int relayNumber) const;
bool setRelayPower(int relayNumber, bool power);
private:
RawHidDeviceWatcher *m_deviceWatcher = nullptr;
hid_device *m_hidDevice = nullptr;
QString m_path;
int m_relayCount = 0;
bool m_connected = false;
bool m_relayOnePower = false;
bool m_relayTwoPower = false;
void setConnected(bool connected);
// Relay map <relay number>, bool value
QHash<int, bool> m_relayMap;
void setRelayPowerInternally(int relayNumber, bool power);
void readStatus();
bool switchRelay(int relayNumber, bool power);
signals:
void connectedChanged(bool connected);
void relayPowerChanged(int relayNumber, bool power);
private slots:
void onDeviceAdded(const QString &devicePath);
void onDeviceRemoved(const QString &devicePath);
};
#endif // USBRELAY_H

14
usbrelay/usbrelay.pro Normal file
View File

@ -0,0 +1,14 @@
include(../plugins.pri)
PKGCONFIG += hidapi-hidraw libudev
SOURCES += \
devicepluginusbrelay.cpp \
rawhiddevicewatcher.cpp \
usbrelay.cpp
HEADERS += \
devicepluginusbrelay.h \
rawhiddevicewatcher.h \
usbrelay.h