New plugin: Add EVerest EV charger plugin

master
Simon Stürz 2024-09-23 10:16:44 +02:00
parent 7bc0f4c314
commit 6185180dce
17 changed files with 1698 additions and 0 deletions

9
debian/control vendored
View File

@ -223,6 +223,15 @@ Description: nymea integration plugin for EVBox
implementing the Protocol Max v4.
Package: nymea-plugin-everest
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
Description: nymea integration plugin for EVerest based EV chargers
This package will install the nymea integration plugin for
EVerest based EV charger
Package: nymea-plugin-fastcom
Architecture: any
Depends: ${misc:Depends},

View File

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

13
everest/README.md Normal file
View File

@ -0,0 +1,13 @@
# nymea-plugin-everest
--------------------------------
This nymea integration plugin allowes to connecto to any everest instance in the network or localhost with an API module and add every connector configured on the instance.
Each connector has to be added manually using the discovery. Once added, the integration creats an EV charger within nymea which makes it available to the energy manager and the overall nymea eco system.
Known issues:
* There is currently no method available on the Everest API module to switch phases
* There are no additional information which could be used to identify the instance, like serialnumber, manufacturer or model
* The action execution is not very clean implemented. There is no feedback if the execution was successfully

342
everest/everest.cpp Normal file
View File

@ -0,0 +1,342 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 "everest.h"
#include "extern-plugininfo.h"
#include <QJsonDocument>
#include <QJsonParseError>
#include <loggingcategories.h>
NYMEA_LOGGING_CATEGORY(dcEverestTraffic, "EverestTraffic")
Everest::Everest(MqttClient *client, Thing *thing, QObject *parent)
: QObject{parent},
m_client{client},
m_thing{thing}
{
m_connector = m_thing->paramValue(everestThingConnectorParamTypeId).toString();
m_topicPrefix = "everest_api/" + m_connector;
m_subscribedTopics.append(buildTopic("hardware_capabilities"));
m_subscribedTopics.append(buildTopic("limits"));
m_subscribedTopics.append(buildTopic("powermeter"));
m_subscribedTopics.append(buildTopic("session_info"));
m_subscribedTopics.append(buildTopic("telemetry"));
connect(m_client, &MqttClient::connected, this, &Everest::onConnected);
connect(m_client, &MqttClient::disconnected, this, &Everest::onDisconnected);
connect(m_client, &MqttClient::publishReceived, this, &Everest::onPublishReceived);
connect(m_client, &MqttClient::subscribed, this, &Everest::onSubscribed);
m_aliveTimer.setInterval(2000);
m_aliveTimer.setSingleShot(true);
connect(&m_aliveTimer, &QTimer::timeout, this, [this](){
qCDebug(dcEverest()) << "No MQTT traffic since" << m_aliveTimer.interval()
<< "ms. Mark device as not connected" << m_thing;
m_thing->setStateValue(everestCurrentPowerStateTypeId, 0);
m_thing->setStateValue(everestConnectedStateTypeId, false);
});
if (m_client->isConnected()) {
qCDebug(dcEverest()) << "The connection is already available. Initializing the instance...";
initialize();
}
}
Everest::~Everest()
{
deinitialize();
}
QString Everest::connector() const
{
return m_connector;
}
void Everest::initialize()
{
qCDebug(dcEverest()) << "Initializing" << m_thing->name();
if (!m_client->isConnected()) {
qCWarning(dcEverest()) << "Cannot initialize because the MQTT client is not connected for" << m_thing;
m_initialized = false;
return;
}
foreach (const QString &topic, m_subscribedTopics)
m_client->subscribe(topic);
m_initialized = true;
qCDebug(dcEverest()) << "Initialized" << m_thing->name() << "successfully";
}
void Everest::deinitialize()
{
qCDebug(dcEverest()) << "Deinitializing" << m_thing->name();
if (m_initialized && m_client->isConnected()) {
foreach (const QString &topic, m_subscribedTopics) {
m_client->unsubscribe(topic);
}
}
m_initialized = false;
}
void Everest::enableCharging(bool enable)
{
QString topic;
if (enable) {
topic = m_topicPrefix + "/cmd/resume_charging";
} else {
topic = m_topicPrefix + "/cmd/pause_charging";
}
m_client->publish(topic, QByteArray::fromHex("01"));
}
void Everest::setMaxChargingCurrent(double current)
{
QString topic = m_topicPrefix + "/cmd/set_limit_amps";
QByteArray payload = QByteArray::number(current);
m_client->publish(topic, payload);
}
void Everest::onConnected()
{
m_aliveTimer.start();
initialize();
}
void Everest::onDisconnected()
{
m_thing->setStateValue(everestConnectedStateTypeId, false);
m_thing->setStateValue(everestCurrentPowerStateTypeId, 0);
m_initialized = false;
}
void Everest::onSubscribed(const QString &topic, Mqtt::SubscribeReturnCode subscribeReturnCode)
{
if (subscribeReturnCode == Mqtt::SubscribeReturnCodeFailure) {
qCWarning(dcEverest()) << "Failed to subscribe to" << topic << m_thing;
} else {
qCDebug(dcEverestTraffic()) << "Subscribed to" << topic << m_thing;
}
}
void Everest::onPublishReceived(const QString &topic, const QByteArray &payload, bool retained)
{
Q_UNUSED(retained)
// Make sure this publish is for this everest instance
if (!topic.startsWith(m_topicPrefix))
return;
// We are for sure connected now
m_aliveTimer.start();
m_thing->setStateValue(everestConnectedStateTypeId, true);
qCDebug(dcEverestTraffic()) << "Received publish on" << topic << qUtf8Printable(payload);
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &jsonError);
if (jsonError.error) {
qCWarning(dcEverestTraffic()) << "Unable read JSON data published on topic"
<< topic << jsonError.errorString() << payload;
return;
}
if (topic.endsWith("hardware_capabilities")) {
/*
{
"max_phase_count_import": 3,
"min_phase_count_import": 1,
"max_phase_count_export": 3,
"min_phase_count_export": 1,
"supports_changing_phases_during_charging": true,
"max_current_A_import": 32,
"min_current_A_import": 6,
"max_current_A_export": 16,
"min_current_A_export": 0,
"connector_type": "IEC62196Type2Cable"
}
*/
QVariantMap dataMap = jsonDoc.toVariant().toMap();
m_thing->setStateMaxValue(everestMaxChargingCurrentStateTypeId,
dataMap.value("max_current_A_import").toUInt());
m_thing->setStateMinValue(everestMaxChargingCurrentStateTypeId,
dataMap.value("min_current_A_import").toUInt());
// bool phaseSwitchingAvailable = dataMap.value("supports_changing_phases_during_charging", false).toBool();
// if (!phaseSwitchingAvailable) {
// // Only option left is set the desired phase count to 3, force that value
// m_thing->setStatePossibleValues(everestDesiredPhaseCountStateTypeId, { 3 });
// m_thing->setStateValue(everestDesiredPhaseCountStateTypeId, 3);
// } else {
// m_thing->setStatePossibleValues(everestDesiredPhaseCountStateTypeId, { 1, 3 });
// }
} else if (topic.endsWith("limits")) {
/*
{
"uuid": "connector_1",
"nr_of_phases_available": 3,
"max_current": 32
}
*/
QVariantMap dataMap = jsonDoc.toVariant().toMap();
m_thing->setStateValue(everestPhaseCountStateTypeId, dataMap.value("nr_of_phases_available").toUInt());
m_thing->setStateValue(everestMaxChargingCurrentStateTypeId, dataMap.value("max_current").toUInt());
} else if (topic.endsWith("powermeter")) {
/*
{
"timestamp": "2024-03-19T10:40:03.427Z",
"meter_id": "YETI_POWERMETER",
"phase_seq_error": false,
"energy_Wh_import": {
"total": 0, "L1": 0, "L2": 0, "L3": 0
},
"power_W": {
"total": 0, "L1": 0, "L2": 0, "L3": 0
},
"voltage_V": {
"L1": 230.55, "L2": 230.55, "L3": 230.55
},
"current_A": {
"L1": 0, "L2": 0, "L3": 0, "N": 0
},
"frequency_Hz": {
"L1": 49.97, "L2": 49.97, "L3": 49.97
}
}
*/
QVariantMap dataMap = jsonDoc.toVariant().toMap();
QVariantMap energyImportedMap = dataMap.value("energy_Wh_import").toMap();
m_thing->setStateValue(everestTotalEnergyConsumedStateTypeId,
energyImportedMap.value("total").toDouble() / 1000.0);
QVariantMap powerMap = dataMap.value("power_W").toMap();
m_thing->setStateValue(everestCurrentPowerStateTypeId, powerMap.value("total").toUInt());
} else if (topic.endsWith("session_info")) {
/*
{
"active_errors": [],
"active_permanent_faults": [],
"charged_energy_wh": 0,
"charging_duration_s": 0,
"datetime": "2024-03-19T10:40:02.986Z",
"discharged_energy_wh": 0,
"latest_total_w": 0,
"state": "Unplugged"
}
*/
QVariantMap dataMap = jsonDoc.toVariant().toMap();
m_thing->setStateValue(everestSessionEnergyStateTypeId,
dataMap.value("charged_energy_wh").toDouble() / 1000.0);
// Interprete state
QString stateString = dataMap.value("state").toString();
m_thing->setStateValue(everestStateStateTypeId, stateString);
State state = convertStringToState(stateString);
if (state == StateUnknown)
return; // No need to proceed here
m_thing->setStateValue(everestChargingStateTypeId, state == StateCharging);
m_thing->setStateValue(everestPluggedInStateTypeId, state != StateUnplugged);
// TODO: check if we can set the power state in other EVSE states
if (state == StateCharging && !m_thing->stateValue(everestPowerStateTypeId).toBool()) {
m_thing->setStateValue(everestPowerStateTypeId, true);
}
} else if (topic.endsWith("telemetry")) {
/*
{
"phase_seq_error": false,
"temperature": 25,
"fan_rpm": 1500,
"supply_voltage_12V": 12.01,
"supply_voltage_minus_12V": -11.8
}
*/
QVariantMap dataMap = jsonDoc.toVariant().toMap();
m_thing->setStateValue(everestTemperatureStateTypeId, dataMap.value("temperature").toDouble());
m_thing->setStateValue(everestFanSpeedStateTypeId, dataMap.value("fan_rpm").toDouble());
}
}
QString Everest::buildTopic(const QString &topic)
{
Q_ASSERT_X(!m_connector.isEmpty(), "Everest::buildTopic", "The connector must be known before building a topic");
QString baseTopic = QString(m_topicPrefix + "/var");
if (!topic.startsWith("/"))
baseTopic.append("/");
return baseTopic + topic;
}
Everest::State Everest::convertStringToState(const QString &stateString)
{
State state = StateUnknown;
if (stateString == "Unplugged") {
state = StateUnplugged;
} else if (stateString == "Disabled") {
state = StateDisabled;
} else if (stateString == "Preparing") {
state = StatePreparing;
} else if (stateString == "Reserved") {
state = StateReserved;
} else if (stateString == "AuthRequired") {
state = StateAuthRequired;
} else if (stateString == "WaitingForEnergy") {
state = StateWaitingForEnergy;
} else if (stateString == "Charging") {
state = StateCharging;
} else if (stateString == "ChargingPausedEV") {
state = StateChargingPausedEV;
} else if (stateString == "ChargingPausedEVSE") {
state = StateChargingPausedEVSE;
} else if (stateString == "Finished") {
state = StateFinished;
}
return state;
}

96
everest/everest.h Normal file
View File

@ -0,0 +1,96 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 EVEREST_H
#define EVEREST_H
#include <QTimer>
#include <QObject>
#include <mqttclient.h>
#include <integrations/thing.h>
class Everest : public QObject
{
Q_OBJECT
public:
enum State {
StateUnplugged,
StateDisabled,
StatePreparing,
StateReserved,
StateAuthRequired,
StateWaitingForEnergy,
StateCharging,
StateChargingPausedEV,
StateChargingPausedEVSE,
StateFinished,
StateUnknown
};
Q_ENUM(State)
explicit Everest(MqttClient *client, Thing *thing, QObject *parent = nullptr);
~Everest();
QString connector() const;
void initialize();
void deinitialize();
void enableCharging(bool enable);
void setMaxChargingCurrent(double current);
signals:
private slots:
void onConnected();
void onDisconnected();
void onSubscribed(const QString &topic, Mqtt::SubscribeReturnCode subscribeReturnCode);
void onPublishReceived(const QString &topic, const QByteArray &payload, bool retained);
private:
MqttClient *m_client = nullptr;
Thing *m_thing = nullptr;
QTimer m_aliveTimer;
QString m_connector;
QString m_topicPrefix;
QStringList m_subscribedTopics;
bool m_initialized = false;
// Prepends everest_api/<connector>/ to the given topic
QString buildTopic(const QString &topic);
State convertStringToState(const QString &stateString);
};
#endif // EVEREST_H

17
everest/everest.pro Normal file
View File

@ -0,0 +1,17 @@
include(../plugins.pri)
QT += network
PKGCONFIG += nymea-mqtt
SOURCES += \
everest.cpp \
everestclient.cpp \
everestdiscovery.cpp \
integrationplugineverest.cpp
HEADERS += \
everest.h \
everestclient.h \
everestdiscovery.h \
integrationplugineverest.h

212
everest/everestclient.cpp Normal file
View File

@ -0,0 +1,212 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 "everestclient.h"
#include "extern-plugininfo.h"
EverestClient::EverestClient(QObject *parent)
: QObject{parent}
{
m_client = new MqttClient("nymea-" + QUuid::createUuid().toString().left(8), 300,
QString(), QByteArray(), Mqtt::QoS0, false, this);
connect(m_client, &MqttClient::disconnected, this, [this](){
qCDebug(dcEverest()) << "The MQTT client is now disconnected" << this;
if (!m_address.isNull()) {
// Start the reconnect timer
qCDebug(dcEverest()) << "Starting reconnect timer for mqtt connection to" << m_address.toString();
m_reconnectTimer.start();
}
});
connect(m_client, &MqttClient::connected, this, [this](){
qCDebug(dcEverest()) << "The MQTT client is now connected" << this;
m_reconnectTimer.stop();
});
connect(m_client, &MqttClient::error, this, [this](QAbstractSocket::SocketError socketError){
qCWarning(dcEverest()) << "An MQTT error occurred on" << this << socketError;
});
// Reconnect timer is only required for IP based connections, otherwise we have the NetworkDeviceMonitor
m_reconnectTimer.setInterval(5000);
m_reconnectTimer.setSingleShot(false);
connect(&m_reconnectTimer, &QTimer::timeout, this, [this](){
if (m_client->isConnected())
return;
if (!m_running) {
qCDebug(dcEverest()) << "The everest client is not running. Ignoring event...";
return;
}
m_client->connectToHost(m_address.toString(), m_port);
});
}
MqttClient *EverestClient::client() const
{
return m_client;
}
Things EverestClient::things() const
{
return m_everests.keys();
}
void EverestClient::addThing(Thing *thing)
{
if (m_everests.contains(thing)) {
qCWarning(dcEverest()) << "The" << thing << "has already been added to the everest client. "
"Please report a bug if you see this message.";
// FIXME: maybe cleanup and recreate the client due to reconfigure
return;
}
Everest *everest = new Everest(m_client, thing, this);
m_everests.insert(thing, everest);
}
void EverestClient::removeThing(Thing *thing)
{
if (!m_everests.contains(thing)) {
qCWarning(dcEverest()) << "The" << thing << "has already been removed from the everest client. "
"Please report a bug if you see this message.";
return;
}
Everest *everest = m_everests.take(thing);
everest->deinitialize();
everest->deleteLater();
}
Everest *EverestClient::getEverest(Thing *thing) const
{
if (!m_everests.contains(thing))
return nullptr;
return m_everests.value(thing);
}
QHostAddress EverestClient::address() const
{
return m_address;
}
void EverestClient::setAddress(const QHostAddress &address)
{
m_address = address;
}
MacAddress EverestClient::macAddress() const
{
return m_macAddress;
}
void EverestClient::setMacAddress(const MacAddress &macAddress)
{
m_macAddress = macAddress;
}
NetworkDeviceMonitor *EverestClient::monitor() const
{
return m_monitor;
}
void EverestClient::setMonitor(NetworkDeviceMonitor *monitor)
{
m_monitor = monitor;
connect(m_monitor, &NetworkDeviceMonitor::reachableChanged, this, &EverestClient::onMonitorReachableChanged);
}
void EverestClient::start()
{
qCDebug(dcEverest()) << "Starting" << this;
m_running = true;
if (m_monitor) {
if (m_monitor->reachable()) {
qCDebug(dcEverest()) << "Connecting MQTT client to" << m_monitor->networkDeviceInfo();
if (m_client->isConnected())
m_client->disconnectFromHost();
m_client->connectToHost(m_monitor->networkDeviceInfo().address().toString(), m_port);
}
} else {
qCDebug(dcEverest()) << "Connecting MQTT client to" << m_address.toString();
m_client->connectToHost(m_address.toString(), m_port);
// Note: on connected this will be stopped, otherwise we want the timer running
m_reconnectTimer.start();
}
}
void EverestClient::stop()
{
qCDebug(dcEverest()) << "Stopping" << this;
m_running = false;
m_reconnectTimer.stop();
m_client->disconnectFromHost();
}
void EverestClient::onMonitorReachableChanged(bool reachable)
{
qCDebug(dcEverest()) << "Network monitor for" << m_monitor->macAddress().toString()
<< (reachable ? " is now reachable" : "is not reachable any more");
if (!m_running) {
qCDebug(dcEverest()) << "The everest client is not running. Ignoring event...";
return;
}
if (reachable) {
// The monitor is reachable which means we have a verfied IP, (re)connecting the mqtt client
qCDebug(dcEverest()) << "Connecting MQTT client to" << m_monitor->networkDeviceInfo();
if (m_client->isConnected())
m_client->disconnectFromHost();
m_client->connectToHost(m_monitor->networkDeviceInfo().address().toString(), m_port);
}
}
QDebug operator<<(QDebug debug, EverestClient *everestClient)
{
QDebugStateSaver saver(debug);
debug.nospace() << "EverestClient(";
if (everestClient->monitor()) {
debug.nospace() << everestClient->monitor()->networkDeviceInfo().macAddress() << ", ";
debug.nospace() << everestClient->monitor()->networkDeviceInfo().address().toString() << ", ";
} else {
debug.nospace() << everestClient->address().toString() << ", ";
}
debug.nospace() << "MQTT connected: " << everestClient->client()->isConnected() << ")";
return debug;
}

96
everest/everestclient.h Normal file
View File

@ -0,0 +1,96 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 EVERESTCLIENT_H
#define EVERESTCLIENT_H
#include <QTimer>
#include <QObject>
#include <mqttclient.h>
#include <integrations/thing.h>
#include <network/macaddress.h>
#include <network/networkdevicemonitor.h>
#include <QDebug>
#include "everest.h"
class EverestClient : public QObject
{
Q_OBJECT
public:
explicit EverestClient(QObject *parent = nullptr);
MqttClient *client() const;
Things things() const;
void addThing(Thing *thing);
void removeThing(Thing *thing);
Everest *getEverest(Thing *thing) const;
QHostAddress address() const;
void setAddress(const QHostAddress &address);
MacAddress macAddress() const;
void setMacAddress(const MacAddress &macAddress);
NetworkDeviceMonitor *monitor() const;
void setMonitor(NetworkDeviceMonitor *monitor);
public slots:
void start();
void stop();
private slots:
void onMonitorReachableChanged(bool reachable);
private:
MqttClient *m_client = nullptr;
QTimer m_reconnectTimer;
quint16 m_port = 1883;
bool m_running = false;
QHash<Thing *, Everest *> m_everests;
// One of both must be defined, never both
QHostAddress m_address;
MacAddress m_macAddress;
// Available for mac based connections
NetworkDeviceMonitor *m_monitor = nullptr;
};
QDebug operator<<(QDebug debug, EverestClient *everestClient);
#endif // EVERESTCLIENT_H

View File

@ -0,0 +1,174 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 "everestdiscovery.h"
#include "extern-plugininfo.h"
#include "everest.h"
#include <QJsonDocument>
#include <QJsonParseError>
EverestDiscovery::EverestDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent)
: QObject{parent},
m_networkDeviceDiscovery{networkDeviceDiscovery}
{
}
void EverestDiscovery::start()
{
qCInfo(dcEverest()) << "Discovery: Start discovering Everest MQTT brokers in the network...";
m_startDateTime = QDateTime::currentDateTime();
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &EverestDiscovery::checkNetworkDevice);
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
qCDebug(dcEverest()) << "Discovery: Network discovery finished. Found"
<< discoveryReply->networkDeviceInfos().count() << "network devices";
// Give the last connections added right before the network discovery finished a chance to check the device...
QTimer::singleShot(3000, this, [this](){
qCDebug(dcEverest()) << "Discovery: Grace period timer triggered.";
finishDiscovery();
});
});
// For development, check local host
NetworkDeviceInfo localHostInfo;
localHostInfo.setAddress(QHostAddress::LocalHost);
checkNetworkDevice(localHostInfo);
}
QList<EverestDiscovery::Result> EverestDiscovery::results() const
{
return m_results;
}
void EverestDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
{
MqttClient *client = new MqttClient("nymea-" + QUuid::createUuid().toString().left(8), 300,
QString(), QByteArray(), Mqtt::QoS0, false, this);
client->setAutoReconnect(false);
m_clients.append(client);
connect(client, &MqttClient::error, this, [this, client, networkDeviceInfo](QAbstractSocket::SocketError socketError){
qCDebug(dcEverest()) << "Discovery: MQTT client error occurred on"
<< networkDeviceInfo.address().toString() << socketError
<< "...skip connection";
// We give up on the first error here
cleanupClient(client);
});
connect(client, &MqttClient::disconnected, this, [this, client](){
cleanupClient(client);
});
connect(client, &MqttClient::connected, this, [this, client, networkDeviceInfo](){
// We found a mqtt server, let's check if we find everest_api module on it...
qCDebug(dcEverest()) << "Discovery: Successfully connected to host" << networkDeviceInfo;
connect(client, &MqttClient::publishReceived, client, [this, client, networkDeviceInfo]
(const QString &topic, const QByteArray &payload, bool retained) {
qCDebug(dcEverest()) << "Discovery: Received publish on" << topic
<< "retained:" << retained << qUtf8Printable(payload);
if (topic == m_everestApiModuleTopicConnectors) {
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &jsonError);
if (jsonError.error) {
qCDebug(dcEverest()) << "Discovery: Received payload on topic" << topic
<< "with JSON error:" << jsonError.errorString();
cleanupClient(client);
return;
}
QStringList connectors = jsonDoc.toVariant().toStringList();
qCInfo(dcEverest()) << "Discovery: Found Everest on" << networkDeviceInfo << connectors;
Result result;
result.networkDeviceInfo = networkDeviceInfo;
result.connectors = connectors;
m_results.append(result);
cleanupClient(client);
}
});
connect(client, &MqttClient::subscribeResult, client, [this, client]
(quint16 packetId, const Mqtt::SubscribeReturnCodes &subscribeReturnCodes) {
Q_UNUSED(packetId)
if (subscribeReturnCodes.contains(Mqtt::SubscribeReturnCodeFailure)) {
qCDebug(dcEverest()) << "Discovery: Failed to subscribe to topic ...skip connection";
cleanupClient(client);
}
});
client->subscribe(m_everestApiModuleTopicConnectors);
// // Subscribe in the next event loop due to IO error on socket
// QTimer::singleShot(100, client, [this, client](){
// qCDebug(dcEverest()) << "Discovery: Try to subscribe to the API module on the broker...";
// client->subscribe(m_everestApiModuleTopicConnectors);
// });
});
qCDebug(dcEverest()) << "Discovery: Verifying host" << networkDeviceInfo;
client->connectToHost(networkDeviceInfo.address().toString(), 1883);
}
void EverestDiscovery::cleanupClient(MqttClient *client)
{
if (!m_clients.contains(client))
return;
m_clients.removeAll(client);
client->disconnectFromHost();
client->deleteLater();
}
void EverestDiscovery::finishDiscovery()
{
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
// Cleanup any leftovers...we don't care any more
foreach (MqttClient *client, m_clients)
cleanupClient(client);
qCInfo(dcEverest()) << "Discovery: Finished the discovery process. Found"
<< m_results.count() << "Everest instances in"
<< QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
emit finished();
}

View File

@ -0,0 +1,71 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 EVERESTDISCOVERY_H
#define EVERESTDISCOVERY_H
#include <QList>
#include <QObject>
#include <mqttclient.h>
#include <network/networkdevicediscovery.h>
class EverestDiscovery : public QObject
{
Q_OBJECT
public:
typedef struct Result {
QStringList connectors;
NetworkDeviceInfo networkDeviceInfo;
} Result;
explicit EverestDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr);
void start();
QList<EverestDiscovery::Result> results() const;
signals:
void finished();
private:
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
QDateTime m_startDateTime;
QList<EverestDiscovery::Result> m_results;
QList<MqttClient *> m_clients;
QString m_everestApiModuleTopicConnectors = "everest_api/connectors";
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
void cleanupClient(MqttClient *client);
void finishDiscovery();
};
#endif // EVERESTDISCOVERY_H

BIN
everest/everst.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,257 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 "integrationplugineverest.h"
#include "plugininfo.h"
#include "everestdiscovery.h"
#include <network/networkdevicediscovery.h>
IntegrationPluginTruffle::IntegrationPluginTruffle()
{
}
void IntegrationPluginTruffle::init()
{
}
void IntegrationPluginTruffle::startMonitoringAutoThings()
{
// TODO: auto setup everest instance running on localhost
}
void IntegrationPluginTruffle::discoverThings(ThingDiscoveryInfo *info)
{
qCDebug(dcEverest()) << "Start discovering Everest systems in the local network";
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
qCWarning(dcEverest()) << "The network discovery is not available on this platform.";
info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
return;
}
EverestDiscovery *discovery = new EverestDiscovery(hardwareManager()->networkDeviceDiscovery(), this);
connect(discovery, &EverestDiscovery::finished, discovery, &EverestDiscovery::deleteLater);
connect(discovery, &EverestDiscovery::finished, info, [this, info, discovery](){
foreach (const EverestDiscovery::Result &result, discovery->results()) {
// Create one EV charger foreach available connector on that host
foreach(const QString &connectorName, result.connectors) {
QString title = QString("Everest (%1)").arg(connectorName);
QString description = result.networkDeviceInfo.address().toString() +
" " + result.networkDeviceInfo.macAddress();
ThingDescriptor descriptor(everestThingClassId, title, description);
qCInfo(dcEverest()) << "Discovered -->" << title << description;
ParamList params;
params.append(Param(everestThingConnectorParamTypeId, connectorName));
if (!MacAddress(result.networkDeviceInfo.macAddress()).isNull())
params.append(Param(everestThingMacParamTypeId, result.networkDeviceInfo.macAddress()));
if (!result.networkDeviceInfo.address().isNull())
params.append(Param(everestThingAddressParamTypeId,
result.networkDeviceInfo.address().toString()));
descriptor.setParams(params);
// Let's check if we aleardy have a thing with those parms
bool thingExists = true;
Thing *existingThing = nullptr;
foreach (Thing *thing, myThings()) {
if (thing->thingClassId() != info->thingClassId())
continue;
foreach(const Param &param, params) {
if (param.value() != thing->paramValue(param.paramTypeId())) {
thingExists = false;
break;
}
}
// The params are equal, we already know this thing
if (thingExists)
existingThing = thing;
}
// Set the thing ID id we already have this device (for reconfiguration)
if (existingThing)
descriptor.setThingId(existingThing->id());
info->addThingDescriptor(descriptor);
}
}
// All discovery results processed, we are done
info->finish(Thing::ThingErrorNoError);
});
discovery->start();
}
void IntegrationPluginTruffle::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
QHostAddress address(thing->paramValue(everestThingAddressParamTypeId).toString());
MacAddress macAddress(thing->paramValue(everestThingMacParamTypeId).toString());
QString connector(thing->paramValue(everestThingConnectorParamTypeId).toString());
if (!macAddress.isNull()) {
qCInfo(dcEverest()) << "Setting up everest for" << macAddress.toString() << connector;
EverestClient *everstClient = nullptr;
foreach (EverestClient *ec, m_everstClients) {
if (ec->macAddress() == macAddress) {
// We have already a client for this host
qCDebug(dcEverest()) << "Using existing" << ec;
everstClient = ec;
}
}
if (!everstClient) {
qCDebug(dcEverest()) << "Creating new mac address based everst client";
everstClient = new EverestClient(this);
everstClient->setMacAddress(macAddress);
everstClient->setMonitor(hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress));
m_everstClients.append(everstClient);
everstClient->start();
}
everstClient->addThing(thing);
m_thingClients.insert(thing, everstClient);
info->finish(Thing::ThingErrorNoError);
return;
} else {
qCInfo(dcEverest()) << "Setting up IP based everest for" << address.toString() << connector;
EverestClient *everstClient = nullptr;
foreach (EverestClient *ec, m_everstClients) {
if (ec->address().isNull())
continue;
if (ec->address() == address) {
// We have already a client for this host
qCDebug(dcEverest()) << "Using existing" << ec;
everstClient = ec;
break;
}
}
if (!everstClient) {
qCDebug(dcEverest()) << "Creating new IP based everst client";
everstClient = new EverestClient(this);
everstClient->setAddress(address);
m_everstClients.append(everstClient);
everstClient->start();
}
everstClient->addThing(thing);
m_thingClients.insert(thing, everstClient);
info->finish(Thing::ThingErrorNoError);
return;
}
}
void IntegrationPluginTruffle::executeAction(ThingActionInfo *info)
{
qCDebug(dcEverest()) << "Executing action for thing" << info->thing()
<< info->action().actionTypeId().toString() << info->action().params();
if (info->thing()->thingClassId() == everestThingClassId) {
Thing *thing = info->thing();
EverestClient *everstClient = m_thingClients.value(thing);
if (!everstClient) {
qCWarning(dcEverest()) << "Failed to execute action. Unable to find everst client for" << thing;
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
Everest *everest = everstClient->getEverest(thing);
if (!everest) {
qCWarning(dcEverest()) << "Failed to execute action. Unable to find everst for"
<< thing << "on" << everstClient;
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
if (!thing->stateValue(everestConnectedStateTypeId).toBool()) {
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
// All checks where good, let's execute the action
if (info->action().actionTypeId() == everestPowerActionTypeId) {
bool power = info->action().paramValue(everestPowerActionPowerParamTypeId).toBool();
qCDebug(dcEverest()) << (power ? "Resume charging on" : "Pause charging on") << thing;
everest->enableCharging(power);
thing->setStateValue(everestPowerStateTypeId, power);
info->finish(Thing::ThingErrorNoError);
} else if (info->action().actionTypeId() == everestMaxChargingCurrentActionTypeId) {
double current = info->action().paramValue(everestMaxChargingCurrentActionMaxChargingCurrentParamTypeId).toDouble();
qCDebug(dcEverest()) << "Setting max charging current to" << current << thing;
everest->setMaxChargingCurrent(current);
thing->setStateValue(everestMaxChargingCurrentStateTypeId, current);
info->finish(Thing::ThingErrorNoError);
}
return;
}
info->finish(Thing::ThingErrorNoError);
}
void IntegrationPluginTruffle::thingRemoved(Thing *thing)
{
qCDebug(dcEverest()) << "Remove thing" << thing;
if (thing->thingClassId() == everestThingClassId) {
EverestClient *everestClient = m_thingClients.take(thing);
everestClient->removeThing(thing);
if (everestClient->things().isEmpty()) {
qCDebug(dcEverest()) << "Deleting" << everestClient << "since there is no thing left";
// No more things releated to this client, we can delete it
m_everstClients.removeAll(everestClient);
everestClient->deleteLater();
}
}
}

View File

@ -0,0 +1,66 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2024, 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 INTEGRATIONPLUGINEVEREST_H
#define INTEGRATIONPLUGINEVEREST_H
#include "integrations/integrationplugin.h"
#include "extern-plugininfo.h"
#include "everestclient.h"
#include <mqttclient.h>
class IntegrationPluginTruffle: public IntegrationPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationplugineverest.json")
Q_INTERFACES(IntegrationPlugin)
public:
explicit IntegrationPluginTruffle();
void init() override;
void startMonitoringAutoThings() override;
void discoverThings(ThingDiscoveryInfo *info) override;
void setupThing(ThingSetupInfo *info) override;
void thingRemoved(Thing *thing) override;
void executeAction(ThingActionInfo *info) override;
private:
QList<EverestClient *> m_everstClients;
QHash<Thing *, EverestClient *> m_thingClients;
};
#endif // INTEGRATIONPLUGINEVEREST_H

View File

@ -0,0 +1,164 @@
{
"name": "everest",
"displayName": "Everest",
"id": "27e4b1c4-bc27-4699-84ef-41af1a9e1cea",
"vendors": [
{
"name": "Chargebyte",
"displayName": "Chargebyte GmbH",
"id": "07ba8a98-799f-4a6e-a8d9-b45cd38dbcc5",
"thingClasses": [
{
"name": "everest",
"displayName": "Everest",
"id": "965cbe0d-088c-42a2-965d-ceafbb8b01e9",
"setupMethod": "JustAdd",
"createMethods": ["discovery", "user"],
"interfaces": [ "evcharger", "smartmeterconsumer", "connectable" ],
"paramTypes": [
{
"id": "911b6fa3-010c-486e-8251-71a6aa21adb3",
"name": "address",
"displayName": "Host address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": ""
},
{
"id": "cb9517ef-1ae2-49c9-9036-0c6e15bb3652",
"name": "mac",
"displayName": "MAC address",
"type": "QString",
"readOnly": true,
"defaultValue": "00:00:00:00:00:00"
},
{
"id": "73f27e36-6f68-40a9-8805-22e88911736c",
"name": "connector",
"displayName": "Connector name",
"type": "QString",
"defaultValue": ""
}
],
"settingsTypes": [],
"stateTypes": [
{
"id": "018d1f2f-4ede-4ab0-b76e-369684cd0da5",
"name": "power",
"displayName": "Charging enabled",
"displayNameAction": "Enable or disable charging",
"type": "bool",
"defaultValue": false,
"writable": true
},
{
"id": "d5d71037-a2b1-4abe-be75-883e6fef75c6",
"name": "maxChargingCurrent",
"displayName": "Maximum charging current",
"displayNameAction": "Set maximum charging current",
"type": "uint",
"unit": "Ampere",
"defaultValue": 6,
"minValue": 6,
"maxValue": 32,
"writable": true
},
{
"id": "5b963e54-15f0-459f-8754-d172f2febee2",
"name": "pluggedIn",
"displayName": "Plugged in",
"type": "bool",
"defaultValue": false
},
{
"id": "79f44541-7967-4394-bb50-8bf69e4115ca",
"name": "charging",
"displayName": "Charging",
"type": "bool",
"defaultValue": false
},
{
"id": "9597c21f-d8a6-424d-8e35-bdd69dc57d35",
"name": "phaseCount",
"displayName": "Active phases",
"type": "uint",
"minValue": 1,
"maxValue": 3,
"defaultValue": 1
},
{
"id": "2104641b-1004-4637-8eca-108f53dcb402",
"name": "connected",
"displayName": "Connected",
"type": "bool",
"cached": false,
"defaultValue": false
},
{
"id": "8946aee7-caa1-47cc-bef9-37ca4c8b5e49",
"name": "totalEnergyConsumed",
"displayName": "Total energy",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0,
"cached": true
},
{
"id": "b7455160-98e4-47ad-a1db-f3fbda009aa2",
"name": "sessionEnergy",
"displayName": "Session energy",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0,
"suggestLogging": true
},
{
"id": "120a4028-b01c-4a31-8d9b-bf34f602a095",
"name": "currentPower",
"displayName": "Current power",
"type": "double",
"unit": "Watt",
"defaultValue": 0,
"cached": false
},
{
"id": "75a52f23-7d65-4b33-9262-99085beb7ee2",
"name": "state",
"displayName": "State",
"type": "QString",
"defaultValue": "",
"cached": false
},
{
"id": "b7c76f2d-520a-4bb1-9bd3-d6ffcbef73b6",
"name": "temperature",
"displayName": "Temperature",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0.0,
"cached": false
},
{
"id": "2ca678ff-6d6f-49cf-b265-545ed15c12b7",
"name": "fanSpeed",
"displayName": "Fan speed",
"type": "double",
"unit": "Rpm",
"defaultValue": 0,
"cached": false
}
],
"actionTypes": [
],
"eventTypes": [
]
}
]
}
]
}

13
everest/meta.json Normal file
View File

@ -0,0 +1,13 @@
{
"title": "EVerest",
"tagline": "Control and monitor EVerest based EV chargers",
"icon": "everest.png",
"stability": "consumer",
"offline": true,
"technologies": [
"network"
],
"categories": [
"energy"
]
}

View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>IntegrationPluginTruffle</name>
<message>
<location filename="../integrationplugineverest.cpp" line="47"/>
<source>The network device discovery is not available.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>everest</name>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="50"/>
<source>1</source>
<extracomment>The name of a possible value of StateType {19abdab4-5607-4f6f-9f73-1c42ffe8939f} of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="53"/>
<source>3</source>
<extracomment>The name of a possible value of StateType {19abdab4-5607-4f6f-9f73-1c42ffe8939f} of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="56"/>
<source>Active phases</source>
<extracomment>The name of the StateType ({9597c21f-d8a6-424d-8e35-bdd69dc57d35}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="59"/>
<source>Chargebyte GmbH</source>
<extracomment>The name of the vendor ({07ba8a98-799f-4a6e-a8d9-b45cd38dbcc5})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="62"/>
<source>Charging</source>
<extracomment>The name of the StateType ({79f44541-7967-4394-bb50-8bf69e4115ca}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="65"/>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="68"/>
<source>Charging enabled</source>
<extracomment>The name of the ParamType (ThingClass: everest, ActionType: power, ID: {018d1f2f-4ede-4ab0-b76e-369684cd0da5})
----------
The name of the StateType ({018d1f2f-4ede-4ab0-b76e-369684cd0da5}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="71"/>
<source>Connected</source>
<extracomment>The name of the StateType ({2104641b-1004-4637-8eca-108f53dcb402}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="74"/>
<source>Connector name</source>
<extracomment>The name of the ParamType (ThingClass: everest, Type: thing, ID: {73f27e36-6f68-40a9-8805-22e88911736c})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="77"/>
<source>Current power</source>
<extracomment>The name of the StateType ({120a4028-b01c-4a31-8d9b-bf34f602a095}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="80"/>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="83"/>
<source>Desired phase count</source>
<extracomment>The name of the ParamType (ThingClass: everest, ActionType: desiredPhaseCount, ID: {19abdab4-5607-4f6f-9f73-1c42ffe8939f})
----------
The name of the StateType ({19abdab4-5607-4f6f-9f73-1c42ffe8939f}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="86"/>
<source>Enable or disable charging</source>
<extracomment>The name of the ActionType ({018d1f2f-4ede-4ab0-b76e-369684cd0da5}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="89"/>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="92"/>
<source>Everest</source>
<extracomment>The name of the ThingClass ({965cbe0d-088c-42a2-965d-ceafbb8b01e9})
----------
The name of the plugin everest ({27e4b1c4-bc27-4699-84ef-41af1a9e1cea})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="95"/>
<source>Fan speed</source>
<extracomment>The name of the StateType ({2ca678ff-6d6f-49cf-b265-545ed15c12b7}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="98"/>
<source>Host address</source>
<extracomment>The name of the ParamType (ThingClass: everest, Type: thing, ID: {911b6fa3-010c-486e-8251-71a6aa21adb3})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="101"/>
<source>MAC address</source>
<extracomment>The name of the ParamType (ThingClass: everest, Type: thing, ID: {cb9517ef-1ae2-49c9-9036-0c6e15bb3652})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="104"/>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="107"/>
<source>Maximum charging current</source>
<extracomment>The name of the ParamType (ThingClass: everest, ActionType: maxChargingCurrent, ID: {d5d71037-a2b1-4abe-be75-883e6fef75c6})
----------
The name of the StateType ({d5d71037-a2b1-4abe-be75-883e6fef75c6}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="110"/>
<source>Plugged in</source>
<extracomment>The name of the StateType ({5b963e54-15f0-459f-8754-d172f2febee2}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="113"/>
<source>Session energy</source>
<extracomment>The name of the StateType ({b7455160-98e4-47ad-a1db-f3fbda009aa2}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="116"/>
<source>Set desired phase count</source>
<extracomment>The name of the ActionType ({19abdab4-5607-4f6f-9f73-1c42ffe8939f}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="119"/>
<source>Set maximum charging current</source>
<extracomment>The name of the ActionType ({d5d71037-a2b1-4abe-be75-883e6fef75c6}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="122"/>
<source>State</source>
<extracomment>The name of the StateType ({75a52f23-7d65-4b33-9262-99085beb7ee2}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="125"/>
<source>Temperature</source>
<extracomment>The name of the StateType ({b7c76f2d-520a-4bb1-9bd3-d6ffcbef73b6}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../build-everest-Desktop-Debug/plugininfo.h" line="128"/>
<source>Total energy</source>
<extracomment>The name of the StateType ({8946aee7-caa1-47cc-bef9-37ca4c8b5e49}) of ThingClass everest</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View File

@ -23,6 +23,7 @@ PLUGIN_DIRS = \
eq-3 \
espuino \
evbox \
everest \
fastcom \
flowercare \
fronius \