/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2015 Simon Stürz * * * * 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 * * . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /*! \page eq3.html \title eQ-3 Max! \brief Plugin for the eQ-3 heating system. \ingroup plugins \ingroup nymea-plugins This plugin allows to find and control devices from Max!(eQ-3). To use this devices, you need at least one \l{http://www.eq-3.de/max-heizungssteuerung-produktdetail/items/bc-lgw-o-tw.html}{Max! Cube LAN Gateway} in you local network. Once the cube is connected (DHCP), you can auto detect the cube in the network and add it to your \l{https://nymea.io}{nymea} devices. Also more than one cube in the network is supported. All devices, which are connected to a cube, will be autogenerated. For the setup of a cube, the original software is recomanded (min/max setpoint temperature, weekly program...). \chapter Supported devices \section2 Max! Cube LAN Gateway This \l{http://www.eq-3.de/max-heizungssteuerung-produktdetail/ items/bc-lgw-o-tw.html}{cube} can be discovered in the network. Every device, which is connected with the cube, will be appear automatically, once the cube is configrued and added to nymea. \section2 Max! Wall Thermostat In order to use this device, you need a \l{http://www.eq-3.de/max-heizungssteuerung-produktdetail/ items/bc-lgw-o-tw.html}{Max! Cube LAN Gateway}. A \l{http://www.eq-3.de/max-raumloesung-produktdetail/items/bc-tc-c-wm.html}{MAX! Wall Thermostat} can not be added, it will appear automatically in the device list, once you add it to the cube. \section2 Max! Radiator Thermostat In order to use this device, you need a \l{http://www.eq-3.de/max-heizungssteuerung-produktdetail/ items/bc-lgw-o-tw.html}{Max! Cube LAN Gateway}. A \l{http://www.eq-3.de/max-heizungssteuerung-produktdetail/items/bc-rt-trx-cyg.html}{MAX! Radiator Thermostat} can not be added, it will appear automatically in the device list, once you add it to the cube. \chapter Plugin properties Following JSON file contains the definition and the description of all available \l{DeviceClass}{DeviceClasses} and \l{Vendor}{Vendors} of this \l{DevicePlugin}. For more details how to read this JSON file please check out the documentation for \l{The plugin JSON File}. \quotefile plugins/deviceplugins/eq-3/deviceplugineq-3.json */ #include "deviceplugineq-3.h" #include "plugin/device.h" #include "devicemanager.h" #include "types/param.h" #include "plugininfo.h" #include DevicePluginEQ3::DevicePluginEQ3() { } DevicePluginEQ3::~DevicePluginEQ3() { hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer); } void DevicePluginEQ3::init() { m_cubeDiscovery = new MaxCubeDiscovery(this); connect(m_cubeDiscovery, &MaxCubeDiscovery::cubesDetected, this, &DevicePluginEQ3::discoveryDone); m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10); connect(m_pluginTimer, &PluginTimer::timeout, this, &DevicePluginEQ3::onPluginTimer); } DeviceManager::DeviceError DevicePluginEQ3::discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms) { Q_UNUSED(params) if(deviceClassId == cubeDeviceClassId){ m_cubeDiscovery->detectCubes(); return DeviceManager::DeviceErrorAsync; } return DeviceManager::DeviceErrorDeviceClassNotFound; } void DevicePluginEQ3::startMonitoringAutoDevices() { } DeviceManager::DeviceSetupStatus DevicePluginEQ3::setupDevice(Device *device) { qCDebug(dcEQ3) << "Setup device" << device->params(); if(device->deviceClassId() == cubeDeviceClassId){ foreach (MaxCube *cube, m_cubes.keys()) { if(cube->serialNumber() == device->paramValue(cubeDeviceSerialParamTypeId).toString()){ qCDebug(dcEQ3) << cube->serialNumber() << " already exists..."; return DeviceManager::DeviceSetupStatusFailure; } } MaxCube *cube = new MaxCube(this,device->paramValue(cubeDeviceSerialParamTypeId).toString(),QHostAddress(device->paramValue(cubeDeviceHostParamTypeId).toString()),device->paramValue(cubeDevicePortParamTypeId).toInt()); m_cubes.insert(cube,device); connect(cube,SIGNAL(cubeConnectionStatusChanged(bool)),this,SLOT(cubeConnectionStatusChanged(bool))); connect(cube,SIGNAL(commandActionFinished(bool,ActionId)),this,SLOT(commandActionFinished(bool,ActionId))); connect(cube,SIGNAL(cubeConfigReady()),this,SLOT(updateCubeConfig())); connect(cube,SIGNAL(wallThermostatFound()),this,SLOT(wallThermostatFound())); connect(cube,SIGNAL(wallThermostatDataUpdated()),this,SLOT(wallThermostatDataUpdated())); connect(cube,SIGNAL(radiatorThermostatFound()),this,SLOT(radiatorThermostatFound())); connect(cube,SIGNAL(radiatorThermostatDataUpdated()),this,SLOT(radiatorThermostatDataUpdated())); cube->connectToCube(); return DeviceManager::DeviceSetupStatusAsync; } if(device->deviceClassId() == wallThermostateDeviceClassId){ device->setName("Max! Wall Thermostat (" + device->paramValue(wallThermostateDeviceSerialParamTypeId).toString() + ")"); } return DeviceManager::DeviceSetupStatusSuccess; } void DevicePluginEQ3::deviceRemoved(Device *device) { if (!m_cubes.values().contains(device)) { return; } MaxCube *cube = m_cubes.key(device); cube->disconnectFromCube(); qCDebug(dcEQ3) << "remove cube " << cube->serialNumber(); m_cubes.remove(cube); cube->deleteLater(); } DeviceManager::DeviceError DevicePluginEQ3::executeAction(Device *device, const Action &action) { if(device->deviceClassId() == wallThermostateDeviceClassId){ foreach (MaxCube *cube, m_cubes.keys()){ if(cube->serialNumber() == device->paramValue(wallThermostateDeviceParentParamTypeId).toString()){ QByteArray rfAddress = device->paramValue(wallThermostateDeviceRfParamTypeId).toByteArray(); int roomId = device->paramValue(wallThermostateDeviceRoomParamTypeId).toInt(); if (action.actionTypeId() == wallThermostateDesiredTemperatureActionTypeId){ cube->setDeviceSetpointTemp(rfAddress, roomId, action.param(wallThermostateDesiredTemperatureActionDesiredTemperatureParamTypeId).value().toDouble(), action.id()); } else if (action.actionTypeId() == wallThermostateSetAutoModeActionTypeId){ cube->setDeviceAutoMode(rfAddress, roomId, action.id()); } else if (action.actionTypeId() == wallThermostateSetManualModeActionTypeId){ cube->setDeviceManuelMode(rfAddress, roomId, action.id()); } else if (action.actionTypeId() == wallThermostateSetEcoModeActionTypeId){ cube->setDeviceEcoMode(rfAddress, roomId, action.id()); } else if (action.actionTypeId() == wallThermostateDisplayCurrentTempActionTypeId){ cube->displayCurrentTemperature(rfAddress, roomId, action.param(wallThermostateDisplayCurrentTempActionDisplayParamTypeId).value().toBool(), action.id()); } return DeviceManager::DeviceErrorAsync; } } } else if (device->deviceClassId() == radiatorThermostateDeviceClassId){ foreach (MaxCube *cube, m_cubes.keys()){ if(cube->serialNumber() == device->paramValue(radiatorThermostateDeviceParentParamTypeId).toString()){ QByteArray rfAddress = device->paramValue(radiatorThermostateDeviceRfParamTypeId).toByteArray(); int roomId = device->paramValue(radiatorThermostateDeviceRoomParamTypeId).toInt(); if (action.actionTypeId() == radiatorThermostateDesiredTemperatureActionTypeId){ cube->setDeviceSetpointTemp(rfAddress, roomId, action.param(radiatorThermostateDesiredTemperatureActionDesiredTemperatureParamTypeId).value().toDouble(), action.id()); } else if (action.actionTypeId() == radiatorThermostateSetAutoModeActionTypeId){ cube->setDeviceAutoMode(rfAddress, roomId, action.id()); } else if (action.actionTypeId() == radiatorThermostateSetManualModeActionTypeId){ cube->setDeviceManuelMode(rfAddress, roomId, action.id()); } else if (action.actionTypeId() == radiatorThermostateSetEcoModeActionTypeId){ cube->setDeviceEcoMode(rfAddress, roomId, action.id()); } return DeviceManager::DeviceErrorAsync; } } } return DeviceManager::DeviceErrorActionTypeNotFound; } void DevicePluginEQ3::onPluginTimer() { foreach (MaxCube *cube, m_cubes.keys()){ if(cube->isConnected() && cube->isInitialized()){ cube->refresh(); } } } void DevicePluginEQ3::cubeConnectionStatusChanged(const bool &connected) { if(connected){ MaxCube *cube = static_cast(sender()); Device *device; if (m_cubes.contains(cube)) { device = m_cubes.value(cube); device->setName("Max! Cube " + cube->serialNumber()); device->setStateValue(cubeConnectionStateTypeId,true); emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess); } }else{ MaxCube *cube = static_cast(sender()); Device *device; if (m_cubes.contains(cube)){ device = m_cubes.value(cube); device->setStateValue(cubeConnectionStateTypeId,false); emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusFailure); } } } void DevicePluginEQ3::discoveryDone(const QList &cubeList) { QList retList; foreach (MaxCube *cube, cubeList) { DeviceDescriptor descriptor(cubeDeviceClassId, "Max! Cube LAN Gateway",cube->serialNumber()); ParamList params; Param hostParam(cubeDeviceHostParamTypeId, cube->hostAddress().toString()); params.append(hostParam); Param portParam(cubeDevicePortParamTypeId, cube->port()); params.append(portParam); Param firmwareParam(cubeDeviceFirmwareParamTypeId, cube->firmware()); params.append(firmwareParam); Param serialNumberParam(cubeDeviceSerialParamTypeId, cube->serialNumber()); params.append(serialNumberParam); descriptor.setParams(params); retList.append(descriptor); } emit devicesDiscovered(cubeDeviceClassId,retList); } void DevicePluginEQ3::commandActionFinished(const bool &succeeded, const ActionId &actionId) { if(succeeded){ emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError); }else{ emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorSetupFailed); } } void DevicePluginEQ3::wallThermostatFound() { MaxCube *cube = static_cast(sender()); QList descriptorList; foreach (WallThermostat *wallThermostat, cube->wallThermostatList()) { bool allreadyAdded = false; foreach (Device *device, deviceManager()->findConfiguredDevices(wallThermostateDeviceClassId)){ if(wallThermostat->serialNumber() == device->paramValue(wallThermostateDeviceSerialParamTypeId).toString()){ allreadyAdded = true; break; } } if(!allreadyAdded){ DeviceDescriptor descriptor(wallThermostateDeviceClassId, wallThermostat->serialNumber()); ParamList params; params.append(Param(wallThermostateDeviceNameParamTypeId, wallThermostat->deviceName())); params.append(Param(wallThermostateDeviceParentParamTypeId, cube->serialNumber())); params.append(Param(wallThermostateDeviceSerialParamTypeId, wallThermostat->serialNumber())); params.append(Param(wallThermostateDeviceRfParamTypeId, wallThermostat->rfAddress())); params.append(Param(wallThermostateDeviceRoomParamTypeId, wallThermostat->roomId())); params.append(Param(wallThermostateDeviceRoomNameParamTypeId, wallThermostat->roomName())); descriptor.setParams(params); descriptorList.append(descriptor); } } if(!descriptorList.isEmpty()){ metaObject()->invokeMethod(this, "autoDevicesAppeared", Qt::QueuedConnection, Q_ARG(DeviceClassId, wallThermostateDeviceClassId), Q_ARG(QList, descriptorList)); } } void DevicePluginEQ3::radiatorThermostatFound() { MaxCube *cube = static_cast(sender()); QList descriptorList; foreach (RadiatorThermostat *radiatorThermostat, cube->radiatorThermostatList()) { bool allreadyAdded = false; foreach (Device *device, deviceManager()->findConfiguredDevices(radiatorThermostateDeviceClassId)){ if(radiatorThermostat->serialNumber() == device->paramValue(radiatorThermostateDeviceSerialParamTypeId).toString()){ allreadyAdded = true; break; } } if(!allreadyAdded){ DeviceDescriptor descriptor(radiatorThermostateDeviceClassId, radiatorThermostat->serialNumber()); ParamList params; params.append(Param(radiatorThermostateDeviceNameParamTypeId, radiatorThermostat->deviceName())); params.append(Param(radiatorThermostateDeviceParentParamTypeId, cube->serialNumber())); params.append(Param(radiatorThermostateDeviceSerialParamTypeId, radiatorThermostat->serialNumber())); params.append(Param(radiatorThermostateDeviceRfParamTypeId, radiatorThermostat->rfAddress())); params.append(Param(radiatorThermostateDeviceRoomParamTypeId, radiatorThermostat->roomId())); params.append(Param(radiatorThermostateDeviceRoomNameParamTypeId, radiatorThermostat->roomName())); descriptor.setParams(params); descriptorList.append(descriptor); } } if(!descriptorList.isEmpty()){ metaObject()->invokeMethod(this, "autoDevicesAppeared", Qt::QueuedConnection, Q_ARG(DeviceClassId, radiatorThermostateDeviceClassId), Q_ARG(QList, descriptorList)); } } void DevicePluginEQ3::updateCubeConfig() { MaxCube *cube = static_cast(sender()); Device *device; if (m_cubes.contains(cube)) { device = m_cubes.value(cube); device->setStateValue(cubePortalEnabledStateTypeId,cube->portalEnabeld()); return; } } void DevicePluginEQ3::wallThermostatDataUpdated() { MaxCube *cube = static_cast(sender()); foreach (WallThermostat *wallThermostat, cube->wallThermostatList()) { foreach (Device *device, deviceManager()->findConfiguredDevices(wallThermostateDeviceClassId)){ if(device->paramValue(wallThermostateDeviceSerialParamTypeId).toString() == wallThermostat->serialNumber()){ device->setStateValue(wallThermostateComfortTempStateTypeId, wallThermostat->comfortTemp()); device->setStateValue(wallThermostateEcoTempStateTypeId, wallThermostat->ecoTemp()); device->setStateValue(wallThermostateMaxSetpointTempStateTypeId, wallThermostat->maxSetPointTemp()); device->setStateValue(wallThermostateMinSetpointTempStateTypeId, wallThermostat->minSetPointTemp()); device->setStateValue(wallThermostateErrorOccurredStateTypeId, wallThermostat->errorOccurred()); device->setStateValue(wallThermostateInitializedStateTypeId, wallThermostat->initialized()); device->setStateValue(wallThermostateBatteryLowStateTypeId, wallThermostat->batteryLow()); device->setStateValue(wallThermostateLinkStatusOKStateTypeId, wallThermostat->linkStatusOK()); device->setStateValue(wallThermostatePanelLockedStateTypeId, wallThermostat->panelLocked()); device->setStateValue(wallThermostateGatewayKnownStateTypeId, wallThermostat->gatewayKnown()); device->setStateValue(wallThermostateDtsActiveStateTypeId, wallThermostat->dtsActive()); device->setStateValue(wallThermostateDeviceModeStateTypeId, wallThermostat->deviceMode()); device->setStateValue(wallThermostateDeviceModeStringStateTypeId, wallThermostat->deviceModeString()); device->setStateValue(wallThermostateDesiredTemperatureStateTypeId, wallThermostat->setpointTemperature()); device->setStateValue(wallThermostateCurrentTemperatureStateTypeId, wallThermostat->currentTemperature()); } } } } void DevicePluginEQ3::radiatorThermostatDataUpdated() { MaxCube *cube = static_cast(sender()); foreach (RadiatorThermostat *radiatorThermostat, cube->radiatorThermostatList()) { foreach (Device *device, deviceManager()->findConfiguredDevices(radiatorThermostateDeviceClassId)){ if(device->paramValue(radiatorThermostateDeviceSerialParamTypeId).toString() == radiatorThermostat->serialNumber()){ device->setStateValue(radiatorThermostateComfortTempStateTypeId, radiatorThermostat->comfortTemp()); device->setStateValue(radiatorThermostateMaxSetpointTempStateTypeId, radiatorThermostat->maxSetPointTemp()); device->setStateValue(radiatorThermostateMinSetpointTempStateTypeId, radiatorThermostat->minSetPointTemp()); device->setStateValue(radiatorThermostateErrorOccurredStateTypeId, radiatorThermostat->errorOccurred()); device->setStateValue(radiatorThermostateInitializedStateTypeId, radiatorThermostat->initialized()); device->setStateValue(radiatorThermostateBatteryLowStateTypeId, radiatorThermostat->batteryLow()); device->setStateValue(radiatorThermostatePanelLockedStateTypeId, radiatorThermostat->panelLocked()); device->setStateValue(radiatorThermostateGatewayKnownStateTypeId, radiatorThermostat->gatewayKnown()); device->setStateValue(radiatorThermostateDtsActiveStateTypeId, radiatorThermostat->dtsActive()); device->setStateValue(radiatorThermostateDeviceModeStateTypeId, radiatorThermostat->deviceMode()); device->setStateValue(radiatorThermostateDeviceModeStringStateTypeId, radiatorThermostat->deviceModeString()); device->setStateValue(radiatorThermostateDesiredTemperatureStateTypeId, radiatorThermostat->setpointTemperature()); device->setStateValue(radiatorThermostateOffsetTempStateTypeId, radiatorThermostat->offsetTemp()); device->setStateValue(radiatorThermostateWindowOpenDurationStateTypeId, radiatorThermostat->windowOpenDuration()); device->setStateValue(radiatorThermostateBoostValveValueStateTypeId, radiatorThermostat->boostValveValue()); device->setStateValue(radiatorThermostateBoostDurationStateTypeId, radiatorThermostat->boostDuration()); device->setStateValue(radiatorThermostateDiscalcWeekDayStateTypeId, radiatorThermostat->discalcingWeekDay()); device->setStateValue(radiatorThermostateDiscalcTimeStateTypeId, radiatorThermostat->discalcingTime().toString("HH:mm")); device->setStateValue(radiatorThermostateValveMaximumSettingsStateTypeId, radiatorThermostat->valveMaximumSettings()); device->setStateValue(radiatorThermostateValveOffsetStateTypeId, radiatorThermostat->valveOffset()); device->setStateValue(radiatorThermostateValvePositionStateTypeId, radiatorThermostat->valvePosition()); } } } }