├── README.md └── components ├── cc1101 ├── CC1101.cpp ├── CC1101.h ├── CC1101Packet.h ├── IthoCC1101.cpp ├── IthoCC1101.h ├── IthoPacket.h ├── README.md ├── __init__.py ├── button.py ├── cc1101.yaml ├── fan.cpp ├── fan.h └── fan.py └── ithofan ├── ithofan.cpp ├── ithofan.h └── ithofan.yaml /README.md: -------------------------------------------------------------------------------- 1 | # ESPhome External Componentes 2 | 3 | - [cc1101 (Itho Mechanical Ventilation)](components/cc1101) 4 | -------------------------------------------------------------------------------- /components/cc1101/CC1101.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 3 | */ 4 | 5 | #include "CC1101.h" 6 | 7 | // default constructor 8 | CC1101::CC1101() 9 | { 10 | SPI.begin(); 11 | #ifdef ESP8266 12 | pinMode(SS, OUTPUT); 13 | #endif 14 | } //CC1101 15 | 16 | // default destructor 17 | CC1101::~CC1101() 18 | { 19 | } //~CC1101 20 | 21 | /***********************/ 22 | // SPI helper functions select() and deselect() 23 | inline void CC1101::select(void) { 24 | digitalWrite(SS, LOW); 25 | } 26 | 27 | inline void CC1101::deselect(void) { 28 | digitalWrite(SS, HIGH); 29 | } 30 | 31 | void CC1101::spi_waitMiso() 32 | { 33 | while(digitalRead(MISO) == HIGH) yield(); 34 | } 35 | 36 | void CC1101::init() 37 | { 38 | reset(); 39 | } 40 | 41 | void CC1101::reset() 42 | { 43 | deselect(); 44 | delayMicroseconds(5); 45 | select(); 46 | delayMicroseconds(10); 47 | deselect(); 48 | delayMicroseconds(45); 49 | select(); 50 | 51 | spi_waitMiso(); 52 | SPI.transfer(CC1101_SRES); 53 | delay(10); 54 | spi_waitMiso(); 55 | deselect(); 56 | } 57 | 58 | uint8_t CC1101::writeCommand(uint8_t command) 59 | { 60 | uint8_t result; 61 | 62 | select(); 63 | spi_waitMiso(); 64 | result = SPI.transfer(command); 65 | deselect(); 66 | 67 | return result; 68 | } 69 | 70 | void CC1101::writeRegister(uint8_t address, uint8_t data) 71 | { 72 | select(); 73 | spi_waitMiso(); 74 | SPI.transfer(address); 75 | SPI.transfer(data); 76 | deselect(); 77 | } 78 | 79 | uint8_t CC1101::readRegister(uint8_t address) 80 | { 81 | uint8_t val; 82 | 83 | select(); 84 | spi_waitMiso(); 85 | SPI.transfer(address); 86 | val = SPI.transfer(0); 87 | deselect(); 88 | 89 | return val; 90 | } 91 | 92 | uint8_t CC1101::readRegisterMedian3(uint8_t address) 93 | { 94 | uint8_t val, val1, val2, val3; 95 | 96 | select(); 97 | spi_waitMiso(); 98 | SPI.transfer(address); 99 | val1 = SPI.transfer(0); 100 | SPI.transfer(address); 101 | val2 = SPI.transfer(0); 102 | SPI.transfer(address); 103 | val3 = SPI.transfer(0); 104 | deselect(); 105 | // reverse sort (largest in val1) because this is te expected order for TX_BUFFER 106 | if (val3 > val2) {val = val3; val3 = val2; val2 = val; } //Swap(val3,val2) 107 | if (val2 > val1) {val = val2; val2 = val1, val1 = val; } //Swap(val2,val1) 108 | if (val3 > val2) {val = val3; val3 = val2, val2 = val; } //Swap(val3,val2) 109 | 110 | return val2; 111 | } 112 | 113 | /* Known SPI/26MHz synchronization bug (see CC1101 errata) 114 | This issue affects the following registers: SPI status byte (fields STATE and FIFO_BYTES_AVAILABLE), 115 | FREQEST or RSSI while the receiver is active, MARCSTATE at any time other than an IDLE radio state, 116 | RXBYTES when receiving or TXBYTES when transmitting, and WORTIME1/WORTIME0 at any time.*/ 117 | //uint8_t CC1101::readRegisterWithSyncProblem(uint8_t address, uint8_t registerType) 118 | uint8_t /* ICACHE_RAM_ATTR */ CC1101::readRegisterWithSyncProblem(uint8_t address, uint8_t registerType) 119 | { 120 | uint8_t value1, value2; 121 | 122 | value1 = readRegister(address | registerType); 123 | 124 | //if two consecutive reads gives us the same result then we know we are ok 125 | do 126 | { 127 | value2 = value1; 128 | value1 = readRegister(address | registerType); 129 | } 130 | while (value1 != value2); 131 | 132 | return value1; 133 | } 134 | 135 | //registerType = CC1101_CONFIG_REGISTER or CC1101_STATUS_REGISTER 136 | uint8_t CC1101::readRegister(uint8_t address, uint8_t registerType) 137 | { 138 | switch (address) 139 | { 140 | case CC1101_FREQEST: 141 | case CC1101_MARCSTATE: 142 | case CC1101_RXBYTES: 143 | case CC1101_TXBYTES: 144 | case CC1101_WORTIME1: 145 | case CC1101_WORTIME0: 146 | return readRegisterWithSyncProblem(address, registerType); 147 | 148 | default: 149 | return readRegister(address | registerType); 150 | } 151 | } 152 | 153 | void CC1101::writeBurstRegister(uint8_t address, uint8_t* data, uint8_t length) 154 | { 155 | uint8_t i; 156 | 157 | select(); 158 | spi_waitMiso(); 159 | SPI.transfer(address | CC1101_WRITE_BURST); 160 | for (i = 0; i < length; i++) { 161 | SPI.transfer(data[i]); 162 | } 163 | deselect(); 164 | } 165 | 166 | void CC1101::readBurstRegister(uint8_t* buffer, uint8_t address, uint8_t length) 167 | { 168 | uint8_t i; 169 | 170 | select(); 171 | spi_waitMiso(); 172 | SPI.transfer(address | CC1101_READ_BURST); 173 | 174 | for (i = 0; i < length; i++) { 175 | buffer[i] = SPI.transfer(0x00); 176 | } 177 | 178 | deselect(); 179 | } 180 | 181 | //wait for fixed length in rx fifo 182 | uint8_t CC1101::receiveData(CC1101Packet* packet, uint8_t length) 183 | { 184 | uint8_t rxBytes = readRegisterWithSyncProblem(CC1101_RXBYTES, CC1101_STATUS_REGISTER); 185 | rxBytes = rxBytes & CC1101_BITS_RX_BYTES_IN_FIFO; 186 | 187 | //check for rx fifo overflow 188 | if ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) & CC1101_BITS_MARCSTATE) == CC1101_MARCSTATE_RXFIFO_OVERFLOW) 189 | { 190 | writeCommand(CC1101_SIDLE); //idle 191 | writeCommand(CC1101_SFRX); //flush RX buffer 192 | writeCommand(CC1101_SRX); //switch to RX state 193 | } 194 | else if (rxBytes == length) 195 | { 196 | readBurstRegister(packet->data, CC1101_RXFIFO, rxBytes); 197 | 198 | //continue RX 199 | writeCommand(CC1101_SIDLE); //idle 200 | writeCommand(CC1101_SFRX); //flush RX buffer 201 | writeCommand(CC1101_SRX); //switch to RX state 202 | 203 | packet->length = rxBytes; 204 | } 205 | else 206 | { 207 | //empty fifo 208 | packet->length = 0; 209 | } 210 | 211 | return packet->length; 212 | } 213 | 214 | //This function is able to send packets bigger then the FIFO size. 215 | void CC1101::sendData(CC1101Packet *packet) 216 | { 217 | uint8_t index = 0; 218 | uint8_t txStatus, MarcState; 219 | uint8_t length; 220 | 221 | writeCommand(CC1101_SIDLE); //idle 222 | 223 | txStatus = readRegisterWithSyncProblem(CC1101_TXBYTES, CC1101_STATUS_REGISTER); 224 | 225 | //clear TX fifo if needed 226 | if (txStatus & CC1101_BITS_TX_FIFO_UNDERFLOW) 227 | { 228 | writeCommand(CC1101_SIDLE); //idle 229 | writeCommand(CC1101_SFTX); //flush TX buffer 230 | } 231 | 232 | writeCommand(CC1101_SIDLE); //idle 233 | 234 | //determine how many bytes to send 235 | length = (packet->length <= CC1101_DATA_LEN ? packet->length : CC1101_DATA_LEN); 236 | 237 | writeBurstRegister(CC1101_TXFIFO, packet->data, length); 238 | 239 | writeCommand(CC1101_SIDLE); 240 | //start sending packet 241 | writeCommand(CC1101_STX); 242 | 243 | //continue sending when packet is bigger than 64 bytes 244 | if (packet->length > CC1101_DATA_LEN) 245 | { 246 | index += length; 247 | 248 | //loop until all bytes are transmitted 249 | while (index < packet->length) 250 | { 251 | //check if there is free space in the fifo 252 | while ((txStatus = (readRegisterMedian3(CC1101_TXBYTES | CC1101_STATUS_REGISTER) & CC1101_BITS_RX_BYTES_IN_FIFO)) > (CC1101_DATA_LEN - 2)); 253 | 254 | //calculate how many bytes we can send 255 | length = (CC1101_DATA_LEN - txStatus); 256 | length = ((packet->length - index) < length ? (packet->length - index) : length); 257 | 258 | //send some more bytes 259 | for (int i=0; idata[index+i]); 261 | 262 | index += length; 263 | } 264 | } 265 | 266 | //wait until transmission is finished (TXOFF_MODE is expected to be set to 0/IDLE or TXFIFO_UNDERFLOW) 267 | do 268 | { 269 | MarcState = (readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) & CC1101_BITS_MARCSTATE); 270 | // if (MarcState == CC1101_MARCSTATE_TXFIFO_UNDERFLOW) Serial.print(F("TXFIFO_UNDERFLOW occured in sendData() \n")); 271 | } 272 | while((MarcState != CC1101_MARCSTATE_IDLE) && (MarcState != CC1101_MARCSTATE_TXFIFO_UNDERFLOW)); 273 | } 274 | -------------------------------------------------------------------------------- /components/cc1101/CC1101.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 3 | */ 4 | 5 | #ifndef __CC1101_H__ 6 | #define __CC1101_H__ 7 | 8 | #include 9 | #include "CC1101Packet.h" 10 | #include 11 | // On Arduino, SPI pins are predefined 12 | 13 | /* Type of transfers */ 14 | #define CC1101_WRITE_BURST 0x40 15 | #define CC1101_READ_SINGLE 0x80 16 | #define CC1101_READ_BURST 0xC0 17 | 18 | /* Type of register */ 19 | #define CC1101_CONFIG_REGISTER CC1101_READ_SINGLE 20 | #define CC1101_STATUS_REGISTER CC1101_READ_BURST 21 | 22 | /* PATABLE & FIFO's */ 23 | #define CC1101_PATABLE 0x3E // PATABLE address 24 | #define CC1101_TXFIFO 0x3F // TX FIFO address 25 | #define CC1101_RXFIFO 0x3F // RX FIFO address 26 | #define CC1101_PA_LowPower 0x60 27 | #define CC1101_PA_LongDistance 0xC0 28 | 29 | /* Command strobes */ 30 | #define CC1101_SRES 0x30 // Reset CC1101 chip 31 | #define CC1101_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). 32 | #define CC1101_SXOFF 0x32 // Turn off crystal oscillator 33 | #define CC1101_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) 34 | #define CC1101_SRX 0x34 // Enable RX. Perform calibration first if coming from IDLE and MCSM0.FS_AUTOCAL=1 35 | #define CC1101_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 36 | #define CC1101_SIDLE 0x36 // Exit RX / TX, turn off frequency synthesizer and exit Wake-On-Radio mode if applicable 37 | #define CC1101_SWOR 0x38 // Start automatic RX polling sequence (Wake-on-Radio) as described in Section 19.5 if WORCTRL.RC_PD=0 38 | #define CC1101_SPWD 0x39 // Enter power down mode when CSn goes high 39 | #define CC1101_SFRX 0x3A // Flush the RX FIFO buffer. Only issue SFRX in IDLE or RXFIFO_OVERFLOW states 40 | #define CC1101_SFTX 0x3B // Flush the TX FIFO buffer. Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states 41 | #define CC1101_SWORRST 0x3C // Reset real time clock to Event1 value 42 | #define CC1101_SNOP 0x3D // No operation. May be used to get access to the chip status byte 43 | 44 | /* CC1101 configuration registers */ 45 | #define CC1101_IOCFG2 0x00 // GDO2 Output Pin Configuration 46 | #define CC1101_IOCFG1 0x01 // GDO1 Output Pin Configuration 47 | #define CC1101_IOCFG0 0x02 // GDO0 Output Pin Configuration 48 | #define CC1101_FIFOTHR 0x03 // RX FIFO and TX FIFO Thresholds 49 | #define CC1101_SYNC1 0x04 // Sync Word, High Byte 50 | #define CC1101_SYNC0 0x05 // Sync Word, Low Byte 51 | #define CC1101_PKTLEN 0x06 // Packet Length 52 | #define CC1101_PKTCTRL1 0x07 // Packet Automation Control 53 | #define CC1101_PKTCTRL0 0x08 // Packet Automation Control 54 | #define CC1101_ADDR 0x09 // Device Address 55 | #define CC1101_CHANNR 0x0A // Channel Number 56 | #define CC1101_FSCTRL1 0x0B // Frequency Synthesizer Control 57 | #define CC1101_FSCTRL0 0x0C // Frequency Synthesizer Control 58 | #define CC1101_FREQ2 0x0D // Frequency Control Word, High Byte 59 | #define CC1101_FREQ1 0x0E // Frequency Control Word, Middle Byte 60 | #define CC1101_FREQ0 0x0F // Frequency Control Word, Low Byte 61 | #define CC1101_MDMCFG4 0x10 // Modem Configuration 62 | #define CC1101_MDMCFG3 0x11 // Modem Configuration 63 | #define CC1101_MDMCFG2 0x12 // Modem Configuration 64 | #define CC1101_MDMCFG1 0x13 // Modem Configuration 65 | #define CC1101_MDMCFG0 0x14 // Modem Configuration 66 | #define CC1101_DEVIATN 0x15 // Modem Deviation Setting 67 | #define CC1101_MCSM2 0x16 // Main Radio Control State Machine Configuration 68 | #define CC1101_MCSM1 0x17 // Main Radio Control State Machine Configuration 69 | #define CC1101_MCSM0 0x18 // Main Radio Control State Machine Configuration 70 | #define CC1101_FOCCFG 0x19 // Frequency Offset Compensation Configuration 71 | #define CC1101_BSCFG 0x1A // Bit Synchronization Configuration 72 | #define CC1101_AGCCTRL2 0x1B // AGC Control 73 | #define CC1101_AGCCTRL1 0x1C // AGC Control 74 | #define CC1101_AGCCTRL0 0x1D // AGC Control 75 | #define CC1101_WOREVT1 0x1E // High Byte Event0 Timeout 76 | #define CC1101_WOREVT0 0x1F // Low Byte Event0 Timeout 77 | #define CC1101_WORCTRL 0x20 // Wake On Radio Control 78 | #define CC1101_FREND1 0x21 // Front End RX Configuration 79 | #define CC1101_FREND0 0x22 // Front End TX Configuration 80 | #define CC1101_FSCAL3 0x23 // Frequency Synthesizer Calibration 81 | #define CC1101_FSCAL2 0x24 // Frequency Synthesizer Calibration 82 | #define CC1101_FSCAL1 0x25 // Frequency Synthesizer Calibration 83 | #define CC1101_FSCAL0 0x26 // Frequency Synthesizer Calibration 84 | #define CC1101_RCCTRL1 0x27 // RC Oscillator Configuration 85 | #define CC1101_RCCTRL0 0x28 // RC Oscillator Configuration 86 | #define CC1101_FSTEST 0x29 // Frequency Synthesizer Calibration Control 87 | #define CC1101_PTEST 0x2A // Production Test 88 | #define CC1101_AGCTEST 0x2B // AGC Test 89 | #define CC1101_TEST2 0x2C // Various Test Settings 90 | #define CC1101_TEST1 0x2D // Various Test Settings 91 | #define CC1101_TEST0 0x2E // Various Test Settings 92 | 93 | /* Status registers */ 94 | #define CC1101_PARTNUM 0x30 // Chip ID 95 | #define CC1101_VERSION 0x31 // Chip ID 96 | #define CC1101_FREQEST 0x32 // Frequency Offset Estimate from Demodulator 97 | #define CC1101_LQI 0x33 // Demodulator Estimate for Link Quality 98 | #define CC1101_RSSI 0x34 // Received Signal Strength Indication 99 | #define CC1101_MARCSTATE 0x35 // Main Radio Control State Machine State 100 | #define CC1101_WORTIME1 0x36 // High Byte of WOR Time 101 | #define CC1101_WORTIME0 0x37 // Low Byte of WOR Time 102 | #define CC1101_PKTSTATUS 0x38 // Current GDOx Status and Packet Status 103 | #define CC1101_VCO_VC_DAC 0x39 // Current Setting from PLL Calibration Module 104 | #define CC1101_TXBYTES 0x3A // Underflow and Number of Bytes 105 | #define CC1101_RXBYTES 0x3B // Overflow and Number of Bytes 106 | #define CC1101_RCCTRL1_STATUS 0x3C // Last RC Oscillator Calibration Result 107 | #define CC1101_RCCTRL0_STATUS 0x3D // Last RC Oscillator Calibration Result 108 | 109 | /* Bit fields in the chip status byte */ 110 | #define CC1101_STATUS_CHIP_RDYn_BM 0x80 111 | #define CC1101_STATUS_STATE_BM 0x70 112 | #define CC1101_STATUS_FIFO_BYTES_AVAILABLE_BM 0x0F 113 | 114 | /* Masks to retrieve status bit */ 115 | #define CC1101_BITS_TX_FIFO_UNDERFLOW 0x80 116 | #define CC1101_BITS_RX_BYTES_IN_FIFO 0x7F 117 | #define CC1101_BITS_MARCSTATE 0x1F 118 | 119 | 120 | /* Marc states */ 121 | enum CC1101MarcStates 122 | { 123 | CC1101_MARCSTATE_SLEEP = 0x00, 124 | CC1101_MARCSTATE_IDLE = 0x01, 125 | CC1101_MARCSTATE_XOFF = 0x02, 126 | CC1101_MARCSTATE_VCOON_MC = 0x03, 127 | CC1101_MARCSTATE_REGON_MC = 0x04, 128 | CC1101_MARCSTATE_MANCAL = 0x05, 129 | CC1101_MARCSTATE_VCOON = 0x06, 130 | CC1101_MARCSTATE_REGON = 0x07, 131 | CC1101_MARCSTATE_STARTCAL = 0x08, 132 | CC1101_MARCSTATE_BWBOOST = 0x09, 133 | CC1101_MARCSTATE_FS_LOCK = 0x0A, 134 | CC1101_MARCSTATE_IFADCON = 0x0B, 135 | CC1101_MARCSTATE_ENDCAL = 0x0C, 136 | CC1101_MARCSTATE_RX = 0x0D, 137 | CC1101_MARCSTATE_RX_END = 0x0E, 138 | CC1101_MARCSTATE_RX_RST = 0x0F, 139 | CC1101_MARCSTATE_TXRX_SWITCH = 0x10, 140 | CC1101_MARCSTATE_RXFIFO_OVERFLOW = 0x11, 141 | CC1101_MARCSTATE_FSTXON = 0x12, 142 | CC1101_MARCSTATE_TX = 0x13, 143 | CC1101_MARCSTATE_TX_END = 0x14, 144 | CC1101_MARCSTATE_RXTX_SWITCH = 0x15, 145 | CC1101_MARCSTATE_TXFIFO_UNDERFLOW = 0x16 146 | }; 147 | 148 | 149 | /* Chip states */ 150 | enum CC1101ChipStates 151 | { 152 | CC1101_STATE_MASK = 0x70, 153 | CC1101_STATE_IDLE = 0x00, 154 | CC1101_STATE_RX = 0x10, 155 | CC1101_STATE_TX = 0x20, 156 | CC1101_STATE_FSTXON = 0x30, 157 | CC1101_STATE_CALIBRATE = 0x40, 158 | CC1101_STATE_SETTLING = 0x50, 159 | CC1101_STATE_RX_OVERFLOW = 0x60, 160 | CC1101_STATE_TX_UNDERFLOW = 0x70 161 | }; 162 | 163 | 164 | 165 | class CC1101 166 | { 167 | protected: 168 | 169 | //functions 170 | public: 171 | CC1101(); 172 | ~CC1101(); 173 | 174 | //spi 175 | void spi_waitMiso(); 176 | 177 | //cc1101 178 | void init(); 179 | 180 | uint8_t writeCommand(uint8_t command); 181 | void writeRegister(uint8_t address, uint8_t data); 182 | 183 | uint8_t readRegister(uint8_t address, uint8_t registerType); 184 | 185 | void writeBurstRegister(uint8_t address, uint8_t* data, uint8_t length); 186 | void readBurstRegister(uint8_t* buffer, uint8_t address, uint8_t length); 187 | 188 | void sendData(CC1101Packet *packet); 189 | uint8_t receiveData(CC1101Packet* packet, uint8_t length); 190 | 191 | private: 192 | CC1101( const CC1101 &c ); 193 | CC1101& operator=( const CC1101 &c ); 194 | // SPI helper functions 195 | void select(void); 196 | void deselect(void); 197 | 198 | protected: 199 | uint8_t readRegister(uint8_t address); 200 | uint8_t readRegisterMedian3(uint8_t address); 201 | uint8_t readRegisterWithSyncProblem(uint8_t address, uint8_t registerType); 202 | 203 | void reset(); 204 | 205 | }; //CC1101 206 | 207 | #endif //__CC1101_H__ 208 | -------------------------------------------------------------------------------- /components/cc1101/CC1101Packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 3 | */ 4 | 5 | #ifndef CC1101PACKET_H_ 6 | #define CC1101PACKET_H_ 7 | 8 | #include 9 | #include 10 | 11 | #define CC1101_BUFFER_LEN 64 12 | #define CC1101_DATA_LEN CC1101_BUFFER_LEN - 3 13 | 14 | 15 | class CC1101Packet 16 | { 17 | public: 18 | uint8_t length; 19 | uint8_t data[72]; 20 | }; 21 | 22 | 23 | #endif /* CC1101PACKET_H_ */ 24 | -------------------------------------------------------------------------------- /components/cc1101/IthoCC1101.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoMPaTech/esphome_ct/156a132d3298b1997cc39ffa3fc2594b9963c352/components/cc1101/IthoCC1101.cpp -------------------------------------------------------------------------------- /components/cc1101/IthoCC1101.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 3 | */ 4 | 5 | #ifndef __ITHOCC1101_H__ 6 | #define __ITHOCC1101_H__ 7 | 8 | #include 9 | #include "CC1101.h" 10 | #include "IthoPacket.h" 11 | 12 | 13 | //pa table settings 14 | const uint8_t ithoPaTableSend[8] = {0x6F, 0x26, 0x2E, 0x8C, 0x87, 0xCD, 0xC7, 0xC0}; 15 | const uint8_t ithoPaTableReceive[8] = {0x6F, 0x26, 0x2E, 0x7F, 0x8A, 0x84, 0xCA, 0xC4}; 16 | 17 | //rft message 1 commands 18 | const uint8_t ithoMessage1HighCommandBytes[] = {1,84,213,85,50,203,52}; 19 | const uint8_t ithoMessage1MediumCommandBytes[] = {1,84,213,85,74,213,52}; 20 | const uint8_t ithoMessage1LowCommandBytes[] = {1,84,213,85,83,83,84}; 21 | const uint8_t ithoMessage1Timer1CommandBytes[] = {1,83,83,84,204,202,180}; 22 | const uint8_t ithoMessage1Timer2CommandBytes[] = {1,83,83,83,53,52,180}; 23 | const uint8_t ithoMessage1Timer3CommandBytes[] = {1,83,83,82,173,82,180}; 24 | const uint8_t ithoMessage1JoinCommandBytes[] = {0,170,171,85,84,202,180}; 25 | const uint8_t ithoMessage1LeaveCommandBytes[] = {0,170,173,85,83,43,84}; 26 | 27 | //duco message1 commands 28 | const uint8_t ducoMessage1HighCommandBytes[] = {1,84,213,85,51,45,52}; 29 | const uint8_t ducoMessage1MediumCommandBytes[] = {1,84,213,85,75,51,52}; 30 | const uint8_t ducoMessage1LowCommandBytes[] = {1,84,213,85,82,181,84}; 31 | const uint8_t ducoMessage1StandByCommandBytes[] = {1,85,53,84,205,85,52}; 32 | const uint8_t ducoMessage1JoinCommandBytes[] = {0,170,171,85,85,44,180}; 33 | const uint8_t ducoMessage1LeaveCommandBytes[] = {0,170,173,85,82,205,84}; 34 | 35 | //message 2 commands 36 | const uint8_t ithoMessage2PowerCommandBytes[] = {6,89,150,170,165,101,90,150,85,149,101,90,102,85,150}; 37 | const uint8_t ithoMessage2HighCommandBytes[] = {6,89,150,170,165,101,90,150,85,149,101,89,102,85,150}; 38 | const uint8_t ithoMessage2MediumCommandBytes[] = {6,89,150,170,165,101,90,150,85,149,101,90,150,85,150}; 39 | const uint8_t ithoMessage2LowCommandBytes[] = {6,89,150,170,165,101,90,150,85,149,101,89,150,85,150}; 40 | const uint8_t ithoMessage2StandByCommandBytes[] = {6,89,150,170,165,101,90,150,85,149,101,90,86,85,150}; 41 | const uint8_t ithoMessage2Timer1CommandBytes[] = {6,89,150,170,169,101,90,150,85,149,101,89,86,85,153}; //10 minutes full speed 42 | const uint8_t ithoMessage2Timer2CommandBytes[] = {6,89,150,170,169,101,90,150,85,149,101,89,86,149,150}; //20 minutes full speed 43 | const uint8_t ithoMessage2Timer3CommandBytes[] = {6,89,150,170,169,101,90,150,85,149,101,89,86,149,154}; //30 minutes full speed 44 | const uint8_t ithoMessage2JoinCommandBytes[] = {9,90,170,90,165,165,89,106,85,149,102,89,150,170,165}; 45 | const uint8_t ithoMessage2LeaveCommandBytes[] = {9,90,170,90,165,165,89,166,85,149,105,90,170,90,165}; 46 | 47 | //message 2, counter 48 | const uint8_t counterBytes24a[] = {1,2}; 49 | const uint8_t counterBytes24b[] = {84,148,100,164,88,152,104,168}; 50 | const uint8_t counterBytes25[] = {149,165,153,169,150,166,154,170}; 51 | const uint8_t counterBytes26[] = {96,160}; 52 | const uint8_t counterBytes41[] = {5, 10, 6, 9}; 53 | const uint8_t counterBytes42[] = {90, 170, 106, 154}; 54 | const uint8_t counterBytes43[] = {154, 90, 166, 102, 150, 86, 170, 106}; 55 | //join/leave 56 | const uint8_t counterBytes64[] = {154,90,166,102,150,86,169,105,153,89,165,101,149,85,170,106}; 57 | const uint8_t counterBytes65[] = {150,169,153,165,149,170,154,166}; 58 | const uint8_t counterBytes66[] = {170,106}; 59 | 60 | 61 | //state machine 62 | enum IthoReceiveStates 63 | { 64 | ExpectMessageStart, 65 | ExpectNormalCommand, 66 | ExpectJoinCommand, 67 | ExpectLeaveCommand 68 | }; 69 | 70 | 71 | 72 | class IthoCC1101 : protected CC1101 73 | { 74 | private: 75 | //receive 76 | IthoReceiveStates receiveState; //state machine receive 77 | unsigned long lastMessage1Received; //used for timeout detection 78 | CC1101Packet inMessage1; //temp storage message1 79 | CC1101Packet inMessage2; //temp storage message2 80 | IthoPacket inIthoPacket; //stores last received message data 81 | 82 | //send 83 | IthoPacket outIthoPacket; //stores state of "remote" 84 | 85 | //settings 86 | uint8_t sendTries; //number of times a command is send at one button press 87 | 88 | //functions 89 | public: 90 | IthoCC1101(uint8_t counter = 0, uint8_t sendTries = 3); //set initial counter value 91 | ~IthoCC1101(); 92 | 93 | //init 94 | void init() { CC1101::init(); } //init,reset CC1101 95 | void initReceive(); 96 | uint8_t getLastCounter() { return outIthoPacket.counter; } //counter is increased before sending a command 97 | void setSendTries(uint8_t sendTries) { this->sendTries = sendTries; } 98 | 99 | //- deviceid should be a setting as well? random gen function? TODO 100 | 101 | //receive 102 | bool checkForNewPacket(); //check RX fifo for new data 103 | IthoPacket getLastPacket() { return inIthoPacket; } //retrieve last received/parsed packet from remote 104 | IthoCommand getLastCommand() { return inIthoPacket.command; } //retrieve last received/parsed command from remote 105 | uint8_t getLastInCounter() { return inIthoPacket.counter; } //retrieve last received/parsed command from remote 106 | uint8_t ReadRSSI(); 107 | bool checkID(const uint8_t *id); 108 | String getLastIDstr(); 109 | 110 | //send 111 | void sendCommand(IthoCommand command); 112 | protected: 113 | private: 114 | IthoCC1101( const IthoCC1101 &c); 115 | IthoCC1101& operator=( const IthoCC1101 &c); 116 | 117 | //init CC1101 for receiving 118 | void initReceiveMessage1(); 119 | void initReceiveMessage2(IthoMessageType expectedMessageType); 120 | 121 | //init CC1101 for sending 122 | void initSendMessage1(); 123 | void initSendMessage2(IthoCommand command); 124 | void finishTransfer(); 125 | 126 | //receive message validation 127 | bool isValidMessageStart(); 128 | bool isValidMessageCommand(); 129 | bool isValidMessageJoin(); 130 | bool isValidMessageLeave(); 131 | 132 | //parse received message 133 | void parseReceivedPackets(); 134 | void parseMessageStart(); 135 | void parseMessageCommand(); 136 | void parseMessageJoin(); 137 | void parseMessageLeave(); 138 | 139 | //send 140 | void createMessageStart(IthoPacket *itho, CC1101Packet *packet); 141 | void createMessageCommand(IthoPacket *itho, CC1101Packet *packet); 142 | void createMessageJoin(IthoPacket *itho, CC1101Packet *packet); 143 | void createMessageLeave(IthoPacket *itho, CC1101Packet *packet); 144 | uint8_t* getMessage1CommandBytes(IthoCommand command); 145 | uint8_t* getMessage2CommandBytes(IthoCommand command); 146 | 147 | //counter bytes calculation (send) 148 | uint8_t getMessage1Byte18(IthoCommand command); 149 | IthoCommand getMessage1PreviousCommand(uint8_t byte18); 150 | uint8_t calculateMessage2Byte24(uint8_t counter); 151 | uint8_t calculateMessage2Byte25(uint8_t counter); 152 | uint8_t calculateMessage2Byte26(uint8_t counter); 153 | uint8_t calculateMessage2Byte41(uint8_t counter, IthoCommand command); 154 | uint8_t calculateMessage2Byte42(uint8_t counter, IthoCommand command); 155 | uint8_t calculateMessage2Byte43(uint8_t counter, IthoCommand command); 156 | uint8_t calculateMessage2Byte49(uint8_t counter); 157 | uint8_t calculateMessage2Byte50(uint8_t counter); 158 | uint8_t calculateMessage2Byte51(uint8_t counter); 159 | uint8_t calculateMessage2Byte64(uint8_t counter); 160 | uint8_t calculateMessage2Byte65(uint8_t counter); 161 | uint8_t calculateMessage2Byte66(uint8_t counter); 162 | 163 | //counter calculation (receive) 164 | uint8_t calculateMessageCounter(uint8_t byte24, uint8_t byte25, uint8_t byte26); 165 | 166 | //general 167 | uint8_t getCounterIndex(const uint8_t *arr, uint8_t length, uint8_t value); 168 | 169 | //test 170 | void testCreateMessage(); 171 | 172 | }; //IthoCC1101 173 | 174 | 175 | extern volatile uint32_t data1[]; 176 | 177 | #endif //__ITHOCC1101_H__ 178 | -------------------------------------------------------------------------------- /components/cc1101/IthoPacket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 3 | */ 4 | 5 | #ifndef ITHOPACKET_H_ 6 | #define ITHOPACKET_H_ 7 | 8 | 9 | enum IthoMessageType 10 | { 11 | ithomsg_unknown = 0, 12 | ithomsg_control = 1, 13 | ithomsg_join = 2, 14 | ithomsg_leave = 3 15 | }; 16 | 17 | //do not change enum because they are used in calculations! 18 | enum IthoCommand 19 | { 20 | IthoUnknown = 0, 21 | 22 | IthoJoin = 4, 23 | IthoLeave = 8, 24 | 25 | IthoStandby = 34, 26 | IthoLow = 35, 27 | IthoMedium = 36, 28 | IthoHigh = 37, 29 | IthoFull = 38, 30 | 31 | IthoTimer1 = 41, 32 | IthoTimer2 = 51, 33 | IthoTimer3 = 61, 34 | 35 | //duco c system remote 36 | DucoStandby = 251, 37 | DucoLow = 252, 38 | DucoMedium = 253, 39 | DucoHigh = 254 40 | }; 41 | 42 | 43 | class IthoPacket 44 | { 45 | public: 46 | uint8_t deviceId1[6]; 47 | uint8_t deviceId2[8]; 48 | IthoMessageType messageType; 49 | IthoCommand command; 50 | IthoCommand previous; 51 | 52 | uint8_t counter; //0-255, counter is increased on every remote button press 53 | }; 54 | 55 | 56 | #endif /* ITHOPACKET_H_ */ 57 | -------------------------------------------------------------------------------- /components/cc1101/README.md: -------------------------------------------------------------------------------- 1 | # Controlling ITHO Mechanical Ventilation 2 | 3 | ## Requirements 4 | 5 | - An ESP8266 (or compatible) 6 | - A C1101 board (including coil) 7 | - Some tinkering [see Mechanics](#mechanics) 8 | 9 | ## How-to using Home Assistant 10 | 11 | Ensure you have the hardware ready ([Mechanics](#mechanics)) and the `esphome` add-on installed. Next follow the ESPHome YAML configuration part. 12 | 13 | When operational from a HA point of view: 14 | 15 | - [ ] Power-off your mechanical fan unit 16 | - [ ] Ensure you are able to send the join command from HA (i.e., have your mobile phone or tablet with you) 17 | - [ ] Power-up the mechanical fan unit 18 | - [ ] *Within* 30 seconds of powering the unit, send the join command 19 | - [ ] The fan should respond by varying it's speed to indicate you joined successfully 20 | - [ ] Test if you can send other commands. 21 | 22 | ## Current status: 23 | 24 | - Sending High/Medium/Low and the Timers work 25 | - Reading state and indicative time (not counting down (yet) - should HA or ESPhome do that?) 26 | 27 | ## ESPHome ITHO control 28 | Trying to get ESPHome to mimic what is comprised in 29 | 30 | - https://github.com/compatech/esphome_c1101 31 | - https://github.com/jodur/ESPEASY_Plugin_ITHO/blob/master/_P145_Itho.ino 32 | - https://github.com/adri/IthoEcoFanRFT / https://github.com/supersjimmie/IthoEcoFanRFT 33 | 34 | 35 | # Mechanics 36 | 37 | ## Wiring schema used 38 | 39 | ``` 40 | Connections between the CC1101 and the ESP8266 or Arduino: 41 | CC11xx pins ESP pins Arduino pins Description 42 | * 1 - VCC VCC VCC 3v3 43 | * 2 - GND GND GND Ground 44 | * 3 - MOSI 13=D7 Pin 11 Data input to CC11xx 45 | * 4 - SCK 14=D5 Pin 13 Clock pin 46 | * 5 - MISO/GDO1 12=D6 Pin 12 Data output from CC11xx / serial clock from CC11xx 47 | * 6 - GDO2 04=D2 Pin 2 Programmable output 48 | * 7 - GDO0 ? Pin ? Programmable output 49 | * 8 - CSN 15=D8 Pin 10 Chip select / (SPI_SS) 50 | ``` 51 | 52 | # Dependencies 53 | 54 | - Home Assistant 55 | - ESPHome 56 | - https://github.com/CoMPaTech/esphome_itho 57 | 58 | # ESPhome YAML configuration 59 | 60 | See [cc1101.yaml](cc1101.yaml) for configuration details, specifically for adding the appropriate buttons. 61 | 62 | ## Top part, configure accordingly, the libaries **must** be there! 63 | 64 | 65 | ```yaml 66 | esphome: 67 | name: fancontrol 68 | platform: ESP8266 69 | board: d1_mini_lite 70 | libraries: 71 | - SPI 72 | - Ticker 73 | 74 | external_components: 75 | - source: 76 | type: git 77 | url: https://github.com/CoMPaTech/esphome_ct 78 | # refresh: 3600s # only set this briefly otherwise it just refreshes daily, see https://esphome.io/components/external_components#external-components-refresh for setting refresh too low 79 | ``` 80 | 81 | ## Fan part 82 | 83 | ```yaml 84 | fan: 85 | - platform: "cc1101" 86 | speed_count: 3 87 | name: "Mechanical Fan" 88 | data_pin: D1 89 | map_off_to_zero: True 90 | id: mech_fan 91 | ``` 92 | 93 | #### Button part 94 | 95 | See example configuration file for details [cc1101.yaml](cc1101.yaml) on the buttons as instructed 96 | 97 | ```yaml 98 | button: 99 | - platform: template 100 | name: "Send join command" 101 | on_press: 102 | then: 103 | - lambda: |- 104 | ((cc1101fan::CC1101Fan*)(&id(mech_fan)))->send_other_command(0); 105 | - platform: template 106 | name: "Run Timer for 10 Minutes" 107 | on_press: 108 | then: 109 | - lambda: |- 110 | ((cc1101fan::CC1101Fan*)(&id(mech_fan)))->send_other_command(1); 111 | - platform: template 112 | name: "Run Timer for 20 Minutes" 113 | on_press: 114 | then: 115 | - lambda: |- 116 | ((cc1101fan::CC1101Fan*)(&id(mech_fan)))->send_other_command(2); 117 | - platform: template 118 | name: "Run Timer for 30 Minutes" 119 | on_press: 120 | then: 121 | - lambda: |- 122 | ((cc1101fan::CC1101Fan*)(&id(mech_fan)))->send_other_command(3); 123 | ``` 124 | 125 | # Known limitations 126 | 127 | Just low/medium/high ... set speed_count to 4 if you need full 128 | If you map off to zero, 'low' and 'off' are the same (i.e., mine only has 3 speeds - there is no off) 129 | 130 | ## References (from archived repo) 131 | 132 | ESPHome usable library for C1101/ITHO: https://github.com/CoMPaTech/esphome_itho 133 | 134 | This is mostly from https://github.com/jodur/ESPEASY_Plugin_ITHO/ 135 | 136 | Although backtracked to (amongst other forks) : https://github.com/adri/IthoEcoFanRFT / https://github.com/supersjimmie/IthoEcoFanRFT 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /components/cc1101/__init__.py: -------------------------------------------------------------------------------- 1 | from . import fan 2 | -------------------------------------------------------------------------------- /components/cc1101/button.py: -------------------------------------------------------------------------------- 1 | import esphome.codegen as cg 2 | import esphome.config_validation as cv 3 | from esphome.components import button 4 | from esphome.const import ( 5 | CONF_ID, 6 | ENTITY_CATEGORY_CONFIG, 7 | ICON_POWER, 8 | ) 9 | 10 | shutdown_ns = cg.esphome_ns.namespace("shutdown") 11 | ShutdownButton = shutdown_ns.class_("ShutdownButton", button.Button, cg.Component) 12 | 13 | CONFIG_SCHEMA = button.button_schema( 14 | ShutdownButton, entity_category=ENTITY_CATEGORY_CONFIG, icon=ICON_POWER 15 | ).extend(cv.COMPONENT_SCHEMA) 16 | 17 | 18 | async def to_code(config): 19 | var = cg.new_Pvariable(config[CONF_ID]) 20 | await cg.register_component(var, config) 21 | await button.register_button(var, config) 22 | ~ 23 | -------------------------------------------------------------------------------- /components/cc1101/cc1101.yaml: -------------------------------------------------------------------------------- 1 | esphome: 2 | name: fancontrol 3 | platform: ESP8266 4 | board: d1_mini_lite 5 | libraries: 6 | - SPI 7 | - Ticker 8 | 9 | external_components: 10 | - source: 11 | type: git 12 | url: https://github.com/CoMPaTech/esphome_ct 13 | ref: dev2 14 | # refresh: 3600s # only set this briefly otherwise it just refreshes daily, see https://esphome.io/components/external_components#external-components-refresh for setting refresh too low 15 | 16 | fan: 17 | - platform: "cc1101" 18 | speed_count: 3 19 | name: "Mechanical Fan" 20 | data_pin: D1 21 | map_off_to_zero: True 22 | id: mech_fan 23 | 24 | button: 25 | - platform: template 26 | name: "Send join command" 27 | on_press: 28 | then: 29 | - lambda: |- 30 | ((cc1101fan::CC1101Fan*)(&id(mech_fan)))->send_other_command(0); 31 | - platform: template 32 | name: "Run Timer for 10 Minutes" 33 | on_press: 34 | then: 35 | - lambda: |- 36 | ((cc1101fan::CC1101Fan*)(&id(mech_fan)))->send_other_command(1); 37 | - platform: template 38 | name: "Run Timer for 20 Minutes" 39 | on_press: 40 | then: 41 | - lambda: |- 42 | ((cc1101fan::CC1101Fan*)(&id(mech_fan)))->send_other_command(2); 43 | - platform: template 44 | name: "Run Timer for 30 Minutes" 45 | on_press: 46 | then: 47 | - lambda: |- 48 | ((cc1101fan::CC1101Fan*)(&id(mech_fan)))->send_other_command(3); 49 | -------------------------------------------------------------------------------- /components/cc1101/fan.cpp: -------------------------------------------------------------------------------- 1 | #include "esphome/core/log.h" 2 | #include "fan.h" 3 | #include "IthoCC1101.h" 4 | #include "Ticker.h" 5 | 6 | namespace esphome { 7 | namespace cc1101fan { 8 | 9 | IthoCC1101 rf; 10 | //void ITHOinterrupt() IRAM_ATTR; 11 | void ITHOcheck(); 12 | 13 | // extra for interrupt handling 14 | //bool ITHOhasPacket = false; 15 | Ticker ITHOticker; 16 | Ticker reset_timer_; 17 | int LastIDindex = 0; 18 | int OldLastIDindex = 0; 19 | long LastPublish=0; 20 | bool InitRunned = false; 21 | IthoPacket pkt; 22 | String LastID = ""; 23 | bool timer_active_; 24 | 25 | // Timer values for hardware timer in Fan 26 | uint16_t Time1 = 10*60; 27 | uint16_t Time2 = 20*60; 28 | uint16_t Time3 = 30*60; 29 | 30 | void CC1101Fan::setup() { 31 | auto restore = this->restore_state_(); 32 | if (restore.has_value()) { 33 | ESP_LOGD("cc1101_fan", "restoring"); 34 | restore->apply(*this); 35 | } 36 | 37 | rf.init(); 38 | this->data_pin_->setup(); 39 | this->data_pin_->pin_mode(gpio::FLAG_INPUT); 40 | // ITHOticker.attach_ms(100, std::bind(&CC1101Fan::check_pin, this)); 41 | 42 | 43 | //this->data_pin_->attach_interrupt(CC1101Fan::ITHOinterrupt, gpio::TriggerMode::RISING); 44 | 45 | //auto gpio_num = this->data_pin_->get_pin(); 46 | //attachInterrupt(digitalPinToInterrupt(gpio_num), []() { 47 | // CC1101Fan::ITHOinterrupt(); 48 | //}, RISING); 49 | 50 | //this->data_pin_->attach_interrupt(CC1101Fan::ITHOinterrupt, RISING); 51 | // Init CC1101 52 | //pinMode(D1, INPUT); 53 | //attachInterrupt(D1, CC1101Fan::ITHOinterrupt, RISING); 54 | rf.initReceive(); 55 | } 56 | 57 | //void CC1101Fan::check_pin() { 58 | // if (this->data_pin_->digital_read()) { 59 | // CC1101Fan::ITHOinterrupt(); 60 | // } 61 | //} 62 | 63 | void CC1101Fan::update() { 64 | CC1101Fan::ITHOcheck(); 65 | 66 | /* 67 | // Only publish if the state has changed 68 | if (fantimer->state != String(Timer).c_str()) { 69 | fantimer->publish_state(String(Timer).c_str()); 70 | } 71 | 72 | if (lastid->state != LastID.c_str()) { 73 | lastid->publish_state(LastID.c_str()); 74 | } 75 | */ 76 | } 77 | 78 | void CC1101Fan::publish_state() { 79 | auto current_state = this->state; 80 | auto current_speed = this->speed; 81 | this->speed = 0; 82 | this->state = 0; 83 | if (this->Speed >= 0) { 84 | this->speed = this->Speed; 85 | this->state = 1; 86 | } 87 | if (current_state != this->state || current_speed != this->speed ) { 88 | ESP_LOGD("cc1101_fan", "Publishing state: %d (was %d) from speed %d (was %d) ", this->state, current_state, this->Speed, current_speed); 89 | this->state_callback_(); // Notify ESPHome about the state change 90 | } 91 | 92 | } 93 | 94 | fan::FanTraits CC1101Fan::get_traits() { 95 | fan::FanTraits traits; 96 | traits.set_speed(true); // The fan supports speed control 97 | traits.set_supported_speed_count(this->speed_count_); // Number of speeds 98 | traits.set_oscillation(false); // The fan does not support oscillation 99 | traits.set_direction(false); // The fan does not support direction control 100 | return traits; 101 | //return fan::FanTraits(this->oscillation_id_.has_value(), this->speed_id_.has_value(), this->direction_id_.has_value(), 102 | } 103 | 104 | void CC1101Fan::control(const fan::FanCall &call) { 105 | auto State = call.get_state() ? "ON" : "OFF"; 106 | auto Speed = call.get_speed().has_value() ? *call.get_speed() : 255; 107 | ESP_LOGD("cc1101_fan", "Call state: %s, speed: %d", State, Speed); 108 | 109 | if (call.get_speed().has_value() || ( strcmp(State,"ON") && Speed == 255 ) ) { 110 | // If fans don't have off, just lowest level 111 | if ( this->map_off_to_zero_ && Speed == 255 ) { 112 | ESP_LOGD("cc1101_fan", "Correcting with map off to zero speed to 0"); 113 | Speed = 0; 114 | } 115 | if ( Speed == 255 ) { 116 | ESP_LOGD("cc1101_fan", "Correcting speed to 0"); 117 | Speed = 0; 118 | } 119 | if ( Speed > this->speed_count_) { 120 | ESP_LOGD("cc1101_fan", "Speed %d to high, correcting to %d (speed_count)", Speed, this->speed_count_); 121 | Speed = this->speed_count_; 122 | } 123 | ESP_LOGD("cc1101_fan", "Setting speed to %d", Speed); 124 | this->set_fan_speed(Speed); 125 | } else { 126 | // Ensure we do 'off' 127 | this->set_fan_speed(0); 128 | } 129 | } 130 | 131 | void CC1101Fan::set_fan_speed(uint8_t speed) { 132 | ESP_LOGD("cc1101_fan", "RF called with %d while last is %d and speed assumed at %d", speed, this->LastSpeed, this->Speed); 133 | if (speed != this->LastSpeed ) { 134 | // Handle speed control 135 | switch (speed) { 136 | case 4: 137 | rf.sendCommand(IthoFull); 138 | break; 139 | case 3: 140 | rf.sendCommand(IthoHigh); 141 | break; 142 | case 2: 143 | rf.sendCommand(IthoMedium); 144 | break; 145 | case 1: 146 | rf.sendCommand(IthoLow); 147 | break; 148 | case 0: 149 | rf.sendCommand(IthoLow); 150 | break; 151 | } 152 | if (timer_active_) { 153 | reset_timer_.detach(); 154 | ESP_LOGD("cc1101_fan", "Timer was active and has been canceled (other manual command send by us)"); 155 | } 156 | this->LastSpeed = this->Speed; 157 | this->Speed = speed; 158 | if ( this->map_off_to_zero_ && speed == 0 ) this->Speed = 1; 159 | 160 | this->publish_state(); 161 | } 162 | } 163 | 164 | void CC1101Fan::send_other_command(uint8_t other_command) { 165 | switch (other_command) { 166 | case 0: // join 167 | ESP_LOGD("cc1101_fan", "RF called with %d, sending Join", other_command); 168 | rf.sendCommand(IthoJoin); 169 | break; 170 | case 1: // timer 1 171 | ESP_LOGD("cc1101_fan", "RF called with %d, sending Timer1", other_command); 172 | rf.sendCommand(IthoTimer1); 173 | this->speed = 1.0; 174 | publish_state(); 175 | startResetTimer(Time1); 176 | 177 | break; 178 | case 2: // timer 2 179 | ESP_LOGD("cc1101_fan", "RF called with %d, sending Timer2", other_command); 180 | rf.sendCommand(IthoTimer2); 181 | this->speed = 1.0; 182 | publish_state(); 183 | startResetTimer(Time2); 184 | 185 | break; 186 | case 3: // timer 3 187 | ESP_LOGD("cc1101_fan", "RF called with %d, sending Timer3", other_command); 188 | rf.sendCommand(IthoTimer3); 189 | this->speed = 1.0; 190 | publish_state(); 191 | startResetTimer(Time3); 192 | break; 193 | } 194 | } 195 | 196 | void CC1101Fan::startResetTimer(uint16_t seconds) { 197 | if (timer_active_) { 198 | reset_timer_.detach(); 199 | ESP_LOGD("cc1101_fan", "Timer was active and has been canceled from new timer"); 200 | } 201 | timer_active_ = true; 202 | reset_timer_.once(seconds, [this, seconds]() { this->resetFanSpeed(seconds); }); 203 | ESP_LOGD("cc1101_fan", "Button timer started for %d seconds", seconds); 204 | this->publish_state(); 205 | } 206 | 207 | void CC1101Fan::resetFanSpeed(uint16_t seconds) { 208 | this->Speed = 0; 209 | this->state = 1; 210 | timer_active_ = false; 211 | ESP_LOGD("cc1101_fan", "Timer of %d seconds lapsed, assuming back to normal speed", seconds); 212 | publish_state(); 213 | } 214 | 215 | void CC1101Fan::set_output(void *output) { 216 | // No-op: This method is required by the ESPHome build system but is unused. 217 | } 218 | 219 | //void IRAM_ATTR CC1101Fan::ITHOinterrupt() { 220 | // ITHOticker.once_ms(10, CC1101Fan::ITHOcheck); 221 | //} 222 | 223 | String converter(uint8_t *str){ 224 | return String((char *)str); 225 | } 226 | 227 | void CC1101Fan::ITHOcheck() { 228 | //noInterrupts(); 229 | if (rf.checkForNewPacket()) { 230 | IthoCommand cmd = rf.getLastCommand(); 231 | IthoPacket pkt = rf.getLastPacket(); 232 | LastID = rf.getLastIDstr(); 233 | ESP_LOGD("c1101_fan", "Debug - RemoteID1: %s", converter(pkt.deviceId1).c_str()); 234 | ESP_LOGD("c1101_fan", "Debug - RemoteID2: %s", converter(pkt.deviceId2).c_str()); 235 | ESP_LOGD("c1101_fan", "Debug - LastID: %s", LastID.c_str()); 236 | switch (cmd) { 237 | case IthoUnknown: 238 | break; 239 | case IthoLow: 240 | ESP_LOGD("c1101_fan", "1 / Low (or 0 / Off)"); 241 | if (timer_active_) { 242 | reset_timer_.detach(); // Cancel the timer if it's active 243 | ESP_LOGD("cc1101_fan", "Timer was active and has been canceled received remote sending low"); 244 | } 245 | this->LastSpeed = this->Speed; 246 | this->Speed = 1; 247 | break; 248 | case IthoMedium: 249 | ESP_LOGD("c1101_fan", "2 / Medium"); 250 | if (timer_active_) { 251 | reset_timer_.detach(); // Cancel the timer if it's active 252 | ESP_LOGD("cc1101_fan", "Timer was active and has been canceled received remote sending medium"); 253 | } 254 | this->LastSpeed = this->Speed; 255 | this->Speed = 2; 256 | break; 257 | case IthoHigh: 258 | ESP_LOGD("c1101_fan", "3 / High"); 259 | if (timer_active_) { 260 | reset_timer_.detach(); // Cancel the timer if it's active 261 | ESP_LOGD("cc1101_fan", "Timer was active and has been canceled received remote sending high"); 262 | } 263 | this->LastSpeed = this->Speed; 264 | this->Speed = 3; 265 | break; 266 | case IthoFull: 267 | ESP_LOGD("c1101_fan", "4 / Full"); 268 | if (timer_active_) { 269 | reset_timer_.detach(); // Cancel the timer if it's active 270 | ESP_LOGD("cc1101_fan", "Timer was active and has been canceled received remote sending full"); 271 | } 272 | this->LastSpeed = this->Speed; 273 | this->Speed = 4; 274 | break; 275 | case IthoTimer1: 276 | ESP_LOGD("c1101_fan", "Timer1"); 277 | ESP_LOGD("cc1101_fan", "Received remote sending timer1, setting our own"); 278 | startResetTimer(Time1); 279 | this->LastSpeed = this->Speed; 280 | this->Speed = 3; 281 | break; 282 | case IthoTimer2: 283 | ESP_LOGD("c1101_fan", "Timer2"); 284 | ESP_LOGD("cc1101_fan", "Received remote sending timer2, setting our own"); 285 | startResetTimer(Time2); 286 | this->LastSpeed = this->Speed; 287 | this->Speed = 3; 288 | break; 289 | case IthoTimer3: 290 | ESP_LOGD("c1101_fan", "Timer3"); 291 | ESP_LOGD("cc1101_fan", "Received remote sending timer3, setting our own"); 292 | startResetTimer(Time3); 293 | this->LastSpeed = this->Speed; 294 | this->Speed = 3; 295 | break; 296 | case IthoJoin: 297 | ESP_LOGD("c1101_fan", "IthoJoin spotted"); 298 | break; 299 | case IthoLeave: 300 | ESP_LOGD("c1101_fan", "IthoLeave spotted"); 301 | break; 302 | default: 303 | ESP_LOGD("c1101_fan", "Other command spotted"); 304 | break; 305 | } 306 | this->publish_state(); 307 | } 308 | //interrupts(); 309 | }; 310 | 311 | } // namespace cc1101fan 312 | } // namespace esphome 313 | -------------------------------------------------------------------------------- /components/cc1101/fan.h: -------------------------------------------------------------------------------- 1 | #include "esphome/components/fan/fan.h" 2 | #include "esphome/core/component.h" 3 | #include "esphome/core/hal.h" 4 | #include "esphome/core/gpio.h" 5 | 6 | namespace esphome { 7 | namespace cc1101fan { 8 | 9 | class CC1101Fan : public PollingComponent, public fan::Fan { 10 | public: 11 | GPIOPin *data_pin_; 12 | 13 | CC1101Fan(int speed_count, bool map_off_to_zero) : speed_count_(speed_count), map_off_to_zero_(map_off_to_zero) {} 14 | void set_data_pin(GPIOPin *data_pin) { data_pin_ = data_pin; } 15 | void setup() override; 16 | void update() override; 17 | void check_pin(); 18 | void set_preset_modes(const std::set &presets) { this->preset_modes_ = presets; } 19 | fan::FanTraits get_traits() override; 20 | void set_output(void *output); 21 | void set_fan_speed(uint8_t speed); 22 | void send_other_command(uint8_t other_command); 23 | // static void ITHOinterrupt(); 24 | void ITHOcheck(); 25 | 26 | protected: 27 | void control(const fan::FanCall &call) override; 28 | void write_state_(); 29 | void publish_state(); 30 | void resetFanSpeed(uint16_t seconds); 31 | void startResetTimer(uint16_t seconds); 32 | int speed_count_{}; 33 | bool map_off_to_zero_{}; 34 | //fan::FanTraits traits_; 35 | std::set preset_modes_{}; 36 | static constexpr uint8_t INVALID_SPEED = 255; 37 | uint8_t Speed = INVALID_SPEED; 38 | uint8_t LastSpeed = INVALID_SPEED; 39 | //static CC1101Fan *instance_; 40 | }; 41 | 42 | } // namespace cc1101fan 43 | } // namespace esphome 44 | 45 | -------------------------------------------------------------------------------- /components/cc1101/fan.py: -------------------------------------------------------------------------------- 1 | from esphome import pins 2 | from esphome.components import fan 3 | import esphome.codegen as cg 4 | import esphome.config_validation as cv 5 | from esphome.cpp_helpers import gpio_pin_expression 6 | 7 | from esphome.const import CONF_DISABLED_BY_DEFAULT, CONF_NAME, CONF_OUTPUT_ID, CONF_DATA_PIN, CONF_PRESET_MODES, CONF_RESTORE_MODE, CONF_SPEED_COUNT 8 | 9 | cc1101fan_ns = cg.esphome_ns.namespace("cc1101fan") 10 | CC1101Fan = cc1101fan_ns.class_("CC1101Fan", cg.PollingComponent, fan.Fan) 11 | 12 | MAP_OFF_TO_ZERO = "map_off_to_zero" 13 | 14 | # Define mapping manually 15 | FAN_RESTORE_MODE_OPTIONS = { 16 | "ALWAYS_ON": fan.FanRestoreMode.ALWAYS_ON, 17 | "ALWAYS_OFF": fan.FanRestoreMode.ALWAYS_OFF, 18 | } 19 | 20 | CONFIG_SCHEMA = fan.fan_schema(CC1101Fan).extend({ 21 | cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(CC1101Fan), 22 | cv.Required(CONF_DATA_PIN): cv.All(pins.internal_gpio_input_pin_schema), 23 | cv.Required(MAP_OFF_TO_ZERO): cv.boolean, 24 | cv.Optional(CONF_NAME, default="Domestic Fan"): str, 25 | cv.Optional(CONF_SPEED_COUNT, default=3): cv.int_range(min=1, max=100), 26 | cv.Optional(CONF_DISABLED_BY_DEFAULT, default=False): cv.boolean, 27 | cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_ON"): cv.enum(FAN_RESTORE_MODE_OPTIONS), 28 | cv.Optional(CONF_PRESET_MODES): fan.validate_preset_modes, 29 | }).extend(cv.COMPONENT_SCHEMA) 30 | 31 | async def to_code(config): 32 | var = cg.new_Pvariable(config[CONF_OUTPUT_ID], config[CONF_SPEED_COUNT], config[MAP_OFF_TO_ZERO]) 33 | await cg.register_component(var, config) 34 | await fan.register_fan(var, config) 35 | 36 | data_pin = await gpio_pin_expression(config[CONF_DATA_PIN]) 37 | cg.add(var.set_data_pin(data_pin)) 38 | 39 | if CONF_PRESET_MODES in config: 40 | cg.add(var.set_preset_modes(config[CONF_PRESET_MODES])) 41 | 42 | return var 43 | 44 | -------------------------------------------------------------------------------- /components/ithofan/ithofan.cpp: -------------------------------------------------------------------------------- 1 | #include "esphome/core/log.h" 2 | #include "esphome/core/helpers.h" 3 | #include "esphome/core/hal.h" 4 | #include "ithofan.h" 5 | #include 6 | 7 | namespace esphome { 8 | namespace ithofan { 9 | 10 | static const char *const TAG = "ithofan"; 11 | static const int32_t SYMBOL = 640; 12 | 13 | void IthoFanComponent::dump_config() { 14 | ESP_LOGCONFIG(TAG, "IthoFan:"); 15 | ESP_LOGCONFIG(TAG, " Name: %" PRIx32, this->address_); 16 | ESP_LOGCONFIG(TAG, " Code: %" PRIu16, this->code_); 17 | } 18 | 19 | void IthoFanComponent::setup() { 20 | uint32_t type = fnv1_hash(std::string("IthoFan: ") + format_hex(this->address_)); 21 | this->preferences_ = global_preferences->make_preference(type); 22 | this->preferences_.load(&this->code_); 23 | this->rx_->register_listener(this); 24 | } 25 | 26 | void IthoFanComponent::set_code(uint16_t code) { 27 | ESP_LOGD(TAG, "IthoFan updating code to %" PRIu16 " from %" PRIu16, code, this->code_); 28 | this->code_ = code; 29 | this->preferences_.save(&this->code_); 30 | } 31 | 32 | void IthoFanComponent::send_command(IthoFanCommand command, uint32_t repeat) { 33 | uint8_t frame[7]; 34 | frame[0] = 0xA7; // encryption key. Doesn't matter much 35 | frame[1] = command << 4; // which button did you press? The 4 LSB will be the checksum 36 | frame[2] = this->code_ >> 8; // rolling code (big endian) 37 | frame[3] = this->code_; // rolling code 38 | frame[4] = this->address_ >> 16; // remote address 39 | frame[5] = this->address_ >> 8; // remote address 40 | frame[6] = this->address_; // remote address 41 | 42 | ESP_LOGD(TAG, "IthoFan sending 0x%" PRIX8 " repeated %" PRIu32 " times", command, repeat); 43 | 44 | // crc 45 | uint8_t crc = 0; 46 | for (uint8_t i = 0; i < 7; i++) { 47 | crc ^= frame[i]; 48 | crc ^= frame[i] >> 4; 49 | } 50 | frame[1] |= crc & 0xF; 51 | 52 | // obfuscation 53 | for (uint8_t i = 1; i < 7; i++) { 54 | frame[i] ^= frame[i - 1]; 55 | } 56 | 57 | // update code 58 | this->code_ += 1; 59 | this->preferences_.save(&this->code_); 60 | 61 | // send frame 62 | auto call = id(this->tx_).transmit(); 63 | remote_base::RemoteTransmitData *dst = call.get_data(); 64 | dst->item(9415, 9565); 65 | dst->space(80000); 66 | for (uint32_t i = 0; i < (repeat + 1); i++) { 67 | // hardware sync, two sync for the first frame, seven for the following ones 68 | uint32_t syncs = (i == 0) ? 2 : 7; 69 | for (uint32_t j = 0; j < syncs; j++) { 70 | dst->item(SYMBOL * 4, SYMBOL * 4); 71 | } 72 | 73 | // software sync 74 | dst->item(4550, SYMBOL); 75 | 76 | // data 77 | for (uint8_t byte : frame) { 78 | for (uint32_t j = 0; j < 8; j++) { 79 | if ((byte & 0x80) != 0) { 80 | dst->space(SYMBOL); 81 | dst->mark(SYMBOL); 82 | } else { 83 | dst->mark(SYMBOL); 84 | dst->space(SYMBOL); 85 | } 86 | byte <<= 1; 87 | } 88 | } 89 | 90 | // inter frame silence 91 | dst->space(415); 92 | if (i < repeat) { 93 | dst->space(30000); 94 | } 95 | } 96 | call.perform(); 97 | } 98 | 99 | bool IthoFanComponent::on_receive(remote_base::RemoteReceiveData data) { 100 | uint8_t sync_count = 0; 101 | while (true) { 102 | if (data.expect_item(SYMBOL * 4, SYMBOL * 4)) { 103 | sync_count++; 104 | } else if (data.expect_mark(4550)) { 105 | break; 106 | } else { 107 | return true; 108 | } 109 | } 110 | if (sync_count < 2) { 111 | return true; 112 | } 113 | data.expect_space(SYMBOL); 114 | 115 | uint8_t frame[7]; 116 | for (uint8_t &byte : frame) { 117 | for (uint32_t i = 0; i < 8; i++) { 118 | byte <<= 1; 119 | if (data.expect_mark(SYMBOL) || data.expect_mark(SYMBOL * 2)) { 120 | data.expect_space(SYMBOL); 121 | byte |= 0; 122 | } else if (data.expect_space(SYMBOL) || data.expect_space(SYMBOL * 2)) { 123 | data.expect_mark(SYMBOL); 124 | byte |= 1; 125 | } else { 126 | return true; 127 | } 128 | } 129 | } 130 | 131 | for (uint8_t i = 6; i >= 1; i--) { 132 | frame[i] ^= frame[i - 1]; 133 | } 134 | 135 | uint8_t crc = 0; 136 | for (uint8_t i = 0; i < 7; i++) { 137 | crc ^= frame[i]; 138 | crc ^= frame[i] >> 4; 139 | } 140 | if ((crc & 0xF) == 0) { 141 | uint8_t command = frame[1] >> 4; 142 | uint16_t code = (frame[2] << 8) | frame[3]; 143 | uint32_t address = (frame[4] << 16) | (frame[5] << 8) | frame[6]; 144 | ESP_LOGD(TAG, "Received: command: %" PRIx8 ", code: %" PRIu16 ", address %" PRIx32, 145 | command, code, address); 146 | if (command == SOMFY_SENSOR) { 147 | for (auto *sensor : this->sensors_) { 148 | sensor->update_windy(address, (code & 1) != 0); 149 | sensor->update_sunny(address, (code & 2) != 0); 150 | } 151 | } 152 | } 153 | 154 | return true; 155 | } 156 | 157 | } // namespace ithofan 158 | } // namespace esphome 159 | -------------------------------------------------------------------------------- /components/ithofan/ithofan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esphome/core/preferences.h" 4 | #include "esphome/components/remote_transmitter/remote_transmitter.h" 5 | #include "esphome/components/remote_receiver/remote_receiver.h" 6 | 7 | namespace esphome { 8 | namespace ithofan { 9 | 10 | struct IthoRFDevice 11 | { 12 | uint32_t deviceId{0}; 13 | RemoteTypes remType{RemoteTypes::RFTCVE}; 14 | // char name[16]; 15 | IthoCommand lastCommand{IthoUnknown}; 16 | time_t timestamp; 17 | uint8_t counter; 18 | bool bidirectional{false}; 19 | int32_t co2{0xEFFF}; 20 | int32_t temp{0xEFFF}; 21 | int32_t hum{0xEFFF}; 22 | uint8_t pir{0xEF}; 23 | int32_t dewpoint{0xEFFF}; 24 | int32_t battery{0xEFFF}; 25 | }; 26 | 27 | enum IthoCommand : uint16_t 28 | { 29 | IthoUnknown = 0, 30 | 31 | IthoJoin = 1, 32 | IthoLeave = 2, 33 | 34 | IthoAway = 3, 35 | IthoLow = 4, 36 | IthoMedium = 5, 37 | IthoHigh = 6, 38 | IthoFull = 7, 39 | 40 | IthoTimer1 = 8, 41 | IthoTimer2 = 9, 42 | IthoTimer3 = 10, 43 | 44 | IthoAuto = 11, 45 | IthoAutoNight = 12, 46 | 47 | IthoCook30 = 13, 48 | IthoCook60 = 14, 49 | 50 | IthoTimerUser = 15, 51 | 52 | IthoJoinReply = 16, 53 | 54 | IthoPIRmotionOn = 17, 55 | IthoPIRmotionOff = 18, 56 | 57 | }; 58 | 59 | enum RemoteTypes : uint16_t 60 | { 61 | UNSETTYPE = 0x0000, 62 | RFTCVE = 0x22F1, 63 | RFTAUTO = 0x22F3, 64 | RFTAUTON = 0x22F4, 65 | DEMANDFLOW = 0x22F8, 66 | RFTRV = 0x12A0, 67 | RFTCO2 = 0x1298, 68 | RFTPIR = 0x2E10, 69 | ORCON15LF01 = 0x6710 70 | }; 71 | 72 | #define F_MASK 0x03 73 | #define F_RQ 0x00 74 | #define F_I 0x01 75 | #define F_W 0x02 76 | #define F_RP 0x03 77 | 78 | #define F_ADDR0 0x10 79 | #define F_ADDR1 0x20 80 | #define F_ADDR2 0x40 81 | 82 | #define F_PARAM0 0x04 83 | #define F_PARAM1 0x08 84 | #define F_RSSI 0x80 85 | 86 | // Only used for received fields 87 | #define F_OPCODE 0x01 88 | #define F_LEN 0x02 89 | 90 | #define MAX_PAYLOAD 64 91 | #define MAX_DECODED MAX_PAYLOAD + 18 92 | 93 | static char const *const MsgType[4] = {"RQ", "_I", "_W", "RP"}; 94 | 95 | // General command structure: 96 | // < opcode 2 bytes >< len 1 byte >< command len bytes > 97 | 98 | // message command bytes for CVE/HRU remote (536-0124) 99 | const uint8_t ithoMessageAwayCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x01, 0x04}; 100 | const uint8_t ithoMessageLowCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x02, 0x04}; 101 | const uint8_t ithoMessageMediumCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x03, 0x04}; 102 | const uint8_t ithoMessageHighCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x04, 0x04}; 103 | const uint8_t ithoMessageFullCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x04, 0x04}; 104 | const uint8_t ithoMessageTimer1CommandBytes[] = {0x22, 0xF3, 0x03, 0x00, 0x00, 0x0A}; // 10 minutes full speed 105 | const uint8_t ithoMessageTimer2CommandBytes[] = {0x22, 0xF3, 0x03, 0x00, 0x00, 0x14}; // 20 minutes full speed 106 | const uint8_t ithoMessageTimer3CommandBytes[] = {0x22, 0xF3, 0x03, 0x00, 0x00, 0x1E}; // 30 minutes full speed 107 | 108 | // message command bytes specific for AUTO RFT (536-0150) 109 | const uint8_t ithoMessageAUTORFTLowCommandBytes[] = {0x22, 0xF1, 0x03, 0x63, 0x02, 0x04}; 110 | const uint8_t ithoMessageAUTORFTAutoCommandBytes[] = {0x22, 0xF1, 0x03, 0x63, 0x03, 0x04}; 111 | const uint8_t ithoMessageAUTORFTAutoNightCommandBytes[] = {0x22, 0xF8, 0x03, 0x63, 0x02, 0x03}; 112 | const uint8_t ithoMessageAUTORFTHighCommandBytes[] = {0x22, 0xF1, 0x03, 0x63, 0x04, 0x04}; 113 | const uint8_t ithoMessageAUTORFTTimer1CommandBytes[] = {0x22, 0xF3, 0x03, 0x63, 0x00, 0x0A}; 114 | const uint8_t ithoMessageAUTORFTTimer2CommandBytes[] = {0x22, 0xF3, 0x03, 0x63, 0x00, 0x14}; 115 | const uint8_t ithoMessageAUTORFTTimer3CommandBytes[] = {0x22, 0xF3, 0x03, 0x63, 0x00, 0x1E}; 116 | 117 | // message command bytes specific for RFT-RV (04-00046) and RFT-CO2 (04-00045) 118 | const uint8_t ithoMessageRV_CO2MediumCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x03, 0x07}; 119 | const uint8_t ithoMessageRV_CO2AutoCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x05, 0x07}; 120 | const uint8_t ithoMessageRV_CO2AutoNightCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x0B, 0x0B}; 121 | const uint8_t ithoMessageRV_CO2Timer1CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00}; 122 | const uint8_t ithoMessageRV_CO2Timer2CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00}; 123 | const uint8_t ithoMessageRV_CO2Timer3CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00}; 124 | 125 | // message command bytes specific for DemandFlow remote (536-0146) 126 | const uint8_t ithoMessageDFLowCommandBytes[] = {0x22, 0xF8, 0x03, 0x00, 0x01, 0x02}; 127 | const uint8_t ithoMessageDFHighCommandBytes[] = {0x22, 0xF8, 0x03, 0x00, 0x02, 0x02}; 128 | const uint8_t ithoMessageDFTimer1CommandBytes[] = {0x22, 0xF3, 0x05, 0x00, 0x42, 0x03, 0x03, 0x03}; 129 | const uint8_t ithoMessageDFTimer2CommandBytes[] = {0x22, 0xF3, 0x05, 0x00, 0x42, 0x06, 0x03, 0x03}; 130 | const uint8_t ithoMessageDFTimer3CommandBytes[] = {0x22, 0xF3, 0x05, 0x00, 0x42, 0x09, 0x03, 0x03}; 131 | const uint8_t ithoMessageDFCook30CommandBytes[] = {0x22, 0xF3, 0x05, 0x00, 0x02, 0x1E, 0x02, 0x03}; 132 | const uint8_t ithoMessageDFCook60CommandBytes[] = {0x22, 0xF3, 0x05, 0x00, 0x02, 0x3C, 0x02, 0x03}; 133 | 134 | // message command bytes specific for RFT PIR remote 135 | const uint8_t ithoMessageRFTPIRonCommandBytes[] = {0x2E, 0x10, 0x03, 0x00, 0x01, 0x00}; 136 | const uint8_t ithoMessageRFTPIRoffCommandBytes[] = {0x2E, 0x10, 0x03, 0x00, 0x00, 0x00}; 137 | 138 | // Join/Leave command structure: 139 | // < opcode 2 bytes >< len 1 byte >[next command + device ID block repeats len/6 times]< command 3 bytes >< device ID 3 bytes > 140 | 141 | // Join/Leave commands: 142 | const uint8_t ithoMessageCVERFTJoinCommandBytes[] = {0x1F, 0xC9, 0x0C, 0x00, 0x22, 0xF1, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x00, 0x00}; // join command of CVE/HRU remote (536-0124) 143 | const uint8_t ithoMessageAUTORFTJoinCommandBytes[] = {0x1F, 0xC9, 0x0C, 0x63, 0x22, 0xF8, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x00, 0x00}; // join command of AUTO RFT (536-0150) 144 | const uint8_t ithoMessageDFJoinCommandBytes[] = {0x1F, 0xC9, 0x0C, 0x00, 0x22, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x10, 0xE0, 0x00, 0x00, 0x00}; // join command of DemandFlow remote (536-0146) 145 | const uint8_t ithoMessageRVJoinCommandBytes[] = {0x1F, 0xC9, 0x18, 0x00, 0x31, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x12, 0xA0, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC9, 0x00, 0x00, 0x00}; // join command of RFT-RV (04-00046) 146 | const uint8_t ithoMessageCO2JoinCommandBytes[] = {0x1F, 0xC9, 0x1E, 0x00, 0x31, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x12, 0x98, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x10, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC9, 0x00, 0x00, 0x00}; // join command of RFT-CO2 (04-00045) 147 | const uint8_t ithoMessagePIRJoinCommandBytes[] = {0x1F, 0xC9, 0x06, 0x00, 0x2E, 0x10, 0x00, 0x00, 0x00}; // join command of RF PIR 148 | const uint8_t ithoMessageLeaveCommandBytes[] = {0x1F, 0xC9, 0x06, 0x00, 0x1F, 0xC9, 0x00, 0x00, 0x00}; // standard leave command 149 | const uint8_t ithoMessageAUTORFTLeaveCommandBytes[] = {0x1F, 0xC9, 0x06, 0x63, 0x1F, 0xC9, 0x00, 0x00, 0x00}; // leave command of AUTO RFT (536-0150) 150 | const uint8_t ithoMessageAUTORFTNJoinCommandBytes[] = {0x1F, 0xC9, 0x12, 0x00, 0x22, 0xF8, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC9, 0x00, 0x00, 0x00}; // join command of Auto RFT-N (04-00161) (bi-directioal) 151 | 152 | // Join/Leave reply commands: 153 | const uint8_t ithoMessageJoinReplyCommandBytes[] = {0x1F, 0xC9, 0x0C, 0x00, 0x31, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x31, 0xDA, 0x00, 0x00, 0x00}; // leave command of AUTO RFT (536-0150) 154 | 155 | // Orcon remote VMN-15LF01 156 | const uint8_t orconMessageAwayCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x00, 0x04}; 157 | const uint8_t orconMessageAutoCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x04, 0x04}; 158 | const uint8_t orconMessageButton1CommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x01, 0x04}; 159 | const uint8_t orconMessageButton2CommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x02, 0x04}; 160 | const uint8_t orconMessageButton3CommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x03, 0x04}; 161 | const uint8_t orconMessageTimer1CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x02, 0x0F, 0x03, 0x04, 0x00, 0x00}; 162 | const uint8_t orconMessageTimer2CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x02, 0x1E, 0x03, 0x04, 0x00, 0x00}; 163 | const uint8_t orconMessageTimer3CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x02, 0x3C, 0x03, 0x04, 0x00, 0x00}; 164 | const uint8_t orconMessageFilterCleanCommandBytes[] = {0x10, 0xD0, 0x02, 0x00, 0xFF}; 165 | const uint8_t orconMessageJoinCommandBytes[] = {0x1F, 0xC9, 0x12, 0x00, 0x22, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x22, 0xF3, 0x00, 0x00, 0x00, 0x67, 0x10, 0xE0, 0x00, 0x00, 0x00}; 166 | const uint8_t orconMessageBatteryStatusCommandBytes[] = {0x10, 0x60, 0x03, 0x00, 0xFF, 0x01}; 167 | 168 | 169 | class IthoFanSensor { 170 | public: 171 | // virtual void update_sunny(uint32_t address, bool value) {} 172 | // virtual void update_windy(uint32_t address, bool value) {} 173 | }; 174 | 175 | class IthoFanComponent : public Component, public remote_base::RemoteReceiverListener { 176 | public: 177 | float get_setup_priority() const override { return setup_priority::LATE; } 178 | void setup() override; 179 | void dump_config() override; 180 | bool on_receive(remote_base::RemoteReceiveData data) override; 181 | void send_command(IthoFanCommand command, uint32_t repeat = 4); 182 | void set_code(uint16_t code); 183 | void set_tx(remote_transmitter::RemoteTransmitterComponent *tx) { this->tx_ = tx; } 184 | void set_rx(remote_receiver::RemoteReceiverComponent *rx) { this->rx_ = rx; } 185 | void set_address(uint32_t address) { this->address_ = address; } 186 | // void add_sensor(IthoFanSensor *sensor) { this->sensors_.push_back(sensor); } 187 | protected: 188 | remote_transmitter::RemoteTransmitterComponent *tx_{nullptr}; 189 | remote_receiver::RemoteReceiverComponent *rx_{nullptr}; 190 | ESPPreferenceObject preferences_; 191 | uint32_t address_; 192 | uint16_t code_; 193 | // std::vector sensors_; 194 | }; 195 | 196 | } // namespace ithofan 197 | } // namespace esphome 198 | -------------------------------------------------------------------------------- /components/ithofan/ithofan.yaml: -------------------------------------------------------------------------------- 1 | external_components: 2 | - source: 3 | type: git 4 | url: https://github.com/compatech/esphome_ct 5 | ref: rc 6 | components: [ ithofan ] 7 | refresh: 0s 8 | - source: github://pr#6300 9 | components: [ cc1101 ] 10 | 11 | spi: 12 | clk_pin: GPIO14 13 | miso_pin: GPIO12 14 | mosi_pin: GPIO13 15 | 16 | cc1101: 17 | id: cc1101_id 18 | cs_pin: GPIO15 19 | bandwidth: 200 20 | frequency: 433420 21 | 22 | remote_receiver: 23 | pin: GPIO04 24 | filter: 200us 25 | idle: 6000us 26 | 27 | #remote_transmitter: 28 | # pin: GPIO13 29 | # carrier_duty_percent: 100% 30 | # on_transmit: 31 | # then: 32 | # - cc1101.begin_tx: cc1101_id 33 | # on_complete: 34 | # then: 35 | # - cc1101.end_tx: cc1101_id 36 | 37 | fan: 38 | - platform: template 39 | name: "Virtual Fan" 40 | speed_count: 3 41 | on_state: 42 | - do something 43 | on_speed_set: 44 | - do something 45 | --------------------------------------------------------------------------------