nymea-plugins/usbrelay/integrationpluginusbrelay.cpp

307 lines
12 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by
* copyright law, and remains the property of nymea GmbH. All rights, including
* reproduction, publication, editing and translation, are reserved. The use of
* this project is subject to the terms of a license agreement to be concluded
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
* under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; version 3. This project is distributed in the hope that
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this project. If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "plugininfo.h"
#include "integrationpluginusbrelay.h"
#include <QTimer>
#include <hidapi/hidapi.h>
IntegrationPluginUsbRelay::IntegrationPluginUsbRelay()
{
}
void IntegrationPluginUsbRelay::init()
{
// Initialize/create objects
}
void IntegrationPluginUsbRelay::startMonitoringAutoThings()
{
// Start seaching for devices which can be discovered and added automatically
}
void IntegrationPluginUsbRelay::postSetupThing(Thing *thing)
{
qCDebug(dcUsbRelay()) << "Post setup thing" << thing;
if (thing->thingClassId() == usbRelayConnectorThingClassId) {
// Initialize the states
UsbRelay *relay = m_relayDevices.key(thing);
if (!relay) {
qCWarning(dcUsbRelay()) << "Could not find relay in post setup.";
return;
}
thing->setStateValue(usbRelayConnectorConnectedStateTypeId, relay->connected());
// Check if we have to create child devices (relays)
if (myThings().filterByParentId(thing->id()).isEmpty()) {
ThingDescriptors descriptors;
for (int i = 0; i < relay->relayCount(); i++) {
int relayNumber = i + 1;
ThingDescriptor descriptor(usbRelayThingClassId, QString("Relay %1").arg(relayNumber), QString(), thing->id());
ParamList params;
params.append(Param(usbRelayThingRelayNumberParamTypeId, relayNumber));
descriptor.setParams(params);
descriptors.append(descriptor);
}
emit autoThingsAppeared(descriptors);
}
} else if (thing->thingClassId() == usbRelayThingClassId) {
UsbRelay *relay = getRelayForDevice(thing);
if (!relay) return;
// Set the current states
int relayNumber = thing->paramValue(usbRelayThingRelayNumberParamTypeId).toInt();
thing->setStateValue(usbRelayConnectedStateTypeId, relay->connected());
thing->setStateValue(usbRelayPowerStateTypeId, relay->relayPower(relayNumber));
}
}
void IntegrationPluginUsbRelay::thingRemoved(Thing *thing)
{
qCDebug(dcUsbRelay()) << "Remove thing" << thing;
if (thing->thingClassId() == usbRelayConnectorThingClassId) {
UsbRelay *relay = m_relayDevices.key(thing);
if (!relay) return;
m_relayDevices.remove(relay);
delete relay;
}
}
void IntegrationPluginUsbRelay::discoverThings(ThingDiscoveryInfo *info)
{
// Init
if (hid_init() < 0) {
qCWarning(dcUsbRelay()) << "Could not initialize HID.";
info->finish(Thing::ThingErrorHardwareFailure, 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 thing 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 thing 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);
ThingDescriptor descriptor(usbRelayConnectorThingClassId, "USB Relay Connector", QString("%1 (%2)").arg(product).arg(path));
ParamList params;
params.append(Param(usbRelayConnectorThingPathParamTypeId, path));
params.append(Param(usbRelayConnectorThingRelayCountParamTypeId, relayCount));
descriptor.setParams(params);
// Set the current thing id if we already have a thing on this path
foreach (Thing *existingThing, myThings()) {
if (existingThing->paramValue(usbRelayConnectorThingPathParamTypeId).toString() == path &&
existingThing->paramValue(usbRelayConnectorThingRelayCountParamTypeId).toInt() == relayCount) {
descriptor.setThingId(existingThing->id());
break;
}
}
info->addThingDescriptor(descriptor);
}
hid_free_enumeration(devices);
hid_exit();
info->finish(Thing::ThingErrorNoError);
}
void IntegrationPluginUsbRelay::setupThing(ThingSetupInfo *info)
{
qCDebug(dcUsbRelay()) << "Setup thing" << info->thing();
// Relay connector
if (info->thing()->thingClassId() == usbRelayConnectorThingClassId) {
Thing *thing = info->thing();
QString path = thing->paramValue(usbRelayConnectorThingPathParamTypeId).toString();
int relayCount = thing->paramValue(usbRelayConnectorThingRelayCountParamTypeId).toInt();
UsbRelay *relay = new UsbRelay(path, relayCount, this);
m_relayDevices.insert(relay, thing);
connect(relay, &UsbRelay::connectedChanged, this, [this, thing, relay](bool connected) {
qCDebug(dcUsbRelay()) << "Device" << thing->name() << (connected ? "connected" : "disconnected");
thing->setStateValue(usbRelayConnectorConnectedStateTypeId, connected);
// Set the connected state of all child devices
foreach (Thing *childDevice, myThings().filterByParentId(thing->id())) {
if (childDevice->thingClassId() == usbRelayThingClassId && childDevice->setupComplete()) {
childDevice->setStateValue(usbRelayConnectedStateTypeId, connected);
if (connected) {
childDevice->setStateValue(usbRelayPowerStateTypeId, relay->relayPower(childDevice->paramValue(usbRelayThingRelayNumberParamTypeId).toInt()));
}
}
}
});
connect(relay, &UsbRelay::relayPowerChanged, this, [this, thing](int relayNumber, bool power) {
Thing *relayDevice = getRelayDevice(thing, relayNumber);
if (!relayDevice) {
// Note: probably not set up yet
qCWarning(dcUsbRelay()) << "Could not find USB relay child thing for" << thing << relayNumber;
return;
}
relayDevice->setStateValue(usbRelayPowerStateTypeId, power);
});
info->finish(Thing::ThingErrorNoError);
return;
}
// Relay
if (info->thing()->thingClassId() == usbRelayThingClassId) {
info->finish(Thing::ThingErrorNoError);
return;
}
info->finish(Thing::ThingErrorSetupFailed);
}
void IntegrationPluginUsbRelay::executeAction(ThingActionInfo *info)
{
qCDebug(dcUsbRelay()) << "Executing action for thing" << info->thing() << info->action().actionTypeId().toString() << info->action().params();
if (info->thing()->thingClassId() == usbRelayThingClassId) {
Thing *thing = info->thing();
UsbRelay *relay = getRelayForDevice(thing);
if (!relay) {
qCWarning(dcUsbRelay()) << "Could execute action because could not find USB relay for" << thing;
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
if (!relay->connected()) {
qCWarning(dcUsbRelay()) << "Relay is not connected";
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
int relayNumber = thing->paramValue(usbRelayThingRelayNumberParamTypeId).toInt();
if (info->action().actionTypeId() == usbRelayPowerActionTypeId) {
bool power = info->action().param(usbRelayPowerActionPowerParamTypeId).value().toBool();
if (!relay->setRelayPower(relayNumber, power)) {
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
info->finish(Thing::ThingErrorNoError);
return;
}
info->finish(Thing::ThingErrorActionTypeNotFound);
}
info->finish(Thing::ThingErrorThingClassNotFound);
}
Thing *IntegrationPluginUsbRelay::getRelayDevice(Thing *parentDevice, int relayNumber)
{
foreach (Thing *childDevice, myThings().filterByParentId(parentDevice->id())) {
if (childDevice->thingClassId() == usbRelayThingClassId) {
if (childDevice->paramValue(usbRelayThingRelayNumberParamTypeId).toInt() == relayNumber) {
return childDevice;
}
}
}
return nullptr;
}
UsbRelay *IntegrationPluginUsbRelay::getRelayForDevice(Thing *relayDevice)
{
Thing *parentDevice = myThings().findById(relayDevice->parentId());
if (!parentDevice) {
qCWarning(dcUsbRelay()) << "Could not find the parent thing 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;
}