Skip to content

Commit c875d1d

Browse files
committed
Add support of nodon inwall module - 2 channels (Enocean)
The patch decodes VLD (D2-04) to detect module channels. I think it also should work for single channel module. Beware: during detection of the 2 channels, 2 new devices are added but domoticz only show one box to add device. Both devices can be found in setup/devices page. WriteToHardware was modified to use "Dimming" profile (A5-38-02) only if a switch is a dimmer. Profile F6-02-01 is used for basic switch (like nodon inwall module). I'm not sure it really is necessary to add sTypeNodon as new subtype. Pairing must be done like with a real switch: 1) in domoticz, use "learn light" to detect the module. switch the enocean module on or off to make it detectable. 2) Create a virtual switch in domoticz 3) put the enocean module into learning mode 4) press the virtual switch in domoticz 5) leave module learning mode 6) attach the light detected during step 1 as subdevice of virtual switch Note: during pairing, domoticz complains about unhandled rorg (d4) but it does not disturb learning and I found no explanation inside Enocean Equipement Profile 2.6.5 PDF (what a mess^Wmind-blowing document)
1 parent 7807147 commit c875d1d

File tree

3 files changed

+193
-35
lines changed

3 files changed

+193
-35
lines changed

hardware/EnOceanESP3.cpp

Lines changed: 191 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,21 @@ typedef enum
228228
#define S_RPS_RPC 0x0F
229229
#define S_RPS_RPC_SHIFT 0
230230
/*@}*/
231+
232+
#define F60201_R1_MASK 0xE0
233+
#define F60201_R1_SHIFT 5
234+
#define F60201_EB_MASK 0x10
235+
#define F60201_EB_SHIFT 4
236+
#define F60201_R2_MASK 0x0E
237+
#define F60201_R2_SHIFT 1
238+
#define F60201_SA_MASK 0x01
239+
#define F60201_SA_SHIFT 0
240+
241+
#define F60201_BUTTON_A1 0
242+
#define F60201_BUTTON_A0 1
243+
#define F60201_BUTTON_B1 2
244+
#define F60201_BUTTON_B0 3
245+
231246
/**
232247
* @defgroup status_rpc Status of telegram (for 1BS, 4BS, HRC or 6DT telegrams)
233248
* Bitmasks for the status-field, if ORG = 1BS, 4BS, HRC or 6DT.
@@ -486,6 +501,18 @@ void CEnOceanESP3::Do_Work()
486501

487502
void CEnOceanESP3::Add2SendQueue(const char* pData, const size_t length)
488503
{
504+
#ifdef ENABLE_LOGGING
505+
std::stringstream sstr;
506+
507+
for (int idx=0;idx<length;idx++)
508+
{
509+
sstr << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << (((unsigned int)pData[idx]) & 0xFF);
510+
if (idx!=length-1)
511+
sstr << " ";
512+
}
513+
_log.Log(LOG_STATUS,"EnOcean Send: %s",sstr.str().c_str());
514+
#endif
515+
489516
std::string sBytes;
490517
sBytes.insert(0,pData,length);
491518
boost::lock_guard<boost::mutex> l(m_sendMutex);
@@ -620,24 +647,10 @@ bool CEnOceanESP3::WriteToHardware(const char *pdata, const unsigned char length
620647
unsigned long sID=(tsen->LIGHTING2.id1<<24)|(tsen->LIGHTING2.id2<<16)|(tsen->LIGHTING2.id3<<8)|tsen->LIGHTING2.id4;
621648
if ((sID<m_id_base)||(sID>m_id_base+129))
622649
{
623-
_log.Log(LOG_ERROR,"EnOcean: Can not switch with this DeviceID, use a switch created with our id_base!...");
650+
_log.Log(LOG_ERROR,"(1) EnOcean: Can not switch with this DeviceID, use a switch created with our id_base!...");
624651
return false;
625652
}
626653

627-
unsigned char buf[100];
628-
buf[0]=0xa5;
629-
buf[1]=0x2;
630-
buf[2]=100; //level
631-
buf[3]=1; //speed
632-
buf[4]=0x09; // Dim Off
633-
634-
buf[5]=(sID >> 24) & 0xff;
635-
buf[6]=(sID >> 16) & 0xff;
636-
buf[7]=(sID >> 8) & 0xff;
637-
buf[8]=sID & 0xff;
638-
639-
buf[9]=0x30; // status
640-
641654
unsigned char RockerID=0;
642655
unsigned char Pressed=1;
643656

@@ -690,41 +703,124 @@ bool CEnOceanESP3::WriteToHardware(const char *pdata, const unsigned char length
690703
cmnd=light2_sSetLevel;
691704
}
692705

693-
if (cmnd!=light2_sSetLevel)
706+
//char buff[512];
707+
//sprintf(buff,"cmnd: %d, level: %d, orgcmd: %d",cmnd, iLevel, orgcmd);
708+
//_log.Log(LOG_ERROR,buff);
709+
unsigned char buf[100];
710+
unsigned char optbuf[100];
711+
712+
if(!bIsDimmer)
694713
{
695-
//On/Off
714+
// on/off switch without dimming capability: Profile F6-02-01
715+
// cf. EnOcean Equipment Profiles v2.6.5 page 11 (RPS format) & 14
696716
unsigned char UpDown = 1;
697-
UpDown = ((cmnd != light2_sOff) && (cmnd != light2_sGroupOff));
698717

718+
buf[0] = RORG_RPS;
719+
720+
UpDown = ((orgcmd != light2_sOff) && (orgcmd != light2_sGroupOff));
721+
722+
switch(RockerID)
723+
{
724+
case 0: // Button A
725+
if(UpDown)
726+
buf[1] = F60201_BUTTON_A1 << F60201_R1_SHIFT;
727+
else
728+
buf[1] = F60201_BUTTON_A0 << F60201_R1_SHIFT;
729+
break;
730+
731+
case 1: // Button B
732+
if(UpDown)
733+
buf[1] = F60201_BUTTON_B1 << F60201_R1_SHIFT;
734+
else
735+
buf[1] = F60201_BUTTON_B0 << F60201_R1_SHIFT;
736+
break;
737+
738+
default:
739+
return false; // not supported
740+
}
741+
742+
buf[1] |= F60201_EB_MASK; // button is pressed
743+
744+
buf[2]=(sID >> 24) & 0xff; // Sender ID
745+
buf[3]=(sID >> 16) & 0xff;
746+
buf[4]=(sID >> 8) & 0xff;
747+
buf[5]=sID & 0xff;
699748

700-
buf[1] = (RockerID<<DB3_RPS_NU_RID_SHIFT) | (UpDown<<DB3_RPS_NU_UD_SHIFT) | (Pressed<<DB3_RPS_NU_PR_SHIFT);//0x30;
701-
buf[9] = 0x30;
749+
buf[6] = S_RPS_T21|S_RPS_NU; // press button // b5=T21, b4=NU, b3-b0= RepeaterCount
702750

703-
sendFrameQueue(PACKET_RADIO,buf,10,NULL,0);
751+
//char buff[512];
752+
//sprintf(buff,"%02X %02X %02X %02X %02X %02X %02X",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6]);
753+
//_log.Log(LOG_ERROR,buff);
754+
755+
sendFrameQueue(PACKET_RADIO,buf,7,NULL,0);
704756

705757
//Next command is send a bit later (button release)
706-
buf[1] = 0;
707-
buf[9] = 0x20;
708-
sendFrameQueue(PACKET_RADIO,buf,10,NULL,0);
758+
buf[1] = 0; // no button press
759+
buf[6] = S_RPS_T21; // release button // b5=T21, b4=NU, b3-b0= RepeaterCount
760+
//sprintf(buff,"%02X %02X %02X %02X %02X %02X %02X",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6]);
761+
//_log.Log(LOG_ERROR,buff);
762+
763+
sendFrameQueue(PACKET_RADIO,buf,7,NULL,0);
709764
}
710765
else
711766
{
712-
//Send dim value
767+
// on/off switch with dimming capability: Profile A5-38-02
768+
// cf. EnOcean Equipment Profiles v2.6.5 page 12 (4BS format) & 103
769+
buf[0]=0xa5;
770+
buf[1]=0x2;
771+
buf[2]=100; //level
772+
buf[3]=1; //speed
773+
buf[4]=0x09; // Dim Off
774+
775+
buf[5]=(sID >> 24) & 0xff;
776+
buf[6]=(sID >> 16) & 0xff;
777+
buf[7]=(sID >> 8) & 0xff;
778+
buf[8]=sID & 0xff;
779+
780+
buf[9]=0x30; // status
781+
782+
if (cmnd!=light2_sSetLevel)
783+
{
784+
//On/Off
785+
unsigned char UpDown = 1;
786+
UpDown = ((cmnd != light2_sOff) && (cmnd != light2_sGroupOff));
787+
788+
buf[1] = (RockerID<<DB3_RPS_NU_RID_SHIFT) | (UpDown<<DB3_RPS_NU_UD_SHIFT) | (Pressed<<DB3_RPS_NU_PR_SHIFT);//0x30;
789+
buf[9] = 0x30;
713790

714-
//Dim On DATA_BYTE0 = 0x09
715-
//Dim Off DATA_BYTE0 = 0x08
791+
sendFrameQueue(PACKET_RADIO,buf,10,NULL,0);
716792

717-
buf[1]=2;
718-
buf[2]=iLevel;
719-
buf[3]=1;//very fast dimming
793+
//char buff[512];
794+
//sprintf(buff,"%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9]);
795+
//_log.Log(LOG_ERROR,buff);
720796

721-
if ((iLevel==0)||(orgcmd==light2_sOff))
722-
buf[4]=0x08; //Dim Off
797+
//Next command is send a bit later (button release)
798+
buf[1] = 0;
799+
buf[9] = 0x20;
800+
//sprintf(buff,"%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9]);
801+
//_log.Log(LOG_ERROR,buff);
802+
sendFrameQueue(PACKET_RADIO,buf,10,NULL,0);
803+
}
723804
else
724-
buf[4]=0x09;//Dim On
805+
{
806+
//Send dim value
807+
808+
//Dim On DATA_BYTE0 = 0x09
809+
//Dim Off DATA_BYTE0 = 0x08
810+
811+
buf[1]=2;
812+
buf[2]=iLevel;
813+
buf[3]=1;//very fast dimming
725814

726-
sendFrameQueue(PACKET_RADIO,buf,10,NULL,0);
815+
if ((iLevel==0)||(orgcmd==light2_sOff))
816+
buf[4]=0x08; //Dim Off
817+
else
818+
buf[4]=0x09;//Dim On
819+
820+
sendFrameQueue(PACKET_RADIO,buf,10,NULL,0);
821+
}
727822
}
823+
728824
return true;
729825
}
730826

@@ -739,7 +835,7 @@ void CEnOceanESP3::SendDimmerTeachIn(const char *pdata, const unsigned char leng
739835
unsigned long sID = (tsen->LIGHTING2.id1 << 24) | (tsen->LIGHTING2.id2 << 16) | (tsen->LIGHTING2.id3 << 8) | tsen->LIGHTING2.id4;
740836
if ((sID<m_id_base) || (sID>m_id_base + 129))
741837
{
742-
_log.Log(LOG_ERROR, "EnOcean: Can not switch with this DeviceID, use a switch created with our id_base!...");
838+
_log.Log(LOG_ERROR, "(2) EnOcean: Can not switch with this DeviceID, use a switch created with our id_base!...");
743839
return;
744840
}
745841

@@ -1710,6 +1806,66 @@ void CEnOceanESP3::ParseRadioDatagram()
17101806
}
17111807
}
17121808
break;
1809+
case RORG_VLD:
1810+
{
1811+
unsigned char DATA_BYTE3=m_buffer[1];
1812+
unsigned char func = m_buffer[1];
1813+
unsigned char type = m_buffer[2];
1814+
1815+
if(func == 0x04)
1816+
{
1817+
// D2-04
1818+
switch((type & 0xE0) >> 5)
1819+
{
1820+
case 3:
1821+
// Nodon wall module event notification
1822+
{
1823+
unsigned char channel = type & 0x1F;
1824+
1825+
unsigned char dim_power = m_buffer[3] & 0x7F; // 0=off, 0x64=100%
1826+
1827+
unsigned char ID_BYTE3=m_buffer[4];
1828+
unsigned char ID_BYTE2=m_buffer[5];
1829+
unsigned char ID_BYTE1=m_buffer[6];
1830+
unsigned char ID_BYTE0=m_buffer[7];
1831+
long id = (ID_BYTE3 << 24) + (ID_BYTE2 << 16) + (ID_BYTE1 << 8) + ID_BYTE0;
1832+
1833+
// m_buffer[8] = 00 ?
1834+
// m_buffer[9] = 01 ?
1835+
RBUF tsen;
1836+
memset(&tsen,0,sizeof(RBUF));
1837+
tsen.LIGHTING2.packetlength=sizeof(tsen.LIGHTING2)-1;
1838+
tsen.LIGHTING2.packettype=pTypeLighting2;
1839+
tsen.LIGHTING2.subtype=sTypeNodon;
1840+
tsen.LIGHTING2.seqnbr=0;
1841+
1842+
tsen.LIGHTING2.id1=(BYTE)ID_BYTE3;
1843+
tsen.LIGHTING2.id2=(BYTE)ID_BYTE2;
1844+
tsen.LIGHTING2.id3=(BYTE)ID_BYTE1;
1845+
tsen.LIGHTING2.id4=(BYTE)ID_BYTE0;
1846+
tsen.LIGHTING2.level=dim_power;
1847+
tsen.LIGHTING2.rssi=12;
1848+
1849+
tsen.LIGHTING2.unitcode = channel + 1;
1850+
tsen.LIGHTING2.cmnd = (dim_power>0) ? light2_sOn : light2_sOff;
1851+
1852+
#ifdef ENOCEAN_BUTTON_DEBUG
1853+
_log.Log(LOG_NORM, "EnOcean message: 0x%02X Node 0x%08x UnitID: %02X cmd: %02X ",
1854+
DATA_BYTE3,
1855+
id,
1856+
tsen.LIGHTING2.unitcode,
1857+
tsen.LIGHTING2.cmnd
1858+
);
1859+
#endif //ENOCEAN_BUTTON_DEBUG
1860+
1861+
sDecodeRXMessage(this, (const unsigned char *)&tsen.LIGHTING2, NULL, 255);
1862+
return;
1863+
}
1864+
break;
1865+
}
1866+
}
1867+
}
1868+
17131869
default:
17141870
_log.Log(LOG_NORM, "EnOcean: Unhandled RORG (%02x)", m_buffer[0]);
17151871
break;

main/RFXNames.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ const char *RFX_Type_SubType_Desc(const unsigned char dType, const unsigned char
549549
{ pTypeLighting2, sTypeAC, "AC" },
550550
{ pTypeLighting2, sTypeHEU, "HomeEasy EU" },
551551
{ pTypeLighting2, sTypeANSLUT, "Anslut" },
552+
{ pTypeLighting2, sTypeNodon, "Nodon" },
552553

553554
{ pTypeLighting3, sTypeKoppla, "Ikea Koppla" },
554555

main/RFXtrx.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ SDK version 4.9
436436
#define sTypeHEU 0x1
437437
#define sTypeANSLUT 0x2
438438
#define sTypeKambrook 0x03
439+
#define sTypeNodon 0x4
439440

440441
#define light2_sOff 0x0
441442
#define light2_sOn 0x1

0 commit comments

Comments
 (0)