Add first version of knx support

This commit is contained in:
Simon Stürz 2019-03-25 08:39:59 +01:00
parent 12ea11f939
commit a6227fbdcd
10 changed files with 2335 additions and 0 deletions

1115
knx/devicepluginknx.cpp Normal file

File diff suppressed because it is too large Load Diff

78
knx/devicepluginknx.h Normal file
View File

@ -0,0 +1,78 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2019 Simon Stürz <simon.stuerz@guh.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 DEVICEPLUGINKNX_H
#define DEVICEPLUGINKNX_H
#include "plugintimer.h"
#include "devicemanager.h"
#include "plugin/deviceplugin.h"
#include "knxtunnel.h"
#include "knxserverdiscovery.h"
class DevicePluginKnx: public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginknx.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginKnx();
void init() override;
void startMonitoringAutoDevices() override;
DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList &params) override;
DeviceManager::DeviceSetupStatus setupDevice(Device *device) override;
void postSetupDevice(Device *device) override;
void deviceRemoved(Device *device) override;
DeviceManager::DeviceError executeAction(Device *device, const Action &action) override;
private:
PluginTimer *m_pluginTimer = nullptr;
KnxServerDiscovery *m_discovery = nullptr;
QHash<KnxTunnel *, Device *> m_tunnels;
KnxTunnel *getTunnelForDevice(Device *device);
void autoCreateKnownDevices(Device *parentDevice);
void createGenericDevices(Device *parentDevice);
void destroyGenericDevices(Device *parentDevice);
private slots:
void onPluginTimerTimeout();
void onPluginConfigurationChanged(const ParamTypeId &paramTypeId, const QVariant &value);
void onDiscoveryFinished();
void onTunnelConnectedChanged();
void onTunnelFrameReceived(const QKnxLinkLayerFrame &frame);
};
#endif // DEVICEPLUGINKNX_H

549
knx/devicepluginknx.json Normal file
View File

@ -0,0 +1,549 @@
{
"name": "Knx",
"displayName": "Knx",
"id": "cadef44b-223b-405f-a240-3e08fb9540ac",
"paramTypes": [
{
"id": "42f72457-f9d1-4166-95ea-81b8dec768c6",
"name": "genericDevicesEnabled",
"displayName": "Create generic devices for known DataPoints",
"type": "bool",
"defaultValue": true
}
],
"vendors": [
{
"name": "Knx",
"displayName": "Knx",
"id": "5971d18f-770c-4bc2-9025-8f2c70196f31",
"deviceClasses": [
{
"id": "1322570a-cc4d-417c-90da-f24ccf59ebdb",
"name": "knxNetIpServer",
"displayName": "KNX NetIp server",
"setupMethod": "JustAdd",
"createMethods": ["user", "discovery"],
"interfaces": [ "gateway" ],
"paramTypes": [
{
"id": "6eec90a3-936c-4885-830e-a2500426ed64",
"name": "address",
"displayName": "Server address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": "192.168.8.140"
},
{
"id": "fab7c5dd-c5cf-445f-833b-cf3379672b14",
"name": "port",
"displayName": "Server port",
"type": "uint",
"defaultValue": 3671
},
{
"id": "a75955c9-edce-46b2-a95a-1c1ab88007f7",
"name": "knxProjectFile",
"displayName": "Knx project file path",
"type": "QString",
"defaultValue": "/etc/nymea/installation.knxproj"
}
],
"stateTypes": [
{
"id": "aa75257f-45cc-440a-990d-6f3a8f696883",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
}
],
"actionTypes": [
{
"id": "d0845f3d-fe84-4eb8-a11f-fe48a6f7cae7",
"name": "autoCreateDevices",
"displayName": "Auto create devices from project file"
}
]
},
{
"id": "943e1329-cf5f-4759-94b3-141b6d3ce0ba",
"name": "knxGenericSwitch",
"displayName": "Knx generic switch",
"setupMethod": "JustAdd",
"createMethods": ["auto"],
"interfaces": [ "power", "connectable" ],
"paramTypes": [
{
"id": "dc38bb80-3d84-4f40-8939-4fd1e8573f80",
"name": "knxName",
"displayName": "Knx name",
"type": "QString",
"defaultValue": "Internal name"
},
{
"id": "69908c22-7fe1-4573-b3a2-32bf214d38c6",
"name": "knxAddress",
"displayName": "Knx address",
"type": "QString",
"defaultValue": "0/0/1"
}
],
"stateTypes": [
{
"id": "5453d80d-6a1e-4fe1-b9e0-bc9177ccef55",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
},
{
"id": "dc82f8ca-6bb8-4fc6-b46c-2d6a6e6e6d2f",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"displayNameAction": "Set power",
"type": "bool",
"defaultValue": false,
"writable": true
}
],
"actionTypes": [
{
"id": "7ad6cea9-993c-4e09-aeee-348948425bc6",
"name": "read",
"displayName": "Read value"
}
]
},
{
"id": "7fd223fb-002f-4ab7-b77e-1073576f9241",
"name": "knxGenericUpDown",
"displayName": "Knx generic UpDown",
"setupMethod": "JustAdd",
"createMethods": ["auto"],
"interfaces": [ "simpleclosable", "connectable" ],
"paramTypes": [
{
"id": "9c4c0a21-5a7b-4ab1-a096-6adea3d4a277",
"name": "knxName",
"displayName": "Knx name",
"type": "QString",
"defaultValue": "Internal name"
},
{
"id": "c81816fa-0394-473c-b0ca-5e74965d1f43",
"name": "knxAddress",
"displayName": "Knx address",
"type": "QString",
"defaultValue": "0/0/1"
}
],
"stateTypes": [
{
"id": "f9403240-2425-49b1-bd9d-8b0f1c872d8b",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
}
],
"actionTypes": [
{
"id": "5f1d6f15-6874-485e-a0ca-7bbd397ea25f",
"name": "open",
"displayName": "Up"
},
{
"id": "f3c0f798-ccaa-4943-975d-736dc09f5850",
"name": "close",
"displayName": "Down"
}
]
},
{
"id": "9b24c5ad-a36f-42d0-8489-275935149894",
"name": "knxGenericScaling",
"displayName": "Knx generic scale",
"setupMethod": "JustAdd",
"createMethods": ["auto"],
"interfaces": [ "connectable" ],
"paramTypes": [
{
"id": "2d984fc0-daa5-45d4-aee0-e46a74d5d905",
"name": "knxName",
"displayName": "Knx name",
"type": "QString",
"defaultValue": "Internal name"
},
{
"id": "d3558e8d-3f33-4276-977e-a02d23d0d985",
"name": "knxAddress",
"displayName": "Knx address",
"type": "QString",
"defaultValue": "0/0/1"
}
],
"stateTypes": [
{
"id": "8b912679-d42c-48cc-834d-cdea68f44e1e",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
},
{
"id": "aa8f8366-b65a-45e3-a10c-9f31b8f6d189",
"name": "scale",
"displayName": "Scale",
"displayNameEvent": "Scale changed",
"displayNameAction": "Set scale",
"minValue": 0,
"maxValue": 100,
"defaultValue": 0,
"type": "int",
"writable": true
}
]
},
{
"id": "1bfa8fbd-0845-4e85-9c50-cacf22f71363",
"name": "knxGenericTemperatureSensor",
"displayName": "Knx generic temperature sensor",
"setupMethod": "JustAdd",
"createMethods": ["auto"],
"interfaces": [ "temperaturesensor", "connectable" ],
"paramTypes": [
{
"id": "51ed9d20-b716-434f-ac00-60f10580fd9e",
"name": "knxName",
"displayName": "Knx name",
"type": "QString",
"defaultValue": "Internal name"
},
{
"id": "fa55f9de-229c-46ce-aeff-99c3a8c5dad9",
"name": "knxAddress",
"displayName": "Knx address",
"type": "QString",
"defaultValue": "0/0/1"
}
],
"stateTypes": [
{
"id": "8c9c94e8-90c6-471d-8b5a-63728776223e",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
},
{
"id": "d6a04ed7-df21-4bf4-8946-76a2802d7172",
"name": "temperature",
"displayName": "Temperature",
"displayNameEvent": "Temperature changed",
"defaultValue": 0,
"unit": "DegreeCelsius",
"type": "double"
}
],
"actionTypes": [
{
"id": "07b829fb-31b5-4219-8f64-1a11e0176282",
"name": "read",
"displayName": "Read value"
}
]
},
{
"id": "3d53cf0b-75b3-44a5-b0ec-36b054d9faa9",
"name": "knxGenericLightSensor",
"displayName": "Knx generic light sensor",
"setupMethod": "JustAdd",
"createMethods": ["auto"],
"interfaces": [ "lightsensor", "connectable" ],
"paramTypes": [
{
"id": "4f8419ee-d753-4894-b9b4-b1c3a3bc326a",
"name": "knxName",
"displayName": "Knx name",
"type": "QString",
"defaultValue": "Internal name"
},
{
"id": "0c09a401-fb87-4855-925e-515fb84e06af",
"name": "knxAddress",
"displayName": "Knx address",
"type": "QString",
"defaultValue": "0/0/1"
}
],
"stateTypes": [
{
"id": "4308c130-0c6f-4b7d-84da-766a1724c2cd",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
},
{
"id": "8bfb2a30-c49f-4a0b-aae1-65b04e0f02d7",
"name": "lightIntensity",
"displayName": "Temperature",
"displayNameEvent": "Temperature changed",
"defaultValue": 0,
"unit": "Lux",
"type": "double"
}
],
"actionTypes": [
{
"id": "96c531b9-9bd8-4e61-8e26-019381f3063d",
"name": "read",
"displayName": "Read value"
}
]
},
{
"id": "c0e8778a-040a-472c-9eca-d9e21d4e10e6",
"name": "knxGenericWindSpeedSensor",
"displayName": "Knx generic wind speed sensor",
"setupMethod": "JustAdd",
"createMethods": ["auto"],
"interfaces": [ "windspeedsensor", "connectable" ],
"paramTypes": [
{
"id": "8579633a-834f-4741-8105-bc3db55814b1",
"name": "knxName",
"displayName": "Knx name",
"type": "QString",
"defaultValue": "Internal name"
},
{
"id": "db454198-a24d-463d-81c4-6260fe93f3bf",
"name": "knxAddress",
"displayName": "Knx address",
"type": "QString",
"defaultValue": "0/0/1"
}
],
"stateTypes": [
{
"id": "20cbdab2-0746-4d11-be46-fb1621dfd5ec",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
},
{
"id": "8c4ab56f-7d6f-4ed0-b602-02591cd55583",
"name": "windSpeed",
"displayName": "Wind speed",
"displayNameEvent": "Wind speed changed",
"defaultValue": 0,
"unit": "MeterPerSecond",
"type": "double"
}
],
"actionTypes": [
{
"id": "3d113a52-4514-41d9-9b15-53b361a36f84",
"name": "read",
"displayName": "Read value"
}
]
},
{
"id": "fef0f771-cd90-4f27-8538-59c4eb5acb35",
"name": "knxShutter",
"displayName": "Knx shutter",
"setupMethod": "JustAdd",
"createMethods": ["user", "auto"],
"interfaces": [ "closable", "connectable" ],
"paramTypes": [
{
"id": "6ce97eeb-37d9-4980-963d-f3ca4084e189",
"name": "address",
"displayName": "Server address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": "192.168.8.140"
},
{
"id": "881461a9-e38a-4305-bad4-9770c204db54",
"name": "knxAddressUpDown",
"displayName": "Knx address UpDown",
"type": "QString",
"defaultValue": "1/0/0"
},
{
"id": "94991675-3b8d-438e-acfa-bb8694b4ccdb",
"name": "knxAddressStep",
"displayName": "Knx address step",
"type": "QString",
"defaultValue": "1/1/0"
}
],
"stateTypes": [
{
"id": "0a2f783b-3f2c-45d2-8b04-7e317127c65b",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
}
],
"actionTypes": [
{
"id": "f46af15e-5f9b-4d34-bacc-6e0f220caf5d",
"name": "open",
"displayName": "Open"
},
{
"id": "178021d3-3916-4cba-812d-a010953d6311",
"name": "close",
"displayName": "Close"
},
{
"id": "3f133a78-c75e-48a6-990e-d2ace96ffdae",
"name": "stop",
"displayName": "Stop"
}
]
},
{
"id": "34582d5d-8755-41f3-9eb5-43898466d86f",
"name": "knxLight",
"displayName": "Knx light",
"setupMethod": "JustAdd",
"createMethods": ["user"],
"interfaces": [ "light", "connectable" ],
"paramTypes": [
{
"id": "c1204f72-f0a1-42cd-a579-596bae4d0de7",
"name": "address",
"displayName": "Server address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": "192.168.8.140"
},
{
"id": "97abac83-54ca-477f-b1c7-6f5ebaa79102",
"name": "knxAddress",
"displayName": "Knx address",
"type": "QString",
"defaultValue": "1/0/0"
}
],
"stateTypes": [
{
"id": "1a39109f-d1d4-45e8-832a-d94b9bc90c1b",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
},
{
"id": "b6fcc7fc-345c-40cf-88ab-698391e584d9",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"displayNameAction": "Set power",
"type": "bool",
"defaultValue": false,
"writable": true
}
],
"actionTypes": [
{
"id": "7510dada-5be0-4c66-baa3-4d03cf13289e",
"name": "read",
"displayName": "Read value"
}
]
},
{
"id": "9debbc19-538b-4ad2-9831-8bc93649cdbd",
"name": "knxDimmableLight",
"displayName": "Knx dimmable light",
"setupMethod": "JustAdd",
"createMethods": ["user", "auto"],
"interfaces": [ "dimmablelight", "connectable" ],
"paramTypes": [
{
"id": "c3176e2e-aeb8-40fa-a555-ae1620af7cc0",
"name": "address",
"displayName": "Server address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": "192.168.8.140"
},
{
"id": "bf149d09-8014-4572-8170-28b48bb67c0c",
"name": "knxSwitchAddress",
"displayName": "Knx switch address",
"type": "QString",
"defaultValue": "1/0/0"
},
{
"id": "c8728c5b-ac6e-4545-8ed7-bdd347757b12",
"name": "knxScalingAddress",
"displayName": "Knx scaling address",
"type": "QString",
"defaultValue": "1/0/0"
}
],
"stateTypes": [
{
"id": "f73c112c-2a22-4d6a-8f94-13a9aa4f3abf",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false
},
{
"id": "b5dbb059-f408-468c-85e7-c052622d9a59",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"displayNameAction": "Set power",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"id": "ce8fc00a-e8cb-4517-94a8-6e2b2ae71bf0",
"name": "brightness",
"displayName": "Brightness",
"displayNameEvent": "Brightness changed",
"displayNameAction": "Set brightness",
"minValue": 0,
"maxValue": 100,
"defaultValue": 0,
"type": "int",
"writable": true
}
],
"actionTypes": [
{
"id": "fa83f289-7bd2-4595-b4e4-52634f5098a1",
"name": "read",
"displayName": "Read value"
}
]
}
]
}
]
}

Binary file not shown.

22
knx/knx.pro Normal file
View File

@ -0,0 +1,22 @@
# Note: make external qtknx linking
exists(/opt/qtknx/build/mkspecs/modules/qt_lib_knx.pri) {
include(/opt/qtknx/build/mkspecs/modules/qt_lib_knx.pri)
}
include(../plugins.pri)
# Note: requires qtknx
QT += network knx
TARGET = $$qtLibraryTarget(nymea_devicepluginknx)
SOURCES += \
devicepluginknx.cpp \
knxtunnel.cpp \
knxserverdiscovery.cpp
HEADERS += \
devicepluginknx.h \
knxtunnel.h \
knxserverdiscovery.h

134
knx/knxserverdiscovery.cpp Normal file
View File

@ -0,0 +1,134 @@
#include "knxserverdiscovery.h"
#include "extern-plugininfo.h"
#include <QNetworkInterface>
#include <QKnxNetIpExtendedDeviceDibProxy>
KnxServerDiscovery::KnxServerDiscovery(QObject *parent) :
QObject(parent)
{
}
QList<QKnxNetIpServerInfo> KnxServerDiscovery::discoveredServers() const
{
return m_discoveredServers;
}
QString KnxServerDiscovery::serviceFamilyToString(QKnx::NetIp::ServiceFamily id)
{
switch (id) {
case QKnxNetIp::ServiceFamily::Core:
return "Core";
case QKnxNetIp::ServiceFamily::DeviceManagement:
return "Device Management";
case QKnxNetIp::ServiceFamily::IpTunneling:
return "Tunnel";
case QKnxNetIp::ServiceFamily::IpRouting:
return "Routing";
case QKnxNetIp::ServiceFamily::RemoteLogging:
return "Remote Logging";
case QKnxNetIp::ServiceFamily::RemoteConfigAndDiagnosis:
return "Remote Configuration";
case QKnxNetIp::ServiceFamily::ObjectServer:
return "Object Server";
case QKnxNetIp::ServiceFamily::Security:
return "Security";
default:
break;
}
return "Unknown";
}
void KnxServerDiscovery::printServerInfo(const QKnxNetIpServerInfo &serverInfo)
{
qCDebug(dcKnx()) << "Server:" << serverInfo.deviceName();
qCDebug(dcKnx()) << " Individual address:" << serverInfo.individualAddress().toString();
qCDebug(dcKnx()) << " Server control endpoint:" << QString("%1:%2").arg(serverInfo.controlEndpointAddress().toString()).arg(serverInfo.controlEndpointPort());
QVector<QKnxServiceInfo> services = serverInfo.supportedServices();
qCDebug(dcKnx()) << " Supported services:";
foreach (const QKnxServiceInfo &service, services)
qCDebug(dcKnx()) << " KNXnet/IP" << serviceFamilyToString(service.ServiceFamily) << ", Version:" << service.ServiceFamilyVersion;
qCDebug(dcKnx()) << " Mask version:" << serverInfo.maskVersion();
qCDebug(dcKnx()) << " Endpoint:" << serverInfo.endpoint();
QKnxNetIpDib dib = serverInfo.hardware();
QKnxNetIpExtendedDeviceDibProxy hardware(dib);
if (hardware.isValid()) {
qCDebug(dcKnx()) << " Extended hardware information:";
qCDebug(dcKnx()).noquote() << QString::fromLatin1(" Mask version: %1").arg(hardware.deviceDescriptorType0(), 4, 16, QLatin1Char('0'));
qCDebug(dcKnx()).noquote() << QString::fromLatin1(" Max. local APDU length: %1").arg(hardware.maximumLocalApduLength());
auto status = serverInfo.mediumStatus();
if (status == QKnx::MediumStatus::CommunicationPossible)
qCDebug(dcKnx()) << " Medium status: Communication possible";
else if (status == QKnx::MediumStatus::CommunicationImpossible)
qCDebug(dcKnx()) << " Medium status: Communication impossible";
else
qCDebug(dcKnx()) << " Medium status: Unknown";
}
}
void KnxServerDiscovery::onDiscoveryAgentErrorOccured(QKnxNetIpServerDiscoveryAgent::Error error)
{
QKnxNetIpServerDiscoveryAgent *discovery = static_cast<QKnxNetIpServerDiscoveryAgent *>(sender());
qCDebug(dcKnx()) << "Discovery error occured" << discovery->localAddress().toString() << error << discovery->errorString();
}
void KnxServerDiscovery::onDiscoveryAgentFinished()
{
QKnxNetIpServerDiscoveryAgent *discovery = static_cast<QKnxNetIpServerDiscoveryAgent *>(sender());
qCDebug(dcKnx()) << "Discovery agent for" << discovery->localAddress() << "has finished";
qCDebug(dcKnx()) << "Found" << discovery->discoveredServers().count() << "servers";
foreach (const QKnxNetIpServerInfo &serverInfo, discovery->discoveredServers()) {
m_discoveredServers.append(serverInfo);
}
m_runningDiscoveryAgents.removeAll(discovery);
discovery->deleteLater();
if (m_runningDiscoveryAgents.isEmpty()) {
emit discoveryFinished();
}
}
bool KnxServerDiscovery::startDisovery()
{
if (!m_runningDiscoveryAgents.isEmpty()) {
qCWarning(dcKnx()) << "Could not start discovery. There are still discovery agents running. (" << m_runningDiscoveryAgents.count() << "discoveries )";
return false;
}
qCDebug(dcKnx()) << "Start KNX server discovery on all interfaces";
m_discoveredServers.clear();
foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
//qCDebug(dcKnx()) << "Checking network interface" << interface.name() << interface.type();
foreach (const QNetworkAddressEntry &addressEntry, interface.addressEntries()) {
// If ipv4 and not local host
if (addressEntry.ip().protocol() == QAbstractSocket::IPv4Protocol && !addressEntry.ip().isLoopback()) {
qCDebug(dcKnx()) << "Start discovery on" << interface.name() << addressEntry.ip().toString();
// Create discovery agent
QKnxNetIpServerDiscoveryAgent *discovery = new QKnxNetIpServerDiscoveryAgent(this);
discovery->setLocalAddress(addressEntry.ip());
discovery->setLocalPort(0);
discovery->setSearchFrequency(60);
discovery->setResponseType(QKnxNetIpServerDiscoveryAgent::ResponseType::Unicast);
discovery->setDiscoveryMode(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV1 | QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2);
connect(discovery, &QKnxNetIpServerDiscoveryAgent::finished, this, &KnxServerDiscovery::onDiscoveryAgentFinished);
connect(discovery, &QKnxNetIpServerDiscoveryAgent::errorOccurred, this, &KnxServerDiscovery::onDiscoveryAgentErrorOccured);
m_runningDiscoveryAgents.append(discovery);
// Start the discovery
discovery->start(m_discoveryTimeout);
}
}
}
return true;
}

38
knx/knxserverdiscovery.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef KNXSERVERDISCOVERY_H
#define KNXSERVERDISCOVERY_H
#include <QObject>
#include <QHostAddress>
#include <QKnxNetIpServerDiscoveryAgent>
#include <QKnxNetIpServerDescriptionAgent>
class KnxServerDiscovery : public QObject
{
Q_OBJECT
public:
explicit KnxServerDiscovery(QObject *parent = nullptr);
QList<QKnxNetIpServerInfo> discoveredServers() const;
static QString serviceFamilyToString(QKnx::NetIp::ServiceFamily id);
static void printServerInfo(const QKnxNetIpServerInfo &serverInfo);
private:
int m_discoveryTimeout = 5000;
QList<QKnxNetIpServerDiscoveryAgent *> m_runningDiscoveryAgents;
QList<QKnxNetIpServerInfo> m_discoveredServers;
signals:
void discoveryFinished();
private slots:
void onDiscoveryAgentErrorOccured(QKnxNetIpServerDiscoveryAgent::Error error);
void onDiscoveryAgentFinished();
public slots:
bool startDisovery();
};
#endif // KNXSERVERDISCOVERY_H

330
knx/knxtunnel.cpp Normal file
View File

@ -0,0 +1,330 @@
#include "knxtunnel.h"
#include "extern-plugininfo.h"
#include <QDateTime>
#include <QNetworkInterface>
#include <QKnxTpdu>
#include <QKnx1Bit>
#include <QKnxDatapointType>
#include <QKnxLinkLayerFrame>
#include <QKnx8BitUnsignedValue>
#include <QKnxLinkLayerFrameBuilder>
KnxTunnel::KnxTunnel(const QHostAddress &remoteAddress, QObject *parent) :
QObject(parent),
m_remoteAddress(remoteAddress)
{
m_timer = new QTimer(this);
m_timer->setSingleShot(false);
m_timer->setInterval(5000);
connect(m_timer, &QTimer::timeout, this, &KnxTunnel::onTimeout);
m_queueTimer = new QTimer(this);
m_queueTimer->setSingleShot(false);
m_queueTimer->setInterval(500);
connect(m_queueTimer, &QTimer::timeout, this, &KnxTunnel::onQueueTimeout);
m_tunnel = new QKnxNetIpTunnel(this);
m_tunnel->setLocalPort(0);
m_tunnel->setHeartbeatTimeout(1000);
connect(m_tunnel, &QKnxNetIpTunnel::frameReceived, this, &KnxTunnel::onTunnelFrameReceived);
connect(m_tunnel, &QKnxNetIpTunnel::connected, this, &KnxTunnel::onTunnelConnected);
connect(m_tunnel, &QKnxNetIpTunnel::disconnected, this, &KnxTunnel::onTunnelDisconnected);
connect(m_tunnel, &QKnxNetIpTunnel::stateChanged, this, &KnxTunnel::onTunnelStateChanged);
}
QHostAddress KnxTunnel::remoteAddress() const
{
return m_remoteAddress;
}
void KnxTunnel::setRemoteAddress(const QHostAddress &remoteAddress)
{
m_remoteAddress = remoteAddress;
}
bool KnxTunnel::connected() const
{
return m_tunnel->state() == QKnxNetIpTunnel::State::Connected;
}
bool KnxTunnel::connectTunnel()
{
// Get the local address for this tunnel remote address
QHostAddress localAddress = getLocalAddress(m_remoteAddress);
if (localAddress.isNull()) {
qCWarning(dcKnx()) << "Could connect to" << m_remoteAddress.toString() << ". There is no local interface for this server address. Make sure this device is connected to the correct network.";
return false;
}
m_tunnel->setLocalAddress(localAddress);
qCDebug(dcKnx()) << "Connecting tunnel to" << m_remoteAddress.toString() << "using" << m_tunnel->localAddress();
m_tunnel->connectToHost(m_remoteAddress, m_port);
return true;
}
void KnxTunnel::disconnectTunnel()
{
qCDebug(dcKnx()) << "Disonnecting tunnel from" << m_remoteAddress.toString();
m_tunnel->disconnectFromHost();
}
void KnxTunnel::sendKnxDpdSwitchFrame(const QKnxAddress &knxAddress, bool power)
{
qCDebug(dcKnx()) << "Send DpdSwitch" << knxAddress.toString() << (power ? "ON" : "OFF");
QKnxTpdu tpdu;
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueWrite);
tpdu.setData(QKnxSwitch(power ? QKnxSwitch::State::On : QKnxSwitch::State::Off).bytes());
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
.setMedium(QKnx::MediumType::NetIP)
.setDestinationAddress(knxAddress)
.setTpdu(tpdu)
.createFrame();
requestSendFrame(frame);
}
void KnxTunnel::sendKnxDpdUpDownFrame(const QKnxAddress &knxAddress, bool status)
{
qCDebug(dcKnx()) << "Send DpdUpDown" << knxAddress.toString() << (status ? "UP" : "DOWN");
QKnxTpdu tpdu;
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueWrite);
tpdu.setData(QKnxUpDown(status ? QKnxUpDown::State::Up : QKnxUpDown::State::Down).bytes());
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
.setMedium(QKnx::MediumType::NetIP)
.setDestinationAddress(knxAddress)
.setTpdu(tpdu)
.createFrame();
requestSendFrame(frame);
}
void KnxTunnel::sendKnxDpdStepFrame(const QKnxAddress &knxAddress, bool status)
{
qCDebug(dcKnx()) << "Send DpdStep" << knxAddress.toString() << (status ? "INCREASE" : "DECREASE");
QKnxTpdu tpdu;
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueWrite);
tpdu.setData(QKnxStep(status ? QKnxStep::State::Increase : QKnxStep::State::Decrease).bytes());
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
.setMedium(QKnx::MediumType::NetIP)
.setDestinationAddress(knxAddress)
.setTpdu(tpdu)
.createFrame();
requestSendFrame(frame);
}
void KnxTunnel::sendKnxDpdScalingFrame(const QKnxAddress &knxAddress, int scale)
{
qCDebug(dcKnx()) << "Send DpdScaling" << knxAddress.toString() << scale;
QKnxTpdu tpdu;
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueWrite);
tpdu.setData(QKnxScaling(scale).bytes());
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
.setMedium(QKnx::MediumType::NetIP)
.setDestinationAddress(knxAddress)
.setTpdu(tpdu)
.createFrame();
requestSendFrame(frame);
}
void KnxTunnel::readKnxGroupValue(const QKnxAddress &knxAddress)
{
qCDebug(dcKnx()) << "Read knx group value from group address" << knxAddress.toString();
QKnxTpdu tpdu;
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueRead);
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
.setMessageCode(QKnxLinkLayerFrame::MessageCode::DataRequest)
.setMedium(QKnx::MediumType::NetIP)
.setDestinationAddress(knxAddress)
.setTpdu(tpdu)
.createFrame();
requestSendFrame(frame);
}
void KnxTunnel::readKnxDpdSwitchState(const QKnxAddress &knxAddress)
{
qCDebug(dcKnx()) << "Read switch group value" << knxAddress.toString();
QKnxTpdu tpdu;
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueRead);
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
.setMessageCode(QKnxLinkLayerFrame::MessageCode::DataRequest)
.setMedium(QKnx::MediumType::NetIP)
.setDestinationAddress(knxAddress)
.setTpdu(tpdu)
.createFrame();
requestSendFrame(frame);
}
void KnxTunnel::readKnxDpdScalingState(const QKnxAddress &knxAddress)
{
qCDebug(dcKnx()) << "Read scaling group value" << knxAddress.toString();
QKnxTpdu tpdu;
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueRead);
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
.setMessageCode(QKnxLinkLayerFrame::MessageCode::DataRequest)
.setMedium(QKnx::MediumType::NetIP)
.setDestinationAddress(knxAddress)
.setTpdu(tpdu)
.createFrame();
requestSendFrame(frame);
}
void KnxTunnel::readKnxDpdTemperatureSensor(const QKnxAddress &knxAddress)
{
qCDebug(dcKnx()) << "Read temperature sensor group value" << knxAddress.toString();
QKnxTpdu tpdu;
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueRead);
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
.setMessageCode(QKnxLinkLayerFrame::MessageCode::DataRequest)
.setMedium(QKnx::MediumType::NetIP)
.setDestinationAddress(knxAddress)
.setTpdu(tpdu)
.createFrame();
requestSendFrame(frame);
}
void KnxTunnel::printFrame(const QKnxLinkLayerFrame &frame)
{
qCDebug(dcKnx()) << "Frame: (" << frame.sourceAddress().toString() << "-->" << frame.destinationAddress().toString() << ")" << frame;
qCDebug(dcKnx()) << " Message code:" << frame.messageCode();
qCDebug(dcKnx()) << " MediumType" << frame.mediumType();
qCDebug(dcKnx()) << " Control field:" << frame.controlField();
qCDebug(dcKnx()) << " Extended control field:" << frame.extendedControlField();
qCDebug(dcKnx()) << " Additional infos:" << frame.additionalInfos();
qCDebug(dcKnx()) << " Bytes:" << frame.bytes().toHex().toByteArray();
qCDebug(dcKnx()) << " TPDU:" << frame.tpdu() << "Size:" << frame.tpdu().size();
qCDebug(dcKnx()) << " " << frame.tpdu().transportControlField();
qCDebug(dcKnx()) << " " << frame.tpdu().applicationControlField();
qCDebug(dcKnx()) << " " << frame.tpdu().mediumType();
qCDebug(dcKnx()) << " Sequence number:" << frame.tpdu().sequenceNumber();
qCDebug(dcKnx()) << " Data:" << frame.tpdu().data().toHex().toByteArray();
}
void KnxTunnel::requestSendFrame(const QKnxLinkLayerFrame &frame)
{
// If the timer is running, enqueue
if (m_queueTimer->isActive()) {
m_sendingQueue.enqueue(frame);
return;
}
// No queue, timer not running, lets send the message directly
sendFrame(frame);
m_queueTimer->start();
}
void KnxTunnel::sendFrame(const QKnxLinkLayerFrame &frame)
{
if (!connected()) {
qCWarning(dcKnx()) << "Cannot send frame on tunnel" << m_remoteAddress.toString() << " because the tunnel is not connected.";
return;
}
qCDebug(dcKnx()) << QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss") << "--> Sending frame" << frame << frame.destinationAddress().toString() << frame.tpdu().data().toHex().toByteArray();
//printFrame(frame);
m_tunnel->sendFrame(frame);
}
QHostAddress KnxTunnel::getLocalAddress(const QHostAddress &remoteAddress)
{
QHostAddress localAddress;
foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
qCDebug(dcKnx()) << "Network interface" << interface.name() << interface.type();
foreach (const QNetworkAddressEntry &addressEntry, interface.addressEntries()) {
if (addressEntry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
qCDebug(dcKnx()) << " - " << addressEntry.ip().toString() << addressEntry.netmask().toString();
if (remoteAddress.isInSubnet(addressEntry.ip(), addressEntry.prefixLength())) {
qCDebug(dcKnx()) << "Found local interface address for" << remoteAddress.toString() << "-->" << addressEntry.ip().toString() << interface.name();
localAddress = addressEntry.ip();
}
}
}
}
return localAddress;
}
void KnxTunnel::onTimeout()
{
qCDebug(dcKnx()) << "Tunnel reconnection timeout.";
connectTunnel();
}
void KnxTunnel::onQueueTimeout()
{
// If queue is empty, we are don for now.
if (m_sendingQueue.isEmpty()) {
m_queueTimer->stop();
return;
}
// Send next message
QKnxLinkLayerFrame frame = m_sendingQueue.dequeue();
sendFrame(frame);
m_queueTimer->start();
}
void KnxTunnel::onTunnelConnected()
{
qCDebug(dcKnx()) << "Tunnel connected.";
emit connectedChanged();
// Stop the reconnect timer
m_timer->stop();
}
void KnxTunnel::onTunnelDisconnected()
{
qCDebug(dcKnx()) << "Tunnel disconnected.";
emit connectedChanged();
// Start the reconnect timer
m_timer->start();
}
void KnxTunnel::onTunnelStateChanged(QKnxNetIpEndpointConnection::State state)
{
qCDebug(dcKnx()) << "Tunnel state changed" << state;
}
void KnxTunnel::onTunnelFrameReceived(const QKnxLinkLayerFrame &frame)
{
qCDebug(dcKnx()) << "<-- Tunnel frame received" << frame;
//printFrame(frame);
emit frameReceived(frame);
}

68
knx/knxtunnel.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef KNXTUNNEL_H
#define KNXTUNNEL_H
#include <QTimer>
#include <QObject>
#include <QQueue>
#include <QHostAddress>
#include <QKnxNetIpTunnel>
class KnxTunnel : public QObject
{
Q_OBJECT
public:
explicit KnxTunnel(const QHostAddress &remoteAddress, QObject *parent = nullptr);
QHostAddress remoteAddress() const;
void setRemoteAddress(const QHostAddress &remoteAddress);
bool connected() const;
bool connectTunnel();
void disconnectTunnel();
void sendKnxDpdSwitchFrame(const QKnxAddress &knxAddress, bool power);
void sendKnxDpdUpDownFrame(const QKnxAddress &knxAddress, bool status);
void sendKnxDpdStepFrame(const QKnxAddress &knxAddress, bool status);
void sendKnxDpdScalingFrame(const QKnxAddress &knxAddress, int scale);
void readKnxGroupValue(const QKnxAddress &knxAddress);
void readKnxDpdSwitchState(const QKnxAddress &knxAddress);
void readKnxDpdScalingState(const QKnxAddress &knxAddress);
void readKnxDpdTemperatureSensor(const QKnxAddress &knxAddress);
static void printFrame(const QKnxLinkLayerFrame &frame);
private:
QHostAddress m_localAddress;
QHostAddress m_remoteAddress;
quint16 m_port = 3671;
QTimer *m_timer = nullptr;
QTimer *m_queueTimer = nullptr;
QKnxNetIpTunnel *m_tunnel = nullptr;
QQueue<QKnxLinkLayerFrame> m_sendingQueue;
// Helper
void requestSendFrame(const QKnxLinkLayerFrame &frame);
void sendFrame(const QKnxLinkLayerFrame &frame);
QHostAddress getLocalAddress(const QHostAddress &remoteAddress);
signals:
void connectedChanged();
void frameReceived(const QKnxLinkLayerFrame &frame);
private slots:
void onTimeout();
void onQueueTimeout();
void onTunnelConnected();
void onTunnelDisconnected();
void onTunnelStateChanged(QKnxNetIpEndpointConnection::State state);
void onTunnelFrameReceived(const QKnxLinkLayerFrame &frame);
};
#endif // KNXTUNNEL_H

View File

@ -20,6 +20,7 @@ PLUGIN_DIRS = \
httpcommander \
intertechno \
keba \
knx \
kodi \
leynew \
lgsmarttv \