kipe / enocean

Python library for EnOcean serial protocol
MIT License
71 stars 102 forks source link

Send a switch on command to a Plug Adapter #13

Open sylvaincherrier opened 8 years ago

sylvaincherrier commented 8 years ago

Hi, I use this python library, and it works well. But i have a problem. I have 3 plug adapters (2 ubiwizz DO21-11B-E and 1 another, i can't find its reference).

I pair each device with my enocean controler of my raspberry.

When i send a packet (i build the packet with the enocean ID), all the plug switch on ! I can't find how to select only one...

this is my code : i try to reproduce the on - released message send by a button :

Building my two messages :

    self.pressed_on=[0xf6, 0x10]
    self.pressed_on.extend(self.id)
    self.pressed_on.append(0x30)
   self.released=[0xf6, 0x0]
    self.released.extend(self.id)
    self.released.append(0x20)

Then i set the communication (self.p is a Packet, self.c is a Communicator)

    self.p = Packet(PACKET.COMMON_COMMAND, [0x08])
    self.c = SerialCommunicator()
    self.c.start()
    self.c.send(self.p)

Ok, just send the message

    self.p = Packet(PACKET.RADIO, data=self.pressed_on)
    self.p.build()
    self.c.send(self.p)
    self.p = Packet(PACKET.RADIO, data=self.released)
    self.p.build()
    self.c.send(self.p)

Then, all light switch on ! (but self.id contains only the specific id of the target)

Thanks if you can help.

kipe commented 8 years ago

Please post the full code, with the device IDs.

sylvaincherrier commented 8 years ago

Here you are : I have tried with different id, but all plug switch on each time.

import time
import logging
try:
    from enocean.communicators.serialcommunicator import SerialCommunicator
    from enocean.protocol.packet import Packet
    from enocean.protocol.constants import PACKET, RORG

except ImportError as error:
    logging.error("EnOcean : You don't have module %s installed" % error.name)

p = Packet(PACKET.COMMON_COMMAND, [0x08])
c = SerialCommunicator()
c.start()
c.send(p)

#id=(0x01, 0x92, 0xd9, 0x70)
id=(0x01, 0x09, 0xd9, 0x70)

pressed_off=[0xf6, 0x30]
pressed_off.extend(id)
pressed_off.append(0x30)
released=[0xf6, 0x0]
released.extend(id)
released.append(0x20)
pressed_on=pressed_off[:]
pressed_on[1] = 0x10

logging.info("Packets build : ON %s, OFF %s and RELEASED %s" % (pressed_on, pressed_off, released))

p = Packet(PACKET.RADIO, data=pressed_on)
p.build()
c.send(p)
p = Packet(PACKET.RADIO, data=released)
p.build()
c.send(p)

time.sleep(1)

p = Packet(PACKET.RADIO, data=pressed_off)
p.build()
c.send(p)
p = Packet(PACKET.RADIO, data=released)
p.build()
c.send(p)
kipe commented 8 years ago

You're not setting optional data, which should include the destination ID. Therefore the packet is handled as a broadcast message -> every switch in range reacts to it. I suggest you look at the EnOcean Serial Protocol 3 -manual, page 14.

Please let me know, if this fixes your issue :)

kipe commented 8 years ago

I've now created the method discussed in #14. I've made also the corresponding changes to the example, so the Packet creation should be fairly easy now.

However, one issue remains: Selecting the correct sender ID. (At least) in the perfect world this should be the module sending the message for real (so, an USB-adapter or the Raspberry Pi -module). For this, I used the existing example structure to request the ID of module before sending any packages.

It's untested at the moment, and as the example was created so long time ago, I have no recollection if it actually ever worked :stuck_out_tongue: It'd be great, if you could also test that when testing the latest changes; if it works, a similar functionality should be created to the initialization of Communicator. It would then use the sender ID gotten from the module when sending anything, in essence preventing of fake IDs being used.

kipe commented 8 years ago

Moving discussion back to this issue from #14.

Hmm, actually according to this document, if correctly translated by Google, you should be able to use other profiles as well. There's a description of the learn process. In essence, the profile F6-02-02 might work, with something like this:

p = Packet.create(
    type=PACKET.RADIO,
    rorg=0xF6, func=0x02, type=0x02,
    destination=[0x01, 0x03, 0x02, 0x13],
    R1='Button AI',
    EBO='pressed',
    T21=True,
    NU=True,
)
c.send(p)

Not entirely sure of the T21 and NU -values though. They're also only supported since the last batch of commits. I'd personally try without the T21 and NU -values, and add them if required.

sylvaincherrier commented 8 years ago

Thanks a lot, @kipe

I will test this on monday !

sylvaincherrier commented 8 years ago

[SOLVED] Hi, i have a weird issue now... Even with my old code, i got a Cannot find type in EEP! (while using parse_eep method in packet.py) Is there a regression in enocean stable package ? thx


Ok i found out. I used parse_eep with 2 param (0x02, 0x04). It used to work, and i thought it was the correct values for my Switch (an old model, i can't even read its EEP from the note over it)

But, there is no func=0x02, type=0x04 in EEP.xml. so i have changed my call to (0x02,0x02), even if it is a single switch, in order to have it recognized.

I don't know why it used to work before ? Nevertheless, it works now... The only thing is that i can't find what EEP these devices used ??

Sorry for this useless issue.

sylvaincherrier commented 8 years ago

The code you give works !

p = Packet.create( paket_type=PACKET.RADIO, rorg=0xF6, func=0x02, type=0x02, destination=[0x01, 0x03, 0x02, 0x13], R1='Button AI', EBO='pressed', T21=True, NU=True, ) c.send(p)

I need T21 and NU to switch on the plug. Changing R1 value to 'Button AO' switch off the plug.

Thanks @kipe Just to be sure : Do your think that i need to pair the plug and the Raspberry EnOcean Card in order to use this code ?

kipe commented 8 years ago

Well, if it works, don't fix it ;) But I'm not actually sure, it might just be enough to use the correct destination address. As per your earlier question, F6-02-04 was previously incorrect, the profile implemented was actually F6-02-02. Therefore the profile "disappeared".

Furthermore, you can make it even a bit simpler by using

p = RadioPacket.create(
    rorg=0xF6, func=0x02, type=0x02,
    destination=[0x01, 0x03, 0x02, 0x13],
    R1='Button AI',
    EBO='pressed',
    T21=True,
    NU=True,
)
c.send(p)

What this does, is create a RadioPacket directly, without need to specify it.

I'm not sure if we should allow integer values to R1, R2, EBO etc also, could aid in responding to specific packages etc. @romor comment?

romor commented 8 years ago

I think it would be a valuable improvement if enums could also be set by value and not only by the description tag. I.e. for the boolean enums like in EEP A5-20-01 it would be nicer to specifiy ES=True instead of ES='true' like given in enocean_example.py:21.

kipe commented 8 years ago

The issue is that the enum values can basically be anything in the XML. I'd rather make the input system take in the numeric value instead, so ES=0 / ES=1. That way it applies to all enums, no matter what the data is.

romor commented 8 years ago

Yes, you are right. And this is still an improvement from my perspective.

sylvaincherrier commented 8 years ago

Hi. I don't know why, i have a regression. It was working (i could select at least only one plug, but now, it is quite messy :-( ) One plug reacts even when i send a message to another destination, and the others plug don't receive any message (even if i put them in the pair mode, and send message to FF FF FF FF)

Maybe i do something wrong : I try to find out the id of an object by using the Communicator. And then, i try to use this id to communicate with this object. But i doesn't work (i think i use the latest version of the lib : 0.32 , given by github) (by the way, deleting packet_type line gives an error, it seems to be required)

Here is my test code

import time
import logging
try:
    from enocean.communicators.serialcommunicator import SerialCommunicator
    from enocean.protocol.packet import Packet
    from enocean.protocol.constants import PACKET, RORG

except ImportError as error:
    logging.error("EnOcean : You don't have module %s installed" % error.name)

p = Packet(PACKET.COMMON_COMMAND, [0x08])
c = SerialCommunicator()
c.start()
c.send(p)

id1=[0x01, 0x89, 0xd9, 0x78]
id2=[0x01, 0x84, 0x40, 0x48]
id3=[0x01, 0x92, 0xA7, 0x01]

#these id were given by listening to the network :
# c.receive.get(block=True, timeout=1) 

# and i get when i try to pair the device
# 01:92:A7:01->FF:FF:FF:FF (-41 dBm): 0x01 ['0xd2', '0x4', '0x0', '0x0', '0x1', '0x92', '0xa7', '0x1', '0x0'] ['0x1', '0xff', '0xff', '0xff', '0xff', '0x29', '0x0'] {}
# I beleive the id of my object is 01:92:a7:01

p = Packet.create(
    packet_type=PACKET.RADIO,
    rorg=0xF6, func=0x02, type=0x02,
    destination=id1,
    R1='Button AI',
    EBO='pressed',
    T21=True,
    NU=True
)
c.send(p)

print('id 01')

time.sleep(1)

p = Packet.create(
    packet_type=PACKET.RADIO,
    rorg=0xF6, func=0x02, type=0x02,
    destination=id2,
    R1='Button AI',
    EBO='pressed',
    T21=True,
    NU=True
)
c.send(p)

print('id 02')

time.sleep(1)

p = Packet.create(
    packet_type=PACKET.RADIO,
    rorg=0xF6, func=0x02, type=0x02,
    destination=id3,
    R1='Button AI',
    EBO='pressed',
    T21=True,
    NU=True
)
c.send(p)

print('id 03')

p = Packet.create(
    packet_type=PACKET.RADIO,
    rorg=0xF6, func=0x02, type=0x02,
    destination=id1,
    R1='Button AO',
    EBO='pressed',
    T21=True,
    NU=True
)
c.send(p)

print('id 01')

time.sleep(1)

p = Packet.create(
    packet_type=PACKET.RADIO,
    rorg=0xF6, func=0x02, type=0x02,
    destination=id2,
    R1='Button AO',
    EBO='pressed',
    T21=True,
    NU=True
)
c.send(p)

print('id 02')

time.sleep(1)

p = Packet.create(
    packet_type=PACKET.RADIO,
    rorg=0xF6, func=0x02, type=0x02,
    destination=id3,
    R1='Button AO',
    EBO='pressed',
    T21=True,
    NU=True
)
c.send(p)

print('id 03')

I probably me, but i am a little lost.. Thanks if you can help me...

kipe commented 8 years ago

As said before, this might require the implementation of RORG.ADT. But to be honest, I have if it's actually required, and don't have any devices to test on :(

sylvaincherrier commented 8 years ago

Hi @kipe I can send your some devices.

kipe commented 8 years ago

Damn, I may have found the issue! I'll try and fix the issue, once I get the devices to test on, thanks to @sylvaincherrier :smile:

Ping also to #14.

//Edit: And to be honest, this is a stupid mistake :blush:

sylvaincherrier commented 8 years ago

Great ! Thanks @kipe :smiley:

kipe commented 8 years ago

Well, it wasn't the issue I was thinking of... I still think that RORG.ADT my require implementing. At least I'll try and create automated learn-in -methods at least the variant used by the switches @sylvaincherrier sent me.

In my mind (do remember, I haven't tested sending to any device yet) it should work like this:

@romor comments, as you seem to be the most knowledgeable regarding the teach-ins? It will need additions to the EEP.xml, but shouldn't mess up anything.

romor commented 8 years ago

I think this extension is a good idea and makes sense as you describe.

Up to now I am just aware of the teach-in for 4BS packet types. Please refer to EnOcean_Equipment_Profiles_EEP_V2.6_public.pdf Section 3.3:

There are 3 variations of the teach-in, specified by DB0.7 and DB0.3. For the third variant 5 bits are in use for teach-in. Also you need to provide a "Manufacturer ID" for the teach-in response.

As I understand, these teach-in bits are equal for all 4BS profiles and currently not contained in EEP.xml. I wonder whether it should be added there or not, because of the common meaning for all profiles?

kipe commented 8 years ago

Thanks, I'll look into this more, once I manage to set everything up (don't even have a transceiver available for the development atm, need to take one from my daily usage :P)

kipe commented 8 years ago

I received the plugs sent by @sylvaincherrier today, thank you very much :smile: I'll try and get my dev-system up and running tomorrow. After that my plan is first to get the switches themselves working (preferably with RORG.ADT). Once that is achieved, I'll try and create automatic responses to the teach-in -procedure(s). This way we could be (fairly) sure that we respond to the teach-in messages in a timely fashion. This will be controlled by Communicator.learn.

sylvaincherrier commented 8 years ago

@kipe You're welcome :-) I have some fun with this library, so, i will enjoy if you find out how to drive each actuator one by one ;-)

kipe commented 8 years ago

Ok, some (minor) progress already. The device is meant to be taught by using Universal Uni- and Bidirectional Teach-in RORG.UTE = 0xD4, which is currently not implemented. It's documented in the EEP page 223. This mode can be turned on by holding the learn button for 5 seconds. Once turned on, the device will listen for responses for 30 seconds.

So now it's just the matter of writing a parser for RORG.UTE and implementing an automatic response. I think I'll try and create the automatic parser tonight, the response I'll do when I have time...

kipe commented 8 years ago

To be honest, I think this might be the same case as @romor and Micropelt described in https://github.com/kipe/enocean/issues/15#issuecomment-184881716.

kipe commented 8 years ago

I've made some minor progress with this and the latest commits in send_vld -branch. I'm fairly sure that I should get the automatic learning of the device and sending commands working (hopefully) during the weekend. Exam week now, so it's hindering the progress a bit :P

kipe commented 8 years ago

Oh, forgot to mention that in my opinion this depends on issue #26, so I'm likely to implement that when creating trying to tackle this once and for all...

kipe commented 8 years ago

SUCCESS \o/ I can now automatically respond to the UTE Teach-in and once taught in, I can control the switches. These switches seem to respond to the Destination ID, even without using ADT, so they seem to function quite nicely. Shouldn't have any reason to change Sender ID etc.

Commits coming to send_vld in a moment, needs a bit of clean-up first...

kipe commented 8 years ago

@sylvaincherrier Can you test the example_DO21-11B-E.py to double confirm, once you've tested, this can be closed...

sylvaincherrier commented 8 years ago

Hi Kipe ! It works great. I get the message : "New device learned" followed by the ID Did it twice (i mean, with two devices). It works perfectly well.

Thx !

kipe commented 8 years ago

Can you also control the devices as the example functions describe?

sylvaincherrier commented 8 years ago

Ok... i make the change in example_D021-118-E.py to check if i can switch on an off... I 'll tell you

sylvaincherrier commented 8 years ago

Well... Only the last appaired device seems to react... I got 3 devices, one was working. I have added the two new devices. Now, only the last once obey to my turn_on command.

kipe commented 8 years ago

As I observed, a second teach-in procedure seems to clear the previous teach-in. So in essence, you should only learn each device once.

sylvaincherrier commented 8 years ago

Ok... I test this way.

sylvaincherrier commented 8 years ago

Have you succeed in having two devices connected ? It's weird, it seems i can have two devices at the same time. Only one can be paired... Sometimes, i can pair a device with my raspberry or my usb stick, but then, i can't add another one, even if i stop the program, and start again for the second device.

kipe commented 8 years ago

Yes, I have. I've successfully managed both, pairing one device at a time and pairing both devices on the same run.

My procedure (when both devices are cleared):

  1. Start the program.
  2. Wait for the Press and hold the teach-in button on the plug now, till it starts turning itself off and on (about 10 seconds or so...) -text.
  3. Hold learn-button on the first device as instructed. The UTE teach-in response is sent.
  4. Wait for the on-off -toggling to stop on the first device.
  5. Hold learn-button on the second device. Once again, the UTE teach-in response is sent.
  6. Wait for the on-off -toggling to stop on the second device.
  7. Stop the program, set the IDs to the example turn_on and turn_off -functions (lines 44 - 52). Be careful not to remove the calls to the sleep -function, as they seem to be important...
  8. Re-run the program, both switches toggle as they should. No pressing learn-buttons etc anymore, as they are paired already.
sylvaincherrier commented 8 years ago

I have written a small test program, in order to test my 3 devices. I think that maybe on of my g-media D021 is out of order... i can't make it pair with my usbStick, or my TCM310 shield for raspberry... The second one is paired, sometimes (following you procedure....) By the first one is never paired, even after a clear, and when i get the message : New device learned! The ID is 01:89:D9:78. Let's say that my first device is broken.

I can't make my last device be paired ? it is a ubiwizz ubid1502. I can't clear its config (the doc is really not clear, they say i must hold the button until the red ligth appears), but then nothing happens. At one moment, i have received the message : "new device learned.." but then it still doesn"t work... But few days ago, it was the only device that was apaired... ??!? Is there a way to clear the USB Stick (if it ever save the ID of paired device ?) You have the same ubiwizz switch, i think, no ? do you know how to clear its memory ? maybe the problem is inside the device ?)

http://my-domotique.com/store/enocean/901-prise-commandee-enocean.html

sylvaincherrier commented 8 years ago

For information, my test program :

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
Example to show automatic UTE Teach-in responses using
http://www.g-media.fr/prise-gigogne-enocean.html

Waits for UTE Teach-ins, sends the response automatically and prints the ID of new device.
'''

import sys
import time
import traceback
import enocean.utils
from enocean.communicators import SerialCommunicator
from enocean.protocol.packet import RadioPacket, UTETeachIn
from enocean.protocol.constants import RORG

try:
    import queue
except ImportError:
    import Queue as queue

def send_command(destination, output_value):
    global communicator
    communicator.send(
        RadioPacket.create(rorg=RORG.VLD, rorg_func=0x01, rorg_type=0x01, destination=destination, sender=communicator.base_id, command=1, IO=0x1E, OV=output_value)
    )

def turn_on(destination):
    send_command(destination, 100)

def turn_off(destination):
    send_command(destination, 0)

communicator = SerialCommunicator(port="/dev/ttyUSB0")
communicator.start()
print('The Base ID of your module is %s.' % enocean.utils.to_hex_string(communicator.base_id))

# Example of turning switches on and off
#turn_on([0x01, 0x94, 0xB9, 0x46])
turn_on([0x01, 0x89, 0xD9, 0x78])
print("on 0");
# Needs a bit of sleep in between, working too fast :S
time.sleep(1)
turn_on([0x01, 0x92, 0xA7, 0x01])
print("on 1!!")
time.sleep(1)
turn_on([0x01, 0x84, 0x40, 0x48])
print("on 2!!")
time.sleep(3)
turn_off([0x01, 0x89, 0xD9, 0x78])
print("off 0")
time.sleep(1)
turn_off([0x01, 0x92, 0xA7, 0x01])
print("off 1!!")
time.sleep(1)
turn_off([0x01, 0x84, 0x40, 0x48])
print("off 2!!")

if communicator.is_alive():
    communicator.stop()
sylvaincherrier commented 8 years ago

..... I'm wondering if teaching is the same way with a G-Media SD01 and a Ubiwizz 1502... I can't find if it is the same EEP ?

sylvaincherrier commented 8 years ago

Okay, i got it : the ubizwii 1502 has eep D2-02-0B

I think i must update this line : RadioPacket.create(rorg=RORG.VLD, rorg_func=0x01, rorg_type=0x01, destination=destination, sender=communicator.base_id, command=1, IO=0x1E, OV=output_value)

sylvaincherrier commented 8 years ago

I think 02 function is missing in EEP.xml

sylvaincherrier commented 8 years ago

I made a test : i added in the EEP.xml files lines for D2-02-0B (by copying from 01 01 :

<profiles func="0x02".....>
    <profile type="0x0b"..... 

) Then, i did a python3 setup.py install I have also upgrade the RadioPacket line : RadioPacket.create(rorg=RORG.VLD, rorg_func=0x02, rorg_type=0x0B, destination=destination, sender=communicator.base_id, command=1, IO=0x1E, OV=output_value)

Then i restart the test : my first plug (D2-01-01) still answer the message (even if the send_command is now for D2-02-0B) and my ubiwizz still listen and refuse to obey the learn message (i see the New device learned message, but i now it is not okay, because my ubiwzz led should turn green, and it does not)

kipe commented 8 years ago

Hmmmm, D2-02 -series should be sensors, so that cannot be correct. I'd assume that the correct EEP is D1-01-0B, which is in the category "Electronic switches and dimmers with Energy Measurement and Local Control".

According to the table in documentation, it should allow switching, local control, energy and power measurements. The exact EEP (RORG, FUNC and TYPE) are sent in the teach-in message, but stupid of me, not printed... So UTETeachIn.eep_rorg, UTETeachIn.eep_func, UTETeachIn.eep_type should reveil the exact information.

Hopefully this all goes away, once I get the storage implemented, so the stuff is actually learned and stored somewhere.

In the meanwhile, can you post messages this switch type sends on a regular basis (I think it should send the energy measurement information), so I can also write parsing for that...

sylvaincherrier commented 8 years ago

It's weird, because it seems that they use this EEP http://www.imagidee-cms.com/medias150/PDF/UBID1502.pdf or http://my-domotique.com/store/enocean/901-prise-commandee-enocean.html (end of the page) I didn't bring this plug at home, so i won't be able to make some tests... But i will verify on Tuesday the UTETeachIn values. Thanks again for your great work :-)

sylvaincherrier commented 8 years ago

Hi,

Just made a test this morning I have printed packet.rorg_of_eep, packet.rorg_func, and packet.rorg_type.

this answer for my devices D021 is : D2-1-1 For the ubiwiiz, i get : D2 - 1- A

I have also scanned the exchanges for this device (01:89:d9:78)

Replacing Packet.optional with default value. INFO:enocean.protocol.packet:Sending response to UTE teach-in. DEBUG:enocean.communicators.SerialCommunicator:01:89:D9:78->FF:FF:FF:FF (-59 dBm): 0x01 ['0xd4', '0xa0', '0x1', '0x46', '0x0', '0xa', '0x1', '0xd2', '0x1', '0x89', '0xd9', '0x78', '0x0'] ['0x1', '0xff', '0xff', '0xff', '0xff', '0x3b', '0x0'] OrderedDict() INFO:enocean.communicators.SerialCommunicator:Sending packet DEBUG:enocean.communicators.SerialCommunicator:FF:F6:3C:00->01:89:D9:78 (-255 dBm): 0x01 ['0xd4', '0x91', '0x1', '0x46', '0x0', '0xa', '0x1', '0xd2', '0xff', '0xf6', '0x3c', '0x0', '0x0'] ['0x3', '0x1', '0x89', '0xd9', '0x78', '0xff', '0x0'] OrderedDict() DEBUG:enocean.communicators.SerialCommunicator:0x02 ['0x0'] [] OrderedDict()

sylvaincherrier commented 8 years ago

I don't understand the values i get : D2-1-A : I thought it was D2-02-0B as written in the user manual... But you were right, these are not the values i get... but why D2-1-A ? (your last message says it could be D1-01-0B, but it doesn't match too)

snmathur4 commented 7 years ago

I can't seem to fake the SenderID. I am doing some testing and want to use a different SenderID. But not matter what I do `# TODO: Should use the correct Base ID as default.

Might want to change the sender to be an offset from the actual address?

    if sender is None:
        Packet.logger.warning('Replacing sender with default address.')
        sender = [0xDE, 0xAD, 0xBE, 0xEF]`

Tried to change this but it persists to same sender address

it shows: 01:93:7E:2F->FF:FF:FF:FF (-255 dBm): 0x01 ['0xa5', '0x4b', '0x0', '0x5a', '0x8', '0x1', '0x93', '0x7e', '0x2f', '0x0'] ['0x3', '0xff', '0xff', '0xff', '0xff', '0xff', '0x0'] OrderedDict()

but I get 55000a0601fea5aa00dc0801a2f39c8001ffffffff2958

zpfvo commented 7 years ago

@snmathur4 you can only set a 'fake' senderID in the range of BASE_ID to BASE_ID + 128 see here: https://www.enocean.com/en/knowledge-base-doku/enoceansystemspecification%3Aissue%3Awhat_is_a_base_id/

snmathur4 commented 7 years ago

Hi, I am trying to send a RPS (F6) packet out USB300 EnOcean USB. If I send out [0x15] user data, I instead get [0x10]. Seems the 2nd part of byte is always changed to 0. Could this be a bug in USB transmitter?