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