New Plugin: Tmate
This commit is contained in:
parent
10c54614d6
commit
122fb6e7d2
9
debian/control
vendored
9
debian/control
vendored
@ -581,6 +581,15 @@ Description: nymea integration plugin for Tempo time tracking
|
||||
This package contains the nymea integration plugin for Tempo time tracking.
|
||||
|
||||
|
||||
Package: nymea-plugin-tmate
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
tmate,
|
||||
Description: nymea integration plugin for tmate
|
||||
This package contains the nymea integration plugin for tmate terminal access.
|
||||
|
||||
|
||||
Package: nymea-plugin-tplink
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
|
||||
2
debian/nymea-plugin-tmate.install.in
vendored
Normal file
2
debian/nymea-plugin-tmate.install.in
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationplugintmate.so
|
||||
tmate/translations/*qm usr/share/nymea/translations/
|
||||
@ -75,6 +75,7 @@ PLUGIN_DIRS = \
|
||||
telegram \
|
||||
tempo \
|
||||
texasinstruments \
|
||||
tmate \
|
||||
tplink \
|
||||
tuya \
|
||||
udpcommander \
|
||||
|
||||
26
tmate/README.md
Normal file
26
tmate/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Tmate
|
||||
|
||||
This plugin allows to establish a terminal connection to the device where nymea is running.
|
||||
|
||||
This is useful when maintaining remote nymea setups which may be hidden behind a firewall
|
||||
and cannot be accessed from the public internet. A user can easily enable SSH access for
|
||||
a nymea setup by adding a Thing using the app without having to deal with DNS and NAT.
|
||||
|
||||
## Setup
|
||||
|
||||
A tmate thing can be created without any additional information. Leaving all the fields
|
||||
empty during setup will establish a default tmate session to tmate.io servers and generate
|
||||
a new random token. This is the equivalent of just running `tmate` in a terminal.
|
||||
|
||||
For a more permanent solution, an API key can be registered at tmate.io. Using the api key a custom session name can be set which will be used for every connection establishment, persisting across nymea restarts. In the
|
||||
next step, provide the SSH credentials for the user on the SSH server which has been created before. Once the login succeeds, the thing should become connected.
|
||||
|
||||
> Note: Using custom/self hosted tmate servers is not supported at this point.
|
||||
|
||||
### Connecting clients
|
||||
|
||||
The thing will inform about the session names via states and can be accessed via ssh or web.
|
||||
|
||||
## More
|
||||
|
||||
https://tmate.io/
|
||||
194
tmate/integrationplugintmate.cpp
Normal file
194
tmate/integrationplugintmate.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2023, 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 "integrationplugintmate.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QRegularExpression>
|
||||
|
||||
IntegrationPluginTmate::IntegrationPluginTmate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
IntegrationPluginTmate::~IntegrationPluginTmate()
|
||||
{
|
||||
foreach (QProcess *process, m_processes) {
|
||||
process->terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginTmate::setupThing(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
|
||||
|
||||
QStringList arguments;
|
||||
QString apiKey = thing->paramValue(tmateThingApiKeyParamTypeId).toString();
|
||||
QString sessionName = thing->paramValue(tmateThingSessionNameParamTypeId).toString();
|
||||
|
||||
arguments << "-F";
|
||||
if (!apiKey.isEmpty()) {
|
||||
arguments << "-k" << apiKey;
|
||||
if (!sessionName.isEmpty()) {
|
||||
arguments << "-n" << sessionName;
|
||||
arguments << "-r" << "ro-" + sessionName;
|
||||
}
|
||||
}
|
||||
QProcess *process = new QProcess(thing);
|
||||
process->setProgram("tmate");
|
||||
process->setArguments(arguments);
|
||||
process->setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
m_processes.insert(info->thing(), process);
|
||||
|
||||
connect(process, &QProcess::stateChanged, thing, [=](QProcess::ProcessState newState){
|
||||
switch (newState) {
|
||||
case QProcess::Starting:
|
||||
qCDebug(dcTmate()) << "Connection starting for" << thing->name();
|
||||
return ;
|
||||
case QProcess::Running:
|
||||
qCInfo(dcTmate()) << "Reverse SSH connected for" << thing->name();
|
||||
thing->setStateValue(tmateConnectedStateTypeId, true);
|
||||
return;
|
||||
case QProcess::NotRunning:
|
||||
qCInfo(dcTmate()) << "Reverse SSH disconnected for" << thing->name();
|
||||
thing->setStateValue(tmateConnectedStateTypeId, false);
|
||||
thing->setStateValue(tmateSshStateTypeId, "");
|
||||
thing->setStateValue(tmateSshRoStateTypeId, "");
|
||||
thing->setStateValue(tmateWebStateTypeId, "");
|
||||
thing->setStateValue(tmateWebRoStateTypeId, "");
|
||||
thing->setStateValue(tmateClientsStateTypeId, 0);
|
||||
return;
|
||||
}
|
||||
});
|
||||
connect(process, &QProcess::readyRead, thing, [=](){
|
||||
while (process->canReadLine()) {
|
||||
QByteArray data = process->readLine();
|
||||
|
||||
qCDebug(dcTmate()) << thing->name() << ":" << data;
|
||||
auto extractSession = [thing](const StateTypeId &stateTypeId, const QString &type, const QString &input) {
|
||||
int sessionStart = input.indexOf(type);
|
||||
if (sessionStart >= 0) {
|
||||
int sessionEnd = input.indexOf(QChar('\n'), sessionStart);
|
||||
qCInfo(dcTmate()) << input << "Session start" << sessionStart << "session end" << sessionEnd;
|
||||
QString session =input.mid(sessionStart + type.length(), sessionEnd);
|
||||
thing->setStateValue(stateTypeId, session);
|
||||
}
|
||||
};
|
||||
|
||||
extractSession(tmateSshStateTypeId, "ssh session: ssh ", data);
|
||||
extractSession(tmateSshRoStateTypeId, "ssh session read only: ssh ", data);
|
||||
extractSession(tmateWebStateTypeId, "web session: ", data);
|
||||
extractSession(tmateWebRoStateTypeId, "web session read only: ", data);
|
||||
|
||||
QRegularExpression joinAddressRegex("joined \\(([0-9\\.]+)\\)");
|
||||
QRegularExpressionMatchIterator it = joinAddressRegex.globalMatch(data);
|
||||
while (it.hasNext()) {
|
||||
QRegularExpressionMatch match = it.next();
|
||||
QString word = match.captured(1);
|
||||
qCInfo(dcTmate()) << "Connected:" << word;
|
||||
thing->emitEvent(tmateClientConnectedEventTypeId, {{tmateClientConnectedEventClientAddressParamTypeId, word}});
|
||||
thing->setStateValue(tmateClientsStateTypeId, thing->stateValue(tmateClientsStateTypeId).toUInt()+1);
|
||||
}
|
||||
QRegularExpression leftAddressRegex("left \\(([0-9\\.]+)\\)");
|
||||
it = leftAddressRegex.globalMatch(data);
|
||||
while (it.hasNext()) {
|
||||
QRegularExpressionMatch match = it.next();
|
||||
QString word = match.captured(1);
|
||||
qCInfo(dcTmate()) << "Disconnected:" << word;
|
||||
thing->emitEvent(tmateClientDisconnectedEventTypeId, {{tmateClientDisconnectedEventClientAddressParamTypeId, word}});
|
||||
thing->setStateValue(tmateClientsStateTypeId, thing->stateValue(tmateClientsStateTypeId).toUInt()-1);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// Start up now if enabled
|
||||
bool enabled = thing->stateValue(tmateActiveStateTypeId).toBool();
|
||||
if (enabled) {
|
||||
process->start();
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
|
||||
// Create a watchdog to reconnect if a connection drops...
|
||||
if (!m_watchdog) {
|
||||
m_watchdog = hardwareManager()->pluginTimerManager()->registerTimer(10);
|
||||
connect(m_watchdog, &PluginTimer::timeout, this, [this](){
|
||||
foreach (Thing *thing, m_processes.keys()) {
|
||||
QProcess *process = m_processes.value(thing);
|
||||
if (thing->stateValue(tmateActiveStateTypeId).toBool() && process->state() == QProcess::NotRunning) {
|
||||
qCInfo(dcTmate()) << "Reconnecting tmate for" << thing->name();
|
||||
process->start();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IntegrationPluginTmate::thingRemoved(Thing *thing)
|
||||
{
|
||||
if (thing->thingClassId() == tmateThingClassId) {
|
||||
QProcess *process = m_processes.take(thing);
|
||||
if (process->state() != QProcess::NotRunning) {
|
||||
process->terminate();
|
||||
process->waitForFinished();
|
||||
}
|
||||
}
|
||||
|
||||
if (myThings().isEmpty()) {
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_watchdog);
|
||||
m_watchdog = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IntegrationPluginTmate::executeAction(ThingActionInfo *info)
|
||||
{
|
||||
if (info->action().actionTypeId() == tmateActiveActionTypeId) {
|
||||
bool active = info->action().paramValue(tmateActiveActionActiveParamTypeId).toBool();
|
||||
QProcess *process = m_processes.value(info->thing());
|
||||
if (active) {
|
||||
qCInfo(dcTmate()) << "Reconnecting tmate for" << info->thing()->name();
|
||||
process->start();
|
||||
} else {
|
||||
qCInfo(dcTmate()) << "Terminating session for" << info->thing()->name();
|
||||
process->terminate();
|
||||
}
|
||||
info->thing()->setStateValue(tmateActiveStateTypeId, active);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
62
tmate/integrationplugintmate.h
Normal file
62
tmate/integrationplugintmate.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2023, 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 INTEGRATIONPLUGINREMOTESSH_H
|
||||
#define INTEGRATIONPLUGINREMOTESSH_H
|
||||
|
||||
#include "plugintimer.h"
|
||||
#include "integrations/integrationplugin.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
class IntegrationPluginTmate : public IntegrationPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationplugintmate.json")
|
||||
Q_INTERFACES(IntegrationPlugin)
|
||||
|
||||
public:
|
||||
explicit IntegrationPluginTmate();
|
||||
~IntegrationPluginTmate();
|
||||
|
||||
// void startPairing(ThingPairingInfo *info) override;
|
||||
// void confirmPairing(ThingPairingInfo *info, const QString &user, const QString &secret) override;
|
||||
void executeAction(ThingActionInfo *info) override;
|
||||
void setupThing(ThingSetupInfo *info) override;
|
||||
void thingRemoved(Thing *thing) override;
|
||||
|
||||
private:
|
||||
QHash<Thing*, QProcess*> m_processes;
|
||||
PluginTimer *m_watchdog = nullptr;
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINREMOTESSH_H
|
||||
131
tmate/integrationplugintmate.json
Normal file
131
tmate/integrationplugintmate.json
Normal file
@ -0,0 +1,131 @@
|
||||
{
|
||||
"id": "d06ab0d1-dbfe-48af-b196-523cc37a1e5e",
|
||||
"name": "Tmate",
|
||||
"displayName": "Tmate",
|
||||
"vendors": [
|
||||
{
|
||||
"id": "b948d5e2-bfc6-4e28-a2ba-e40e46f4c213",
|
||||
"name": "tmate",
|
||||
"displayName": "Tmate",
|
||||
"thingClasses": [
|
||||
{
|
||||
"id": "3f06ad52-9514-41b1-9bf9-031241d34634",
|
||||
"name": "tmate",
|
||||
"displayName": "Tmate",
|
||||
"createMethods": ["user"],
|
||||
"setupMethod": "justadd",
|
||||
"interfaces": [],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "01f0c818-55e1-4842-a9b9-cc58bbfe76c6",
|
||||
"name": "apiKey",
|
||||
"displayName": "API key (optional)",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "e587e3dc-0beb-441f-8f07-b23c25580b10",
|
||||
"name": "sessionName",
|
||||
"displayName": "Session name (requires API key usage)",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
],
|
||||
"stateTypes":[
|
||||
{
|
||||
"id": "7009c176-e1aa-49bc-818c-63f7a9027306",
|
||||
"name": "active",
|
||||
"displayName": "Active",
|
||||
"displayNameAction": "Set active",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
"id": "beac3113-04f1-4d70-875b-44ca8b307866",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"suggestLogging": true,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "ef780fb1-b31a-4333-944b-a02bf3297fea",
|
||||
"name": "sshRo",
|
||||
"displayName": "SSH RO",
|
||||
"type": "QString",
|
||||
"defaultValue": "",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "e8ff1b90-7701-454c-a557-4b91dc8c649b",
|
||||
"name": "ssh",
|
||||
"displayName": "SSH",
|
||||
"type": "QString",
|
||||
"defaultValue": "",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "98248bc0-ddda-4ae6-8558-4d7155a39c33",
|
||||
"name": "webRo",
|
||||
"displayName": "Web RO",
|
||||
"type": "QString",
|
||||
"defaultValue": "",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "9c284ede-eea8-4a9b-a326-e59a6bc7bb7c",
|
||||
"name": "web",
|
||||
"displayName": "Web",
|
||||
"type": "QString",
|
||||
"defaultValue": "",
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "786e7be7-917a-4062-83ff-aade80686ec5",
|
||||
"name": "clients",
|
||||
"displayName": "Clients",
|
||||
"type": "uint",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
}
|
||||
],
|
||||
"eventTypes": [
|
||||
{
|
||||
"id": "0508a1e2-4ed2-42ee-ab70-ed7cdd1e261c",
|
||||
"name": "clientConnected",
|
||||
"displayName": "Client connected",
|
||||
"suggestLogging": true,
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "a334c6e7-dffc-4720-aa21-815636be1bc1",
|
||||
"name": "clientAddress",
|
||||
"displayName": "Client address",
|
||||
"type": "QString"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2871e481-1b67-4d77-b1ce-e0965784aa89",
|
||||
"name": "clientDisconnected",
|
||||
"displayName": "Client disconnected",
|
||||
"suggestLogging": true,
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "0ad4ed71-4b9a-44ab-83b1-5c62482e1625",
|
||||
"name": "clientAddress",
|
||||
"displayName": "Client address",
|
||||
"type": "QString"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
BIN
tmate/logo.png
Normal file
BIN
tmate/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
13
tmate/meta.json
Normal file
13
tmate/meta.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "Tmate",
|
||||
"tagline": "Shell access to the machine running nymea from anywhere via tmate.",
|
||||
"icon": "logo.png",
|
||||
"stability": "community",
|
||||
"offline": false,
|
||||
"technologies": [
|
||||
"cloud"
|
||||
],
|
||||
"categories": [
|
||||
"tool"
|
||||
]
|
||||
}
|
||||
8
tmate/tmate.pro
Normal file
8
tmate/tmate.pro
Normal file
@ -0,0 +1,8 @@
|
||||
include(../plugins.pri)
|
||||
|
||||
SOURCES += \
|
||||
integrationplugintmate.cpp \
|
||||
|
||||
HEADERS += \
|
||||
integrationplugintmate.h \
|
||||
|
||||
103
tmate/translations/d06ab0d1-dbfe-48af-b196-523cc37a1e5e-en_US.ts
Normal file
103
tmate/translations/d06ab0d1-dbfe-48af-b196-523cc37a1e5e-en_US.ts
Normal file
@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>Tmate</name>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="43"/>
|
||||
<source>API key (optional)</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: tmate, Type: thing, ID: {01f0c818-55e1-4842-a9b9-cc58bbfe76c6})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="46"/>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="49"/>
|
||||
<source>Active</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: tmate, ActionType: active, ID: {7009c176-e1aa-49bc-818c-63f7a9027306})
|
||||
----------
|
||||
The name of the StateType ({7009c176-e1aa-49bc-818c-63f7a9027306}) of ThingClass tmate</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="52"/>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="55"/>
|
||||
<source>Client address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: tmate, EventType: clientDisconnected, ID: {0ad4ed71-4b9a-44ab-83b1-5c62482e1625})
|
||||
----------
|
||||
The name of the ParamType (ThingClass: tmate, EventType: clientConnected, ID: {a334c6e7-dffc-4720-aa21-815636be1bc1})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="58"/>
|
||||
<source>Client connected</source>
|
||||
<extracomment>The name of the EventType ({0508a1e2-4ed2-42ee-ab70-ed7cdd1e261c}) of ThingClass tmate</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="61"/>
|
||||
<source>Client disconnected</source>
|
||||
<extracomment>The name of the EventType ({2871e481-1b67-4d77-b1ce-e0965784aa89}) of ThingClass tmate</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="64"/>
|
||||
<source>Clients</source>
|
||||
<extracomment>The name of the StateType ({786e7be7-917a-4062-83ff-aade80686ec5}) of ThingClass tmate</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="67"/>
|
||||
<source>Connected</source>
|
||||
<extracomment>The name of the StateType ({beac3113-04f1-4d70-875b-44ca8b307866}) of ThingClass tmate</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="70"/>
|
||||
<source>SSH</source>
|
||||
<extracomment>The name of the StateType ({e8ff1b90-7701-454c-a557-4b91dc8c649b}) of ThingClass tmate</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="73"/>
|
||||
<source>SSH RO</source>
|
||||
<extracomment>The name of the StateType ({ef780fb1-b31a-4333-944b-a02bf3297fea}) of ThingClass tmate</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="76"/>
|
||||
<source>Session name (requires API key usage)</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: tmate, Type: thing, ID: {e587e3dc-0beb-441f-8f07-b23c25580b10})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="79"/>
|
||||
<source>Set active</source>
|
||||
<extracomment>The name of the ActionType ({7009c176-e1aa-49bc-818c-63f7a9027306}) of ThingClass tmate</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="82"/>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="85"/>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="88"/>
|
||||
<source>Tmate</source>
|
||||
<extracomment>The name of the ThingClass ({3f06ad52-9514-41b1-9bf9-031241d34634})
|
||||
----------
|
||||
The name of the vendor ({b948d5e2-bfc6-4e28-a2ba-e40e46f4c213})
|
||||
----------
|
||||
The name of the plugin Tmate ({d06ab0d1-dbfe-48af-b196-523cc37a1e5e})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="91"/>
|
||||
<source>Web</source>
|
||||
<extracomment>The name of the StateType ({9c284ede-eea8-4a9b-a326-e59a6bc7bb7c}) of ThingClass tmate</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../../build/nymea-plugins-Desktop-Debug/tmate/plugininfo.h" line="94"/>
|
||||
<source>Web RO</source>
|
||||
<extracomment>The name of the StateType ({98248bc0-ddda-4ae6-8558-4d7155a39c33}) of ThingClass tmate</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
Loading…
x
Reference in New Issue
Block a user