From c7ca6164c004d2a9ae7ed904ac5e1af729b45cfc Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 20 Dec 2018 20:22:00 +0100 Subject: [PATCH] New Plugin: Daylight sensor which works offline and without any hardware --- datetime/deviceplugindatetime.cpp | 53 ++-- datetime/deviceplugindatetime.h | 2 + datetime/deviceplugindatetime.json | 13 +- daylightsensor/daylightsensor.pro | 11 + daylightsensor/deviceplugindaylightsensor.cpp | 232 ++++++++++++++++++ daylightsensor/deviceplugindaylightsensor.h | 55 +++++ .../deviceplugindaylightsensor.json | 66 +++++ ...d63a7-fc39-4a50-a459-457fa7653089-en_US.ts | 4 + nymea-plugins.pro | 1 + 9 files changed, 415 insertions(+), 22 deletions(-) create mode 100644 daylightsensor/daylightsensor.pro create mode 100644 daylightsensor/deviceplugindaylightsensor.cpp create mode 100644 daylightsensor/deviceplugindaylightsensor.h create mode 100644 daylightsensor/deviceplugindaylightsensor.json create mode 100644 daylightsensor/translations/4b7d63a7-fc39-4a50-a459-457fa7653089-en_US.ts diff --git a/datetime/deviceplugindatetime.cpp b/datetime/deviceplugindatetime.cpp index 59bad2f8..e13c5fd1 100644 --- a/datetime/deviceplugindatetime.cpp +++ b/datetime/deviceplugindatetime.cpp @@ -332,6 +332,7 @@ void DevicePluginDateTime::processTimesData(const QByteArray &data) return; } + qCDebug(dcDateTime()) << "Raw data:" << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented)); QVariantMap response = jsonDoc.toVariant().toMap(); if (response.value("status") != "OK") { qCWarning(dcDateTime) << "failed to request time data:" << response.value("status"); @@ -346,23 +347,36 @@ void DevicePluginDateTime::processTimesData(const QByteArray &data) QString sunsetString = result.value("sunset").toString(); QString duskString = result.value("civil_twilight_end").toString(); - m_dawn = QDateTime(QDate::currentDate(), QTime::fromString(dawnString, "h:m:s AP"), Qt::UTC).toTimeZone(m_timeZone); - m_sunrise = QDateTime(QDate::currentDate(), QTime::fromString(sunriseString, "h:m:s AP"), Qt::UTC).toTimeZone(m_timeZone); - m_noon = QDateTime(QDate::currentDate(), QTime::fromString(noonString, "h:m:s AP"), Qt::UTC).toTimeZone(m_timeZone); - m_sunset = QDateTime(QDate::currentDate(), QTime::fromString(sunsetString, "h:m:s AP"), Qt::UTC).toTimeZone(m_timeZone); - m_dusk = QDateTime(QDate::currentDate(), QTime::fromString(duskString, "h:m:s AP"), Qt::UTC).toTimeZone(m_timeZone); - // calculate the times in each alarm - // qCDebug(dcDateTime) << " dawn :" << m_dawn.toString(); - // qCDebug(dcDateTime) << " sunrise :" << m_sunrise.toString(); - // qCDebug(dcDateTime) << " noon :" << m_noon.toString(); - // qCDebug(dcDateTime) << " sunset :" << m_sunset.toString(); - // qCDebug(dcDateTime) << " dusk :" << m_dusk.toString(); - // qCDebug(dcDateTime) << "---------------------------------------------"; + + m_dawn = QDateTime(QDate::currentDate(), parseTime(dawnString), Qt::UTC).toTimeZone(m_timeZone); + m_sunrise = QDateTime(QDate::currentDate(), parseTime(sunriseString), Qt::UTC).toTimeZone(m_timeZone); + m_noon = QDateTime(QDate::currentDate(), parseTime(noonString), Qt::UTC).toTimeZone(m_timeZone); + m_sunset = QDateTime(QDate::currentDate(), parseTime(sunsetString), Qt::UTC).toTimeZone(m_timeZone); + m_dusk = QDateTime(QDate::currentDate(), parseTime(duskString), Qt::UTC).toTimeZone(m_timeZone); + + qCDebug(dcDateTime) << " dawn :" << m_dawn.toString() << dawnString; + qCDebug(dcDateTime) << " sunrise :" << m_sunrise.toString() << sunriseString; + qCDebug(dcDateTime) << " noon :" << m_noon.toString() << noonString; + qCDebug(dcDateTime) << " sunset :" << m_sunset.toString() << sunsetString; + qCDebug(dcDateTime) << " dusk :" << m_dusk.toString() << duskString; + qCDebug(dcDateTime) << "---------------------------------------------"; updateTimes(); } +QTime DevicePluginDateTime::parseTime(const QString &timeString) const +{ + bool isPm = timeString.endsWith(" PM"); + QString tmp = QString(timeString).remove(QRegExp("[ APM]*")); + QStringList parts = tmp.split(":"); + if (parts.count() != 3) { + qCWarning(dcDateTime()) << "Error parsing timeString:" << timeString; + return QTime(); + } + return QTime(parts.first().toInt(), parts.at(1).toInt(), parts.last().toInt()).addSecs(isPm ? 60 * 60 * 12 : 0); +} + void DevicePluginDateTime::onNetworkReplayFinished() { QNetworkReply *reply = static_cast(sender()); @@ -490,21 +504,20 @@ void DevicePluginDateTime::updateTimes() } else { m_todayDevice->setStateValue(todayDuskStateTypeId, 0); } - if (m_dusk.isValid()) { - m_todayDevice->setStateValue(todaySunriseStateTypeId, m_sunrise.toTime_t()); + if (m_sunrise.isValid() && m_sunset.isValid()) { + m_todayDevice->setStateValue(todaySunriseTimeStateTypeId, m_sunrise.toTime_t()); + m_todayDevice->setStateValue(todaySunsetTimeStateTypeId, m_sunset.toTime_t()); + m_todayDevice->setStateValue(todayDaylightStateTypeId, m_sunrise < m_currentDateTime && m_currentDateTime < m_sunset); } else { - m_todayDevice->setStateValue(todaySunriseStateTypeId, 0); + m_todayDevice->setStateValue(todaySunriseTimeStateTypeId, 0); + m_todayDevice->setStateValue(todaySunsetTimeStateTypeId, 0); + m_todayDevice->setStateValue(todayDaylightStateTypeId, false); } if (m_dusk.isValid()) { m_todayDevice->setStateValue(todayNoonStateTypeId, m_noon.toTime_t()); } else { m_todayDevice->setStateValue(todayNoonStateTypeId, 0); } - if (m_dusk.isValid()) { - m_todayDevice->setStateValue(todaySunsetStateTypeId, m_sunset.toTime_t()); - } else { - m_todayDevice->setStateValue(todaySunsetStateTypeId, 0); - } if (m_dusk.isValid()) { m_todayDevice->setStateValue(todayDawnStateTypeId, m_dawn.toTime_t()); } else { diff --git a/datetime/deviceplugindatetime.h b/datetime/deviceplugindatetime.h index bdb993dc..d731e0cc 100644 --- a/datetime/deviceplugindatetime.h +++ b/datetime/deviceplugindatetime.h @@ -75,6 +75,8 @@ private: void getTimes(const QString &latitude, const QString &longitude); void processTimesData(const QByteArray &data); + QTime parseTime(const QString &timeString) const; + signals: void dusk(); void sunset(); diff --git a/datetime/deviceplugindatetime.json b/datetime/deviceplugindatetime.json index 3bda703f..023a9912 100644 --- a/datetime/deviceplugindatetime.json +++ b/datetime/deviceplugindatetime.json @@ -13,6 +13,7 @@ "name": "today", "displayName": "Today", "deviceIcon": "Time", + "interfaces": [ "daylightsensor" ], "basicTags": [ "Service", "Time" @@ -133,7 +134,7 @@ }, { "id": "3a08824d-285b-412e-a515-9664b491a85c", - "name": "sunrise", + "name": "sunriseTime", "displayName": "sunrise", "displayNameEvent": "sunrise changed", "unit": "UnixTime", @@ -163,13 +164,21 @@ }, { "id": "377f04a7-df58-42ad-a234-e9e23bdc2f85", - "name": "sunset", + "name": "sunsetTime", "displayName": "sunset", "displayNameEvent": "sunset changed", "unit": "UnixTime", "type": "int", "eventRuleRelevant": false, "defaultValue": 0 + }, + { + "id": "1c3d6179-3b00-456c-841a-2d26ce960c25", + "name": "daylight", + "displayName": "Daylight", + "displayNameEvent": "Daylight changed", + "type": "bool", + "defaultValue": false } ], "eventTypes": [ diff --git a/daylightsensor/daylightsensor.pro b/daylightsensor/daylightsensor.pro new file mode 100644 index 00000000..25b7b93d --- /dev/null +++ b/daylightsensor/daylightsensor.pro @@ -0,0 +1,11 @@ +include(../plugins.pri) + +QT += network + +TARGET = $$qtLibraryTarget(nymea_deviceplugindaylightsensor) + +SOURCES += \ + deviceplugindaylightsensor.cpp \ + +HEADERS += \ + deviceplugindaylightsensor.h \ diff --git a/daylightsensor/deviceplugindaylightsensor.cpp b/daylightsensor/deviceplugindaylightsensor.cpp new file mode 100644 index 00000000..a61a15f4 --- /dev/null +++ b/daylightsensor/deviceplugindaylightsensor.cpp @@ -0,0 +1,232 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + +#include "plugininfo.h" +#include "devicemanager.h" +#include "deviceplugindaylightsensor.h" +#include "network/networkaccessmanager.h" + +#include +#include +#include +#include +#include +#include + +#define CIVIL_ZENITH 90.83333 + +DevicePluginDaylightSensor::DevicePluginDaylightSensor() +{ + +} + +DevicePluginDaylightSensor::~DevicePluginDaylightSensor() +{ + +} + +DeviceManager::DeviceError DevicePluginDaylightSensor::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) +{ + Q_UNUSED(deviceClassId) + Q_UNUSED(params) + + QNetworkRequest request(QUrl("http://ip-api.com/json")); + QNetworkReply* reply = hardwareManager()->networkManager()->get(request); + connect(reply, &QNetworkReply::finished, this, [this, reply, deviceClassId]() { + reply->deleteLater(); + QList results; + if (reply->error() != QNetworkReply::NoError) { + qCWarning(dcDaylightSensor()) << "Error fetching geolocation from ip-api:" << reply->error() << reply->errorString(); + emit devicesDiscovered(deviceClassId, results); + return; + } + QJsonParseError error; + QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error); + if (error.error != QJsonParseError::NoError) { + qCWarning(dcDaylightSensor()) << "Failed to parse json from ip-api:" << error.error << error.errorString(); + emit devicesDiscovered(deviceClassId, results); + return; + } + if (!jsonDoc.toVariant().toMap().contains("lat") || !jsonDoc.toVariant().toMap().contains("lon")) { + qCWarning(dcDaylightSensor()) << "Reply missing geolocation info" << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented)); + emit devicesDiscovered(deviceClassId, results); + return; + } + qreal lat = jsonDoc.toVariant().toMap().value("lat").toDouble(); + qreal lon = jsonDoc.toVariant().toMap().value("lon").toDouble(); + + DeviceDescriptor descriptor(deviceClassId, tr("Daylight sensor"), jsonDoc.toVariant().toMap().value("city").toString()); + ParamList params; + params.append(Param(daylightSensorDeviceLatitudeParamTypeId, lat)); + params.append(Param(daylightSensorDeviceLongitudeParamTypeId, lon)); + descriptor.setParams(params); + results.append(descriptor); + emit devicesDiscovered(deviceClassId, results); + }); + + return DeviceManager::DeviceErrorAsync; +} + +DeviceManager::DeviceSetupStatus DevicePluginDaylightSensor::setupDevice(Device *device) +{ + updateDevice(device); + + return DeviceManager::DeviceSetupStatusSuccess; +} + +void DevicePluginDaylightSensor::deviceRemoved(Device *device) +{ + hardwareManager()->pluginTimerManager()->unregisterTimer(m_timers.take(device)); +} + +void DevicePluginDaylightSensor::pluginTimerEvent() +{ + PluginTimer *timer = static_cast(sender()); + Device *device = m_timers.key(timer); + + if (!myDevices().contains(device)) { + qCDebug(dcDaylightSensor()) << "Device has disappeared. Exiting timer loop."; + return; + } + + updateDevice(device); +} + +void DevicePluginDaylightSensor::updateDevice(Device *device) +{ + PluginTimer *timer = m_timers.value(device); + if (timer) { + hardwareManager()->pluginTimerManager()->unregisterTimer(timer); + } + + QTimeZone tz = QTimeZone(QTimeZone::systemTimeZoneId()); + QDateTime now = QDateTime::currentDateTime().toTimeZone(tz); + auto sunriseSunset = calculateSunriseSunset(device->paramValue(daylightSensorDeviceLatitudeParamTypeId).toDouble(), device->paramValue(daylightSensorDeviceLongitudeParamTypeId).toDouble(), now); + QDateTime sunrise = sunriseSunset.first.toTimeZone(tz); + QDateTime sunset = sunriseSunset.second.toTimeZone(tz); + qCDebug(dcDaylightSensor()) << "Setting up daylight sensor:" << device->name() << "Sunrise:" << sunrise.toString() << "Sunset:" << sunset.toString(); + device->setStateValue(daylightSensorSunriseTimeStateTypeId, sunrise.toTime_t()); + device->setStateValue(daylightSensorSunsetTimeStateTypeId, sunset.toTime_t()); + device->setStateValue(daylightSensorDaylightStateTypeId, sunrise < now && sunset > now); + + qint64 timeToNext = -1; + if (now < sunrise) { + timeToNext = now.secsTo(sunrise); + } else if (now < sunset) { + timeToNext = now.secsTo(sunset); + } else { + timeToNext = now.secsTo(sunrise) + (60 * 60 * 24); + } + // Refresh at earliest in 5 secs to avoid spamming the system when we get close + timeToNext = qMax(static_cast(timeToNext), 5); + + timer = hardwareManager()->pluginTimerManager()->registerTimer(static_cast(timeToNext)); + qCDebug(dcDaylightSensor()) << "Recalculating in" << timer->interval() << "seconds"; + m_timers.insert(device, timer); +} + +QPair DevicePluginDaylightSensor::calculateSunriseSunset(qreal latitude, qreal longitude, const QDateTime &dateTime) +{ + int dayOfYear = dateTime.date().dayOfYear(); + int offset = dateTime.offsetFromUtc() / 60 / 60; + + // Convert the longitude to hour value and calculate an approximate time + qreal longitudeHour = longitude / 15; + qreal tRise = dayOfYear + ((6 - longitudeHour) / 24); + qreal tSet = dayOfYear + ((18 - longitudeHour) / 24); + + // Calculate the Sun's mean anomaly + qreal mRise = (0.9856 * tRise) - 3.289; + qreal mSet = (0.9856 * tSet) - 3.289; + + // Calculate the Sun's true longitude, and adjust angle to be between 0 and 360 + qreal tmp = mRise + (1.916 * qSin(qDegreesToRadians(mRise))) + (0.020 * qSin(qDegreesToRadians(2 * mRise))) + 282.634; + qreal lRise = qFloor(tmp + 360) % 360 + (tmp - qFloor(tmp)); + tmp = mSet + (1.916 * qSin(qDegreesToRadians(mSet))) + (0.020 * qSin(qDegreesToRadians(2 * mSet))) + 282.634; + qreal lSet = qFloor(tmp + 360) % 360 + (tmp - qFloor(tmp)); + + // Calculate the Sun's right ascension, and adjust angle to be between 0 and 360 + tmp = qRadiansToDegrees(qAtan(0.91764 * qTan(qDegreesToRadians(1.0 * lRise)))); + qreal raRise = qFloor(tmp + 360) % 360 + (tmp - qFloor(tmp)); + tmp = qRadiansToDegrees(qAtan(0.91764 * qTan(qDegreesToRadians(1.0 * lSet)))); + qreal raSet = qRound(tmp + 360) % 360 + (tmp - qFloor(tmp)); + + // Right ascension value needs to be in the same quadrant as L + qlonglong lQuadrantRise = qFloor(lRise/90) * 90; + qlonglong raQuadrantRise = qFloor(raRise/90) * 90; + raRise = raRise + (lQuadrantRise - raQuadrantRise); + + qlonglong lQuadrantSet = qFloor(lSet/90) * 90; + qlonglong raQuadrantSet = qFloor(raSet/90) * 90; + raSet = raSet + (lQuadrantSet - raQuadrantSet); + + // Right ascension value needs to be converted into hours + raRise = raRise / 15; + raSet = raSet / 15; + + // Calculate the Sun's declination + qreal sinDecRise = 0.39782 * qSin(qDegreesToRadians(1.0 * lRise)); + qreal cosDecRise = qCos(qAsin(sinDecRise)); + + qreal sinDecSet = 0.39782 * qSin(qDegreesToRadians(1.0 * lSet)); + qreal cosDecSet = qCos(qAsin(sinDecSet)); + + // Calculate the Sun's local hour angle + qreal cosZenith = qCos(qDegreesToRadians(CIVIL_ZENITH)); + qreal radianLat = qDegreesToRadians(latitude); + qreal sinLatitude = qSin(radianLat); + qreal cosLatitude = qCos(radianLat); + qreal cosHRise = (cosZenith - (sinDecRise * sinLatitude)) / (cosDecRise * cosLatitude); + qreal cosHSet = (cosZenith - (sinDecSet * sinLatitude)) / (cosDecSet * cosLatitude); + + // Finish calculating H and convert into hours + qreal hRise = (360 - qRadiansToDegrees(qAcos(cosHRise))) / 15; + qreal hSet = qRadiansToDegrees(qAcos(cosHSet)) / 15; + + // Calculate local mean time of rising/setting + tRise = hRise + raRise - (0.06571 * tRise) - 6.622; + tSet = hSet + raSet - (0.06571 * tSet) - 6.622; + + // Adjust back to UTC, and keep the time between 0 and 24 + tmp = tRise - longitudeHour; + qreal utRise = qFloor(tmp + 24) % 24 + (tmp - qFloor(tmp)); + tmp = tSet - longitudeHour; + qreal utSet = qFloor(tmp + 24) % 24 + (tmp - qFloor(tmp)); + + // Adjust again to localtime + tmp = utRise + offset; + qreal localtRise = qFloor(tmp + 24) % 24 + (tmp - qFloor(tmp)); + tmp = utSet + offset; + qreal localtSet = qFloor(tmp + 24) % 24 + (tmp - qFloor(tmp)); + + // Conversion + int hourRise = qFloor(localtRise); + int minuteRise = qFloor((localtRise - qFloor(localtRise)) * 60); + int hourSet = qFloor(localtSet); + int minuteSet = qFloor((localtSet - qFloor(localtSet)) * 60); + + QDateTime sunrise(dateTime.date(), QTime(hourRise, minuteRise)); + QDateTime sunset(dateTime.date(), QTime(hourSet, minuteSet)); + + return qMakePair(sunrise, sunset); +} diff --git a/daylightsensor/deviceplugindaylightsensor.h b/daylightsensor/deviceplugindaylightsensor.h new file mode 100644 index 00000000..e5252ec6 --- /dev/null +++ b/daylightsensor/deviceplugindaylightsensor.h @@ -0,0 +1,55 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2018 Michael Zanetti * + * * + * This file is part of nymea. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; If not, see * + * . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEVICEPLUGINDAYLIGHTSENSOR_H +#define DEVICEPLUGINDAYLIGHTSENSOR_H + + +#include "plugin/deviceplugin.h" +#include "devicemanager.h" +#include "plugintimer.h" + +class DevicePluginDaylightSensor: public DevicePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "deviceplugindaylightsensor.json") + Q_INTERFACES(DevicePlugin) + +public: + explicit DevicePluginDaylightSensor(); + ~DevicePluginDaylightSensor() override; + + DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) override; + DeviceManager::DeviceSetupStatus setupDevice(Device *device) override; + void deviceRemoved(Device *device) override; + +private slots: + void pluginTimerEvent(); + + void updateDevice(Device *device); +private: + QHash m_timers; + QPair calculateSunriseSunset(qreal lat, qreal lon, const QDateTime &dateTime); + +}; + +#endif // DEVICEPLUGINDAYLIGHTSENSOR_H diff --git a/daylightsensor/deviceplugindaylightsensor.json b/daylightsensor/deviceplugindaylightsensor.json new file mode 100644 index 00000000..0c0d5341 --- /dev/null +++ b/daylightsensor/deviceplugindaylightsensor.json @@ -0,0 +1,66 @@ +{ + "displayName": "Daylight sensor", + "name": "daylightSensor", + "id": "4b7d63a7-fc39-4a50-a459-457fa7653089", + "vendors": [ + { + "name": "guh", + "displayName": "guh GmbH", + "id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6", + "deviceClasses": [ + { + "id": "1d43d0ed-efe2-46ab-a567-af632e550931", + "name": "daylightSensor", + "displayName": "Daylight sensor", + "createMethods": ["user", "discovery"], + "interfaces": ["daylightsensor"], + "paramTypes": [ + { + "id": "124b2574-8f57-4c62-bc55-40e467d4aea3", + "name": "latitude", + "displayName": "Latitude", + "type": "QString", + "inputType": "TextLine" + }, + { + "id": "581c6914-8dbf-4882-a38c-453b808db374", + "name": "longitude", + "displayName": "Longitude", + "type": "QString", + "inputType": "TextLine" + } + ], + "stateTypes": [ + { + "id": "6e7bc630-a19c-41ec-b6a3-2e0cbcc4fadf", + "name": "daylight", + "displayName": "Daylight", + "displayNameEvent": "Daylight changed", + "type": "bool", + "cached": false, + "defaultValue": false + }, + { + "id": "d7c1f356-1efe-4ab7-ad4b-e7ab12cb53b7", + "name": "sunriseTime", + "displayName": "Sunrise time", + "displayNameEvent": "Sunrise time changed", + "unit": "UnixTime", + "type": "int", + "defaultValue": 0 + }, + { + "id": "6d064789-b9c3-44c8-a5ea-b722fc2f56b7", + "name": "sunsetTime", + "displayName": "Sunset time", + "displayNameEvent": "Sunset time changed", + "unit": "UnixTime", + "type": "int", + "defaultValue": 0 + } + ] + } + ] + } + ] +} diff --git a/daylightsensor/translations/4b7d63a7-fc39-4a50-a459-457fa7653089-en_US.ts b/daylightsensor/translations/4b7d63a7-fc39-4a50-a459-457fa7653089-en_US.ts new file mode 100644 index 00000000..f7f66d85 --- /dev/null +++ b/daylightsensor/translations/4b7d63a7-fc39-4a50-a459-457fa7653089-en_US.ts @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/nymea-plugins.pro b/nymea-plugins.pro index 96c9ee27..ae13b1b4 100644 --- a/nymea-plugins.pro +++ b/nymea-plugins.pro @@ -7,6 +7,7 @@ PLUGIN_DIRS = \ commandlauncher \ conrad \ datetime \ + daylightsensor \ denon \ dweetio \ elgato \