mirror of
https://github.com/nymea/nymea-plugins.git
synced 2026-10-20 14:13:30 +02:00
New plugin: Senec: Add support for SENEC.home V2 and V3 energy storages
This commit is contained in:
parent
64b64f26d3
commit
8f72d83369
@ -714,6 +714,7 @@ Description: nymea integration plugin for TCP commander
|
||||
This package contains the nymea integration plugin for sending arbitrary
|
||||
TCP packets from and to nymea.
|
||||
|
||||
|
||||
Package: nymea-plugin-httpcommander
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
@ -724,6 +725,16 @@ Description: nymea integration plugin for HTTP commander
|
||||
HTTP requests from and to nymea.
|
||||
|
||||
|
||||
Package: nymea-plugin-senec
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
Conflicts: nymea-plugins-translations (<< 1.0.1)
|
||||
Description: nymea integration plugin for SENEC.home
|
||||
This package contains the nymea integration plugin for the SENEC.home
|
||||
energy storages.
|
||||
|
||||
|
||||
Package: nymea-plugin-senic
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
|
||||
2
debian-qt5/nymea-plugin-senec.install.in
Normal file
2
debian-qt5/nymea-plugin-senec.install.in
Normal file
@ -0,0 +1,2 @@
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginsenec.so
|
||||
senec/translations/*qm usr/share/nymea/translations/
|
||||
2
debian-qt6/nymea-plugin-senec.install.in
Normal file
2
debian-qt6/nymea-plugin-senec.install.in
Normal file
@ -0,0 +1,2 @@
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_integrationpluginsenec.so
|
||||
senec/translations/*qm usr/share/nymea/translations/
|
||||
@ -56,6 +56,7 @@ PLUGIN_DIRS = \
|
||||
pushbullet \
|
||||
pushnotifications \
|
||||
reversessh \
|
||||
senec \
|
||||
senic \
|
||||
serialportcommander \
|
||||
sgready \
|
||||
|
||||
11
senec/README.md
Normal file
11
senec/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# SENEC.Home
|
||||
|
||||
Connects to SENEC.Home energy storages in the local network and integrates them into the system. Optionally the grid meter of the storage can be added.
|
||||
|
||||
Currently supported and tested models:
|
||||
|
||||
* SENEC.Home P4
|
||||
|
||||
## More
|
||||
|
||||
https://senec.com
|
||||
259
senec/integrationpluginsenec.cpp
Normal file
259
senec/integrationpluginsenec.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2025, 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 "integrationpluginsenec.h"
|
||||
#include "senecdiscovery.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
IntegrationPluginSenec::IntegrationPluginSenec()
|
||||
{
|
||||
// Testing the convert methods
|
||||
|
||||
// QString rawValue = "fl_42A2E5E4"; // 81.449
|
||||
// float value = SenecStorageLan::parseFloat(rawValue);
|
||||
// qCWarning(dcSenec()) << rawValue << value;
|
||||
|
||||
// QString rawValue = "st_foobar"; // foobar
|
||||
// QString value = SenecStorageLan::parseString(rawValue);
|
||||
// qCWarning(dcSenec()) << rawValue << value;
|
||||
|
||||
// QString rawValue = "u8_64"; // 100
|
||||
// quint8 value = SenecStorageLan::parseUInt8(rawValue);
|
||||
// qCWarning(dcSenec()) << rawValue << value;
|
||||
}
|
||||
|
||||
IntegrationPluginSenec::~IntegrationPluginSenec()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IntegrationPluginSenec::discoverThings(ThingDiscoveryInfo *info)
|
||||
{
|
||||
if (info->thingClassId() == senecStorageLanThingClassId) {
|
||||
|
||||
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
|
||||
qCWarning(dcSenec()) << "Failed to discover network devices. The network device discovery is not available.";
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to discover devices in your network."));
|
||||
return;
|
||||
}
|
||||
|
||||
qCInfo(dcSenec()) << "Starting network discovery...";
|
||||
SenecDiscovery *discovery = new SenecDiscovery(hardwareManager()->networkManager(), hardwareManager()->networkDeviceDiscovery(), info);
|
||||
connect(discovery, &SenecDiscovery::discoveryFinished, info, [=](){
|
||||
ThingDescriptors descriptors;
|
||||
qCInfo(dcSenec()) << "Discovery finished. Found" << discovery->results().count() << "devices";
|
||||
foreach (const SenecDiscovery::Result &result, discovery->results()) {
|
||||
qCInfo(dcSenec()) << "Discovered device on" << result.networkDeviceInfo;
|
||||
|
||||
QString title = "SENEC connection (" + result.deviceId + ")";
|
||||
QString description = result.networkDeviceInfo.address().toString();
|
||||
|
||||
ThingDescriptor descriptor(senecStorageLanThingClassId, title, description);
|
||||
|
||||
ParamList params;
|
||||
params << Param(senecStorageLanThingMacAddressParamTypeId, result.networkDeviceInfo.thingParamValueMacAddress());
|
||||
params << Param(senecStorageLanThingHostNameParamTypeId, result.networkDeviceInfo.thingParamValueHostName());
|
||||
params << Param(senecStorageLanThingAddressParamTypeId, result.networkDeviceInfo.thingParamValueAddress());
|
||||
descriptor.setParams(params);
|
||||
|
||||
// Check if we already have set up this device
|
||||
Thing *existingThing = myThings().findByParams(params);
|
||||
if (existingThing) {
|
||||
qCDebug(dcSenec()) << "This thing already exists in the system:" << result.networkDeviceInfo;
|
||||
descriptor.setThingId(existingThing->id());
|
||||
}
|
||||
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
discovery->startDiscovery();
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginSenec::setupThing(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
qCDebug(dcSenec()) << "Setting up" << thing->name() << thing->params();
|
||||
|
||||
if (thing->thingClassId() == senecStorageLanThingClassId) {
|
||||
|
||||
// Handle reconfigure
|
||||
if (m_monitors.contains(thing)) {
|
||||
qCDebug(dcSenec()) << "Unregister existing monitor and recreate a new one...";
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
}
|
||||
|
||||
if (m_storages.contains(thing))
|
||||
m_storages.take(thing)->deleteLater();
|
||||
|
||||
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(thing);
|
||||
m_monitors.insert(thing, monitor);
|
||||
|
||||
SenecStorageLan *storage = new SenecStorageLan(hardwareManager()->networkManager(), this);
|
||||
m_storages.insert(thing, storage);
|
||||
|
||||
storage->setAddress(monitor->networkDeviceInfo().address());
|
||||
connect(monitor, &NetworkDeviceMonitor::reachableChanged, storage, [monitor, storage](bool reachable){
|
||||
if (reachable) {
|
||||
storage->setAddress(monitor->networkDeviceInfo().address());
|
||||
}
|
||||
});
|
||||
|
||||
connect(storage, &SenecStorageLan::availableChanged, thing, [this, thing](bool available){
|
||||
setConnected(thing, available);
|
||||
});
|
||||
|
||||
connect(storage, &SenecStorageLan::initializeFinished, thing, [storage, thing](bool success){
|
||||
if (success) {
|
||||
thing->setStateValue(senecStorageLanCapacityStateTypeId, storage->capacity());
|
||||
}
|
||||
});
|
||||
|
||||
connect(storage, &SenecStorageLan::updatedFinished, thing, [storage, thing, this](bool success){
|
||||
setConnected(thing, storage->available());
|
||||
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
int batteryLevel = qRound(storage->batteryLevel());
|
||||
thing->setStateValue(senecStorageLanBatteryLevelStateTypeId, batteryLevel);
|
||||
thing->setStateValue(senecStorageLanBatteryCriticalStateTypeId, batteryLevel < 10);
|
||||
thing->setStateValue(senecStorageLanCurrentPowerStateTypeId, storage->batteryPower());
|
||||
if (storage->batteryPower() > 0) {
|
||||
thing->setStateValue(senecStorageLanChargingStateStateTypeId, "charging");
|
||||
} else if (storage->batteryPower() < 0) {
|
||||
thing->setStateValue(senecStorageLanChargingStateStateTypeId, "discharging");
|
||||
} else {
|
||||
thing->setStateValue(senecStorageLanChargingStateStateTypeId, "idle");
|
||||
}
|
||||
|
||||
Thing *meter = meterThing(thing);
|
||||
if (meter) {
|
||||
meter->setStateValue(senecMeterCurrentPowerStateTypeId, storage->gridPower());
|
||||
}
|
||||
});
|
||||
|
||||
connect(thing, &Thing::settingChanged, this, [this, thing](const ParamTypeId ¶mTypeId, const QVariant &value){
|
||||
if (paramTypeId == senecStorageLanSettingsAddMeterParamTypeId) {
|
||||
|
||||
if (value.toBool()) {
|
||||
// Check if we have to add the meter
|
||||
if (!meterThing(thing)) {
|
||||
qCDebug(dcSenec()) << "Add meter for" << thing->name();
|
||||
emit autoThingsAppeared(ThingDescriptors() << ThingDescriptor(senecMeterThingClassId, "SENEC Meter", QString(), thing->id()));
|
||||
}
|
||||
} else {
|
||||
// Check if we have to remove the meter
|
||||
Thing *meter = meterThing(thing);
|
||||
if (meter) {
|
||||
qCDebug(dcSenec()) << "Remove meter thing for" << thing->name();
|
||||
emit autoThingDisappeared(meter->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
} else if (thing->thingClassId() == senecMeterThingClassId) {
|
||||
Thing *parentThing = myThings().findById(thing->parentId());
|
||||
if (parentThing) {
|
||||
thing->setStateValue(senecMeterConnectedStateTypeId, parentThing->stateValue(senecStorageLanConnectedStateTypeId).toBool());
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginSenec::postSetupThing(Thing *thing)
|
||||
{
|
||||
if (thing->thingClassId() == senecStorageLanThingClassId) {
|
||||
m_storages.value(thing)->initialize();
|
||||
}
|
||||
|
||||
// Create the refresh timer if not already set up
|
||||
if (!m_refreshTimer) {
|
||||
qCDebug(dcSenec()) << "Starting refresh timer ...";
|
||||
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(5);
|
||||
connect(m_refreshTimer, &PluginTimer::timeout, this, &IntegrationPluginSenec::refresh);
|
||||
m_refreshTimer->start();
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginSenec::thingRemoved(Thing *thing)
|
||||
{
|
||||
if (thing->thingClassId() == senecStorageLanThingClassId) {
|
||||
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
|
||||
m_storages.take(thing)->deleteLater();
|
||||
}
|
||||
|
||||
if (myThings().isEmpty()) {
|
||||
qCDebug(dcSenec()) << "Stopping refresh timer";
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
|
||||
m_refreshTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Thing *IntegrationPluginSenec::meterThing(Thing *storageThing) const
|
||||
{
|
||||
Things meterThings = myThings().filterByThingClassId(senecMeterThingClassId).filterByParentId(storageThing->id());
|
||||
if (meterThings.isEmpty())
|
||||
return nullptr;
|
||||
|
||||
return meterThings.first();
|
||||
}
|
||||
|
||||
void IntegrationPluginSenec::setConnected(Thing *storageThing, bool connected)
|
||||
{
|
||||
storageThing->setStateValue(senecStorageLanConnectedStateTypeId, connected);
|
||||
|
||||
Thing *meter = meterThing(storageThing);
|
||||
if (meter) {
|
||||
meter->setStateValue(senecMeterConnectedStateTypeId, connected);
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginSenec::refresh()
|
||||
{
|
||||
foreach (Thing *storageThing, myThings().filterByThingClassId(senecStorageLanThingClassId)) {
|
||||
SenecStorageLan *storage = m_storages.value(storageThing);
|
||||
// Retry fetching the static information until the storage could be initialized once
|
||||
if (storage->initialized()) {
|
||||
storage->update();
|
||||
} else {
|
||||
storage->initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
75
senec/integrationpluginsenec.h
Normal file
75
senec/integrationpluginsenec.h
Normal file
@ -0,0 +1,75 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2025, 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 INTEGRATIONPLUGINSENEC_H
|
||||
#define INTEGRATIONPLUGINSENEC_H
|
||||
|
||||
#include <plugintimer.h>
|
||||
#include <integrations/integrationplugin.h>
|
||||
|
||||
#include <network/networkaccessmanager.h>
|
||||
#include <network/networkdevicemonitor.h>
|
||||
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include "senecstoragelan.h"
|
||||
|
||||
class IntegrationPluginSenec : public IntegrationPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginsenec.json")
|
||||
Q_INTERFACES(IntegrationPlugin)
|
||||
|
||||
public:
|
||||
explicit IntegrationPluginSenec();
|
||||
~IntegrationPluginSenec();
|
||||
|
||||
void discoverThings(ThingDiscoveryInfo *info) override;
|
||||
|
||||
void setupThing(ThingSetupInfo *info) override;
|
||||
void postSetupThing(Thing *thing) override;
|
||||
void thingRemoved(Thing *thing) override;
|
||||
|
||||
private:
|
||||
PluginTimer *m_refreshTimer = nullptr;
|
||||
|
||||
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
|
||||
QHash<Thing *, SenecStorageLan *> m_storages;
|
||||
|
||||
Thing *meterThing(Thing *storageThing) const;
|
||||
void setConnected(Thing *storageThing, bool connected);
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINSENEC_H
|
||||
232
senec/integrationpluginsenec.json
Normal file
232
senec/integrationpluginsenec.json
Normal file
@ -0,0 +1,232 @@
|
||||
{
|
||||
"displayName": "SENEC",
|
||||
"name": "Senec",
|
||||
"id": "3f055ea5-a883-445d-9556-675f5eda6c9a",
|
||||
"vendors": [
|
||||
{
|
||||
"displayName": "SENEC",
|
||||
"name": "senec",
|
||||
"id": "1be922b9-4439-42cf-9caa-67f2ac7cc425",
|
||||
"thingClasses": [
|
||||
{
|
||||
"id": "345df3b1-d411-4db5-bbb2-3b14eb86c1ba",
|
||||
"name": "senecStorageLan",
|
||||
"displayName": "SENEC.Home storage",
|
||||
"interfaces": ["energystorage", "networkdevice"],
|
||||
"createMethods": ["Discovery", "User"],
|
||||
"settingsTypes": [
|
||||
{
|
||||
"id": "239d388c-f4a2-4055-88c3-decc6dace2b8",
|
||||
"name": "addMeter",
|
||||
"displayName": "Add meter",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
}
|
||||
],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "a45cabe9-9f76-405e-b47c-0a3d00bdd44b",
|
||||
"name": "address",
|
||||
"displayName": "Host address",
|
||||
"type": "QString",
|
||||
"inputType": "IPv4Address",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "51a1bbc0-28a5-43ac-9021-896acbf9413f",
|
||||
"name": "hostName",
|
||||
"displayName": "Host name",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "0790e1b1-c8c9-488f-9cda-2a0bbf14b4af",
|
||||
"name": "macAddress",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString",
|
||||
"inputType": "MacAddress",
|
||||
"readOnly": true,
|
||||
"defaultValue": ""
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "d3bdc64b-67fb-4d84-97ed-c5142e5db55d",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "3b00470d-11bc-4352-a885-52ab9443cee5",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0.00,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "66a3a7d2-1210-4891-a8f2-6fe4f9b8c6cc",
|
||||
"name": "batteryLevel",
|
||||
"displayName": "Battery level",
|
||||
"type": "int",
|
||||
"unit": "Percentage",
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "a9aa13fb-3f51-40e3-9d29-ab882513a87a",
|
||||
"name": "batteryCritical",
|
||||
"displayName": "Battery critical",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "339f602f-c9f1-446d-bc96-1c7f9a7db922",
|
||||
"name": "chargingState",
|
||||
"displayName": "Charging state",
|
||||
"type": "QString",
|
||||
"possibleValues": ["idle", "charging", "discharging"],
|
||||
"defaultValue": "idle"
|
||||
},
|
||||
{
|
||||
"id": "d927e6cf-29f0-412f-b74d-7d803fd3e191",
|
||||
"name": "capacity",
|
||||
"displayName": "Capacity",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "senecMeter",
|
||||
"displayName": "SENEC Meter",
|
||||
"id": "303f454d-93b7-4f8f-b295-a4c22e09be6d",
|
||||
"createMethods": [ "auto" ],
|
||||
"interfaces": ["energymeter", "connectable"],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "9aa4dbfb-255b-408b-b148-8fb145ba8c98",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "56e58d03-782d-40bb-8d32-fad396ffefdc",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power usage",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "f9920cfa-26a0-4247-ad50-b4fca91596f0",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "Energy returned",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "21728bf0-a566-4eb0-a163-5f108f34bb9b",
|
||||
"name": "totalEnergyConsumed",
|
||||
"displayName": "Energy consumed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "385df15e-4f1d-4842-a10a-597d087bdac0",
|
||||
"name": "currentPowerPhaseA",
|
||||
"displayName": "Current power phase A",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "3bab2b05-99d7-4b3a-b903-054d269a3527",
|
||||
"name": "currentPowerPhaseB",
|
||||
"displayName": "Current power phase B",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "01a63c94-b635-4e98-8037-d5d69fe0dd7c",
|
||||
"name": "currentPowerPhaseC",
|
||||
"displayName": "Current power phase C",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "db469573-bb11-48d2-84ba-2b8ff4b13413",
|
||||
"name": "voltagePhaseA",
|
||||
"displayName": "Voltage phase A",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "176d5a01-220d-44da-b875-9a40aaa9c85a",
|
||||
"name": "voltagePhaseB",
|
||||
"displayName": "Voltage phase B",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "e7ab02d3-016a-44ca-8da1-5d7ac9acbc6d",
|
||||
"name": "voltagePhaseC",
|
||||
"displayName": "Voltage phase C",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "1923f7b0-5b54-46d8-a7e9-9364b7c9eeb8",
|
||||
"name": "currentPhaseA",
|
||||
"displayName": "Current phase A",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "4908abf8-f655-4e14-a007-3eefeda7088e",
|
||||
"name": "currentPhaseB",
|
||||
"displayName": "Current phase B",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "e8415152-0fac-4cd9-98ac-b0e7c006d637",
|
||||
"name": "currentPhaseC",
|
||||
"displayName": "Current phase C",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0,
|
||||
"cached": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
13
senec/meta.json
Normal file
13
senec/meta.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "SENEC",
|
||||
"tagline": "Integrate SENEC.Home energy storages from the local network.",
|
||||
"icon": "senec.svg",
|
||||
"stability": "community",
|
||||
"offline": true,
|
||||
"technologies": [
|
||||
"network"
|
||||
],
|
||||
"categories": [
|
||||
"energy"
|
||||
]
|
||||
}
|
||||
14
senec/senec.pro
Normal file
14
senec/senec.pro
Normal file
@ -0,0 +1,14 @@
|
||||
include(../plugins.pri)
|
||||
|
||||
QT+= network
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginsenec.cpp \
|
||||
senecdiscovery.cpp \
|
||||
senecstoragelan.cpp
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginsenec.h \
|
||||
senecdiscovery.h \
|
||||
senecstoragelan.h
|
||||
|
||||
40
senec/senec.svg
Normal file
40
senec/senec.svg
Normal file
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
data-name="Ebene 1"
|
||||
viewBox="0 0 100 23"
|
||||
version="1.1"
|
||||
id="svg6"
|
||||
sodipodi:docname="senec_logo_de_clean_resize_1.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
width="100"
|
||||
height="23"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
id="namedview8"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.0458796"
|
||||
inkscape:cx="5.9096229"
|
||||
inkscape:cy="65.170008"
|
||||
inkscape:window-width="1728"
|
||||
inkscape:window-height="1006"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg6" />
|
||||
<path
|
||||
d="m 16.483695,17.845517 c 0,2.942408 -2.713129,5.216087 -6.9738891,5.216087 H 1.045605 c -0.55408995,0 -1.01264754,-0.458557 -1.01264754,-1.012647 v -2.445638 c 0,-0.55409 0.43945112,-1.012647 0.99354104,-1.031754 h 7.5661923 c 0.6687298,0 2.7322372,0 2.7322372,-1.757802 V 15.973075 A 1.6259672,1.6259672 0 0 0 9.8537235,14.215273 C 9.2614206,14.06242 4.6376363,13.030667 4.1981852,12.95424 A 5.1797851,5.1797851 0 0 1 0.01385098,7.4897667 V 5.7892839 C 0.01385098,2.789556 2.6505554,0 7.1596997,0 h 8.2158163 c 0.592303,0 0.802475,0.43945066 0.515876,0.84068815 L 14.381975,3.592031 C 14.076271,4.1079077 13.522181,4.432719 12.910771,4.432719 H 7.2552332 c -1.1463929,0 -2.0444004,0.9553274 -2.0444004,2.1017203 v 0.5731964 c 0.057314,0.7642619 0.6305158,1.4138846 1.394777,1.5476304 0,0 4.0314822,0.8406881 5.5791122,1.10818 3.229007,0.6687289 4.337187,2.9424099 4.337187,5.0059169 v 3.076154 z M 20.66803,1.0317536 C 20.66803,0.47766372 21.107481,0.01910658 21.661571,0 h 14.501868 c 0.592304,0 0.840688,0.36302443 0.515877,0.84068815 L 35.131686,3.592031 C 34.787769,4.0696947 34.252785,4.3753994 33.660482,4.432719 H 21.623357 c -0.554089,0 -1.012647,-0.420344 -1.031753,-0.9744339 V 3.4200721 Z M 55.499266,0 c -0.55409,0 -1.012647,0.42034408 -1.031753,0.97443391 V 13.56565 c 0,0.592303 -0.229279,0.668729 -0.592303,0.152852 L 45.277263,0.84068815 C 44.914238,0.34391785 44.341042,0.03821306 43.729633,0 H 41.455954 C 40.901864,0 40.443306,0.42034408 40.4242,0.97443391 V 22.02985 c 0,0.55409 0.420344,1.012648 0.974434,1.031754 h 3.114367 c 0.55409,0 1.012647,-0.420344 1.031753,-0.974434 V 9.9162979 c 0,-0.5923027 0.229279,-0.6687289 0.592303,-0.1528518 l 8.521521,12.4956829 c 0.363025,0.496771 0.936221,0.802475 1.547631,0.840688 h 2.426531 c 0.55409,0 1.012647,-0.420343 1.031754,-0.974433 V 1.0317536 A 1.0317536,1.0317536 0 0 0 58.670953,0 H 55.480159 Z M 21.699784,18.590672 c -0.55409,0 -1.012648,0.420344 -1.031754,0.974434 v 0.03821 l -0.07643,2.407425 c 0,0.55409 0.420343,1.012647 0.974433,1.031754 h 12.094445 c 0.592303,-0.05732 1.127287,-0.363024 1.471204,-0.840688 l 1.54763,-2.732237 c 0.286599,-0.515877 0.07643,-0.840688 -0.515877,-0.840688 L 21.699784,18.571565 Z M 20.66803,10.069151 c 0,-0.5540902 0.439451,-1.0126474 0.993541,-1.031754 h 11.998911 c 0.592303,0 0.840689,0.3630245 0.515876,0.840688 l -1.54763,2.713131 c -0.343917,0.477664 -0.878901,0.783369 -1.471204,0.840688 H 21.60425 c -0.55409,0 -1.012646,-0.420344 -1.031753,-0.974434 v -0.05732 l 0.07643,-2.350106 z M 65.855015,0 c -0.55409,0 -1.012647,0.42034408 -1.031754,0.97443391 v 0.0573194 l 0.07643,2.4265316 c 0,0.5540899 0.439451,1.0126471 0.993541,1.0317535 h 11.903379 c 0.592304,-0.057319 1.127286,-0.3630243 1.471205,-0.840688 L 80.853655,0.85979441 C 81.159359,0.34391785 80.93008,0.01910658 80.337778,0.01910658 H 65.874122 Z m -1.031754,19.603319 c 0,-0.55409 0.420344,-1.012647 0.974434,-1.031754 h 14.520976 c 0.592303,0 0.840688,0.363024 0.515877,0.840688 l -1.54763,2.713131 c -0.343919,0.477663 -0.878901,0.783367 -1.471204,0.840688 H 65.950548 c -0.55409,0.03821 -1.031754,-0.382132 -1.069967,-0.936222 v -0.03821 l -0.07643,-2.407424 z M 65.855015,9.037397 c -0.55409,0 -1.012647,0.4203441 -1.031754,0.974434 v 0.05731 l 0.07643,2.426533 c -0.03821,0.55409 0.382132,1.031753 0.955328,1.069967 h 9.495954 c 0.592303,-0.05733 1.127287,-0.363025 1.471204,-0.840688 l 1.54763,-2.713132 c 0.305702,-0.515867 0.07642,-0.8406783 -0.51588,-0.8406783 L 65.874122,9.0756101 Z m 25.62188,14.024207 h 5.36894 c 0.611409,0 1.165502,-0.324811 1.471206,-0.840688 l 1.54763,-2.732237 c 0.305699,-0.43945 0.07643,-0.840688 -0.51588,-0.840688 H 93.043632 C 90.024797,18.495139 89.012151,17.253214 89.012151,14.406339 V 8.0820696 a 3.5347113,3.5347113 0 0 1 3.439178,-3.6111374 h 4.337186 c 0.61141,0 1.165498,-0.3248114 1.471201,-0.8406882 l 1.54763,-2.78955585 C 100.11305,0.40123749 99.883772,0 99.291477,0 H 91.610641 C 86.834004,0 83.81517,3.6684572 83.81517,8.3113482 v 6.5344408 c 0.09554,4.26076 2.311892,8.234922 7.661725,8.234922 z"
|
||||
style="fill:#000099;fill-rule:evenodd;stroke-width:1"
|
||||
id="path4" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
112
senec/senecdiscovery.cpp
Normal file
112
senec/senecdiscovery.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2025, 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 "senecdiscovery.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
SenecDiscovery::SenecDiscovery(NetworkAccessManager *networkManager, NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent)
|
||||
: QObject{parent},
|
||||
m_networkManager{networkManager},
|
||||
m_networkDeviceDiscovery{networkDeviceDiscovery}
|
||||
{
|
||||
m_gracePeriodTimer.setSingleShot(true);
|
||||
m_gracePeriodTimer.setInterval(3000);
|
||||
connect(&m_gracePeriodTimer, &QTimer::timeout, this, [this](){
|
||||
finishDiscovery();
|
||||
});
|
||||
}
|
||||
|
||||
void SenecDiscovery::startDiscovery()
|
||||
{
|
||||
qCInfo(dcSenec()) << "Discovery: Searching SENEC energy storages in the local network...";
|
||||
m_startDateTime = QDateTime::currentDateTime();
|
||||
|
||||
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::hostAddressDiscovered, this, &SenecDiscovery::checkNetworkDevice);
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
|
||||
qCDebug(dcSenec()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices";
|
||||
m_networkDeviceInfos = discoveryReply->networkDeviceInfos();
|
||||
m_gracePeriodTimer.start();
|
||||
discoveryReply->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
QList<SenecDiscovery::Result> SenecDiscovery::results() const
|
||||
{
|
||||
return m_results;
|
||||
}
|
||||
|
||||
void SenecDiscovery::checkNetworkDevice(const QHostAddress &address)
|
||||
{
|
||||
qCDebug(dcSenec()) << "Discovery: Verifying" << address;
|
||||
|
||||
SenecStorageLan *storage = new SenecStorageLan(m_networkManager, address, this);
|
||||
m_storages.append(storage);
|
||||
|
||||
connect(storage, &SenecStorageLan::initializeFinished, this, [this, storage, address](bool success){
|
||||
if (!success) {
|
||||
qCDebug(dcSenec()) << "Discovery: Failed to initialize host" << address.toString() << "Continue ...";
|
||||
cleanupStorage(storage);
|
||||
return;
|
||||
}
|
||||
|
||||
// Successfully initialized
|
||||
Result result;
|
||||
result.deviceId = storage->deviceId();
|
||||
result.address = address;
|
||||
m_results.append(result);
|
||||
|
||||
qCInfo(dcSenec()) << "Discovery: Found SENEC storage on" << address.toString() << storage->deviceId();
|
||||
cleanupStorage(storage);
|
||||
});
|
||||
|
||||
storage->initialize();
|
||||
}
|
||||
|
||||
void SenecDiscovery::cleanupStorage(SenecStorageLan *storage)
|
||||
{
|
||||
m_storages.removeAll(storage);
|
||||
storage->deleteLater();
|
||||
}
|
||||
|
||||
void SenecDiscovery::finishDiscovery()
|
||||
{
|
||||
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
|
||||
|
||||
// Fill in all network device infos we have
|
||||
for (int i = 0; i < m_results.count(); i++)
|
||||
m_results[i].networkDeviceInfo = m_networkDeviceInfos.get(m_results.at(i).address);
|
||||
|
||||
qCInfo(dcSenec()) << "Discovery: Finished the discovery process. Found" << m_results.count()
|
||||
<< "SENEC devices in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
|
||||
m_gracePeriodTimer.stop();
|
||||
|
||||
emit discoveryFinished();
|
||||
}
|
||||
79
senec/senecdiscovery.h
Normal file
79
senec/senecdiscovery.h
Normal file
@ -0,0 +1,79 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2025, 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 SENECDISCOVERY_H
|
||||
#define SENECDISCOVERY_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include <network/networkaccessmanager.h>
|
||||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
#include "senecstoragelan.h"
|
||||
|
||||
class SenecDiscovery : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SenecDiscovery(NetworkAccessManager *networkManager, NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr);
|
||||
|
||||
typedef struct Result {
|
||||
QString deviceId;
|
||||
QHostAddress address;
|
||||
NetworkDeviceInfo networkDeviceInfo;
|
||||
} Result;
|
||||
|
||||
void startDiscovery();
|
||||
|
||||
QList<SenecDiscovery::Result> results() const;
|
||||
|
||||
signals:
|
||||
void discoveryFinished();
|
||||
|
||||
private:
|
||||
NetworkAccessManager *m_networkManager = nullptr;
|
||||
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||
|
||||
QTimer m_gracePeriodTimer;
|
||||
QDateTime m_startDateTime;
|
||||
|
||||
NetworkDeviceInfos m_networkDeviceInfos;
|
||||
QList<SenecStorageLan *> m_storages;
|
||||
QList<SenecDiscovery::Result> m_results;
|
||||
|
||||
void checkNetworkDevice(const QHostAddress &address);
|
||||
void cleanupStorage(SenecStorageLan *storage);
|
||||
|
||||
void finishDiscovery();
|
||||
|
||||
};
|
||||
|
||||
#endif // SENECDISCOVERY_H
|
||||
351
senec/senecstoragelan.cpp
Normal file
351
senec/senecstoragelan.cpp
Normal file
@ -0,0 +1,351 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2025, 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 "senecstoragelan.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
|
||||
SenecStorageLan::SenecStorageLan(NetworkAccessManager *networkManager, QObject *parent)
|
||||
: QObject{parent},
|
||||
m_networkManager{networkManager}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SenecStorageLan::SenecStorageLan(NetworkAccessManager *networkManager, const QHostAddress &address, QObject *parent)
|
||||
: QObject{parent},
|
||||
m_networkManager{networkManager},
|
||||
m_address{address}
|
||||
{
|
||||
updateUrl();
|
||||
}
|
||||
|
||||
QHostAddress SenecStorageLan::address() const
|
||||
{
|
||||
return m_address;
|
||||
}
|
||||
|
||||
void SenecStorageLan::setAddress(const QHostAddress &address)
|
||||
{
|
||||
m_address = address;
|
||||
updateUrl();
|
||||
}
|
||||
|
||||
QUrl SenecStorageLan::url() const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
bool SenecStorageLan::available() const
|
||||
{
|
||||
return m_available;
|
||||
}
|
||||
|
||||
bool SenecStorageLan::initialized() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
QString SenecStorageLan::deviceId() const
|
||||
{
|
||||
return m_deviceId;
|
||||
}
|
||||
|
||||
float SenecStorageLan::capacity() const
|
||||
{
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
float SenecStorageLan::maxChargePower() const
|
||||
{
|
||||
return m_maxChargePower;
|
||||
}
|
||||
|
||||
float SenecStorageLan::maxDischargePower() const
|
||||
{
|
||||
return m_maxDischargePower;
|
||||
}
|
||||
|
||||
float SenecStorageLan::batteryLevel() const
|
||||
{
|
||||
return m_batteryLevel;
|
||||
}
|
||||
|
||||
float SenecStorageLan::batteryPower() const
|
||||
{
|
||||
return m_batteryPower;
|
||||
}
|
||||
|
||||
float SenecStorageLan::gridPower() const
|
||||
{
|
||||
return m_gridPower;
|
||||
}
|
||||
|
||||
float SenecStorageLan::inverterPower() const
|
||||
{
|
||||
return m_inverterPower;
|
||||
}
|
||||
|
||||
float SenecStorageLan::parseFloat(const QString &value)
|
||||
{
|
||||
if (!value.startsWith("fl_")) {
|
||||
qCWarning(dcSenec()) << "The given value does not seem to be a float, it is not starting with fl_" << value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
QByteArray rawData = QByteArray::fromHex(value.mid(3).toUtf8());
|
||||
if (rawData.size() != 4) {
|
||||
qCWarning(dcSenec()) << "Invalid raw data size for converting value to float32" << value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
float result = 0;
|
||||
quint32 dataValue = 0;
|
||||
QDataStream stream(rawData);
|
||||
stream >> dataValue;
|
||||
memcpy(&result, &dataValue, sizeof(quint32));
|
||||
return result;
|
||||
}
|
||||
|
||||
QString SenecStorageLan::parseString(const QString &value)
|
||||
{
|
||||
if (!value.startsWith("st_")) {
|
||||
qCWarning(dcSenec()) << "The given value does not seem to be a string, it is not starting with st_" << value;
|
||||
return QString();
|
||||
}
|
||||
|
||||
return value.mid(3);
|
||||
}
|
||||
|
||||
// quint8 SenecStorageLan::parseUInt8(const QString &value)
|
||||
// {
|
||||
// Q_ASSERT_X(value.left(3) == "u8_", "SenecStorageLan", "The given value does not seem to be a uint8, it is not starting with u8_");
|
||||
|
||||
// }
|
||||
|
||||
|
||||
void SenecStorageLan::initialize()
|
||||
{
|
||||
if (!m_url.isValid()) {
|
||||
qCWarning(dcSenec()) << "Cannot initialize the storage. The request URL is not valid. Maybe the IP is not known yet or invalid.";
|
||||
emit initializeFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify the debug request is working
|
||||
QVariantMap request;
|
||||
request.insert("DEBUG", QVariantMap());
|
||||
|
||||
QNetworkReply *reply = m_networkManager->post(QNetworkRequest(m_url), QJsonDocument::fromVariant(request).toJson());
|
||||
connect(reply, &QNetworkReply::sslErrors, this, &SenecStorageLan::ignoreSslErrors);
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply] {
|
||||
|
||||
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
// Check HTTP status code
|
||||
if (status != 200 || reply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcSenec()) << "Debug request finished with error. Status:" << status << "Error:" << reply->errorString();
|
||||
setAvailable(false);
|
||||
emit initializeFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray responseData = reply->readAll();
|
||||
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData, &jsonError);
|
||||
QVariantMap responseMap = jsonDoc.toVariant().toMap();
|
||||
if (jsonError.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcSenec()) << "Debug request finished successfully, but the response contains invalid JSON object:" << responseData;
|
||||
setAvailable(false);
|
||||
emit initializeFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify content, if the content is valid, we assume we have found a SENEC storage
|
||||
if (!responseMap.contains("DEBUG")) {
|
||||
qCWarning(dcSenec()) << "Unexpected reponse data from debug request. Aborting...";
|
||||
setAvailable(false);
|
||||
emit initializeFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcSenec()) << "Debug request finished successfully" << qUtf8Printable(jsonDoc.toJson());
|
||||
|
||||
// Request basic information
|
||||
|
||||
QVariantMap request;
|
||||
QVariantMap factoryMap;
|
||||
factoryMap.insert("DEVICE_ID", QString());
|
||||
factoryMap.insert("DESIGN_CAPACITY", QString());
|
||||
factoryMap.insert("MAX_CHARGE_POWER_DC", QString());
|
||||
factoryMap.insert("MAX_DISCHARGE_POWER_DC", QString());
|
||||
request.insert("FACTORY", factoryMap);
|
||||
|
||||
QNetworkReply *reply = m_networkManager->post(QNetworkRequest(m_url), QJsonDocument::fromVariant(request).toJson());
|
||||
connect(reply, &QNetworkReply::sslErrors, this, &SenecStorageLan::ignoreSslErrors);
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply] {
|
||||
|
||||
|
||||
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
// Check HTTP status code
|
||||
if (status != 200 || reply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcSenec()) << "Factory request finished with error. Status:" << status << "Error:" << reply->errorString();
|
||||
setAvailable(false);
|
||||
emit initializeFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray responseData = reply->readAll();
|
||||
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData, &jsonError);
|
||||
QVariantMap responseMap = jsonDoc.toVariant().toMap();
|
||||
if (jsonError.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcSenec()) << "Factory request finished successfully, but the response contains invalid JSON object:" << responseData;
|
||||
setAvailable(false);
|
||||
emit initializeFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcSenec()) << "Factory request finished successfully" << qUtf8Printable(jsonDoc.toJson());
|
||||
|
||||
QVariantMap factoryResponseMap = responseMap.value("FACTORY").toMap();
|
||||
m_deviceId = parseString(factoryResponseMap.value("DEVICE_ID").toString());
|
||||
m_capacity = parseFloat(factoryResponseMap.value("DESIGN_CAPACITY").toString());
|
||||
m_maxChargePower = parseFloat(factoryResponseMap.value("MAX_CHARGE_POWER_DC").toString());
|
||||
m_maxDischargePower = parseFloat(factoryResponseMap.value("MAX_DISCHARGE_POWER_DC").toString());
|
||||
|
||||
m_initialized = true;
|
||||
emit initializeFinished(true);
|
||||
setAvailable(true);
|
||||
|
||||
qCDebug(dcSenec()) << "Initialized successfully";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void SenecStorageLan::update()
|
||||
{
|
||||
if (!m_url.isValid()) {
|
||||
qCDebug(dcSenec()) << "Cannot update the storage. The request URL is not valid. Maybe the IP is not known yet or invalid.";
|
||||
emit updatedFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap request;
|
||||
|
||||
QVariantMap energyMap;
|
||||
energyMap.insert("GUI_BAT_DATA_POWER", QString());
|
||||
energyMap.insert("GUI_INVERTER_POWER", QString());
|
||||
energyMap.insert("GUI_BAT_DATA_FUEL_CHARGE", QString());
|
||||
energyMap.insert("GUI_HOUSE_POW", QString());
|
||||
energyMap.insert("GUI_GRID_POW", QString());
|
||||
|
||||
request.insert("ENERGY", energyMap);
|
||||
|
||||
QNetworkReply *reply = m_networkManager->post(QNetworkRequest(m_url), QJsonDocument::fromVariant(request).toJson());
|
||||
connect(reply, &QNetworkReply::sslErrors, this, &SenecStorageLan::ignoreSslErrors);
|
||||
connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater);
|
||||
connect(reply, &QNetworkReply::finished, this, [this, reply] {
|
||||
|
||||
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
// Check HTTP status code
|
||||
if (status != 200 || reply->error() != QNetworkReply::NoError) {
|
||||
qCWarning(dcSenec()) << "Update request finished with error. Status:" << status << "Error:" << reply->errorString();
|
||||
setAvailable(false);
|
||||
emit updatedFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray responseData = reply->readAll();
|
||||
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData, &jsonError);
|
||||
QVariantMap responseMap = jsonDoc.toVariant().toMap();
|
||||
if (jsonError.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcSenec()) << "Update request finished successfully, but the response contains invalid JSON object:" << responseData;
|
||||
setAvailable(false);
|
||||
emit updatedFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcSenec()) << "Update request finished successfully" << qUtf8Printable(jsonDoc.toJson());
|
||||
|
||||
QVariantMap energyResponseMap = responseMap.value("ENERGY").toMap();
|
||||
m_batteryPower = parseFloat(energyResponseMap.value("GUI_BAT_DATA_POWER").toString());
|
||||
m_batteryLevel = parseFloat(energyResponseMap.value("GUI_BAT_DATA_FUEL_CHARGE").toString());
|
||||
m_gridPower = parseFloat(energyResponseMap.value("GUI_GRID_POW").toString());
|
||||
m_inverterPower = parseFloat(energyResponseMap.value("GUI_INVERTER_POWER").toString());
|
||||
|
||||
setAvailable(true);
|
||||
|
||||
qCDebug(dcSenec()).nospace().noquote() << "Update values: Battery power: " << m_batteryPower
|
||||
<< "W, Battery level: " << m_batteryLevel
|
||||
<< "%, Grid power: " << m_gridPower
|
||||
<< "W, Inverter power: " << m_inverterPower << "W";
|
||||
|
||||
emit updatedFinished(true);
|
||||
});
|
||||
}
|
||||
|
||||
void SenecStorageLan::updateUrl()
|
||||
{
|
||||
QUrl url;
|
||||
if (m_address.isNull()) {
|
||||
m_url = url;
|
||||
return;
|
||||
}
|
||||
|
||||
url.setScheme("https");
|
||||
url.setHost(m_address.toString());
|
||||
url.setPath("/lala.cgi");
|
||||
m_url = url;
|
||||
}
|
||||
|
||||
void SenecStorageLan::setAvailable(bool available)
|
||||
{
|
||||
if (m_available == available)
|
||||
return;
|
||||
|
||||
m_available = available;
|
||||
emit availableChanged(m_available);
|
||||
}
|
||||
|
||||
void SenecStorageLan::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
qCDebug(dcSenec()) << "SSL errors occurred" << errors << "but we are ignoring them in the LAN environment...";
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||
reply->ignoreSslErrors();
|
||||
}
|
||||
112
senec/senecstoragelan.h
Normal file
112
senec/senecstoragelan.h
Normal file
@ -0,0 +1,112 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2025, 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 SENECSTORAGELAN_H
|
||||
#define SENECSTORAGELAN_H
|
||||
|
||||
#include <QUrl>
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
|
||||
#include <network/networkaccessmanager.h>
|
||||
|
||||
// https://blog.odenthal.cc/query-the-hidden-api-of-your-senec-photovoltaik-appliance/
|
||||
|
||||
class SenecStorageLan : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
explicit SenecStorageLan(NetworkAccessManager *networkManager, QObject *parent = nullptr);
|
||||
explicit SenecStorageLan(NetworkAccessManager *networkManager, const QHostAddress &address, QObject *parent = nullptr);
|
||||
|
||||
QHostAddress address() const;
|
||||
void setAddress(const QHostAddress &address);
|
||||
|
||||
QUrl url() const;
|
||||
|
||||
bool available() const;
|
||||
bool initialized() const;
|
||||
|
||||
// Init properties
|
||||
QString deviceId() const;
|
||||
float capacity() const;
|
||||
float maxChargePower() const;
|
||||
float maxDischargePower() const;
|
||||
|
||||
// Update properties
|
||||
float batteryLevel() const;
|
||||
float batteryPower() const;
|
||||
float gridPower() const;
|
||||
float inverterPower() const;
|
||||
|
||||
|
||||
static float parseFloat(const QString &value); // fl_
|
||||
static QString parseString(const QString &value); // st_
|
||||
//static quint8 parseUInt8(const QString &value); // u8_
|
||||
|
||||
|
||||
public slots:
|
||||
void initialize();
|
||||
void update();
|
||||
|
||||
signals:
|
||||
void initializeFinished(bool success);
|
||||
void availableChanged(bool available);
|
||||
void updatedFinished(bool success);
|
||||
|
||||
private:
|
||||
NetworkAccessManager *m_networkManager = nullptr;
|
||||
|
||||
QUrl m_url;
|
||||
QHostAddress m_address;
|
||||
|
||||
bool m_available = false;
|
||||
bool m_initialized = false;
|
||||
|
||||
QString m_deviceId;
|
||||
float m_capacity = 0;
|
||||
float m_maxChargePower = 0;
|
||||
float m_maxDischargePower = 0;
|
||||
|
||||
float m_batteryLevel = 0;
|
||||
float m_batteryPower = 0;
|
||||
float m_gridPower = 0;
|
||||
float m_inverterPower = 0;
|
||||
|
||||
void updateUrl();
|
||||
void setAvailable(bool available);
|
||||
|
||||
private slots:
|
||||
void ignoreSslErrors(const QList<QSslError> &errors);
|
||||
|
||||
};
|
||||
|
||||
#endif // SENECSTORAGELAN_H
|
||||
189
senec/translations/3f055ea5-a883-445d-9556-675f5eda6c9a-de.ts
Normal file
189
senec/translations/3f055ea5-a883-445d-9556-675f5eda6c9a-de.ts
Normal file
@ -0,0 +1,189 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="de">
|
||||
<context>
|
||||
<name>IntegrationPluginSenec</name>
|
||||
<message>
|
||||
<location filename="../integrationpluginsenec.cpp" line="65"/>
|
||||
<source>Unable to discover devices in your network.</source>
|
||||
<translation>Geräte im Netzwerk können nicht gesucht werden.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Senec</name>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="52"/>
|
||||
<source>Add meter</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: senecStorageLan, Type: settings, ID: {239d388c-f4a2-4055-88c3-decc6dace2b8})</extracomment>
|
||||
<translation>Zähler hinzufügen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="55"/>
|
||||
<source>Battery critical</source>
|
||||
<extracomment>The name of the StateType ({a9aa13fb-3f51-40e3-9d29-ab882513a87a}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation>Batterieladung kritisch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="58"/>
|
||||
<source>Battery level</source>
|
||||
<extracomment>The name of the StateType ({66a3a7d2-1210-4891-a8f2-6fe4f9b8c6cc}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation>Ladestand</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="61"/>
|
||||
<source>Capacity</source>
|
||||
<extracomment>The name of the StateType ({d927e6cf-29f0-412f-b74d-7d803fd3e191}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation>Kapazität</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="64"/>
|
||||
<source>Charging state</source>
|
||||
<extracomment>The name of the StateType ({339f602f-c9f1-446d-bc96-1c7f9a7db922}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation>Ladezustand</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="67"/>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="70"/>
|
||||
<source>Connected</source>
|
||||
<extracomment>The name of the StateType ({9aa4dbfb-255b-408b-b148-8fb145ba8c98}) of ThingClass senecMeter
|
||||
----------
|
||||
The name of the StateType ({d3bdc64b-67fb-4d84-97ed-c5142e5db55d}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation>Verbunden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="73"/>
|
||||
<source>Current phase A</source>
|
||||
<extracomment>The name of the StateType ({1923f7b0-5b54-46d8-a7e9-9364b7c9eeb8}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Strom Phase A</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="76"/>
|
||||
<source>Current phase B</source>
|
||||
<extracomment>The name of the StateType ({4908abf8-f655-4e14-a007-3eefeda7088e}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Strom Phase B</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="79"/>
|
||||
<source>Current phase C</source>
|
||||
<extracomment>The name of the StateType ({e8415152-0fac-4cd9-98ac-b0e7c006d637}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Strom Phase C</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="82"/>
|
||||
<source>Current power</source>
|
||||
<extracomment>The name of the StateType ({3b00470d-11bc-4352-a885-52ab9443cee5}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation>Aktuelle Leistung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="85"/>
|
||||
<source>Current power phase A</source>
|
||||
<extracomment>The name of the StateType ({385df15e-4f1d-4842-a10a-597d087bdac0}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Leistung Phase A</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="88"/>
|
||||
<source>Current power phase B</source>
|
||||
<extracomment>The name of the StateType ({3bab2b05-99d7-4b3a-b903-054d269a3527}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Leistung Phase B</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="91"/>
|
||||
<source>Current power phase C</source>
|
||||
<extracomment>The name of the StateType ({01a63c94-b635-4e98-8037-d5d69fe0dd7c}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Leistung Phase C</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="94"/>
|
||||
<source>Current power usage</source>
|
||||
<extracomment>The name of the StateType ({56e58d03-782d-40bb-8d32-fad396ffefdc}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Aktuelle Leistung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="97"/>
|
||||
<source>Energy consumed</source>
|
||||
<extracomment>The name of the StateType ({21728bf0-a566-4eb0-a163-5f108f34bb9b}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Bezogene Energie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="100"/>
|
||||
<source>Energy returned</source>
|
||||
<extracomment>The name of the StateType ({f9920cfa-26a0-4247-ad50-b4fca91596f0}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Eingespeiste Energie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="103"/>
|
||||
<source>Host address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: senecStorageLan, Type: thing, ID: {a45cabe9-9f76-405e-b47c-0a3d00bdd44b})</extracomment>
|
||||
<translation>IP-Adresse</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="106"/>
|
||||
<source>Host name</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: senecStorageLan, Type: thing, ID: {51a1bbc0-28a5-43ac-9021-896acbf9413f})</extracomment>
|
||||
<translation>Hostname</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="109"/>
|
||||
<source>MAC address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: senecStorageLan, Type: thing, ID: {0790e1b1-c8c9-488f-9cda-2a0bbf14b4af})</extracomment>
|
||||
<translation>MAC-Adresse</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="118"/>
|
||||
<source>SENEC Meter</source>
|
||||
<extracomment>The name of the ThingClass ({303f454d-93b7-4f8f-b295-a4c22e09be6d})</extracomment>
|
||||
<translation>SENEC Zähler</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="124"/>
|
||||
<source>Voltage phase A</source>
|
||||
<extracomment>The name of the StateType ({db469573-bb11-48d2-84ba-2b8ff4b13413}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Spannung Phase A</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="127"/>
|
||||
<source>Voltage phase B</source>
|
||||
<extracomment>The name of the StateType ({176d5a01-220d-44da-b875-9a40aaa9c85a}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Spannung Phase B</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="130"/>
|
||||
<source>Voltage phase C</source>
|
||||
<extracomment>The name of the StateType ({e7ab02d3-016a-44ca-8da1-5d7ac9acbc6d}) of ThingClass senecMeter</extracomment>
|
||||
<translation>Spannung Phase C</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="112"/>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="115"/>
|
||||
<source>SENEC</source>
|
||||
<extracomment>The name of the vendor ({1be922b9-4439-42cf-9caa-67f2ac7cc425})
|
||||
----------
|
||||
The name of the plugin Senec ({3f055ea5-a883-445d-9556-675f5eda6c9a})</extracomment>
|
||||
<translation>SENEC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="121"/>
|
||||
<source>SENEC.Home storage</source>
|
||||
<extracomment>The name of the ThingClass ({345df3b1-d411-4db5-bbb2-3b14eb86c1ba})</extracomment>
|
||||
<translation>SENEC.Home Speicher</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="133"/>
|
||||
<source>charging</source>
|
||||
<extracomment>The name of a possible value of StateType {339f602f-c9f1-446d-bc96-1c7f9a7db922} of ThingClass senecStorageLan</extracomment>
|
||||
<translation>lade</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="136"/>
|
||||
<source>discharging</source>
|
||||
<extracomment>The name of a possible value of StateType {339f602f-c9f1-446d-bc96-1c7f9a7db922} of ThingClass senecStorageLan</extracomment>
|
||||
<translation>entlade</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="139"/>
|
||||
<source>idle</source>
|
||||
<extracomment>The name of a possible value of StateType {339f602f-c9f1-446d-bc96-1c7f9a7db922} of ThingClass senecStorageLan</extracomment>
|
||||
<translation>leerlauf</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
189
senec/translations/3f055ea5-a883-445d-9556-675f5eda6c9a-en_US.ts
Normal file
189
senec/translations/3f055ea5-a883-445d-9556-675f5eda6c9a-en_US.ts
Normal file
@ -0,0 +1,189 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>IntegrationPluginSenec</name>
|
||||
<message>
|
||||
<location filename="../integrationpluginsenec.cpp" line="65"/>
|
||||
<source>Unable to discover devices in your network.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Senec</name>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="52"/>
|
||||
<source>Add meter</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: senecStorageLan, Type: settings, ID: {239d388c-f4a2-4055-88c3-decc6dace2b8})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="55"/>
|
||||
<source>Battery critical</source>
|
||||
<extracomment>The name of the StateType ({a9aa13fb-3f51-40e3-9d29-ab882513a87a}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="58"/>
|
||||
<source>Battery level</source>
|
||||
<extracomment>The name of the StateType ({66a3a7d2-1210-4891-a8f2-6fe4f9b8c6cc}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="61"/>
|
||||
<source>Capacity</source>
|
||||
<extracomment>The name of the StateType ({d927e6cf-29f0-412f-b74d-7d803fd3e191}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="64"/>
|
||||
<source>Charging state</source>
|
||||
<extracomment>The name of the StateType ({339f602f-c9f1-446d-bc96-1c7f9a7db922}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="67"/>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="70"/>
|
||||
<source>Connected</source>
|
||||
<extracomment>The name of the StateType ({9aa4dbfb-255b-408b-b148-8fb145ba8c98}) of ThingClass senecMeter
|
||||
----------
|
||||
The name of the StateType ({d3bdc64b-67fb-4d84-97ed-c5142e5db55d}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="73"/>
|
||||
<source>Current phase A</source>
|
||||
<extracomment>The name of the StateType ({1923f7b0-5b54-46d8-a7e9-9364b7c9eeb8}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="76"/>
|
||||
<source>Current phase B</source>
|
||||
<extracomment>The name of the StateType ({4908abf8-f655-4e14-a007-3eefeda7088e}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="79"/>
|
||||
<source>Current phase C</source>
|
||||
<extracomment>The name of the StateType ({e8415152-0fac-4cd9-98ac-b0e7c006d637}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="82"/>
|
||||
<source>Current power</source>
|
||||
<extracomment>The name of the StateType ({3b00470d-11bc-4352-a885-52ab9443cee5}) of ThingClass senecStorageLan</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="85"/>
|
||||
<source>Current power phase A</source>
|
||||
<extracomment>The name of the StateType ({385df15e-4f1d-4842-a10a-597d087bdac0}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="88"/>
|
||||
<source>Current power phase B</source>
|
||||
<extracomment>The name of the StateType ({3bab2b05-99d7-4b3a-b903-054d269a3527}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="91"/>
|
||||
<source>Current power phase C</source>
|
||||
<extracomment>The name of the StateType ({01a63c94-b635-4e98-8037-d5d69fe0dd7c}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="94"/>
|
||||
<source>Current power usage</source>
|
||||
<extracomment>The name of the StateType ({56e58d03-782d-40bb-8d32-fad396ffefdc}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="97"/>
|
||||
<source>Energy consumed</source>
|
||||
<extracomment>The name of the StateType ({21728bf0-a566-4eb0-a163-5f108f34bb9b}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="100"/>
|
||||
<source>Energy returned</source>
|
||||
<extracomment>The name of the StateType ({f9920cfa-26a0-4247-ad50-b4fca91596f0}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="103"/>
|
||||
<source>Host address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: senecStorageLan, Type: thing, ID: {a45cabe9-9f76-405e-b47c-0a3d00bdd44b})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="106"/>
|
||||
<source>Host name</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: senecStorageLan, Type: thing, ID: {51a1bbc0-28a5-43ac-9021-896acbf9413f})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="109"/>
|
||||
<source>MAC address</source>
|
||||
<extracomment>The name of the ParamType (ThingClass: senecStorageLan, Type: thing, ID: {0790e1b1-c8c9-488f-9cda-2a0bbf14b4af})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="118"/>
|
||||
<source>SENEC Meter</source>
|
||||
<extracomment>The name of the ThingClass ({303f454d-93b7-4f8f-b295-a4c22e09be6d})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="124"/>
|
||||
<source>Voltage phase A</source>
|
||||
<extracomment>The name of the StateType ({db469573-bb11-48d2-84ba-2b8ff4b13413}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="127"/>
|
||||
<source>Voltage phase B</source>
|
||||
<extracomment>The name of the StateType ({176d5a01-220d-44da-b875-9a40aaa9c85a}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="130"/>
|
||||
<source>Voltage phase C</source>
|
||||
<extracomment>The name of the StateType ({e7ab02d3-016a-44ca-8da1-5d7ac9acbc6d}) of ThingClass senecMeter</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="112"/>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="115"/>
|
||||
<source>SENEC</source>
|
||||
<extracomment>The name of the vendor ({1be922b9-4439-42cf-9caa-67f2ac7cc425})
|
||||
----------
|
||||
The name of the plugin Senec ({3f055ea5-a883-445d-9556-675f5eda6c9a})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="121"/>
|
||||
<source>SENEC.Home storage</source>
|
||||
<extracomment>The name of the ThingClass ({345df3b1-d411-4db5-bbb2-3b14eb86c1ba})</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="133"/>
|
||||
<source>charging</source>
|
||||
<extracomment>The name of a possible value of StateType {339f602f-c9f1-446d-bc96-1c7f9a7db922} of ThingClass senecStorageLan</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="136"/>
|
||||
<source>discharging</source>
|
||||
<extracomment>The name of a possible value of StateType {339f602f-c9f1-446d-bc96-1c7f9a7db922} of ThingClass senecStorageLan</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../build/Desktop_Qt_6_11_1_Debug/senec/plugininfo.h" line="139"/>
|
||||
<source>idle</source>
|
||||
<extracomment>The name of a possible value of StateType {339f602f-c9f1-446d-bc96-1c7f9a7db922} of ThingClass senecStorageLan</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
Loading…
x
Reference in New Issue
Block a user