diff --git a/CMakeLists.txt b/CMakeLists.txt index e69e87e..15a73c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,8 @@ set(SOURCE_FILES config.h src/Families/EnOcean.cpp src/Families/EnOcean.h + src/Families/HomeMaticCc1101.cpp + src/Families/HomeMaticCc1101.h src/Families/HomeMaticCulfw.cpp src/Families/HomeMaticCulfw.h src/Families/ICommunicationInterface.cpp diff --git a/debian/postinst b/debian/postinst index 770333b..46f1744 100644 --- a/debian/postinst +++ b/debian/postinst @@ -17,6 +17,42 @@ case $1 in chown homegear:homegear /var/log/homegear-gateway chmod 750 /var/log/homegear-gateway + mkdir -p /var/lib/homegear-gateway + chown homegear:homegear /var/lib/homegear-gateway + chmod 750 /var/lib/homegear-gateway + + uuid=$(cat /proc/sys/kernel/random/uuid) + sed -i "s/^#uPnPUDN .*/uPnPUDN = ${uuid}/g" /etc/homegear/gateway.conf + password=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 64 | head -n 1) + sed -i "s/^#configurationPassword .*/configurationPassword = ${password}/g" /etc/homegear/gateway.conf + + if test ! -e /var/lib/homegear-gateway/dh.pem; then + if [[ $DIALOG_OUTPUT ]]; then + platform=$(dpkg --print-architecture) + if [[ $platform == "armhf" ]] || [[ $platform == "arm64" ]]; then + echo "The generation can take up to 30 minutes." + fi + openssl dhparam -check -text -5 -out /var/lib/homegear-gateway/dh.pem 1024 > /dev/null 2>&1 & + opensslPid=$! + exitcode=0 + counter=0 + while [[ $exitcode -eq 0 ]]; do + kill -0 $opensslPid > /dev/null 2>&1 + exitcode=$? + counter=$((counter+1)) + if [[ $counter -eq 30 ]]; then + counter=0 + echo "Please wait..." + fi + sleep 1 + done + else + openssl dhparam -check -text -5 -out /var/lib/homegear-gateway/dh.pem 1024 + fi + chown homegear:homegear /var/lib/homegear-gateway/dh.pem + chmod 400 /var/lib/homegear-gateway/dh.pem + fi + pidof systemd if [ $? -eq 0 ]; then systemctl enable homegear-gateway.service diff --git a/misc/Config Directory/gateway.conf b/misc/Config Directory/gateway.conf index ab97576..aced0fc 100644 --- a/misc/Config Directory/gateway.conf +++ b/misc/Config Directory/gateway.conf @@ -32,26 +32,36 @@ workingDirectory = /var/log/homegear-gateway # Default: logfilePath = /var/log/homegear-gateway logfilePath = /var/log/homegear-gateway +# Default: dataPath = /var/lib/homegear-gateway +dataPath = /var/lib/homegear-gateway + # Default: lockfilePath = /var/lock lockfilePath = /var/lock ### TLS options ### -# The path to the certificate authority's certificate +# The path to the certificate authority's certificate. +# If empty, Homegear Gateway looks for "ca.crt" in dataPath. caFile = # The path to the PEM encoded client certificate. +# If empty, Homegear Gateway looks for "gatewary.crt" in dataPath. certPath = # The path to the PEM encoded client keyfile. +# If empty, Homegear Gateway looks for "gatewary.key" in dataPath. keyPath = # The path to the Diffie-Hellman parameters. +# If empty, Homegear Gateway looks for "dh.pem" in dataPath. dhPath = +# The password used to decrypt configuration data sent to an unconfigured gateway. +#configurationPassword = changeme + # Enable UPnP server for other devices to be able to discover this installation of Homegear Gateway # Default: enableUPnP = true -enableUPnP = false +enableUPnP = true # Sets the IP address, the UPnP server listens on. By default auto discovery is used, but this might not work, # if multiple interfaces exist. Only set specific IP addresses not "0.0.0.0". Alternatively the interface name can be @@ -59,16 +69,15 @@ enableUPnP = false # Default: uPnPIpAddress = # uPnPIpAddress = -# Please change this setting! Set a unique UPnP UDN here. +# Please change this setting! Set a unique UPnP UDN here. When installed as a Debian package, a unique UUID is created +# automatically. # E. g.: 0660e537-dada-affe-cafe-001ff3590148 # Default: uPnPUDN = -uPnPUDN = 0660e537-dada-affe-cafe-001ff3590148 +#uPnPUDN = 0660e537-dada-affe-cafe-001ff3590148 #{{{ EnOcean example config ## The device family the gateway is for. -## Options: -## - EnOcean, HomeMaticCulfw #family = EnOcean ## The device to use @@ -79,8 +88,6 @@ uPnPUDN = 0660e537-dada-affe-cafe-001ff3590148 #{{{ HomeMatic CUL example config ## The device family the gateway is for. -## Options: -## - EnOcean, HomeMaticCulfw #family = HomeMaticCulfw ## The device to use @@ -92,7 +99,6 @@ uPnPUDN = 0660e537-dada-affe-cafe-001ff3590148 ## The device family the gateway is for. ## Options: -## - EnOcean, HomeMaticCulfw #family = HomeMaticCulfw ## The device to use @@ -103,4 +109,20 @@ uPnPUDN = 0660e537-dada-affe-cafe-001ff3590148 #}}} +#{{{ HomeMatic TICC1101 example config + +## The device family the gateway is for. +#family = HomeMaticCc1101 +## The device to use +#device = /dev/spidev0.0 + +## The interrupt pin to use. "0" for GDO0 or "2" for GDO2. +## You only need to connect one of them. Specify the GPIO +## you connected the interrupt pin to below. +#interruptPin = 2 + +## The GPIO GDO0 or GDO2 is connected to. Specify which GDO to use above. +#gpio1 = 24 + +#}}} diff --git a/src/Families/EnOcean.cpp b/src/Families/EnOcean.cpp index 8d1c76d..05e145d 100644 --- a/src/Families/EnOcean.cpp +++ b/src/Families/EnOcean.cpp @@ -35,6 +35,8 @@ EnOcean::EnOcean(BaseLib::SharedObjects* bl) : ICommunicationInterface(bl) { try { + _familyId = ENOCEAN_FAMILY_ID; + _initComplete = false; _stopped = true; diff --git a/src/Families/HomeMaticCc1101.cpp b/src/Families/HomeMaticCc1101.cpp new file mode 100644 index 0000000..2492dc4 --- /dev/null +++ b/src/Families/HomeMaticCc1101.cpp @@ -0,0 +1,1133 @@ +/* Copyright 2013-2017 Sathya Laufer + * + * Homegear 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 3 of the + * License, or (at your option) any later version. + * + * Homegear 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 Homegear. If not, see + * . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU Lesser General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. +*/ + +#include "HomeMaticCc1101.h" +#include "../GD.h" + +HomeMaticCc1101::HomeMaticCc1101(BaseLib::SharedObjects* bl) : ICommunicationInterface(bl) +{ + try + { + _familyId = HOMEMATIC_CC1101_FAMILY_ID; + + _stopCallbackThread = true; + _stopped = true; + _sending = false; + _sendingPending = false; + _firstPacket = true; + _updateMode = false; + _gpio.reset(new BaseLib::LowLevel::Gpio(bl)); + + _localRpcMethods.emplace("sendPacket", std::bind(&HomeMaticCc1101::sendPacket, this, std::placeholders::_1)); + _localRpcMethods.emplace("enableUpdateMode", std::bind(&HomeMaticCc1101::enableUpdateMode, this, std::placeholders::_1)); + _localRpcMethods.emplace("disableUpdateMode", std::bind(&HomeMaticCc1101::disableUpdateMode, this, std::placeholders::_1)); + + _oscillatorFrequency = GD::settings.oscillatorFrequency(); + _interruptPin = GD::settings.interruptPin(); + + if(_oscillatorFrequency < 0) _oscillatorFrequency = 26000000; + if(_interruptPin != 0 && _interruptPin != 2) + { + if(_interruptPin > 0) GD::out.printWarning("Warning: Setting for interruptPin in gateway.conf is invalid."); + _interruptPin = 2; + } + + _transfer = { (uint64_t)0, (uint64_t)0, (uint32_t)0, (uint32_t)4000000, (uint16_t)0, (uint8_t)8, (uint8_t)0, (uint32_t)0 }; + + setConfig(); + + start(); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +HomeMaticCc1101::~HomeMaticCc1101() +{ + try + { + stop(); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +void HomeMaticCc1101::start() +{ + try + { + if(GD::settings.device().empty()) + { + GD::out.printError("Error: No device defined for family HomeMatic BidCoS CC1101. Please specify it in \"gateway.conf\"."); + return; + } + + initDevice(); + + _stopped = false; + _firstPacket = true; + _stopCallbackThread = false; + GD::bl->threadManager.start(_listenThread, true, 45, SCHED_FIFO, &HomeMaticCc1101::mainThread, this); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +void HomeMaticCc1101::stop() +{ + try + { + _stopCallbackThread = true; + GD::bl->threadManager.join(_listenThread); + _stopCallbackThread = false; + if(_fileDescriptor->descriptor != -1) closeDevice(); + _gpio->closeDevice(GD::settings.gpio1()); + _stopped = true; + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +void HomeMaticCc1101::mainThread() +{ + try + { + int32_t pollResult; + int32_t bytesRead; + std::vector readBuffer({'0'}); + + while(!_stopCallbackThread) + { + try + { + if(_stopped) + { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + continue; + } + if(!_stopCallbackThread && (_fileDescriptor->descriptor == -1 || !_gpio->isOpen(GD::settings.gpio1()))) + { + GD::out.printError("Connection to TI CC1101 closed unexpectedly... Trying to reconnect..."); + _stopped = true; //Set to true, so that sendPacket aborts + if(_sending) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + _sending = false; + } + _txMutex.unlock(); //Make sure _txMutex is unlocked + + _gpio->closeDevice(GD::settings.gpio1()); + initDevice(); + _stopped = false; + continue; + } + + pollfd pollstruct { + (int)_gpio->getFileDescriptor(GD::settings.gpio1())->descriptor, + (short)(POLLPRI | POLLERR), + (short)0 + }; + + pollResult = poll(&pollstruct, 1, 100); + /*if(pollstruct.revents & POLLERR) + { + _out.printWarning("Warning: Error polling GPIO. Reopening..."); + closeGPIO(); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + openGPIO(_settings->gpio1); + }*/ + if(pollResult > 0) + { + if(lseek(_gpio->getFileDescriptor(GD::settings.gpio1())->descriptor, 0, SEEK_SET) == -1) throw BaseLib::Exception("Could not poll gpio: " + std::string(strerror(errno))); + bytesRead = read(_gpio->getFileDescriptor(GD::settings.gpio1())->descriptor, &readBuffer[0], 1); + if(!bytesRead) continue; + if(readBuffer.at(0) == 0x30) + { + if(!_sending) _txMutex.try_lock(); //We are receiving, don't send now + continue; //Packet is being received. Wait for GDO high + } + if(_sending) + { + endSending(); + _txMutex.unlock(); + } + else + { + //sendCommandStrobe(CommandStrobes::Enum::SIDLE); + std::string packet; + if(crcOK()) + { + uint8_t firstByte = readRegister(Registers::Enum::FIFO); + std::vector encodedData = readRegisters(Registers::Enum::FIFO, firstByte + 1); //Read packet + RSSI + std::vector decodedData(encodedData.size()); + if(decodedData.size() > 200) + { + if(!_firstPacket) + { + GD::out.printWarning("Warning: Too large packet received: " + BaseLib::HelperFunctions::getHexString(encodedData)); + closeDevice(); + _txMutex.unlock(); + continue; + } + } + else if(encodedData.size() >= 9) + { + decodedData[0] = firstByte; + decodedData[1] = (~encodedData[1]) ^ 0x89; + uint32_t i = 2; + for(; i < firstByte; i++) + { + decodedData[i] = (encodedData[i - 1] + 0xDC) ^ encodedData[i]; + } + decodedData[i] = encodedData[i] ^ decodedData[2]; + decodedData[i + 1] = encodedData[i + 1]; //RSSI_DEVICE + + packet = BaseLib::HelperFunctions::getHexString(decodedData); + } + else GD::out.printWarning("Warning: Too small packet received: " + BaseLib::HelperFunctions::getHexString(encodedData)); + } + else GD::out.printDebug("Debug: BidCoS packet received, but CRC failed."); + if(!_sendingPending) + { + sendCommandStrobe(CommandStrobes::Enum::SFRX); + sendCommandStrobe(CommandStrobes::Enum::SRX); + } + _txMutex.unlock(); + if(!packet.empty()) + { + if(_firstPacket) _firstPacket = false; + else + { + BaseLib::PArray parameters = std::make_shared(); + parameters->reserve(2); + parameters->push_back(std::make_shared(HOMEMATIC_CC1101_FAMILY_ID)); + parameters->push_back(std::make_shared(packet)); + + if(_invoke) + { + auto result = _invoke("packetReceived", parameters); + if(result->errorStruct && result->structValue->at("faultCode")->integerValue != -1) + { + GD::out.printError("Error calling packetReceived(): " + result->structValue->at("faultString")->stringValue); + } + } + } + } + } + } + else if(pollResult < 0) + { + _txMutex.unlock(); + GD::out.printError("Error: Could not poll gpio: " + std::string(strerror(errno)) + ". Reopening..."); + _gpio->closeDevice(GD::settings.gpio1()); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + _gpio->openDevice(GD::settings.gpio1(), true); + } + //pollResult == 0 is timeout + } + catch(const std::exception& ex) + { + _txMutex.unlock(); + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + _txMutex.unlock(); + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + _txMutex.unlock(); + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + } + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + _txMutex.unlock(); +} + +void HomeMaticCc1101::initDevice() +{ + try + { + openDevice(); + if(!_fileDescriptor || _fileDescriptor->descriptor == -1) return; + + initChip(); + GD::out.printDebug("Debug: CC1100: Setting GPIO direction"); + int32_t gpioIndex = GD::settings.gpio1(); + if(gpioIndex == -1) + { + GD::out.printError("Error: GPIO 1 is not defined in settings."); + return; + } + _gpio->setDirection(gpioIndex, BaseLib::LowLevel::Gpio::GpioDirection::Enum::IN); + GD::out.printDebug("Debug: CC1100: Setting GPIO edge"); + _gpio->setEdge(gpioIndex, BaseLib::LowLevel::Gpio::GpioEdge::Enum::BOTH); + _gpio->openDevice(gpioIndex, true); + if(!_gpio->isOpen(gpioIndex)) + { + GD::out.printError("Error: Couldn't listen to rf device, because the GPIO descriptor is not valid: " + GD::settings.device()); + return; + } + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +void HomeMaticCc1101::openDevice() +{ + try + { + if(_fileDescriptor->descriptor != -1) closeDevice(); + + _lockfile = GD::bl->settings.lockFilePath() + "LCK.." + GD::settings.device().substr(GD::settings.device().find_last_of('/') + 1); + int lockfileDescriptor = open(_lockfile.c_str(), O_WRONLY | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if(lockfileDescriptor == -1) + { + if(errno != EEXIST) + { + GD::out.printCritical("Couldn't create lockfile " + _lockfile + ": " + strerror(errno)); + return; + } + + int processID = 0; + std::ifstream lockfileStream(_lockfile.c_str()); + lockfileStream >> processID; + if(getpid() != processID && kill(processID, 0) == 0) + { + GD::out.printCritical("Rf device is in use: " + GD::settings.device()); + return; + } + unlink(_lockfile.c_str()); + lockfileDescriptor = open(_lockfile.c_str(), O_WRONLY | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if(lockfileDescriptor == -1) + { + GD::out.printCritical("Couldn't create lockfile " + _lockfile + ": " + strerror(errno)); + return; + } + } + dprintf(lockfileDescriptor, "%10i", getpid()); + close(lockfileDescriptor); + + _fileDescriptor = _bl->fileDescriptorManager.add(open(GD::settings.device().c_str(), O_RDWR | O_NONBLOCK)); + usleep(1000); + + if(_fileDescriptor->descriptor == -1) + { + GD::out.printCritical("Couldn't open rf device \"" + GD::settings.device() + "\": " + strerror(errno)); + return; + } + + setupDevice(); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +void HomeMaticCc1101::closeDevice() +{ + try + { + _bl->fileDescriptorManager.close(_fileDescriptor); + unlink(_lockfile.c_str()); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +void HomeMaticCc1101::setupDevice() +{ + try + { + if(_fileDescriptor->descriptor == -1) return; + + uint8_t mode = 0; + uint8_t bits = 8; + uint32_t speed = 4000000; //4MHz, see page 25 in datasheet + + if(ioctl(_fileDescriptor->descriptor, SPI_IOC_WR_MODE, &mode)) throw(BaseLib::Exception("Couldn't set spi mode on device " + GD::settings.device())); + if(ioctl(_fileDescriptor->descriptor, SPI_IOC_RD_MODE, &mode)) throw(BaseLib::Exception("Couldn't get spi mode off device " + GD::settings.device())); + + if(ioctl(_fileDescriptor->descriptor, SPI_IOC_WR_BITS_PER_WORD, &bits)) throw(BaseLib::Exception("Couldn't set bits per word on device " + GD::settings.device())); + if(ioctl(_fileDescriptor->descriptor, SPI_IOC_RD_BITS_PER_WORD, &bits)) throw(BaseLib::Exception("Couldn't get bits per word off device " + GD::settings.device())); + + if(ioctl(_fileDescriptor->descriptor, SPI_IOC_WR_MAX_SPEED_HZ, &speed)) throw(BaseLib::Exception("Couldn't set speed on device " + GD::settings.device())); + if(ioctl(_fileDescriptor->descriptor, SPI_IOC_RD_MAX_SPEED_HZ, &speed)) throw(BaseLib::Exception("Couldn't get speed off device " + GD::settings.device())); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +void HomeMaticCc1101::initChip() +{ + try + { + if(_fileDescriptor->descriptor == -1) + { + GD::out.printError("Error: Could not initialize TI CC1101. The spi device's file descriptor is not valid."); + return; + } + reset(); + + int32_t index = 0; + for(std::vector::const_iterator i = _config.begin(); i != _config.end(); ++i) + { + if(writeRegister((Registers::Enum)index, *i, true) != *i) + { + closeDevice(); + return; + } + index++; + } + if(writeRegister(Registers::Enum::FSTEST, 0x59, true) != 0x59) + { + closeDevice(); + return; + } + if(writeRegister(Registers::Enum::TEST2, 0x81, true) != 0x81) //Determined by SmartRF Studio + { + closeDevice(); + return; + } + if(writeRegister(Registers::Enum::TEST1, 0x35, true) != 0x35) //Determined by SmartRF Studio + { + closeDevice(); + return; + } + if(writeRegister(Registers::Enum::PATABLE, 0xC2, true) != 0xC2) + { + closeDevice(); + return; + } + + sendCommandStrobe(CommandStrobes::Enum::SFRX); + + enableRX(true); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +void HomeMaticCc1101::reset() +{ + try + { + if(_fileDescriptor->descriptor == -1) return; + sendCommandStrobe(CommandStrobes::Enum::SRES); + + usleep(70); //Measured on HM-CC-VD + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +void HomeMaticCc1101::enableRX(bool flushRXFIFO) +{ + try + { + if(_fileDescriptor->descriptor == -1) return; + std::lock_guard txGuard(_txMutex); + if(flushRXFIFO) sendCommandStrobe(CommandStrobes::Enum::SFRX); + sendCommandStrobe(CommandStrobes::Enum::SRX); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +void HomeMaticCc1101::endSending() +{ + try + { + sendCommandStrobe(CommandStrobes::Enum::SIDLE); + sendCommandStrobe(CommandStrobes::Enum::SFRX); + sendCommandStrobe(CommandStrobes::Enum::SRX); + _sending = false; + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +bool HomeMaticCc1101::crcOK() +{ + try + { + if(_fileDescriptor->descriptor == -1) return false; + std::vector result = readRegisters(Registers::Enum::LQI, 1); + if((result.size() == 2) && (result.at(1) & 0x80)) return true; + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + return false; +} + +void HomeMaticCc1101::readwrite(std::vector& data) +{ + try + { + std::lock_guard sendGuard(_sendMutex); + _transfer.tx_buf = (uint64_t)&data[0]; + _transfer.rx_buf = (uint64_t)&data[0]; + _transfer.len = (uint32_t)data.size(); + if(_bl->debugLevel >= 6) GD::out.printDebug("Debug: Sending: " + _bl->hf.getHexString(data)); + if(!ioctl(_fileDescriptor->descriptor, SPI_IOC_MESSAGE(1), &_transfer)) + { + GD::out.printError("Couldn't write to device " + GD::settings.device() + ": " + std::string(strerror(errno))); + return; + } + if(_bl->debugLevel >= 6) GD::out.printDebug("Debug: Received: " + _bl->hf.getHexString(data)); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +bool HomeMaticCc1101::checkStatus(uint8_t statusByte, Status::Enum status) +{ + try + { + if(_fileDescriptor->descriptor == -1 || !_gpio->isOpen(GD::settings.gpio1())) return false; + if((statusByte & (StatusBitmasks::Enum::CHIP_RDYn | StatusBitmasks::Enum::STATE)) != status) return false; + return true; + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + return false; +} + +uint8_t HomeMaticCc1101::readRegister(Registers::Enum registerAddress) +{ + try + { + if(_fileDescriptor->descriptor == -1) return 0; + std::vector data({(uint8_t)(registerAddress | RegisterBitmasks::Enum::READ_SINGLE), 0x00}); + for(uint32_t i = 0; i < 5; i++) + { + readwrite(data); + if(!(data.at(0) & StatusBitmasks::Enum::CHIP_RDYn)) break; + data.at(0) = (uint8_t)(registerAddress | RegisterBitmasks::Enum::READ_SINGLE); + data.at(1) = 0; + usleep(20); + } + return data.at(1); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + return 0; +} + +std::vector HomeMaticCc1101::readRegisters(Registers::Enum startAddress, uint8_t count) +{ + try + { + if(_fileDescriptor->descriptor == -1) return std::vector(); + std::vector data({(uint8_t)(startAddress | RegisterBitmasks::Enum::READ_BURST)}); + data.resize(count + 1, 0); + for(uint32_t i = 0; i < 5; i++) + { + readwrite(data); + if(!(data.at(0) & StatusBitmasks::Enum::CHIP_RDYn)) break; + data.clear(); + data.push_back((uint8_t)(startAddress | RegisterBitmasks::Enum::READ_BURST)); + data.resize(count + 1, 0); + usleep(20); + } + return data; + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + return std::vector(); +} + +uint8_t HomeMaticCc1101::writeRegister(Registers::Enum registerAddress, uint8_t value, bool check) +{ + try + { + if(_fileDescriptor->descriptor == -1) return 0xFF; + std::vector data({(uint8_t)registerAddress, value}); + readwrite(data); + if((data.at(0) & StatusBitmasks::Enum::CHIP_RDYn) || (data.at(1) & StatusBitmasks::Enum::CHIP_RDYn)) throw BaseLib::Exception("Error writing to register " + std::to_string(registerAddress) + "."); + + if(check) + { + data.at(0) = registerAddress | RegisterBitmasks::Enum::READ_SINGLE; + data.at(1) = 0; + readwrite(data); + if(data.at(1) != value) + { + GD::out.printError("Error (check) writing to register " + std::to_string(registerAddress) + "."); + return 0; + } + } + return value; + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + return 0; +} + +void HomeMaticCc1101::writeRegisters(Registers::Enum startAddress, std::vector& values) +{ + try + { + if(_fileDescriptor->descriptor == -1) return; + std::vector data({(uint8_t)(startAddress | RegisterBitmasks::Enum::WRITE_BURST) }); + data.insert(data.end(), values.begin(), values.end()); + readwrite(data); + if((data.at(0) & StatusBitmasks::Enum::CHIP_RDYn)) GD::out.printError("Error writing to registers " + std::to_string(startAddress) + "."); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } +} + +uint8_t HomeMaticCc1101::sendCommandStrobe(CommandStrobes::Enum commandStrobe) +{ + try + { + if(_fileDescriptor->descriptor == -1) return 0xFF; + std::vector data({(uint8_t)commandStrobe}); + for(uint32_t i = 0; i < 5; i++) + { + readwrite(data); + if(!(data.at(0) & StatusBitmasks::Enum::CHIP_RDYn)) break; + data.at(0) = (uint8_t)commandStrobe; + usleep(20); + } + return data.at(0); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + return 0; +} + +BaseLib::PVariable HomeMaticCc1101::callMethod(std::string& method, BaseLib::PArray parameters) +{ + try + { + auto localMethodIterator = _localRpcMethods.find(method); + if(localMethodIterator == _localRpcMethods.end()) return BaseLib::Variable::createError(-32601, ": Requested method not found."); + + if(GD::bl->debugLevel >= 5) GD::out.printDebug("Debug: Server is calling RPC method: " + method); + + return localMethodIterator->second(parameters); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + return BaseLib::Variable::createError(-32500, "Unknown application error. See log for more details."); +} + +//{{{ RPC methods +BaseLib::PVariable HomeMaticCc1101::sendPacket(BaseLib::PArray& parameters) +{ + try + { + if(parameters->size() != 2 || parameters->at(1)->type != BaseLib::VariableType::tString || parameters->at(1)->stringValue.empty()) return BaseLib::Variable::createError(-1, "Invalid parameters."); + + if(_fileDescriptor->descriptor == -1 || !_gpio->isOpen(GD::settings.gpio1()) || _stopped) return BaseLib::Variable::createError(-1, "SPI device or GPIO is not open."); + + std::vector decodedPacket = _bl->hf.getUBinary(parameters->at(1)->stringValue); + bool burst = decodedPacket.at(2) & 0x10; + std::vector encodedPacket(decodedPacket.size()); + encodedPacket[0] = decodedPacket[0]; + encodedPacket[1] = (~decodedPacket[1]) ^ 0x89; + uint32_t i = 2; + for(; i < decodedPacket[0]; i++) + { + encodedPacket[i] = (encodedPacket[i - 1] + 0xDC) ^ decodedPacket[i]; + } + encodedPacket[i] = decodedPacket[i] ^ decodedPacket[2]; + + int64_t timeBeforeLock = BaseLib::HelperFunctions::getTime(); + _sendingPending = true; + if(!_txMutex.try_lock_for(std::chrono::milliseconds(10000))) + { + GD::out.printCritical("Critical: Could not acquire lock for sending packet. This should never happen. Please report this error."); + _txMutex.unlock(); + if(!_txMutex.try_lock_for(std::chrono::milliseconds(100))) + { + _sendingPending = false; + return BaseLib::Variable::createError(-2, "Could not acquire lock for sending packet."); + } + } + _sendingPending = false; + if(_stopCallbackThread || _fileDescriptor->descriptor == -1 || !_gpio->isOpen(GD::settings.gpio1()) || _stopped) + { + _txMutex.unlock(); + return BaseLib::Variable::createError(-1, "SPI device or GPIO is not open."); + } + _sending = true; + sendCommandStrobe(CommandStrobes::Enum::SIDLE); + sendCommandStrobe(CommandStrobes::Enum::SFTX); + if(BaseLib::HelperFunctions::getTime() - timeBeforeLock > 100) + { + GD::out.printWarning("Warning: Timing problem. Sending took more than 100ms. Do you have enough system resources?"); + } + if(burst) + { + //int32_t waitIndex = 0; + //while(waitIndex < 200) + //{ + sendCommandStrobe(CommandStrobes::Enum::STX); + //if(readRegister(Registers::MARCSTATE) == 0x13) break; + //std::this_thread::sleep_for(std::chrono::milliseconds(2)); + //waitIndex++; + //} + //if(waitIndex == 200) _out.printError("Error sending BidCoS packet. No CCA within 400ms."); + usleep(360000); + } + writeRegisters(Registers::Enum::FIFO, encodedPacket); + if(!burst) + { + //int32_t waitIndex = 0; + //while(waitIndex < 200) + //{ + sendCommandStrobe(CommandStrobes::Enum::STX); + //if(readRegister(Registers::MARCSTATE) == 0x13) break; + //std::this_thread::sleep_for(std::chrono::milliseconds(2)); + //waitIndex++; + //} + //if(waitIndex == 200) + //{ + //_out.printError("Error sending BidCoS packet. No CCA within 400ms."); + //sendCommandStrobe(CommandStrobes::Enum::SFTX); + //} + } + + //Unlocking of _txMutex takes place in mainThread + + return std::make_shared(); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + return BaseLib::Variable::createError(-32500, "Unknown application error. See log for more details."); +} + +void HomeMaticCc1101::setConfig() +{ + if(_oscillatorFrequency == 26000000) + { + _config = //Read from HM-CC-VD + { + (_interruptPin == 2) ? (uint8_t)0x46 : (uint8_t)0x5B, //00: IOCFG2 (GDO2_CFG) + 0x2E, //01: IOCFG1 (GDO1_CFG to High impedance (3-state)) + (_interruptPin == 0) ? (uint8_t)0x46 : (uint8_t)0x5B, //02: IOCFG0 (GDO0_CFG) + 0x07, //03: FIFOTHR (FIFO threshold to 33 (TX) and 32 (RX) + 0xE9, //04: SYNC1 + 0xCA, //05: SYNC0 + 0xFF, //06: PKTLEN (Maximum packet length) + 0x0C, //07: PKTCTRL1: CRC_AUTOFLUSH | APPEND_STATUS | NO_ADDR_CHECK + 0x45, //08: PKTCTRL0 + 0x00, //09: ADDR + 0x00, //0A: CHANNR + 0x06, //0B: FSCTRL1 + 0x00, //0C: FSCTRL0 + 0x21, //0D: FREQ2 + 0x65, //0E: FREQ1 + 0x6A, //0F: FREQ0 + 0xC8, //10: MDMCFG4 + 0x93, //11: MDMCFG3 + 0x03, //12: MDMCFG2 + 0x22, //13: MDMCFG1 + 0xF8, //14: MDMCFG0 + 0x34, //15: DEVIATN + 0x07, //16: MCSM2 + 0x30, //17: MCSM1: IDLE when packet has been received, RX after sending + 0x18, //18: MCSM0 + 0x16, //19: FOCCFG + 0x6C, //1A: BSCFG + 0x03, //1B: AGCCTRL2 + 0x40, //1C: AGCCTRL1 + 0x91, //1D: AGCCTRL0 + 0x87, //1E: WOREVT1 + 0x6B, //1F: WOREVT0 + 0xF8, //20: WORCRTL + 0x56, //21: FREND1 + 0x10, //22: FREND0 + 0xE9, //23: FSCAL3 + 0x2A, //24: FSCAL2 + 0x00, //25: FSCAL1 + 0x1F, //26: FSCAL0 + 0x41, //27: RCCTRL1 + 0x00, //28: RCCTRL0 + }; + } + else if(_oscillatorFrequency == 27000000) + { + _config = + { + (_interruptPin == 2) ? (uint8_t)0x46 : (uint8_t)0x5B, //00: IOCFG2 (GDO2_CFG: GDO2 connected to RPi interrupt pin, asserts when packet sent/received, active low) + 0x2E, //01: IOCFG1 (GDO1_CFG to High impedance (3-state)) + (_interruptPin == 0) ? (uint8_t)0x46 : (uint8_t)0x5B, //02: IOCFG0 (GDO0_CFG, GDO0 (optionally) connected to CC1190 PA_EN, PA_PD, active low(?!)) + 0x07, //03: FIFOTHR (FIFO threshold to 33 (TX) and 32 (RX) + 0xE9, //04: SYNC1 + 0xCA, //05: SYNC0 + 0xFF, //06: PKTLEN (Maximum packet length) + 0x0C, //07: PKTCTRL1 (CRC_AUTOFLUSH | APPEND_STATUS | NO_ADDR_CHECK) + 0x45, //08: PKTCTRL0 (WHITE_DATA = on, PKT_FORMAT = normal mode, CRC_EN = on, LENGTH_CONFIG = "Variable packet length mode. Packet length configured by the first byte after sync word") + 0x00, //09: ADDR + 0x00, //0A: CHANNR + 0x06, //0B: FSCTRL1 (0x06 gives f_IF=152.34375kHz@26.0MHz XTAL, 158.203125kHz@f_XOSC=27.0MHz; default value is 0x0F which gives f_IF=381kHz@f_XOSC=26MHz; formula is f_IF=(f_XOSC/2^10)*FSCTRL1[5:0]) + 0x00, //0C: FSCTRL0 + 0x20, //0D: FREQ2 (base freq f_carrier=(f_XOSC/2^16)*FREQ[23:0]; register value FREQ[23:0]=(2^16/f_XOSC)*f_carrier; 0x21656A gives f_carrier=868.299866MHz@f_XOSC=26.0MHz, 0x2028C5 gives f_carrier=868.299911MHz@f_XOSC=27.0MHz) + 0x28, //0E: FREQ1 + 0xC5, //0F: FREQ0 + 0xC8, //10: MDMCFG4 (CHANBW_E = 3, CHANBW_M = 0, gives BW_channel=f_XOSC/(8*(4+CHANBW_M)*2^CHANBW_E)=102kHz@f_XOSC=26MHz, 105kHz@f_XOSC=27MHz) +// 0x93, //11: MDMCFG3 (26MHz: DRATE_E = 0x8, DRATE_M = 0x93, gives R_DATA=((256+DRATE_M)*2^DRATE_E/2^28)*f_XOSC=9993Baud) + 0x84, //11: MDMCFG3 (27MHz: DRATE_M=(R_DATA*2^28)/(f_XOSC*2^DRATE_E)-256 ==> DRATE_E = 0x8, DRATE_M = 132=0x84, gives R_DATA=((256+DRATE_M)*2^DRATE_E/2^28)*f_XOSC=9991Baud) + 0x03, //12: MDMCFG2 (DEM_DCFILT_OFF = 0, MOD_FORMAT = 0 (2-FSK), MANCHESTER_EN = 0, SYNC_MODE = 3 = 30/32 sync word bits detected) + 0x22, //13: MDMCFG1 (FEC_EN = 0, NUM_PREAMBLE = 2 = 4 preamble bytes, CHANSPC_E = 2) +// 0xF8, //14: MDMCFG0 (CHANSPC_M = 248 = 0xF8, Delta f_channel=(f_XOSC/2^18)*(256+CHANSPC_M)*2^CHANSPC_E=199.951kHz@f_XOSC=26MHz) + 0xE5, //14: MDMCFG0 (CHANSPC_M=(Delta_F_channel*2^18/(f_XOSC*2^CHANSPC_E)-256 ==> CHANSPC_M = 229 = 0xE5, Delta_f_channel=(f_XOSC/2^18)*(256+CHANSPC_M)*2^CHANSPC_E=199.814kHz@f_XOSC=27MHz) + 0x34, //15: DEVIATN (DEVIATION_E = 3, DEVIATION_M = 4, gives f_dev=(f_XOSC/2^17)*(8+DEVIATION_M)*2^DEVIATION_E=19.043kHz@f_XOSC=26MHz, =19.775kHz@f_XOSC=27MHz) + 0x07, //16: MCSM2 (RX_TIME_RSSI = 0, RX_TIME_QUAL = 0, RX_TIME = 7) + 0x30, //17: MCSM1 (CCA_MODE = 0b00 = "Always", RXOFF_MODE = 0 = IDLE, TXOFF_MODE = 0 = IDLE) + 0x18, //18: MCSM0 (FS_AUTOCAL = 0b01 = cal@IDLE->RX/TX, PO_TIMEOUT = 0b10 = 149µs@27MHz, PIN_CTRL_EN = 0, XOSC_FORCE_ON = 0) + 0x16, //19: FOCCFG (FOD_BS_CS_GATE = 0, FOC_PRE_K = 0b10 = 3K, FOC_POST_K = 1 = K/2, FOC_LIMIT = 0b10) + 0x6C, //1A: BSCFG + 0x03, //1B: AGCCTRL2 + 0x40, //1C: AGCCTRL1 + 0x91, //1D: AGCCTRL0 + 0x87, //1E: WOREVT1 + 0x6B, //1F: WOREVT0 + 0xF8, //20: WORCRTL + 0x56, //21: FREND1 + 0x10, //22: FREND0 + 0xE9, //23: FSCAL3 + 0x2A, //24: FSCAL2 + 0x00, //25: FSCAL1 + 0x1F, //26: FSCAL0 + 0x41, //27: RCCTRL1 + 0x00, //28: RCCTRL0 + }; + } + else GD::out.printError("Error: Unknown value for \"oscillatorFrequency\" in gateway.conf. Valid values are 26000000 and 27000000."); +} + +BaseLib::PVariable HomeMaticCc1101::enableUpdateMode(BaseLib::PArray& parameters) +{ + try + { + _updateMode = true; + while(_sending) std::this_thread::sleep_for(std::chrono::milliseconds(3)); + _txMutex.try_lock(); + sendCommandStrobe(CommandStrobes::Enum::SIDLE); + writeRegister(Registers::Enum::FSCTRL1, 0x08, true); // gives higher IF, IF=203kHz@f_XOSC=26MHz, 211kHz@f_XOSC=27MHz + writeRegister(Registers::Enum::MDMCFG4, 0x5B, true); // CHANBW_E = 1, CHANBW_M = 1, BW_channel = 325kHz@26MHz, 338kHz@27MHz (higher) + writeRegister(Registers::Enum::MDMCFG3, (_oscillatorFrequency == 26000000) ? 0xF8 : 0xE5, true); // 0xF8 gives R_DATA = 99.975kBaud@26MHz; 0xE5 gives R_DATA=99.907kBaud@27MHz + writeRegister(Registers::Enum::DEVIATN, (_oscillatorFrequency == 26000000) ? 0x47 : 0x46, true); // 47.607kHz deviation @26MHz, 46.143kHz dev @27MHz + writeRegister(Registers::Enum::FOCCFG, 0x1D, true); + writeRegister(Registers::Enum::BSCFG, 0x1C, true); + writeRegister(Registers::Enum::AGCCTRL2, 0xC7, true); + writeRegister(Registers::Enum::AGCCTRL1, 0x00, true); + writeRegister(Registers::Enum::AGCCTRL0, 0xB2, true); + writeRegister(Registers::Enum::FREND1, 0xB6, true); + writeRegister(Registers::Enum::FSCAL3, 0xEA, true); //Changes, because of the change of data rate + usleep(20); + sendCommandStrobe(CommandStrobes::Enum::SFRX); + sendCommandStrobe(CommandStrobes::Enum::SRX); + + _txMutex.unlock(); + + return std::make_shared(); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + + _txMutex.unlock(); + + return BaseLib::Variable::createError(-32500, "Unknown application error. See log for more details."); +} + +BaseLib::PVariable HomeMaticCc1101::disableUpdateMode(BaseLib::PArray& parameters) +{ + try + { + setConfig(); + stop(); + _updateMode = false; + start(); + + return std::make_shared(); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + return BaseLib::Variable::createError(-32500, "Unknown application error. See log for more details."); +} +//}}} \ No newline at end of file diff --git a/src/Families/HomeMaticCc1101.h b/src/Families/HomeMaticCc1101.h new file mode 100644 index 0000000..7e2d216 --- /dev/null +++ b/src/Families/HomeMaticCc1101.h @@ -0,0 +1,224 @@ +/* Copyright 2013-2017 Sathya Laufer + * + * Homegear 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 3 of the + * License, or (at your option) any later version. + * + * Homegear 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 Homegear. If not, see + * . + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations + * including the two. + * You must obey the GNU Lesser General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. +*/ + +#ifndef HOMEGEAR_GATEWAY_HOMEMATIC_CC1101_H +#define HOMEGEAR_GATEWAY_HOMEMATIC_CC1101_H + +#include "ICommunicationInterface.h" + +#define HOMEMATIC_CC1101_FAMILY_ID 0 + +class HomeMaticCc1101 : public ICommunicationInterface +{ +public: + HomeMaticCc1101(BaseLib::SharedObjects* bl); + virtual ~HomeMaticCc1101(); + virtual BaseLib::PVariable callMethod(std::string& method, BaseLib::PArray parameters); +private: + struct CommandStrobes + { + enum Enum + { + SRES = 0x30, // Reset chip. + SFSTXON = 0x31, // Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1). If in RX (with CCA): + // Go to a wait state where only the synthesizer is running (for quick RX / TX turnaround). + SXOFF = 0x32, // Turn off crystal oscillator. + SCAL = 0x33, // Calibrate frequency synthesizer and turn it off. SCAL can be strobed from IDLE mode without + // setting manual calibration mode (MCSM0.FS_AUTOCAL=0) + SRX = 0x34, // Enable RX. Perform calibration first if coming from IDLE and MCSM0.FS_AUTOCAL=1. + STX = 0x35, // In IDLE state: Enable TX. Perform calibration first if MCSM0.FS_AUTOCAL=1. + // If in RX state and CCA is enabled: Only go to TX if channel is clear. + SIDLE = 0x36, // Exit RX / TX, turn off frequency synthesizer and exit Wake-On-Radio mode if applicable. + SWOR = 0x38, // Start automatic RX polling sequence (Wake-on-Radio) as described in Section 19.5 if + // WORCTRL.RC_PD=0. + SPWD = 0x39, // Enter power down mode when CSn goes high. + SFRX = 0x3A, // Flush the RX FIFO buffer. Only issue SFRX in IDLE or, RXFIFO_OVERFLOW states. + SFTX = 0x3B, // Flush the TX FIFO buffer. Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states. + SWORRST = 0x3C, // Reset real time clock to Event1 value. + SNOP = 0x3D // No operation. May be used to get access to the chip status byte. + }; + }; + + struct Status + { + enum Enum + { + IDLE = 0x00, + RX = 0x10, + TX = 0x20, + FSTXON = 0x30, + CALIBRATE = 0x40, + SETTLING = 0x50, + RXFIFO_OVERFLOW = 0x60, + TXFIFO_OVERFLOW = 0x70 + }; + }; + + struct StatusBitmasks + { + enum Enum + { + CHIP_RDYn = 0x80, + STATE = 0x70, + FIFO_BYTES_AVAILABLE = 0x0F + }; + }; + + struct RegisterBitmasks + { + enum Enum + { + WRITE_BURST = 0x40, + READ_SINGLE = 0x80, + READ_BURST = 0xC0 + }; + }; + + struct Registers + { + enum Enum + { + IOCFG2 = 0x00, + IOCFG1 = 0x01, + IOCFG0 = 0x02, + FIFOTHR = 0x03, + SYNC1 = 0x04, + SYNC2 = 0x05, + PKTLEN = 0x06, + PKTCTRL1 = 0x07, + PKTCTRL0 = 0x08, + ADDR = 0x09, + CHANNR = 0x0A, + FSCTRL1 = 0x0B, + FSCTRL0 = 0x0C, + FREQ2 = 0x0D, + FREQ1 = 0x0E, + FREQ0 = 0x0F, + MDMCFG4 = 0x10, + MDMCFG3 = 0x11, + MDMCFG2 = 0x12, + MDMCFG1 = 0x13, + MDMCFG0 = 0x14, + DEVIATN = 0x15, + MCSM2 = 0x16, + MCSM1 = 0x17, + MCSM0 = 0x18, + FOCCFG = 0x19, + BSCFG = 0x1A, + AGCCTRL2 = 0x1B, + AGCCTRL1 = 0x1C, + AGCCTRL0 = 0x1D, + WOREVT1 = 0x1E, + WOREVT0 = 0x1F, + WORCTRL = 0x20, + FREND1 = 0x21, + FREND0 = 0x22, + FSCAL3 = 0x23, + FSCAL2 = 0x24, + FSCAL1 = 0x25, + FSCAL0 = 0x26, + RCCTRL1 = 0x27, + RCCTRL0 = 0x28, + FSTEST = 0x29, + PTEST = 0x2A, + AGCTEST = 0x2B, + TEST2 = 0x2C, + TEST1 = 0x2D, + TEST0 = 0x2E, + //No 0x2F + PARTNUM = 0x30, + CHIPVERSION = 0x31, + FREQTEST = 0x32, + LQI = 0x33, + RSSI = 0x34, + MARCSTATE = 0x35, + WORTIME1 = 0x36, + WORTIME0 = 0x37, + PKTSTATUS = 0x38, + VCO_VC_DAC = 0x39, + TXBYTES = 0x3A, + RXBYTES = 0x3B, + RCCTRL1_STATUS = 0x3C, + RCCTRL0_STATUS = 0x3D, + PATABLE = 0x3E, + FIFO = 0x3F + }; + }; + + BaseLib::PFileDescriptor _fileDescriptor; + std::unique_ptr _gpio; + std::string _lockfile; + int32_t _oscillatorFrequency = 26000000; + int32_t _interruptPin = 2; + std::atomic_bool _updateMode; + std::vector _config; + std::vector _patable; + struct spi_ioc_transfer _transfer; + std::timed_mutex _txMutex; + std::mutex _sendMutex; + std::atomic_bool _sending; + std::atomic_bool _sendingPending; + std::atomic_bool _firstPacket; + std::thread _listenThread; + std::atomic_bool _stopped; + std::atomic_bool _stopCallbackThread; + + void start(); + void stop(); + + void mainThread(); + + void setConfig(); + void setupDevice(); + void initDevice(); + void openDevice(); + void closeDevice(); + void initChip(); + void reset(); + bool crcOK(); + void enableRX(bool flushRXFIFO); + void endSending(); + + void readwrite(std::vector& data); + uint8_t sendCommandStrobe(CommandStrobes::Enum commandStrobe); + uint8_t readRegister(Registers::Enum registerAddress); + std::vector readRegisters(Registers::Enum startAddress, uint8_t count); + uint8_t writeRegister(Registers::Enum registerAddress, uint8_t value, bool check = false); + void writeRegisters(Registers::Enum startAddress, std::vector& values); + bool checkStatus(uint8_t statusByte, Status::Enum status); + +//{{{ RPC methods + BaseLib::PVariable sendPacket(BaseLib::PArray& parameters); + BaseLib::PVariable enableUpdateMode(BaseLib::PArray& parameters); + BaseLib::PVariable disableUpdateMode(BaseLib::PArray& parameters); +//}}} +}; + +#endif diff --git a/src/Families/HomeMaticCulfw.cpp b/src/Families/HomeMaticCulfw.cpp index fc3d5c0..517d65d 100644 --- a/src/Families/HomeMaticCulfw.cpp +++ b/src/Families/HomeMaticCulfw.cpp @@ -35,6 +35,8 @@ HomeMaticCulfw::HomeMaticCulfw(BaseLib::SharedObjects* bl) : ICommunicationInter { try { + _familyId = HOMEMATIC_COC_FAMILY_ID; + _updateMode = false; _localRpcMethods.emplace("sendPacket", std::bind(&HomeMaticCulfw::sendPacket, this, std::placeholders::_1)); @@ -85,7 +87,7 @@ void HomeMaticCulfw::start() { if(GD::settings.device().empty()) { - GD::out.printError("Error: No device defined for family EnOcean. Please specify it in \"gateway.conf\"."); + GD::out.printError("Error: No device defined for family HomeMatic BidCoS CUL. Please specify it in \"gateway.conf\"."); return; } diff --git a/src/Families/ICommunicationInterface.h b/src/Families/ICommunicationInterface.h index 94ae4b6..7c153cd 100644 --- a/src/Families/ICommunicationInterface.h +++ b/src/Families/ICommunicationInterface.h @@ -38,10 +38,14 @@ class ICommunicationInterface public: ICommunicationInterface(BaseLib::SharedObjects* bl); virtual ~ICommunicationInterface() = default; + + int32_t familyId() { return _familyId; } + virtual BaseLib::PVariable callMethod(std::string& method, BaseLib::PArray parameters) = 0; void setInvoke(std::function value) { _invoke.swap(value); } protected: BaseLib::SharedObjects* _bl = nullptr; + int32_t _familyId = -1; std::map> _localRpcMethods; std::function _invoke; }; diff --git a/src/Makefile.am b/src/Makefile.am index c2df383..5d256f1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,7 +4,7 @@ AM_CPPFLAGS = -Wall -std=c++11 -DFORTIFY_SOURCE=2 -DGCRYPT_NO_DEPRECATED AM_LDFLAGS = -Wl,-rpath=/lib/homegear -Wl,-rpath=/usr/lib/homegear -Wl,-rpath=/usr/local/lib/homegear bin_PROGRAMS = homegear-gateway -homegear_gateway_SOURCES = main.cpp RpcServer.cpp Settings.cpp GD.cpp UPnP.cpp Families/EnOcean.cpp Families/HomeMaticCulfw.cpp Families/ICommunicationInterface.cpp +homegear_gateway_SOURCES = main.cpp RpcServer.cpp Settings.cpp GD.cpp UPnP.cpp Families/EnOcean.cpp Families/HomeMaticCc1101.cpp Families/HomeMaticCulfw.cpp Families/ICommunicationInterface.cpp homegear_gateway_LDADD = -lpthread -lhomegear-base -lgcrypt -lgnutls if BSDSYSTEM diff --git a/src/RpcServer.cpp b/src/RpcServer.cpp index 0b959e3..5fce8ab 100644 --- a/src/RpcServer.cpp +++ b/src/RpcServer.cpp @@ -32,12 +32,14 @@ #include "GD.h" #include "Families/EnOcean.h" #include "Families/HomeMaticCulfw.h" +#include "Families/HomeMaticCc1101.h" RpcServer::RpcServer(BaseLib::SharedObjects* bl) { signal(SIGPIPE, SIG_IGN); _stopped = true; + _unconfigured = false; _clientConnected = false; _waitForResponse = false; @@ -49,7 +51,15 @@ RpcServer::RpcServer(BaseLib::SharedObjects* bl) RpcServer::~RpcServer() { + std::lock_guard maintenanceThreadGuard(_maintenanceThreadMutex); + _bl->threadManager.join(_maintenanceThread); +} + +int32_t RpcServer::familyId() +{ + if(_interface) return _interface->familyId(); + return -1; } bool RpcServer::start() @@ -64,6 +74,7 @@ bool RpcServer::start() if(GD::settings.family() == "enocean") _interface = std::unique_ptr(new EnOcean(_bl)); else if(GD::settings.family() == "homematicculfw") _interface = std::unique_ptr(new HomeMaticCulfw(_bl)); + else if(GD::settings.family() == "homematiccc1101") _interface = std::unique_ptr(new HomeMaticCc1101(_bl)); if(!_interface) { @@ -77,12 +88,52 @@ bool RpcServer::start() serverInfo.maxConnections = 1; serverInfo.useSsl = true; BaseLib::TcpSocket::PCertificateInfo certificateInfo = std::make_shared(); - certificateInfo->caFile = GD::settings.caFile(); - certificateInfo->certFile = GD::settings.certPath(); - certificateInfo->keyFile = GD::settings.keyPath(); - serverInfo.certificates.emplace("*", certificateInfo); - serverInfo.dhParamFile = GD::settings.dhPath(); - serverInfo.requireClientCert = true; + + std::string caFile = GD::settings.caFile(); + if(caFile.empty()) caFile = GD::settings.dataPath() + "ca.crt"; + if(!BaseLib::Io::fileExists(caFile)) + { + caFile = ""; + _unconfigured = true; + serverInfo.useSsl = false; + } + certificateInfo->caFile = caFile; + + std::string certFile = GD::settings.certPath(); + if(certFile.empty()) certFile = GD::settings.dataPath() + "gateway.crt"; + if(!BaseLib::Io::fileExists(certFile)) + { + certFile = ""; + _unconfigured = true; + serverInfo.useSsl = false; + } + certificateInfo->certFile = certFile; + + std::string keyFile = GD::settings.keyPath(); + if(keyFile.empty()) keyFile = GD::settings.dataPath() + "gateway.key"; + if(!BaseLib::Io::fileExists(keyFile)) + { + keyFile = ""; + _unconfigured = true; + serverInfo.useSsl = false; + } + certificateInfo->keyFile = keyFile; + + if(_unconfigured && GD::settings.configurationPassword().empty()) + { + _interface.reset(); + GD::out.printError("Error: Gateway is unconfigured but configurationPassword is not set in gateway.conf."); + return false; + } + + if(!_unconfigured) + { + serverInfo.certificates.emplace("*", certificateInfo); + std::string dhFile = GD::settings.dhPath(); + if(dhFile.empty()) dhFile = GD::settings.dataPath() + "dh.pem"; + serverInfo.dhParamFile = dhFile; + serverInfo.requireClientCert = true; + } serverInfo.newConnectionCallback = std::bind(&RpcServer::newConnection, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); serverInfo.packetReceivedCallback = std::bind(&RpcServer::packetReceived, this, std::placeholders::_1, std::placeholders::_2); @@ -134,6 +185,83 @@ void RpcServer::stop() } } +void RpcServer::restart() +{ + stop(); + start(); +} + +BaseLib::PVariable RpcServer::configure(BaseLib::PArray& parameters) +{ + try + { + if(parameters->size() != 1) return BaseLib::Variable::createError(-1, "Wrong parameter count."); + if(parameters->at(0)->type != BaseLib::VariableType::tString) return BaseLib::Variable::createError(-1, "Parameter is not of type String."); + if(parameters->at(0)->stringValue.size() < 128 || parameters->at(0)->stringValue.size() > 100000) return BaseLib::Variable::createError(-2, "Data is invalid."); + + BaseLib::Security::Gcrypt aes(GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_GCM, GCRY_CIPHER_SECURE); + std::vector iv = _bl->hf.getUBinary(parameters->at(0)->stringValue.substr(0, 64)); + std::vector counter(16); + aes.setCounter(counter); + aes.setIv(iv); + + std::vector key; + if(!BaseLib::Security::Hash::sha256(GD::bl->hf.getUBinary(GD::settings.configurationPassword()), key) || key.empty()) + { + GD::out.printError("Error: Could not generate SHA256 of configuration password."); + return BaseLib::Variable::createError(-32500, "Unknown application error. See log for more details."); + } + aes.setKey(key); + + std::vector payload = _bl->hf.getUBinary(parameters->at(0)->stringValue.substr(64)); + if(!aes.authenticate(payload)) return BaseLib::Variable::createError(-2, "Data is invalid."); + + std::vector decryptedData; + aes.decrypt(decryptedData, payload); + + BaseLib::Rpc::RpcDecoder rpcDecoder(_bl, false, false); + auto data = rpcDecoder.decodeResponse(decryptedData); + + if(data->type != BaseLib::VariableType::tStruct) return BaseLib::Variable::createError(-1, "Data is not of type Struct."); + + auto dataIterator = data->structValue->find("caCert"); + if(dataIterator == data->structValue->end()) return BaseLib::Variable::createError(-1, "Data does not contain element \"caCert\"."); + std::string certPath = GD::settings.dataPath() + "ca.crt"; + BaseLib::Io::writeFile(certPath, dataIterator->second->stringValue); + + dataIterator = data->structValue->find("gatewayCert"); + if(dataIterator == data->structValue->end()) return BaseLib::Variable::createError(-1, "Data does not contain element \"gatewayCert\"."); + certPath = GD::settings.dataPath() + "gateway.crt"; + BaseLib::Io::writeFile(certPath, dataIterator->second->stringValue); + + dataIterator = data->structValue->find("gatewayKey"); + if(dataIterator == data->structValue->end()) return BaseLib::Variable::createError(-1, "Data does not contain element \"gatewayKey\"."); + certPath = GD::settings.dataPath() + "gateway.key"; + BaseLib::Io::writeFile(certPath, dataIterator->second->stringValue); + + uid_t userId = GD::bl->hf.userId(GD::runAsUser); + gid_t groupId = GD::bl->hf.groupId(GD::runAsGroup); + + if(chown(certPath.c_str(), userId, groupId) == -1) GD::out.printWarning("Warning: Could net set owner on " + certPath + ": " + std::string(strerror(errno))); + if(chmod(certPath.c_str(), S_IRUSR | S_IWUSR) == -1) GD::out.printWarning("Warning: Could net set permissions on " + certPath + ": " + std::string(strerror(errno)));; + + return std::make_shared(); + } + catch(BaseLib::Exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(const std::exception& ex) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what()); + } + catch(...) + { + GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); + } + return BaseLib::Variable::createError(-32500, "Unknown application error. See log for more details."); +} + void RpcServer::newConnection(int32_t clientId, std::string address, uint16_t port) { try @@ -160,7 +288,7 @@ void RpcServer::packetReceived(int32_t clientId, BaseLib::TcpSocket::TcpPacket p { try { - _binaryRpc->process((char*)packet.data(), packet.size()); + _binaryRpc->process((char*) packet.data(), packet.size()); if(_binaryRpc->isFinished()) { if(_binaryRpc->getType() == BaseLib::Rpc::BinaryRpc::Type::request) @@ -168,12 +296,40 @@ void RpcServer::packetReceived(int32_t clientId, BaseLib::TcpSocket::TcpPacket p std::string method; auto parameters = _rpcDecoder->decodeRequest(_binaryRpc->getData(), method); - BaseLib::PVariable response = _interface->callMethod(method, parameters); - std::vector data; - _rpcEncoder->encodeResponse(response, data); - _tcpServer->sendToClient(clientId, data); + BaseLib::PVariable response; + if(_unconfigured) + { + if(method == "configure") + { + response = configure(parameters); + std::vector data; + _rpcEncoder->encodeResponse(response, data); + _tcpServer->sendToClient(clientId, data, true); + + if(!response->errorStruct) + { + std::lock_guard maintenanceThreadGuard(_maintenanceThreadMutex); + _bl->threadManager.join(_maintenanceThread); + _bl->threadManager.start(_maintenanceThread, true, &RpcServer::restart, this); + } + } + else + { + response = BaseLib::Variable::createError(-1, "Unknown method."); + std::vector data; + _rpcEncoder->encodeResponse(response, data); + _tcpServer->sendToClient(clientId, data, true); + } + } + else + { + response = _interface->callMethod(method, parameters); + std::vector data; + _rpcEncoder->encodeResponse(response, data); + _tcpServer->sendToClient(clientId, data); + } } - else if(_binaryRpc->getType() == BaseLib::Rpc::BinaryRpc::Type::response && _waitForResponse) + else if(!_unconfigured && _binaryRpc->getType() == BaseLib::Rpc::BinaryRpc::Type::response && _waitForResponse) { std::unique_lock requestLock(_requestMutex); _rpcResponse = _rpcDecoder->decodeResponse(_binaryRpc->getData()); @@ -201,7 +357,7 @@ BaseLib::PVariable RpcServer::invoke(std::string methodName, BaseLib::PArray& pa { try { - if(_tcpServer->clientCount() == 0) return BaseLib::Variable::createError(-1, "No client connected."); + if(_unconfigured || _tcpServer->clientCount() == 0) return BaseLib::Variable::createError(-1, "No client connected."); std::lock_guard invokeGuard(_invokeMutex); std::unique_lock requestLock(_requestMutex); @@ -237,4 +393,4 @@ BaseLib::PVariable RpcServer::invoke(std::string methodName, BaseLib::PArray& pa GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__); } return BaseLib::Variable::createError(-32500, "Unknown application error. See log for more details."); -} \ No newline at end of file +} diff --git a/src/RpcServer.h b/src/RpcServer.h index a8f3e84..b79af4e 100644 --- a/src/RpcServer.h +++ b/src/RpcServer.h @@ -34,12 +34,17 @@ #include #include "Families/ICommunicationInterface.h" +#include + class RpcServer { public: RpcServer(BaseLib::SharedObjects* bl); virtual ~RpcServer(); + int32_t familyId(); + bool isUnconfigured() { return _unconfigured; } + bool start(); void stop(); BaseLib::PVariable invoke(std::string methodName, BaseLib::PArray& parameters); @@ -51,6 +56,10 @@ class RpcServer std::unique_ptr _rpcEncoder; std::unique_ptr _rpcDecoder; + std::mutex _maintenanceThreadMutex; + std::thread _maintenanceThread; + + std::atomic_bool _unconfigured; std::atomic_bool _stopped; std::atomic_bool _clientConnected; int32_t _clientId = 0; @@ -63,6 +72,10 @@ class RpcServer std::unique_ptr _interface; + BaseLib::PVariable configure(BaseLib::PArray& parameters); + + void restart(); + void newConnection(int32_t clientId, std::string address, uint16_t port); void packetReceived(int32_t clientId, BaseLib::TcpSocket::TcpPacket packet); }; diff --git a/src/Settings.cpp b/src/Settings.cpp index 26e94ea..1a06639 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -46,6 +46,7 @@ void Settings::reset() _enableCoreDumps = true; _workingDirectory = _executablePath; _logFilePath = "/var/log/homegear-gateway/"; + _dataPath = "/var/lib/homegear-gateway/"; _lockFilePath = "/var/lock/"; _secureMemorySize = 65536; _caFile = ""; @@ -53,6 +54,8 @@ void Settings::reset() _keyPath = ""; _dhPath = ""; + _configurationPassword = ""; + _enableUpnp = true; _upnpIpAddress = ""; _upnpUdn = ""; @@ -168,6 +171,13 @@ void Settings::load(std::string filename, std::string executablePath) if(_logFilePath.back() != '/') _logFilePath.push_back('/'); GD::bl->out.printDebug("Debug: logfilePath set to " + _logFilePath); } + else if(name == "datapath") + { + _dataPath = value; + if(_dataPath.empty()) _dataPath = "/var/lib/homegear-gateway/"; + if(_dataPath.back() != '/') _dataPath.push_back('/'); + GD::bl->out.printDebug("Debug: dataPath set to " + _dataPath); + } else if(name == "lockfilepath") { _lockFilePath = value; @@ -203,6 +213,11 @@ void Settings::load(std::string filename, std::string executablePath) _dhPath = value; GD::bl->out.printDebug("Debug: dhPath set to " + _dhPath); } + else if(name == "configurationpassword") + { + _configurationPassword = value; + GD::bl->out.printDebug("Debug: configurationPassword was set"); + } else if(name == "enableupnp") { _enableUpnp = BaseLib::HelperFunctions::toLower(value) == "true"; @@ -240,6 +255,21 @@ void Settings::load(std::string filename, std::string executablePath) if(_gpio2 < 0) _gpio2 = -1; GD::bl->out.printDebug("Debug: gpio2 set to " + std::to_string(_gpio2)); } + else if(name == "oscillatorfrequency") + { + _oscillatorFrequency = BaseLib::Math::getNumber(value); + if(_oscillatorFrequency < 0) _oscillatorFrequency = -1; + GD::bl->out.printDebug("Debug: oscillatorFrequency set to " + std::to_string(_oscillatorFrequency)); + } + else if(name == "interruptpin") + { + int32_t number = BaseLib::Math::getNumber(value); + if(number >= 0) + { + _interruptPin = number; + GD::bl->out.printDebug("Debug: interruptPin set to " + std::to_string(_interruptPin)); + } + } else { GD::bl->out.printWarning("Warning: Setting not found: " + std::string(input)); diff --git a/src/Settings.h b/src/Settings.h index abaa5d0..c26f7fb 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -50,6 +50,7 @@ class Settings bool enableCoreDumps() { return _enableCoreDumps; }; std::string workingDirectory() { return _workingDirectory; } std::string logFilePath() { return _logFilePath; } + std::string dataPath() { return _dataPath; } std::string lockFilePath() { return _lockFilePath; } uint32_t secureMemorySize() { return _secureMemorySize; } std::string caFile() { return _caFile; } @@ -57,6 +58,8 @@ class Settings std::string keyPath() { return _keyPath; } std::string dhPath() { return _dhPath; } + std::string configurationPassword() { return _configurationPassword; } + bool enableUpnp() { return _enableUpnp; } std::string upnpIpAddress() { return _upnpIpAddress; } std::string upnpUdn() { return _upnpUdn; } @@ -65,6 +68,8 @@ class Settings std::string device() { return _device; } int32_t gpio1() { return _gpio1; } int32_t gpio2() { return _gpio2; } + int32_t oscillatorFrequency() { return _oscillatorFrequency; } + int32_t interruptPin() { return _interruptPin; } private: std::string _executablePath; std::string _path; @@ -79,6 +84,7 @@ class Settings bool _enableCoreDumps = true; std::string _workingDirectory; std::string _logFilePath; + std::string _dataPath; std::string _lockFilePath; uint32_t _secureMemorySize = 65536; std::string _caFile; @@ -86,6 +92,8 @@ class Settings std::string _keyPath; std::string _dhPath; + std::string _configurationPassword; + bool _enableUpnp = false; std::string _upnpIpAddress; std::string _upnpUdn; @@ -94,6 +102,8 @@ class Settings std::string _device; int32_t _gpio1 = -1; int32_t _gpio2 = -1; + int32_t _oscillatorFrequency = -1; + int32_t _interruptPin = -1; void reset(); }; diff --git a/src/UPnP.cpp b/src/UPnP.cpp index 37bcfd1..9bac746 100644 --- a/src/UPnP.cpp +++ b/src/UPnP.cpp @@ -304,7 +304,7 @@ void UPnP::setPackets() { try { - std::string notifyPacketBase = "NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=1800\r\nSERVER: Homegear Gateway " + std::string(VERSION) + "\r\nLOCATION: " + "binrpcs://" + _address + ":" + std::to_string(GD::settings.port()) + "/\r\n"; + std::string notifyPacketBase = "NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=1800\r\nSERVER: Homegear Gateway " + std::string(VERSION) + "\r\nLOCATION: " + "binrpcs://" + _address + ":" + std::to_string(GD::settings.port()) + "/\r\nHG-FAMILY-ID: " + std::to_string(GD::rpcServer->familyId()) + "\r\nHG-GATEWAY-CONFIGURED: " + (GD::rpcServer->isUnconfigured() ? "0" : "1") + "\r\n"; std::string alivePacketRoot = notifyPacketBase + "NT: upnp:rootdevice\r\nUSN: " + _st + "::upnp:rootdevice\r\nNTS: ssdp:alive\r\n\r\n"; std::string alivePacketRootUUID = notifyPacketBase + "NT: " + _st + "\r\nUSN: " + _st + "\r\nNTS: ssdp:alive\r\n\r\n"; std::string alivePacket = notifyPacketBase + "NT: urn:schemas-upnp-org:device:basic:1\r\nUSN: " + _st + "\r\nNTS: ssdp:alive\r\n\r\n"; @@ -319,7 +319,7 @@ void UPnP::setPackets() _packets.byebyeRootUUID = std::vector(&byebyePacketRootUUID.at(0), &byebyePacketRootUUID.at(0) + byebyePacketRootUUID.size()); _packets.byebye = std::vector(&byebyePacket.at(0), &byebyePacket.at(0) + byebyePacket.size()); - std::string okPacketBase = std::string("HTTP/1.1 200 OK\r\nCache-Control: max-age=1800\r\nLocation: ") + "binrpcs://" + _address + ":" + std::to_string(GD::settings.port()) + "/\r\nServer: Homegear Gateway " + std::string(VERSION) + "\r\n"; + std::string okPacketBase = std::string("HTTP/1.1 200 OK\r\nCache-Control: max-age=1800\r\nLocation: ") + "binrpcs://" + _address + ":" + std::to_string(GD::settings.port()) + "/\r\nServer: Homegear Gateway " + std::string(VERSION) + "\r\nHG-Family-ID: " + std::to_string(GD::rpcServer->familyId()) + "\r\nHG-Gateway-Configured: " + (GD::rpcServer->isUnconfigured() ? "0" : "1") + "\r\n"; std::string okPacketRoot = okPacketBase + "ST: upnp:rootdevice\r\nUSN: " + _st + "::upnp:rootdevice\r\n\r\n"; std::string okPacketRootUUID = okPacketBase + "ST: " + _st + "\r\nUSN: " + _st + "\r\n\r\n"; std::string okPacket = okPacketBase + "ST: urn:schemas-upnp-org:device:basic:1\r\nUSN: " + _st + "\r\n\r\n"; diff --git a/src/homegear-gateway b/src/homegear-gateway deleted file mode 100755 index 3e1da31..0000000 Binary files a/src/homegear-gateway and /dev/null differ