From: rtrimana Date: Wed, 9 May 2018 02:04:01 +0000 (-0700) Subject: Initial version of Lifx lightbulb controller in C++ for Fidelius X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=71206b57d4d84e9bca26f75493c75dc57ced6cc0;p=iotcloud.git Initial version of Lifx lightbulb controller in C++ for Fidelius --- diff --git a/version2/src/RPi/BulbColor.h b/version2/src/RPi/BulbColor.h new file mode 100644 index 0000000..329d060 --- /dev/null +++ b/version2/src/RPi/BulbColor.h @@ -0,0 +1,82 @@ +#ifndef _BULBCOLOR_H__ +#define _BULBCOLOR_H__ +#include + +class BulbColor { + + private: + int hue; + int saturation; + int brightness; + int kelvin; + + public: + + BulbColor(int _hue, int _saturation, int _brightness, int _kelvin) { + + if ((_hue > 65535) || (_hue < 0)) { + cerr << "BulbColor: Invalid parameter value for _hue (0-65535)" << endl; + exit(1); + } + + if ((_saturation > 65535) || (_saturation < 0)) { + cerr << "BulbColor: Invalid parameter value for _saturation (0-65535)" << endl; + exit(1); + } + + if ((_brightness > 65535) || (_brightness < 0)) { + cerr << "BulbColor: Invalid parameter value for _brightness (0-65535)" << endl; + exit(1); + } + + if ((_kelvin > 65535) || (_kelvin < 0)) { + cerr << "BulbColor: Invalid parameter value for _kelvin (0-65535)" << endl; + exit(1); + } + + hue = _hue; + saturation = _saturation; + brightness = _brightness; + kelvin = _kelvin; + } + + + BulbColor(char* data) { + hue = ((data[1] & 0xFF) << 8); + hue |= (data[0] & 0xFF); + + saturation = ((data[3] & 0xFF) << 8); + saturation |= (data[2] & 0xFF); + + brightness = ((data[5] & 0xFF) << 8); + brightness |= (data[4] & 0xFF); + + kelvin = ((data[7] & 0xFF) << 8); + kelvin |= (data[6] & 0xFF); + } + + + ~BulbColor() { + } + + + int getHue() { + return hue; + } + + + int getSaturation() { + return saturation; + } + + + int getBrightness() { + return brightness; + } + + + int getKelvin() { + return kelvin; + } +}; +#endif diff --git a/version2/src/RPi/DeviceStateGroup.h b/version2/src/RPi/DeviceStateGroup.h new file mode 100644 index 0000000..c54bce0 --- /dev/null +++ b/version2/src/RPi/DeviceStateGroup.h @@ -0,0 +1,41 @@ +#ifndef _DEVICESTATEGROUP_H__ +#define _DEVICESTATEGROUP_H__ +#include +#include + +class DeviceStateGroup { + + private: + char group[16]; + string label; + int64_t updatedAt; + + public: + + DeviceStateGroup(char _location[16], string _label, int64_t _updatedAt) { + + strcpy(group, _location); + label = _label; + updatedAt = _updatedAt; + } + + + ~DeviceStateGroup() { + } + + + char* getGroup() { + return group; + } + + + string getLabel() { + return label; + } + + + int64_t getUpdatedAt() { + return updatedAt; + } +}; +#endif diff --git a/version2/src/RPi/DeviceStateHostFirmware.h b/version2/src/RPi/DeviceStateHostFirmware.h new file mode 100644 index 0000000..b2dd88a --- /dev/null +++ b/version2/src/RPi/DeviceStateHostFirmware.h @@ -0,0 +1,35 @@ +#ifndef _DEVICESTATEHOSTFIRMWARE_H__ +#define _DEVICESTATEHOSTFIRMWARE_H__ +#include + +class DeviceStateHostFirmware { + // time of build in nanosecond accuracy + // after some tests + private: + int64_t build; + int64_t version; // firmware version + + public: + + DeviceStateHostFirmware(int64_t _build, int64_t _version) { + + build = _build; + version = _version; + } + + + ~DeviceStateHostFirmware() { + } + + + int64_t getBuild() { + return build; + } + + + int64_t getVersion() { + return version; + } + +}; +#endif diff --git a/version2/src/RPi/DeviceStateHostInfo.h b/version2/src/RPi/DeviceStateHostInfo.h new file mode 100644 index 0000000..081f6b9 --- /dev/null +++ b/version2/src/RPi/DeviceStateHostInfo.h @@ -0,0 +1,39 @@ +#ifndef _DEVICESTATEHOSTINFO_H__ +#define _DEVICESTATEHOSTINFO_H__ +#include + +class DeviceStateHostInfo { + private: + int64_t signal; + int64_t tx; + int64_t rx; + + public: + + DeviceStateHostInfo(int64_t _signal, int64_t _tx, int64_t _rx) { + + signal = _signal; + tx = _tx; + rx = _rx; + } + + + ~DeviceStateHostInfo() { + } + + + int64_t getSignal() { + return signal; + } + + + int64_t getTx() { + return tx; + } + + + int64_t getRx() { + return rx; + } +}; +#endif diff --git a/version2/src/RPi/DeviceStateInfo.h b/version2/src/RPi/DeviceStateInfo.h new file mode 100644 index 0000000..68d4bd5 --- /dev/null +++ b/version2/src/RPi/DeviceStateInfo.h @@ -0,0 +1,39 @@ +#ifndef _DEVICESTATEINFO_H__ +#define _DEVICESTATEINFO_H__ +#include + +class DeviceStateInfo { + private: + int64_t time; + int64_t upTime; + int64_t downTime; + + public: + + DeviceStateInfo(int64_t _time, int64_t _upTime, int64_t _downTime) { + + time = _time; + upTime = _upTime; + downTime = _downTime; + } + + + ~DeviceStateInfo() { + } + + + int64_t getTime() { + return time; + } + + + int64_t getUpTime() { + return upTime; + } + + + int64_t getDownTime() { + return downTime; + } +}; +#endif diff --git a/version2/src/RPi/DeviceStateLocation.h b/version2/src/RPi/DeviceStateLocation.h new file mode 100644 index 0000000..70f9bab --- /dev/null +++ b/version2/src/RPi/DeviceStateLocation.h @@ -0,0 +1,40 @@ +#ifndef _DEVICESTATELOCATION_H__ +#define _DEVICESTATELOCATION_H__ +#include +#include + +class DeviceStateLocation { + private: + char location[16]; + string label; + int64_t updatedAt; + + public: + + DeviceStateLocation(char _location[16], string _label, long _updatedAt) { + + strcpy(location, _location); + label = _label; + updatedAt = _updatedAt; + } + + + ~DeviceStateLocation() { + } + + + char* getLocation() { + return location; + } + + + string getLabel() { + return label; + } + + + int64_t getUpdatedAt() { + return updatedAt; + } +}; +#endif diff --git a/version2/src/RPi/DeviceStateService.h b/version2/src/RPi/DeviceStateService.h new file mode 100644 index 0000000..784cc3f --- /dev/null +++ b/version2/src/RPi/DeviceStateService.h @@ -0,0 +1,32 @@ +#ifndef _DEVICESTATESERVICE_H__ +#define _DEVICESTATESERVICE_H__ +#include + +class DeviceStateService { + private: + int service; + int64_t port; + + public: + + DeviceStateService(int _service, long _port) { + + service = _service; + port = _port; + } + + + ~DeviceStateService() { + } + + + int getService() { + return service; + } + + + int64_t getPort() { + return port; + } +}; +#endif diff --git a/version2/src/RPi/DeviceStateVersion.h b/version2/src/RPi/DeviceStateVersion.h new file mode 100644 index 0000000..2e40d03 --- /dev/null +++ b/version2/src/RPi/DeviceStateVersion.h @@ -0,0 +1,39 @@ +#ifndef _DEVICESTATEVERSION_H__ +#define _DEVICESTATEVERSION_H__ +#include + +class DeviceStateVersion { + private: + int64_t vender; + int64_t product; + int64_t version; + + public: + + DeviceStateVersion(int64_t _vender, int64_t _product, int64_t _version) { + + vender = _vender; + product = _product; + version = _version; + } + + + ~DeviceStateVersion() { + } + + + int64_t getVender() { + return vender; + } + + + int64_t getProduct() { + return product; + } + + + int64_t getVersion() { + return version; + } +}; +#endif diff --git a/version2/src/RPi/DeviceStateWifiFirmware.h b/version2/src/RPi/DeviceStateWifiFirmware.h new file mode 100644 index 0000000..28c1d26 --- /dev/null +++ b/version2/src/RPi/DeviceStateWifiFirmware.h @@ -0,0 +1,34 @@ +#ifndef _DEVICESTATEWIFIFIRMWARE_H__ +#define _DEVICESTATEWIFIFIRMWARE_H__ +#include + +class DeviceStateWifiFirmware { + // time of build in nanosecond accuracy + // after some tests + private: + int64_t build; + int64_t version; // firmware version + + public: + + DeviceStateWifiFirmware(int64_t _build, int64_t _version) { + + build = _build; + version = _version; + } + + + ~DeviceStateWifiFirmware() { + } + + + int64_t getBuild() { + return build; + } + + + int64_t getVersion() { + return version; + } +}; +#endif diff --git a/version2/src/RPi/DeviceStateWifiInfo.h b/version2/src/RPi/DeviceStateWifiInfo.h new file mode 100644 index 0000000..7bbc83b --- /dev/null +++ b/version2/src/RPi/DeviceStateWifiInfo.h @@ -0,0 +1,39 @@ +#ifndef _DEVICESTATEWIFIINFO_H__ +#define _DEVICESTATEWIFIINFO_H__ +#include + +class DeviceStateWifiInfo { + private: + int64_t signal; + int64_t tx; + int64_t rx; + + public: + + DeviceStateWifiInfo(int64_t _signal, int64_t _tx, int64_t _rx) { + + signal = _signal; + tx = _tx; + rx = _rx; + } + + + ~DeviceStateWifiInfo() { + } + + + int64_t getSignal() { + return signal; + } + + + int64_t getTx() { + return tx; + } + + + int64_t getRx() { + return rx; + } +}; +#endif diff --git a/version2/src/RPi/IoTAddress.h b/version2/src/RPi/IoTAddress.h new file mode 100644 index 0000000..a057d3d --- /dev/null +++ b/version2/src/RPi/IoTAddress.h @@ -0,0 +1,59 @@ +#ifndef _IOTADDRESS_HPP__ +#define _IOTADDRESS_HPP__ +#include + +using namespace std; + + +// IoTAddress class for iotruntime +// Implemented based on IoTAddress.java that is used to wrap address +// +// @author Rahmadi Trimananda +// @version 1.0 +// @since 2017-01-09 + +class IoTAddress +{ + public: + + // Constructor + IoTAddress(uint8_t* _sAddress) { + + inetAddress = _sAddress; + } + + + // Constructor + IoTAddress() { + } + + + ~IoTAddress() { + } + + + uint8_t* getAddress() { + + return inetAddress; + } + + + /*string getURL(string strURLComplete) { + + return "http://" + inetAddress + strURLComplete; + } + + + // Custom hasher for IoTAddress / IoTDeviceAddress iterator + size_t hash(IoTAddress const& devAddress) const { + + std::hash hashVal; + return hashVal(inetAddress); + }*/ + + + // IoTAddress class properties + protected: + uint8_t* inetAddress; +}; +#endif diff --git a/version2/src/RPi/IoTDeviceAddress.h b/version2/src/RPi/IoTDeviceAddress.h new file mode 100644 index 0000000..c9c2828 --- /dev/null +++ b/version2/src/RPi/IoTDeviceAddress.h @@ -0,0 +1,91 @@ +#ifndef _IOTDEVICEADDRESS_H__ +#define _IOTDEVICEADDRESS_H__ +#include + +#include "IoTAddress.h" + +using namespace std; + +// IoTDeviceAddress class for iotruntime +// Implemented based on IoTDeviceAddress.java that is used to wrap device address +// +// @author Rahmadi Trimananda +// @version 1.0 +// @since 2017-01-09 + + +class IoTDeviceAddress final : public IoTAddress +{ + + public: + + // Constructor + IoTDeviceAddress(uint8_t* _sAddress, int _iSrcPort, int _iDstPort, bool _isSrcPortWildCard, bool _isDstPortWildCard) : IoTAddress(_sAddress) { + + iSrcPort = _iSrcPort; + iDstPort = _iDstPort; + isSrcPortWildCard = _isSrcPortWildCard; + isDstPortWildCard = _isDstPortWildCard; + } + + + // Constructor + IoTDeviceAddress() { + } + + + ~IoTDeviceAddress() { + } + + + // Getter methods + int getSourcePortNumber() { + + return iSrcPort; + } + + + int getDestinationPortNumber() { + + return iDstPort; + } + + + bool getIsSrcPortWildcard() { + + return isSrcPortWildCard; + } + + + bool getIsDstPortWildcard() { + + return isDstPortWildCard; + } + + + // Setter methods + void setSrcPort(int port) { + + if (isDstPortWildCard) { + iDstPort = port; + } + } + + + void setDstPort(int port) { + + if (isSrcPortWildCard) { + iSrcPort = port; + } + } + + + // IoTDeviceAddress class properties + private: + int iSrcPort; + int iDstPort; + + bool isSrcPortWildCard; + bool isDstPortWildCard; +}; +#endif diff --git a/version2/src/RPi/IoTSet.h b/version2/src/RPi/IoTSet.h new file mode 100644 index 0000000..0104809 --- /dev/null +++ b/version2/src/RPi/IoTSet.h @@ -0,0 +1,110 @@ +#ifndef _IOTSET_H__ +#define _IOTSET_H__ +#include +#include +#include + +using namespace std; + +/** This is the IoTSet implementation for C++ + * + * @author Rahmadi Trimananda + * @version 1.0 + * @since 2016-09-06 + */ +template +class IoTSet final { + private: + const unordered_set* set; + public: + IoTSet(); + IoTSet(const unordered_set* s); + ~IoTSet(); + public: + typename unordered_set::const_iterator find(const T& k); // Find the object + bool empty(); // Test is empty? + typename unordered_set::const_iterator begin(); // Iterator + typename unordered_set::const_iterator end(); // Iterator + int size(); // Set size + unordered_set* values(); // Return set contents +}; + + +/** + * Default constructor + */ +template +IoTSet::IoTSet() { + +} + + +/** + * Useful constructor + */ +template +IoTSet::IoTSet(const unordered_set* s) { + + set = s; +} + + +/** + * Default destructor + */ +template +IoTSet::~IoTSet() { + +} + + +/** + * Find the object k in the set + */ +template +typename unordered_set::const_iterator IoTSet::find(const T& k) { + + return (new unordered_set(*set))->find(k); +} + + +/** + * Return the "begin" iterator + */ +template +typename unordered_set::const_iterator IoTSet::begin() { + + return (new unordered_set(*set))->begin(); +} + + +/** + * Return the "end" iterator + */ +template +typename unordered_set::const_iterator IoTSet::end() { + + return (new unordered_set(*set))->end(); +} + + +/** + * Return the size of the set + */ +template +int IoTSet::size() { + + return set->size(); +} + + +/** + * Return a new copy of the set + */ +template +unordered_set* IoTSet::values() { + + return new unordered_set(*set); +} +#endif + diff --git a/version2/src/RPi/IoTUDP.h b/version2/src/RPi/IoTUDP.h new file mode 100644 index 0000000..e9f50ca --- /dev/null +++ b/version2/src/RPi/IoTUDP.h @@ -0,0 +1,88 @@ +#ifndef _IOTUDP_H__ +#define _IOTUDP_H__ +#include + +#include "IoTDeviceAddress.h" +#include "application.h" + +using namespace std; + +// IoTUDP class for iotruntime +// Implemented based on IoTUDP.java that is used to wrap communication socket for UDP +// +// @author Rahmadi Trimananda +// @version 1.0 +// @since 2017-01-09 + +class IoTUDP final +{ + // IoTUDP class properties + private: + //UDPSocket *socket; + UDP socket; + uint8_t* strHostAddress; + int iSrcPort; + int iDstPort; + bool didClose; + int timeOut; + + public: + + // Constructor + IoTUDP(IoTDeviceAddress* iotDevAdd) { + + strHostAddress = iotDevAdd->getAddress(); + iSrcPort = iotDevAdd->getSourcePortNumber(); + iDstPort = iotDevAdd->getDestinationPortNumber(); + timeOut = 0; + + //socket = new UDPSocket(iSrcPort); + socket.begin(iSrcPort); + didClose = false; + } + + + ~IoTUDP() { + } + + + uint8_t* getHostAddress() { + return strHostAddress; + } + + + int getSourcePort() { + return iSrcPort; + } + + + int getDestinationPort() { + return iDstPort; + } + + + void setTimeOut(int interval) { + + timeOut = interval; + } + + + // Send data packet + void sendData(const char* buffer, int bufferLen) { + //void sendData(const void* buffer, int bufferLen) { + unsigned short destinationPort = (unsigned short) iDstPort; + IPAddress ipFromBytes = strHostAddress; + socket.sendPacket(buffer, bufferLen, ipFromBytes, destinationPort); + //socket->sendTo(buffer, bufferLen, strHostAddress, destinationPort); + } + + + // Receive data packet + int receiveData(char* buffer, int iMaxDataLength) { + //int receiveData(void* buffer, int iMaxDataLength) { + //unsigned short destinationPort = (unsigned short) iDstPort; + return socket.receivePacket(buffer, iMaxDataLength); + //return socket->recvFrom(buffer, iMaxDataLength, strHostAddress, destinationPort); + } +}; +#endif diff --git a/version2/src/RPi/LifxHeader.h b/version2/src/RPi/LifxHeader.h new file mode 100644 index 0000000..6d4fbe5 --- /dev/null +++ b/version2/src/RPi/LifxHeader.h @@ -0,0 +1,287 @@ +#ifndef _LIFXHEADER_H__ +#define _LIFXHEADER_H__ +#include + +class LifxHeader { + + private: + // Frame variables + int size; + int origin; + bool tagged; + bool addressable; + int protocol; + int64_t source; + + // Frame address variables + char macAddress[8]; + bool ack_required; + bool res_required; + int sequence; + + // Protocol header + int type; + + public: + + LifxHeader() { + origin = 0; + addressable = true; + protocol =1024; + } + + + ~LifxHeader() { + } + + + void setSize(int _size) { + if (_size < 0) { + cerr << "Header: size cannot be less than 0" << endl; + exit(1); + } else if (_size > 65535) { + cerr << "Header: size too large" << endl; + exit(1); + } + size = _size; + } + + + void setOrigin(int _origin) { + if (_origin < 0) { + cerr << "Header: origin cannot be less than 0" << endl; + exit(1); + } else if (_origin > 3) { + cerr << "Header: origin too large" << endl; + exit(1); + } + origin = _origin; + } + + + void setTagged(bool _tagged) { + tagged = _tagged; + } + + + void setAddressable(bool _addressable) { + addressable = _addressable; + } + + + void setProtocol(int _protocol) { + if (_protocol < 0) { + cerr << "Header: protocol cannot be less than 0" << endl; + exit(1); + } else if (_protocol > 4095) { + cerr << "Header: protocol too large" << endl; + exit(1); + } + protocol = _protocol; + } + + + void setSource(int64_t _source) { + if (_source < 0) { + cerr << "Header: source cannot be less than 0" << endl; + exit(1); + } else if (_source > 4294967295) { + cerr << "Header: source too large" << endl; + exit(1); + } + source = _source; + } + + + void setSequence(int _sequence) { + if (_sequence < 0) { + cerr << "Header: sequence cannot be less than 0" << endl; + exit(1); + } else if (_sequence > 255) { + cerr << "Header: sequence too large" << endl; + exit(1); + } + sequence = _sequence; + } + + + void setType(int _type) { + if (_type < 0) { + cerr << "Header: type cannot be less than 0" << endl; + exit(1); + } else if (_type > 65535) { + cerr << "Header: type too large" << endl; + exit(1); + } + type = _type; + } + + + void setAck_required(bool _ack_required) { + ack_required = _ack_required; + } + + + void setRes_required(bool _res_required) { + res_required = _res_required; + } + + + void setMacAddress(char _macAddress[8]) { + strcpy(macAddress, _macAddress); + } + + + int getSize() { + return size; + } + + + int getOrigin() { + return origin; + } + + + bool getTagged() { + return tagged; + } + + + bool getAddressable() { + return addressable; + } + + + int getProtocol() { + return protocol; + } + + + int64_t getSource() { + return source; + } + + + int64_t getSequence() { + return sequence; + } + + + int getType() { + return type; + } + + + char* getHeaderBytes(char headerBytes[36]) { + + //char headerBytes[36]; + headerBytes[0] = (char)(size & 0xFF); + headerBytes[1] = (char)((size >> 8) & 0xFF); + + + headerBytes[2] = (char)(protocol & 0xFF); + headerBytes[3] = (char)((protocol >> 8) & 0x0F); + + headerBytes[3] |= (char)((origin & 0x03) << 6); + + if (tagged) { + headerBytes[3] |= (1 << 5); + } + + if (addressable) { + headerBytes[3] |= (1 << 4); + } + + headerBytes[4] = (char)((source >> 0) & 0xFF); + headerBytes[5] = (char)((source >> 8) & 0xFF); + headerBytes[6] = (char)((source >> 16) & 0xFF); + headerBytes[7] = (char)((source >> 24) & 0xFF); + + // fix in a bit + headerBytes[8] = macAddress[0]; + headerBytes[9] = macAddress[1]; + headerBytes[10] = macAddress[2]; + headerBytes[11] = macAddress[3]; + headerBytes[12] = macAddress[4]; + headerBytes[13] = macAddress[5]; + headerBytes[14] = macAddress[6]; + headerBytes[15] = macAddress[7]; + + // Reserved and set to 0 + headerBytes[16] = 0; + headerBytes[17] = 0; + headerBytes[18] = 0; + headerBytes[19] = 0; + headerBytes[20] = 0; + headerBytes[21] = 0; + + if (ack_required) { + headerBytes[22] = (1 << 1); + } + + if (res_required) { + headerBytes[22] |= (1); + } + + headerBytes[23] = (char)(sequence & 0xFF); + + // Reserved and set to 0 + headerBytes[24] = 0; + headerBytes[25] = 0; + headerBytes[26] = 0; + headerBytes[27] = 0; + headerBytes[28] = 0; + headerBytes[29] = 0; + headerBytes[30] = 0; + headerBytes[31] = 0; + + headerBytes[32] = (char)((type >> 0) & 0xFF); + headerBytes[33] = (char)((type >> 8) & 0xFF); + + // Reserved and set to 0 + headerBytes[34] = 0; + headerBytes[35] = 0; + + return headerBytes; + } + + + void setFromBytes(char dataBytes[36]) { + + size = dataBytes[0] & 0xFF; + size |= ((dataBytes[1] & 0xFF) << 8); + size &= 0xFFFF; + + origin = (dataBytes[3] >> 6) & 0x03; + tagged = ((dataBytes[3] >> 5) & 0x01) == 1; + addressable = ((dataBytes[3] >> 4) & 0x01) == 1; + + + protocol = (dataBytes[3] & 0x0F) << 8; + protocol |= dataBytes[2]; + protocol &= 0x0FFF; + + source = (dataBytes[7] & 0xFFl) << 24; + source |= ((dataBytes[6] & 0xFFl) << 16); + source |= ((dataBytes[5] & 0xFFl) << 8); + source |= ((dataBytes[4] & 0xFFl)); + + macAddress[0] = dataBytes[8]; + macAddress[1] = dataBytes[9]; + macAddress[2] = dataBytes[10]; + macAddress[3] = dataBytes[11]; + macAddress[4] = dataBytes[12]; + macAddress[5] = dataBytes[13]; + macAddress[6] = dataBytes[14]; + macAddress[7] = dataBytes[15]; + + ack_required = (dataBytes[22] & 0x02) == 0x02; + res_required = (dataBytes[22] & 0x01) == 0x01; + + sequence = (dataBytes[23] & 0xFF); + + type = ((dataBytes[33] & 0xFF) << 8); + type |= (dataBytes[32] & 0xFF); + } +}; +#endif diff --git a/version2/src/RPi/LifxLightBulb.cpp b/version2/src/RPi/LifxLightBulb.cpp new file mode 100644 index 0000000..aff004f --- /dev/null +++ b/version2/src/RPi/LifxLightBulb.cpp @@ -0,0 +1,1187 @@ +#include +#include +#include + +#include + +#include "LifxLightBulb.h" +#include "IoTSet.h" +#include "IoTDeviceAddress.h" + +using namespace std; + +/* TODO: Uncomment this to do normal C++ (g++) +int main(int argc, char *argv[]) +{ + string macAddress1 = "D073D5128E300000"; + //string macAddress1 = "D073D50241DA0000"; + string devIPAddress1 = "192.168.1.126"; + //string devIPAddress1 = "192.168.1.232"; + LifxLightBulb *llb1 = new LifxLightBulb(devIPAddress1, macAddress1, 12345); + + llb1->init(); + for(int i=0; i < 1; i++) { + llb1->turnOff(); + this_thread::sleep_for (chrono::milliseconds(1000)); + cout << "Turn off" << endl; + llb1->turnOn(); + this_thread::sleep_for (chrono::milliseconds(1000)); + cout << "Turn on" << endl; + } + + + //delete setDevAddress1; + delete llb1; + + return 0; +}*/ + + +// Constructor +LifxLightBulb::LifxLightBulb() { + // LB1 macAddress: d0:73:d5:12:8e:30 + // LB2 macAddress: d0:73:d5:02:41:da + string macAddress = "D073D5128E300000"; + //string macAddress = "D073D50241DA0000"; + /*bulbMacAddress[0] = 0xD0; + bulbMacAddress[1] = 0x73; + bulbMacAddress[2] = 0xD5; + bulbMacAddress[3] = 0x02; + bulbMacAddress[4] = 0x41; + bulbMacAddress[5] = 0xDA; + bulbMacAddress[6] = 0x00; + bulbMacAddress[7] = 0x00;*/ + + char tmpMacAddress[16]; + strcpy(tmpMacAddress, macAddress.c_str()); + for(int i=0; i<16; i=i+2) { + // Take 2 digits and then convert + char tmpMacByte[2]; + tmpMacByte[0] = tmpMacAddress[i]; + tmpMacByte[1] = tmpMacAddress[i+1]; + bulbMacAddress[i/2] = (char) strtol(tmpMacByte, NULL, 16); + } +} + + +// Constructor +LifxLightBulb::LifxLightBulb(uint8_t* ipAddress, string macAddress, int srcPort) { + + // Initialize macAddress + char tmpMacAddress[16]; + strcpy(tmpMacAddress, macAddress.c_str()); + //test[0] = (char) strtol(strTest.c_str(), NULL, 16); + for(int i=0; i<16; i=i+2) { + // Take 2 digits and then convert + char tmpMacByte[2]; + tmpMacByte[0] = tmpMacAddress[i]; + tmpMacByte[1] = tmpMacAddress[i+1]; + bulbMacAddress[i/2] = (char) strtol(tmpMacByte, NULL, 16); + } + //cout << "MAC address is set. Value: "; + + // Initialize device address + // Port 56700 is the default port for Lifx + IoTDeviceAddress* devAddress = new IoTDeviceAddress(ipAddress, srcPort, 56700, false, false); + unordered_set* myset1 = new unordered_set(); + myset1->insert(devAddress); + IoTSet* setDevAddress = new IoTSet(myset1); + lb_addresses = setDevAddress; + //cout << "Device address is set! " << endl; + +} + + +// Driver constructor always gets a pointer to device address, trailed by class arguments of generic type +LifxLightBulb::LifxLightBulb(IoTSet* _devAddress, string macAddress) { + + // Initialize macAddress + char tmpMacAddress[16]; + strcpy(tmpMacAddress, macAddress.c_str()); + //test[0] = (char) strtol(strTest.c_str(), NULL, 16); + for(int i=0; i<16; i=i+2) { + // Take 2 digits and then convert + char tmpMacByte[2]; + tmpMacByte[0] = tmpMacAddress[i]; + tmpMacByte[1] = tmpMacAddress[i+1]; + bulbMacAddress[i/2] = (char) strtol(tmpMacByte, NULL, 16); + } + //cout << "MAC address is set. Value: "; + + // Initialize device address + lb_addresses = _devAddress; + //cout << "Device address is set! " << endl; +} + + +LifxLightBulb::~LifxLightBulb() { + + // Clean up + if (communicationSocket != NULL) { + + delete communicationSocket; + communicationSocket = NULL; + } + for(void* dev : *lb_addresses) { + IoTDeviceAddress* dv = (IoTDeviceAddress*) dev; + delete dv; + dv = NULL; + } + if (lb_addresses != NULL) { + + delete lb_addresses; + lb_addresses = NULL; + } +} + + +// PUBLIC METHODS +// Initialize the lightbulb +void LifxLightBulb::init() { + + //if (didAlreadyInit.exchange(true)) + if (didAlreadyInit) + return; + // Set to true if not yet + didAlreadyInit = true; + + unordered_set::const_iterator itr = lb_addresses->begin(); + IoTDeviceAddress* deviceAddress = (IoTDeviceAddress*) *itr; + //cout << "Address: " << deviceAddress->getAddress() << endl; + + // Create IoTUDP socket + communicationSocket = new IoTUDP(deviceAddress); + + //cout << "Host address: " << communicationSocket->getHostAddress() << endl; + //cout << "Source port: " << communicationSocket->getSourcePort() << endl; + //cout << "Destination port: " << communicationSocket->getDestinationPort() << endl << endl; + + // Launch the worker function in a separate thread. + // NOTE: "this" pointer is passed into the detached thread because it does not belong + // to this object anymore so if it executes certain methods of "this" object, then it needs + // the correct references to stuff + thread th1 (&LifxLightBulb::workerFunction, this, this); + th1.detach(); + + //cout << "Initialized LifxLightBulb!" << endl; +} + + +void LifxLightBulb::turnOff() { + + //lock_guard guard(bulbStateMutex); + bulbStateMutex.lock(); + bulbIsOn = false; + sendSetLightPowerPacket(0, 0); + stateDidChange = true; + bulbStateMutex.unlock(); +} + + +void LifxLightBulb::turnOn() { + + //lock_guard guard(bulbStateMutex); + bulbStateMutex.lock(); + bulbIsOn = true; + sendSetLightPowerPacket(65535, 0); + stateDidChange = true; + bulbStateMutex.unlock(); +} + + +double LifxLightBulb::getHue() { + double tmp = 0; + settingBulbColorMutex.lock(); + tmp = ((double)currentHue / 65535.0) * 360.0; + settingBulbColorMutex.unlock(); + + return tmp; +} + + +double LifxLightBulb::getSaturation() { + double tmp = 0; + settingBulbColorMutex.lock(); + tmp = ((double)currentSaturation / 65535.0) * 360.0; + settingBulbColorMutex.unlock(); + + return tmp; +} + + +double LifxLightBulb::getBrightness() { + double tmp = 0; + settingBulbColorMutex.lock(); + tmp = ((double)currentBrightness / 65535.0) * 360.0; + settingBulbColorMutex.unlock(); + + return tmp; +} + + +int LifxLightBulb::getTemperature() { + + int tmp = 0; + settingBulbTemperatureMutex.lock(); + tmp = currentTemperature; + settingBulbTemperatureMutex.unlock(); + + return tmp; +} + + +double LifxLightBulb::getHueRangeLowerBound() { + if (!didGetBulbVersion) { + return -1; + } + return ((double)hueLowerBound / 65535.0) * 360.0; +} + + +double LifxLightBulb::getHueRangeUpperBound() { + if (!didGetBulbVersion) { + return -1; + } + return ((double)hueUpperBound / 65535.0) * 360.0; +} + + +double LifxLightBulb::getSaturationRangeLowerBound() { + if (!didGetBulbVersion) { + return -1; + } + return ((double)saturationLowerBound / 65535.0) * 100.0; +} + + +double LifxLightBulb::getSaturationRangeUpperBound() { + if (!didGetBulbVersion) { + return -1; + } + return ((double)saturationUpperBound / 65535.0) * 100.0; +} + + +double LifxLightBulb::getBrightnessRangeLowerBound() { + if (!didGetBulbVersion) { + return -1; + } + return ((double)brightnessLowerBound / 65535.0) * 100.0; +} + + +double LifxLightBulb::getBrightnessRangeUpperBound() { + if (!didGetBulbVersion) { + return -1; + } + return ((double)brightnessUpperBound / 65535.0) * 100.0; +} + + +int LifxLightBulb::getTemperatureRangeLowerBound() { + if (!didGetBulbVersion) { + return -1; + } + return temperatureLowerBound; +} + + +int LifxLightBulb::getTemperatureRangeUpperBound() { + if (!didGetBulbVersion) { + return -1; + } + return temperatureUpperBound; +} + + +void LifxLightBulb::setTemperature(int _temperature) { + + settingBulbTemperatureMutex.lock(); + + BulbColor* newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, _temperature); + sendSetLightColorPacket(newColor, 250); + + currentTemperature = _temperature; + stateDidChange = true; + + settingBulbTemperatureMutex.unlock(); +} + + +void LifxLightBulb::setColor(double _hue, double _saturation, double _brightness) { + + settingBulbColorMutex.lock(); + + _hue /= 360.0; + _saturation /= 100.0; + _brightness /= 100.0; + + + int newHue = (int)(_hue * 65535.0); + int newSaturation = (int)(_saturation * 65535.0); + int newBrightness = (int)(_brightness * 65535.0); + + BulbColor* newColor = new BulbColor(newHue, newSaturation, newBrightness, currentTemperature); + sendSetLightColorPacket(newColor, 250); + + currentHue = newHue; + currentSaturation = newSaturation; + currentBrightness = newBrightness; + stateDidChange = true; + + settingBulbColorMutex.unlock(); +} + + +bool LifxLightBulb::getState() { + + bool tmp = false; + + bulbStateMutex.lock(); + tmp = bulbIsOn; + bulbStateMutex.unlock(); + + return tmp; +} + + +// PRIVATE METHODS +// Communication helpers +void LifxLightBulb::receivedPacket(char* packetData) { + + char headerBytes[36]; + for (int i = 0; i < 36; i++) { + headerBytes[i] = packetData[i]; + } + + LifxHeader recHeader; + recHeader.setFromBytes(headerBytes); + + // load the payload bytes (strip away the header) + char* payloadBytes = new char[recHeader.getSize()]; + for (int i = 36; i < recHeader.getSize(); i++) { + payloadBytes[i - 36] = packetData[i]; + } + + int type = recHeader.getType(); + //cout << "Received: " << type << endl; + + DeviceStateService* dat = NULL; + switch (type) { + + case 3: + dat = parseDeviceStateServiceMessage(payloadBytes); + //cout << "Service: " << dat->getService(); + //cout << "Port : " << dat->getPort(); + // Avoid memory leak - delete this object + delete dat; + break; + + case 33: + handleStateVersionMessageReceived(payloadBytes); + break; + + case 35: + parseDeviceStateInfoMessage(payloadBytes); + break; + + + case 107: + handleLightStateMessageReceived(payloadBytes); + break; + + default: + break; + //cout << "unknown packet Type" << endl; + } + // Avoid memory leaks + delete payloadBytes; +} + + +void LifxLightBulb::sendPacket(char* packetData, int len) { + //cout << "sendPacket: About to send" << endl; + lock_guard guard(socketMutex); + sendSocketFlag = true; + communicationSocket->sendData(packetData, len); + sendSocketFlag = false; +} + + +// Worker function which runs the while loop for receiving data from the bulb. +// Is blocking. +void LifxLightBulb::workerFunction(LifxLightBulb* llb) { + + // Need timeout on receives since we are not sure if a packet will be available + // for processing so don't block waiting + llb->communicationSocket->setTimeOut(50000); // In milliseconds + llb->turnOff(); + + int64_t lastSentGetBulbVersionRequest = 0; // time last request sent + char dat[1024]; + + while (true) { + // Check if we got the bulb version yet + // could have requested it but message could have gotten lost (UDP) + if (!llb->didGetBulbVersion) { + int64_t currentTime = (int64_t) time(NULL); + if ((currentTime - lastSentGetBulbVersionRequest) > llb->GET_BULB_VERSION_RESEND_WAIT_SECONDS) { + // Get the bulb version so we know what type of bulb this is. + //cout << "Sending version packet! " << endl; + llb->sendGetVersionPacket(); + lastSentGetBulbVersionRequest = currentTime; + } + } + + // Communication resource is busy so try again later + if (llb->sendSocketFlag) { + continue; + } + + llb->socketMutex.lock(); + int ret = llb->communicationSocket->receiveData(dat, 1024); + // Never forget to release! + llb->socketMutex.unlock(); + + // A packed arrived + if (ret != -1) { + llb->receivedPacket(dat); + } + + // If a state change occurred then request the bulb state to ensure that the + // bulb did indeed change its state to the correct state + if (llb->stateDidChange) { + llb->sendGetLightStatePacket(); + } + + // Wait a bit as to not tie up system resources + this_thread::sleep_for (chrono::milliseconds(100)); + //cout << endl << "Sleep and wake up!" << endl; + } +} + + +// Sending +// Device Messages +void LifxLightBulb::sendGetServicePacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(true); + header.setMacAddress(bulbMacAddress); + header.setSource(0); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(2); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendGetHostInfoPacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(12); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendGetHostFirmwarePacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(14); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendGetWifiInfoPacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(16); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendGetWifiFirmwarePacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(18); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendGetPowerPacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(20); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendSetPowerPacket(int level) { + // Currently only 0 and 65535 are supported + // This is a fix for now + if ((level != 65535) && (level != 0)) { + cerr << "Invalid parameter values" << endl; + exit(1); + } + + if ((level > 65535) || (level < 0)) { + cerr << "Invalid parameter values" << endl; + exit(1); + } + + char packetBytes[38]; + + LifxHeader header; + header.setSize(38); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(21); + char headerBytes[36]; + header.getHeaderBytes(headerBytes); + + for (int i = 0; i < 36; i++) { + packetBytes[i] = headerBytes[i]; + } + + packetBytes[36] = (char)(level & 0xFF); + packetBytes[37] = (char)((level >> 8) & 0xFF); + + sendPacket(packetBytes, 38); +} + + +void LifxLightBulb::sendGetLabelPacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(23); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendSetLabelPacket(string label) { + // Currently only 0 and 65535 are supported + // This is a fix for now + if (label.length() != 32) { + cerr << "Invalid parameter values, label must be 32 bytes long" << endl; + exit(1); + } + + char packetBytes[68]; + + LifxHeader header; + header.setSize(68); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(24); + char headerBytes[36]; + header.getHeaderBytes(headerBytes); + + for (int i = 0; i < 36; i++) { + packetBytes[i] = headerBytes[i]; + } + + for (int i = 0; i < 32; i++) { + packetBytes[i + 36] = label.c_str()[i]; + } + + sendPacket(packetBytes, 68); +} + + +void LifxLightBulb::sendGetVersionPacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(32); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendGetInfoPacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(34); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendGetLocationPacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(34); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendGetGroupPacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(51); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +// Sending +// Light Messages +void LifxLightBulb::sendGetLightStatePacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(101); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendSetLightColorPacket(BulbColor* bulbColor, long duration) { + + if ((duration > 4294967295l) || (duration < 0)) { + cerr << "Invalid parameter value, duration out of range (0 - 4294967295)" << endl; + exit(1); + } + + char packetBytes[49]; + + LifxHeader header; + header.setSize(49); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(102); + char headerBytes[36]; + header.getHeaderBytes(headerBytes); + + for (int i = 0; i < 36; i++) { + packetBytes[i] = headerBytes[i]; + } + + // 1 reserved packet + packetBytes[37] = (char)(bulbColor->getHue() & 0xFF); + packetBytes[38] = (char)((bulbColor->getHue() >> 8) & 0xFF); + + packetBytes[39] = (char)(bulbColor->getSaturation() & 0xFF); + packetBytes[40] = (char)((bulbColor->getSaturation() >> 8) & 0xFF); + + packetBytes[41] = (char)(bulbColor->getBrightness() & 0xFF); + packetBytes[42] = (char)((bulbColor->getBrightness() >> 8) & 0xFF); + + packetBytes[43] = (char)(bulbColor->getKelvin() & 0xFF); + packetBytes[44] = (char)((bulbColor->getKelvin() >> 8) & 0xFF); + + packetBytes[45] = (char)((duration >> 0) & 0xFF); + packetBytes[46] = (char)((duration >> 8) & 0xFF); + packetBytes[47] = (char)((duration >> 16) & 0xFF); + packetBytes[48] = (char)((duration >> 24) & 0xFF); + + sendPacket(packetBytes, 49); + // Avoid memory leak - delete object + delete bulbColor; +} + + +void LifxLightBulb::sendGetLightPowerPacket() { + LifxHeader header; + header.setSize(36); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(116); + + char dataBytes[36]; + header.getHeaderBytes(dataBytes); + + sendPacket(dataBytes, 36); +} + + +void LifxLightBulb::sendSetLightPowerPacket(int level, long duration) { + + if ((level > 65535) || (duration > 4294967295l) + || (level < 0) || (duration < 0)) { + cerr << "Invalid parameter values" << endl; + exit(1); + } + char packetBytes[42]; + + LifxHeader header; + header.setSize(42); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(117); + char headerBytes[36]; + header.getHeaderBytes(headerBytes); + + for (int i = 0; i < 36; i++) { + packetBytes[i] = headerBytes[i]; + } + + packetBytes[36] = (char)(level & 0xFF); + packetBytes[37] = (char)((level >> 8) & 0xFF); + + packetBytes[38] = (char)((duration >> 0) & 0xFF); + packetBytes[39] = (char)((duration >> 8) & 0xFF); + packetBytes[40] = (char)((duration >> 16) & 0xFF); + packetBytes[41] = (char)((duration >> 24) & 0xFF); + + sendPacket(packetBytes, 42); +} + + +void LifxLightBulb::sendEchoRequestPacket(char data[64]) { + + char packetBytes[100]; + + LifxHeader header; + header.setSize(100); + header.setTagged(false); + header.setMacAddress(bulbMacAddress); + header.setSource(10); // randomly picked + header.setAck_required(false); + header.setRes_required(false); + header.setSequence(0); + header.setType(58); + char headerBytes[36]; + header.getHeaderBytes(headerBytes); + + for (int i = 0; i < 36; i++) { + packetBytes[i] = headerBytes[i]; + } + + for (int i = 0; i < 64; i++) { + packetBytes[i + 36] = data[i]; + } + + sendPacket(packetBytes, 100); +} + + +// Receiving +// Device Messages +DeviceStateService* LifxLightBulb::parseDeviceStateServiceMessage(char* payloadData) { + int service = payloadData[0]; + int64_t port = ((payloadData[3] & 0xFF) << 24); + port |= ((payloadData[2] & 0xFF) << 16); + port |= ((payloadData[1] & 0xFF) << 8); + port |= (payloadData[0] & 0xFF); + + return new DeviceStateService(service, port); +} + + +DeviceStateHostInfo* LifxLightBulb::parseDeviceStateHostInfoMessage(char* payloadData) { + long signal = ((payloadData[3] & 0xFF) << 24); + signal |= ((payloadData[2] & 0xFF) << 16); + signal |= ((payloadData[1] & 0xFF) << 8); + signal |= (payloadData[0] & 0xFF); + + long tx = ((payloadData[7] & 0xFF) << 24); + tx |= ((payloadData[6] & 0xFF) << 16); + tx |= ((payloadData[5] & 0xFF) << 8); + tx |= (payloadData[4] & 0xFF); + + long rx = ((payloadData[11] & 0xFF) << 24); + rx |= ((payloadData[10] & 0xFF) << 16); + rx |= ((payloadData[9] & 0xFF) << 8); + rx |= (payloadData[8] & 0xFF); + + return new DeviceStateHostInfo(signal, tx, rx); +} + + +DeviceStateHostFirmware* LifxLightBulb::parseDeviceStateHostFirmwareMessage(char* payloadData) { + long build = 0; + for (int i = 0; i < 8; i++) { + build += ((int64_t) payloadData[i] & 0xffL) << (8 * i); + } + + // 8 reserved bytes + + int64_t version = ((payloadData[19] & 0xFF) << 24); + version |= ((payloadData[18] & 0xFF) << 16); + version |= ((payloadData[17] & 0xFF) << 8); + version |= (payloadData[16] & 0xFF); + + return new DeviceStateHostFirmware(build, version); +} + + +DeviceStateWifiInfo* LifxLightBulb::parseDeviceStateWifiInfoMessage(char* payloadData) { + int64_t signal = ((payloadData[3] & 0xFF) << 24); + signal |= ((payloadData[2] & 0xFF) << 16); + signal |= ((payloadData[1] & 0xFF) << 8); + signal |= (payloadData[0] & 0xFF); + + int64_t tx = ((payloadData[7] & 0xFF) << 24); + tx |= ((payloadData[6] & 0xFF) << 16); + tx |= ((payloadData[5] & 0xFF) << 8); + tx |= (payloadData[4] & 0xFF); + + int64_t rx = ((payloadData[11] & 0xFF) << 24); + rx |= ((payloadData[10] & 0xFF) << 16); + rx |= ((payloadData[9] & 0xFF) << 8); + rx |= (payloadData[8] & 0xFF); + + return new DeviceStateWifiInfo(signal, tx, rx); +} + + +DeviceStateWifiFirmware* LifxLightBulb::parseDeviceStateWifiFirmwareMessage(char* payloadData) { + long build = 0; + for (int i = 0; i < 8; i++) { + build += ((int64_t) payloadData[i] & 0xffL) << (8 * i); + } + + // 8 reserved bytes + + int64_t version = ((payloadData[19] & 0xFF) << 24); + version |= ((payloadData[18] & 0xFF) << 16); + version |= ((payloadData[17] & 0xFF) << 8); + version |= (payloadData[16] & 0xFF); + + return new DeviceStateWifiFirmware(build, version); +} + + +int LifxLightBulb::parseStatePowerMessage(char* payloadData) { + int level = ((payloadData[1] & 0xFF) << 8); + level |= (payloadData[0] & 0xFF); + return level; +} + + +DeviceStateVersion* LifxLightBulb::parseDeviceStateVersionMessage(char* payloadData) { + int64_t vender = ((payloadData[3] & 0xFF) << 24); + vender |= ((payloadData[2] & 0xFF) << 16); + vender |= ((payloadData[1] & 0xFF) << 8); + vender |= (payloadData[0] & 0xFF); + + int64_t product = ((payloadData[7] & 0xFF) << 24); + product |= ((payloadData[6] & 0xFF) << 16); + product |= ((payloadData[5] & 0xFF) << 8); + product |= (payloadData[4] & 0xFF); + + int64_t version = ((payloadData[11] & 0xFF) << 24); + version |= ((payloadData[10] & 0xFF) << 16); + version |= ((payloadData[9] & 0xFF) << 8); + version |= (payloadData[8] & 0xFF); + + return new DeviceStateVersion(vender, product, version); +} + + +DeviceStateInfo* LifxLightBulb::parseDeviceStateInfoMessage(char* payloadData) { + int64_t time = 0; + int64_t upTime = 0; + int64_t downTime = 0; + for (int i = 0; i < 8; i++) { + time += ((int64_t) payloadData[i] & 0xffL) << (8 * i); + upTime += ((int64_t) payloadData[i + 8] & 0xffL) << (8 * i); + downTime += ((int64_t) payloadData[i + 16] & 0xffL) << (8 * i); + } + + return new DeviceStateInfo(time, upTime, downTime); +} + + +DeviceStateLocation* LifxLightBulb::parseDeviceStateLocationMessage(char* payloadData) { + char location[16]; + for (int i = 0; i < 16; i++) { + location[i] = payloadData[i]; + } + + char labelBytes[32]; + for (int i = 0; i < 32; i++) { + labelBytes[i] = payloadData[i + 16]; + } + + int64_t updatedAt = 0; + for (int i = 0; i < 8; i++) { + updatedAt += ((int64_t) payloadData[48] & 0xffL) << (8 * i); + } + + string str(labelBytes); + return new DeviceStateLocation(location, str, updatedAt); +} + + +DeviceStateGroup* LifxLightBulb::parseDeviceStateGroupMessage(char* payloadData) { + char group[16]; + for (int i = 0; i < 16; i++) { + group[i] = payloadData[i]; + } + + char labelBytes[32]; + for (int i = 0; i < 32; i++) { + labelBytes[i] = payloadData[i + 16]; + } + + int64_t updatedAt = 0; + for (int i = 0; i < 8; i++) { + updatedAt += ((int64_t) payloadData[48] & 0xffL) << (8 * i); + } + + string str(labelBytes); + return new DeviceStateGroup(group, str, updatedAt); +} + + +// Receiving +// Light Messages +LightState* LifxLightBulb::parseLightStateMessage(char* payloadData) { + + char colorData[8]; + for (int i = 0; i < 8; i++) { + colorData[i] = payloadData[i]; + } + //BulbColor color(colorData); + BulbColor* color = new BulbColor(colorData); + + int power = ((payloadData[11] & 0xFF) << 8); + power |= (payloadData[10] & 0xFF); + + string label(payloadData); + + char labelArray[32]; + for (int i = 0; i < 32; i++) { + labelArray[i] = payloadData[12 + i]; + } + + return new LightState(color, power, label); +} + + +int LifxLightBulb::parseLightStatePowerMessage(char* payloadData) { + int level = ((payloadData[1] & 0xFF) << 8); + level |= (payloadData[0] & 0xFF); + return level; +} + + +// Private Handlers +void LifxLightBulb::handleStateVersionMessageReceived(char* payloadData) { + + DeviceStateVersion* deviceState = parseDeviceStateVersionMessage(payloadData); + int productNumber = (int)deviceState->getProduct(); + + bool isColor = false; + + if (productNumber == 1) {// Original 1000 + isColor = true; + } else if (productNumber == 3) {//Color 650 + isColor = true; + } else if (productNumber == 10) {// White 800 (Low Voltage) + isColor = false; + } else if (productNumber == 11) {// White 800 (High Voltage) + isColor = false; + } else if (productNumber == 18) {// White 900 BR30 (Low Voltage) + isColor = false; + } else if (productNumber == 20) {// Color 1000 BR30 + isColor = true; + } else if (productNumber == 22) {// Color 1000 + isColor = true; + } + + if (isColor) { + hueLowerBound = 0; + hueUpperBound = 65535; + saturationLowerBound = 0; + saturationUpperBound = 65535; + brightnessLowerBound = 0; + brightnessUpperBound = 65535; + temperatureLowerBound = 2500; + temperatureUpperBound = 9000; + } else { + hueLowerBound = 0; + hueUpperBound = 0; + saturationLowerBound = 0; + saturationUpperBound = 0; + brightnessLowerBound = 0; + brightnessUpperBound = 65535;// still can dim bulb + temperatureLowerBound = 2500; + temperatureUpperBound = 9000; + } + + //didGetBulbVersion.exchange(true); + didGetBulbVersion = true; + // Avoid memory leak - delete this object + delete deviceState; +} + + +void LifxLightBulb::handleLightStateMessageReceived(char* payloadData) { + LightState* lightState = parseLightStateMessage(payloadData); + + BulbColor* color = lightState->getColor(); + int power = lightState->getPower(); + + //cout << "color->getHue(): " << color->getHue() << " - currentHue: " << currentHue << endl; + //cout << "color->getSaturation(): " << color->getSaturation() << " - currentSaturation: " << currentSaturation << endl; + //cout << "color->getBrightness(): " << color->getBrightness() << " - currentBrightness: " << currentBrightness << endl; + //cout << "color->getKelvin(): " << color->getKelvin() << " - currentTemperature: " << currentTemperature << endl; + + bool bulbWrongColor = false; + bulbWrongColor = bulbWrongColor || (color->getHue() != currentHue); + bulbWrongColor = bulbWrongColor || (color->getSaturation() != currentSaturation); + bulbWrongColor = bulbWrongColor || (color->getBrightness() != currentBrightness); + bulbWrongColor = bulbWrongColor || (color->getKelvin() != currentTemperature); + + + // gets set to true if any of the below if statements are taken + stateDidChange = false; + + if (bulbWrongColor) { + BulbColor* newColor = new BulbColor(currentHue, currentSaturation, currentBrightness, currentTemperature); + sendSetLightColorPacket(newColor, 250); + //cout << "Failed Check 1" << endl; + } + + bulbStateMutex.lock(); + bool bulbIsOnTmp = bulbIsOn; + bulbStateMutex.unlock(); + + if ((!bulbIsOnTmp) && (power != 0)) { + turnOff(); + //cout << "Failed Check 2: " << endl; + + } + + if (bulbIsOnTmp && (power < 65530)) { + turnOn(); + //cout << "Failed Check 3: " << endl; + + } + // Avoid memory leak - delete object + delete lightState; + delete color; +} + diff --git a/version2/src/RPi/LifxLightBulb.h b/version2/src/RPi/LifxLightBulb.h new file mode 100644 index 0000000..216a47a --- /dev/null +++ b/version2/src/RPi/LifxLightBulb.h @@ -0,0 +1,173 @@ +#ifndef _LIFXLIGHTBULB_H__ +#define _LIFXLIGHTBULB_H__ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "LightBulb.h" +//#include "Socket.h" +#include "IoTSet.h" +#include "IoTUDP.h" +#include "IoTDeviceAddress.h" + +// Helper classes for LifxLightBulb +#include "LifxHeader.h" +#include "BulbColor.h" +#include "DeviceStateGroup.h" +#include "DeviceStateHostFirmware.h" +#include "DeviceStateHostInfo.h" +#include "DeviceStateInfo.h" +#include "DeviceStateLocation.h" +#include "DeviceStateService.h" +#include "DeviceStateVersion.h" +#include "DeviceStateWifiFirmware.h" +#include "DeviceStateWifiInfo.h" +#include "LightState.h" + + +using namespace std; + +// Driver LifxLightBulb +// Implemented based on LightBulb virtual class (interface) + +//std::atomic +//std::atomic didAlreadyInit(false); +//std::atomic didGetBulbVersion(false); + +class LifxLightBulb : public LightBulb +{ + private: + // Constants + const static int64_t GET_BULB_VERSION_RESEND_WAIT_SECONDS = 10; + + // Variables + IoTUDP *communicationSocket; + char bulbMacAddress[8]; + //TODO: + //static Semaphore socketMutex = new Semaphore(1); + bool sendSocketFlag = false; + + // Current Bulb Values + int currentHue = 0; + int currentSaturation = 0; + int currentBrightness = 65535; + int currentTemperature = 9000; + bool bulbIsOn = false; + + //std::atomic + //atomic didAlreadyInit; + bool didAlreadyInit; + //atomic didGetBulbVersion; + bool didGetBulbVersion; + + // Mutex locks + mutex socketMutex; + mutex settingBulbColorMutex; + mutex settingBulbTemperatureMutex; + mutex bulbStateMutex; + + // color and temperature ranges for the bulbs + int hueLowerBound = 0; + int hueUpperBound = 0; + int saturationLowerBound = 0; + int saturationUpperBound = 0; + int brightnessLowerBound = 0; + int brightnessUpperBound = 0; + int temperatureLowerBound = 2500; + int temperatureUpperBound = 9000; + + // Check if a state change was requested, used to poll the bulb for if the bulb did + // preform the requested state change + bool stateDidChange = false; + + // Device address + IoTSet* lb_addresses; // IoTSet* lb_addresses + + // Logging + ofstream log; + public: + + // Constructor + LifxLightBulb(); + LifxLightBulb(uint8_t* ipAddress, string macAddress, int srcPort); + //LifxLightBulb(IoTSet* _devAddress, string macAddress); + LifxLightBulb(IoTSet* _devAddress, string macAddress); + ~LifxLightBulb(); + // Initialize the lightbulb + void init(); + void turnOff(); + void turnOn(); + double getHue(); + double getSaturation(); + double getBrightness(); + int getTemperature(); + double getHueRangeLowerBound(); + double getHueRangeUpperBound(); + double getSaturationRangeLowerBound(); + double getSaturationRangeUpperBound(); + double getBrightnessRangeLowerBound(); + double getBrightnessRangeUpperBound(); + int getTemperatureRangeLowerBound(); + int getTemperatureRangeUpperBound(); + void setTemperature(int _temperature); + void setColor(double _hue, double _saturation, double _brightness); + bool getState(); + + private: + // Private functions + // Communication helpers + void receivedPacket(char* packetData); + void sendPacket(char* packetData, int len); + // Worker function which runs the while loop for receiving data from the bulb. + // Is blocking. + void workerFunction(LifxLightBulb* llb); + // Sending + // Device Messages + void sendGetServicePacket(); + void sendGetHostInfoPacket(); + void sendGetHostFirmwarePacket(); + void sendGetWifiInfoPacket(); + void sendGetWifiFirmwarePacket(); + void sendGetPowerPacket(); + void sendSetPowerPacket(int level); + void sendGetLabelPacket(); + void sendSetLabelPacket(string label); + void sendGetVersionPacket(); + void sendGetInfoPacket(); + void sendGetLocationPacket(); + void sendGetGroupPacket(); + // Sending + // Light Messages + void sendGetLightStatePacket(); + void sendSetLightColorPacket(BulbColor* bulbColor, long duration); + void sendGetLightPowerPacket(); + void sendSetLightPowerPacket(int level, long duration); + void sendEchoRequestPacket(char data[64]); + // Receiving + // Device Messages + DeviceStateService* parseDeviceStateServiceMessage(char* payloadData); + DeviceStateHostInfo* parseDeviceStateHostInfoMessage(char* payloadData); + DeviceStateHostFirmware* parseDeviceStateHostFirmwareMessage(char* payloadData); + DeviceStateWifiInfo* parseDeviceStateWifiInfoMessage(char* payloadData); + DeviceStateWifiFirmware* parseDeviceStateWifiFirmwareMessage(char* payloadData); + int parseStatePowerMessage(char* payloadData); + DeviceStateVersion* parseDeviceStateVersionMessage(char* payloadData); + DeviceStateInfo* parseDeviceStateInfoMessage(char* payloadData); + DeviceStateLocation* parseDeviceStateLocationMessage(char* payloadData); + DeviceStateGroup* parseDeviceStateGroupMessage(char* payloadData); + // Receiving + // Light Messages + LightState* parseLightStateMessage(char* payloadData); + int parseLightStatePowerMessage(char* payloadData); + // Private Handlers + void handleStateVersionMessageReceived(char* payloadData); + void handleLightStateMessageReceived(char* payloadData); +}; + +#endif diff --git a/version2/src/RPi/LightBulb.h b/version2/src/RPi/LightBulb.h new file mode 100644 index 0000000..90f9df8 --- /dev/null +++ b/version2/src/RPi/LightBulb.h @@ -0,0 +1,29 @@ +#ifndef _LIGHTBULB_H__ +#define _LIGHTBULB_H__ +#include + +using namespace std; + +class LightBulb +{ + public: + virtual void init() = 0; + virtual void turnOff() = 0; + virtual void turnOn() = 0; + virtual bool getState() = 0; + virtual void setColor(double _hue, double _saturation, double _brightness) = 0; + virtual void setTemperature(int _temperature) = 0; + virtual double getBrightness() = 0; + virtual double getHue() = 0; + virtual double getSaturation() = 0; + virtual int getTemperature() = 0; + virtual double getBrightnessRangeLowerBound() = 0; + virtual double getBrightnessRangeUpperBound() = 0; + virtual double getHueRangeLowerBound() = 0; + virtual double getHueRangeUpperBound() = 0; + virtual double getSaturationRangeLowerBound() = 0; + virtual double getSaturationRangeUpperBound() = 0; + virtual int getTemperatureRangeLowerBound() = 0; + virtual int getTemperatureRangeUpperBound() = 0; +}; +#endif diff --git a/version2/src/RPi/LightState.h b/version2/src/RPi/LightState.h new file mode 100644 index 0000000..ba350e3 --- /dev/null +++ b/version2/src/RPi/LightState.h @@ -0,0 +1,41 @@ +#ifndef _LIGHTSTATE_H__ +#define _LIGHTSTATE_H__ +#include + +#include "BulbColor.h" + +class LightState { + private: + BulbColor* color; + int power; + string label; + + public: + + LightState(BulbColor* _color, int _power, string _label) { + + color = _color; + power = _power; + label = _label; + } + + + ~LightState() { + } + + + BulbColor* getColor() { + return color; + } + + + int getPower() { + return power; + } + + + string getLabel() { + return label; + } +}; +#endif diff --git a/version2/src/RPi/LightsController.ino b/version2/src/RPi/LightsController.ino new file mode 100644 index 0000000..9ffe162 --- /dev/null +++ b/version2/src/RPi/LightsController.ino @@ -0,0 +1,72 @@ +// ------------------------------------------------ +// Controlling LifxLightBulb through Particle Cloud +// @author Rahmadi Trimananda - UC Irvine +// ------------------------------------------------ + +#include "LifxLightBulb.h" + +LifxLightBulb *llb1; +LifxLightBulb *llb2; + +void setup() +{ + // Bulb 1 + string macAddress1 = "D073D5128E300000"; + uint8_t devIPAddress1[] = { 192, 168, 1, 126 }; + llb1 = new LifxLightBulb(devIPAddress1, macAddress1, 12345); + llb1->init(); + + // Bulb 2 + string macAddress2 = "D073D50241DA0000"; + uint8_t devIPAddress2[] = { 192, 168, 1, 232 }; + llb2 = new LifxLightBulb(devIPAddress2, macAddress2, 12346); + llb2->init(); + + Particle.function("lifx1",lifx1Toggle); + Particle.function("lifx2",lifx2Toggle); +} + + +void loop() +{ + // Nothing to do here + for(int i=0; i < 3; i++) { + llb1->turnOff(); + delay(1000); + llb1->turnOn(); + delay(1000); + } +} + + +// Toggling Lifx light bulb 1 +int lifx1Toggle(String command) { + + if (command=="on") { + llb1->turnOn(); + return 1; + } + else if (command=="off") { + llb1->turnOff(); + return 0; + } + else { + return -1; + } +} + +// Toggling Lifx light bulb 2 +int lifx2Toggle(String command) { + + if (command=="on") { + llb2->turnOn(); + return 1; + } + else if (command=="off") { + llb2->turnOff(); + return 0; + } + else { + return -1; + } +} diff --git a/version2/src/RPi/Makefile b/version2/src/RPi/Makefile new file mode 100644 index 0000000..276d090 --- /dev/null +++ b/version2/src/RPi/Makefile @@ -0,0 +1,38 @@ +include common.mk + +all: compile + +# To run Particle, we need to install Particle CLI +# Please see: +# https://docs.particle.io/guide/getting-started/connect/photon/#install-the-particle-cli +monitor: + sudo chmod 666 /dev/ttyACM0 + particle serial monitor + +compile-c: + #g++ -c LifxLightBulb.cpp -o ./LifxLightBulb.o -Wall -O3 -g -I. -Wno-unused-variable -std=c++11 -pthread -pg + g++ ./LifxLightBulb.cpp ./socket/Socket.cpp -o ./LifxLightBulb.o -Wall -Wno-unused-variable --std=c++11 -pthread -pg -I. -I./socket + +compile: + rm -f *.bin + particle compile raspberry-pi + +flash: + particle flash Pi-3 raspberry-pi*.bin + +PHONY += clean +clean: + rm -f *.bin + +PHONY += mrclean +mrclean: clean + rm -rf ../docs + +tabbing: + uncrustify -c C.cfg --no-backup *.cpp + uncrustify -c C.cfg --no-backup *.h + +wc: + wc *.cpp *.h + +.PHONY: $(PHONY) diff --git a/version2/src/RPi/README.txt b/version2/src/RPi/README.txt new file mode 100644 index 0000000..b1ac1ff --- /dev/null +++ b/version2/src/RPi/README.txt @@ -0,0 +1,4 @@ + +This is a RaspberryPi C++ implementation of Lifx light bulb controller. + +Created as a part of our testbed that will include smart home devices that will use Particle cloud. diff --git a/version2/src/RPi/common.mk b/version2/src/RPi/common.mk new file mode 100644 index 0000000..479a31c --- /dev/null +++ b/version2/src/RPi/common.mk @@ -0,0 +1,16 @@ +# A few common Makefile items + +CC := gcc +CXX := g++ + +UNAME := $(shell uname) + +LIB_NAME := iotcloud +LIB_SO := lib_$(LIB_NAME).so + +CPPFLAGS += -Wall -g -O0 + +# Mac OSX options +ifeq ($(UNAME), Darwin) +CPPFLAGS += -D_XOPEN_SOURCE -DMAC +endif diff --git a/version2/src/RPi/gmon.out b/version2/src/RPi/gmon.out new file mode 100644 index 0000000..69b5434 Binary files /dev/null and b/version2/src/RPi/gmon.out differ