Thursday, February 19, 2015

Sending pictures with Yowsup

One feature that could be quite useful in home automation is the ability to send pictures took with a webcam to our smartphone.

This could happen on request, by just asking to take a photo with our parser, or automatically when the script is programmed to do that (at specified intervals or maybe when detecting movements).

Sending pictures using Yowsup is not too difficult, but to make it easier for all of you I created the file with class declaration. You can download it here:


With this script you will be able to send pictures. But there's more: you can use it to send also audio messages and videos!

If you wish you can examine that script, but you can use it as is without analyzing what's inside. If you have an updated Raspbian you should have all the dependecies already installed. Otherwise you should install Pillow

To use it you first need to import the script into your project, so at the beginning section of the parser add the following line:

from wamedia import *

To be able to take pictures from the camera module you should also add the following:

import picamera

and remember to enable the camera module using raspi-config.

I will not show you how to parse a command to take a photo as now you should be able to add it by yourself. You can always refer to http://rpihome.blogspot.com/2015/01/using-whatsapp-for-home-automation.html if you don't remember how to proceed.

Let's see a simple function to take a photo and send it to our smartphone:

def TakePhoto(received):
    Answer("Your photo will be sent soon")
    camera=picamera.PiCamera()
    camera.rotation=180
    camera.resolution=(640, 480)
    time.sleep(2)
    camera.capture("lastimage.jpg", format='jpeg', quality=75, thumbnail=None)
    camera.close()
    if os.path.exists("lastimage.jpg"):
        try:
            stack = SendMediaStack(credential(), [(["390000000000", "lastimage.jpg"])])
            stack.start()
        except ImageSent as e:
            if ("ERROR" in e.value): Answer("Could not send the photo, sorry!")
    else:
        Answer("I found no photo to send!")
    return

The first thing I'm doing is sending a message to inform that the photo is arriving, as the process could take some time. You can delete this if you want.

Then I create a camera object and set a couple of options. I rotate the image as my camera is upside down and set the resolution to values lower than the defaults, to keep the file small and speed up the things a bit.
There are several more options you could set. Just refer to the official documentation.

Now it's time to get the picture and save it to a file. You can save the image wherever you wish of course. You can also change the quality and the format of the resulting file. Just remember that Whatsapp accepts just jpeg and png images.

After closing the camera object I check if the picture file exists, just in case there were some problem with its creation. If there is no file I send an error message to the smartphone.

If the file exists we can try to send it. The statement is quite simple and you should just change the target phone number and the image filename to adapt to your system.

Afer the execution of the class an ImageSent exception is raised with the result of the operation. If there was any problem we can find the word "ERROR" inside the error value and we can send an error message.

Of course you can add some features by just checking for key words in the received message. For example you could add an option to take high resolution pictures or maybe one to take photos in low light conditions...

As i wrote, you can also send video and audio files. Just change the filename and the class will recognize what to send by checking the file extension.

These are the valid extension for the class:

  • image: jpg, png
  • audio: mp3, wav, aac, wma, ogg, oga
  • video: mp4

So you could ask your RPi to make a video and send it to you, instead of a photo. Think about this setup:

A Raspberry Pi with a camera module mounted on a servo. You ask for a video and the script records it while rotating the camera. Then RPi will send a panoramic shot to your smartphone.

Another thing you could do (a bit less useful, bit nice!) is sending you vocal messages instead of text ones. You just need a tts engine able to create an audio file and change the Answer function to send the voice message.
In this case it would be useful to have the file in a compressed format to save space and reduce the time needed to send it (so avoid uncompressed wav if you can).

On one of the next posts I will describe how to install the Pico TTS engine from Android. This is a quite good engine, but unfortunately you can't install it in an easy way, so probably a post about its installation could be useful to some of you.

54 comments:

  1. the code works fine, but no idea how to send image

    pls replay
    thanks

    ReplyDelete
    Replies
    1. You just need to have the image saved in the SD and then put the name of the file, with the full path if needed, in the SendMediaStack function. The script above is a bit more complex as I'm using the Raspberry camera to get the image file.
      In this function you also need to put the telephone number of the recipient an your credentials for Whatsapp connection (see the parser in a previous post to see the returned value of the credential()function).
      The class for sending an image is a bit complex, but if you wish you can take a look inside the file wamedia.py...

      Delete
  2. thanks for your replay.
    in class SendMediaStack(object):
    the name of the file, with the full path is put in the place of object?

    pls replay
    thanks

    noufal
    9744227909

    ReplyDelete
    Replies
    1. Just look at the code above: create a new stack using the SendMediaStack with your Whatsapp credentials, the recipient number and the file path of the image, then you just start the stack. The rows are the following:

      stack = SendMediaStack(credential(), [(["390000000000", "lastimage.jpg"])])
      stack.start()

      Delete
    2. stack = SendMediaStack(credential(), [(["390000000000", "lastimage.jpg"])])
      stack.start()

      this code put inside the wamedia.py ?

      Delete
  3. lass SendMediaStack(object):
    def __init__(self, credentials, messages):
    layers=(SendMediaLayer,)+(YOWSUP_PROTOCOL_LAYERS_FULL,)+YOWSUP_CORE_LAYERS
    self.stack=YowStack(layers)
    self.stack.setProp(SendMediaLayer.PROP_MESSAGES,messages)
    self.stack.setProp(YowAuthenticationProtocolLayer.PROP_PASSIVE,True)
    self.stack.setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS,credentials)
    self.stack.setProp(YowNetworkLayer.PROP_ENDPOINT,YowConstants.ENDPOINTS[0])
    self.stack.setProp(YowCoderLayer.PROP_DOMAIN,YowConstants.DOMAIN)
    self.stack.setProp(YowCoderLayer.PROP_RESOURCE,env.CURRENT_ENV.getResource())

    def start(self):
    self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
    try:
    self.stack.loop()
    except AuthError as e:
    print("Authentication Error: %s" % e.message)

    this is your code and i put the code below this
    stack = SendMediaStack(credential(), [(["390000000000", "lastimage.jpg"])])
    stack.start()

    this okey?

    ReplyDelete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. iam getting an error
    stack = SendMediaStack(credential(), [(["390000000000", "lastimage.jpg"])])
    NameError: name 'credential' is not defined

    ReplyDelete
  6. please send a full working script to raspibian@gmail.com


    thanks

    ReplyDelete
    Replies
    1. Example script (you should reading all the posts about Yowsup anyway):


      from wamedia import *

      def credential():
      return "391122334455","whatsapp_password" # Put your credentials here!

      def sendphoto():
      try:
      stack = SendMediaStack(credential(), [(["390000000000", "/home/pi/image.jpg"])])
      stack.start()
      except ImageSent as e:
      if ("ERROR" in e.value): print("Could not send the image!")

      # Main program
      sendphoto()

      Delete
    2. Note: unfortunately there is a problem with spaces. and the indentation was lost in the code above. Here I just replace them with underscores (of course you need to replace them with spaces!):

      from wamedia import *

      def credential():
      ____return "391122334455","whatsapp_password" # Put your credentials here!

      def sendphoto():
      ____try:
      ________stack = SendMediaStack(credential(), [(["390000000000", "/home/pi/image.jpg"])])
      ________stack.start()
      ____except ImageSent as e:
      ________if ("ERROR" in e.value): print("Could not send the image!")

      # Main program
      sendphoto()

      Delete
  7. Replies
    1. Sorry, but I'm not always online, so I cannot always answer immediately...

      Delete
  8. @Carlo Mascellani.
    your code wamedia.py works fine , i put receipt number and image file but no output,

    ReplyDelete
  9. This comment has been removed by a blog administrator.

    ReplyDelete
  10. sir pls replay
    No handlers could be found for logger "ypwsup stscks yowstack"
    Authentication Error: not-authorized

    ReplyDelete
    Replies
    1. Just ignore the "No handlers..." message. This happens just because I'm not using any logging package for the script and it's just a warning.

      The problem is the Authentication Error message. There is something wrong in the credentials you put in the script. Are you sure you put the right phone number/password received when registering to Whatsapp?

      Delete
    2. thanks for ur replay

      image was sucessfully sended.

      Delete
  11. Sir,
    One more doubt, how to send a image to a group. It is possible?

    If ur free then send a replay.

    Thanks

    ReplyDelete
    Replies
    1. This is possible, but I did not investigate how to make it and I do not think I will for now as it's not a feature that I need...

      Delete
  12. Ok,
    In my project it's very use, but no idea how to get the group I'd. In above the number is replaced by group I'd ? It's possible?

    ReplyDelete
  13. Probably replacing the phone number with the group id will work...

    ReplyDelete
    Replies
    1. but how to get the group id?
      pls replay

      thanks

      Delete
    2. Actually I really do not know... Anyway you could try to:

      - After creating the group, try to send a message (from a smartphone included in the group of course) to the raspberry an look at the sender number. It should be the gid, but I cannot be really sure.

      - Ask in the site of the creator of Yowsup (I'm just using this software and the people that coded it will surely know much better how it works)

      - If still found nothing about the group id, you can always just send multiple messages with the same image to every needed recipient

      Delete
  14. ok , thanks for your replay.
    one more help, in your previous post whatsapp for home automation the parser i get this error.
    NameError: name 'received' is not defined

    please replay

    thanks

    ReplyDelete
  15. ok , thanks for your replay.
    one more help, in your previous post whatsapp for home automation the parser i get this error.
    NameError: name 'received' is not defined

    please replay

    thanks

    ReplyDelete
  16. Hi Carlo,

    First of all thanks for these yowsup classes you wrote. I'm completely new to both python and yowsup, so this helped me an aweful lot to get something decent started in a relatively short amount of time.

    In case you are still interested, I figured out how to send pictures (I guess this should work with any kind of media) to a group using your wamedia.py file. Like you said, you need the group id. To get it you need to get the phone number that was registered with yowsup to be invited to the group. Next, start up the yowsup client (yowsup-cli demos --config config --yowsup) and ask someone from the group to send a message. The yowsup-cli will listen for any incoming messages and print it along with the group ID in your terminal (it should look something like PHONENUMBER-TIMESTAMP@g-us.net).

    Next you need to make a small edit to the wamedia.py file to get it to work. Line 106:

    jid = ‘%s@s.whatsapp.net’ % jid

    should be replaced by the following:

    if '@' in jid:
    ____jid = jid
    elif '-' in jid:
    ____jid = '%s@g.us' % jid
    else:
    ____jid = '%s@s.whatsapp.net' % jid


    Now you can just use the code as explained in the example and replace the target number by the group ID.
    I am using yowsup and your classes for exactly that, sending pictures to a group and it has been working fine. I wanted to add a parser as well, so it could listen for incoming group message using your wareceive.py file, but I haven’t been able to get it to work yet. I keep receiving the same message(s) over and over again. Apparently wareceive is not able to send receipts to a group in a correct way. If you have any ideas how to fix it, let me know. Otherwise if you’re interested I can keep you updated in case I can figure it out myself. Either way, thanks again for the classes. Lots of fun :-)

    cheers!
    Francis

    ReplyDelete
    Replies
    1. Thanks for sharing this info! This could surely help some other people searching how to to the same thing.

      Delete
  17. Thanks carlo this thing work well for me. If anyone is using webcam instead of raspberry pi camera, try using fswebcam and change the code above like this:
    def TakePhoto(received):
    ___Answer("Taking foto....")
    ___os.system("fswebcam -r 640x480 -S 20 foto.jpg")
    ___if os.path.exists("foto.jpg"):
    _____try:
    _______stack = SendMediaStack(credential(), [(["3900000000", "foto.jpg"])])
    _______stack.start()
    _____except ImageSent as e:
    _______if ("ERROR" in e.value): Answer("Could not send the photo, sorry!")
    __else:
    _____Answer("I found no photo to send!")
    __return

    ReplyDelete
  18. Helo,
    You have any idea about how to hack a wifi network??

    Thanks

    ReplyDelete
  19. Hi Carlo,
    how I can receive messages on yowsup? that is sent from my smartphone to yowsup, help me please

    ReplyDelete
    Replies
    1. Look at the other posts before this one. You will find how you can send and receive messages using yowsup.
      Anyway note that yowsup seems to be a bit unstable and sometimes messages are lost. No documentation can be found for this library, so it's also difficult to use it.

      Delete
  20. hi,, Carlo Mascellani
    i try your wamedia.py
    it works fine.. thanks for your work.
    I try to modify your code to send the same image to many numbers (listed in .txt file)
    i added following code in the main function
    for line in cfile:
    ____number=line
    ____jid='%s@s.whatsapp.net' % number
    ____successFn=lambda successEntity, originalEntity: self.onRequestUploadResult(jid,path,successEntity,originalEntity)
    ____errorFn=lambda errorEntity,originalEntity: self.onRequestUploadError(jid,path,errorEntity,originalEntity)
    ____self._sendIq(entity,successFn,errorFn)
    cfile.close()

    but it doesn't work.
    kindly reply..
    how to send the same image to multiple number??

    ReplyDelete
    Replies
    1. "successFn= ***** " in same line... it just preview problem..

      Delete
  21. Good Evening,

    I try to run your examples, but in different ways, I got an error:

    "bytearray object has no atributte tag"

    due to, the image.jpg is not send to the whatsapp client.

    it seems to conect to whatsapp server, and disconnect after that. I am doing debug with pdg, but I cant get the location or why that error appers, I got send text or any message, but not Image!!!

    I reinstall twice, but It continues. Do you know something about that error??

    Could someone help me.

    Thanks.

    ReplyDelete
    Replies
    1. Did you installed Pillow? This library is mandatory for managing images with Yowsup (I wrote about this at the beginning of the post).
      If this library is not available on your system there could be a issue like the one you see...

      Delete
    2. Thank you, It WORKS really fine, I installed everything in order. I received an image (on my smartphone, the photo looks dark, when I open I see the picture!!!).

      I put this example on a loop to interact with the yowsup, in order to wait a word to send the photo when I want it, and sent a confirmation message. I am receiving only the photo. because in your example some atributes disconnect the comunication. and stop the whole program.

      After debugging, I found that on the class associate to sendImage, an interruption occurs and I lose the connection. in order to keep ask for a photo. (send the photo and stop)

      Do you know how I fix it?, I meant, Run the example code on a loop in order to wait for a word ("photo"), and after that, in my code execute a capture and send image to whatsapp, but continues on the loop waiting for other photo request.
      I apprecite your help.

      Delete
    3. To exit from the stack loop an exception is raised with a text that tells why we exited.
      MEDIA SENT means the image has been sent correctly, ERROR UPLOAD mean that something goes wrong.
      In the main script (where your loop is) you should catch this exception (look at my example script above, you will see the try...except statements). You do not need to do something inside the except, but the statement must be there, otherwise your script will terminate because of the unhandled exception.

      Delete
  22. Hi there, first of all, congratulations for your library, it's working perfectly, but I need to put the function that sends images inside a python application, inside a loop, but when the command SendMediaStack is executed and the image is sent, it breaks the execution of the program. Any suggestions?

    ReplyDelete
    Replies
    1. Did you installed also the Pillow library? Maybe that's the problem.
      In case they are installed, you should give some more info about the error or it would be impossible to find what's wrong...

      Delete
  23. every thing went well but i had an error ping thread stopped can some one here solve it

    ReplyDelete
  24. Hello. I need help with this error:

    sudo python wamediasend.py
    No handlers could be found for logger "yowsup.stacks.yowstack"
    Traceback (most recent call last):
    File "wamediasend.py", line 14, in
    sendphoto()
    File "wamediasend.py", line 8, in sendphoto
    stack = SendMediaStack(credential(), [(["390000000000", "/home/pi/image.jpg"])])
    File "/home/pi/yowsup/wamedia.py", line 130, in __init__
    self.stack.setProp(YowCoderLayer.PROP_RESOURCE,env.CURRENT_ENV.getResource())
    AttributeError: 'module' object has no attribute 'CURRENT_ENV'

    ReplyDelete
    Replies
    1. Is Pillow installed? If not install it, if yes try updating yowsup.

      Delete
    2. I got the same error. yowsup is updated and pillow too.

      sudo python z_sendimage.py
      No handlers could be found for logger "yowsup.stacks.yowstack"
      Traceback (most recent call last):
      File "z_sendimage.py", line 14, in
      sendphoto()
      File "z_sendimage.py", line 8, in sendphoto
      stack = SendMediaStack(credential(), [(["491604111780", "/home/pi/yowsup/test.jpg"])])
      File "/home/pi/yowsup/z_wamedia.py", line 130, in __init__
      self.stack.setProp(YowCoderLayer.PROP_RESOURCE,env.CURRENT_ENV.getResource())
      AttributeError: 'module' object has no attribute 'CURRENT_ENV'

      Delete
    3. I have the solution :)
      Change
      from yowsup.env import YowsupEnv
      to
      from yowsup import env

      and
      self.stack.setProp(YowCoderLayer.PROP_RESOURCE,env.CURRENT_ENV.getResource())
      to
      self.stack.setProp(YowCoderLayer.PROP_RESOURCE, YowsupEnv.getCurrent().getResource())

      pls update your script :)

      Delete
    4. Probably something changed in Yowsup since I created the scripts. Thanks for you info. Now the script is updated.

      Delete
  25. Don't know if i already posted this comment, but it's not showing me the comment.
    I have a problem with your script. Its not working for me. I hope you can help me.

    Traceback (most recent call last):
    File "/home/pi/Desktop/YowsupBot/run.py", line 31, in
    stack.loop() #this is the program mainloop
    File "/usr/local/lib/python2.7/dist-packages/yowsup/stacks/yowstack.py", line 196, in loop
    asyncore.loop(*args, **kwargs)
    File "/usr/lib/python2.7/asyncore.py", line 216, in loop
    poll_fun(timeout, map)
    File "/usr/lib/python2.7/asyncore.py", line 156, in poll
    read(obj)
    File "/usr/lib/python2.7/asyncore.py", line 87, in read
    obj.handle_error()
    File "/usr/lib/python2.7/asyncore.py", line 83, in read
    obj.handle_read_event()
    File "/usr/lib/python2.7/asyncore.py", line 449, in handle_read_event
    self.handle_read()
    File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/network/layer.py", line 102, in handle_read
    ....TOO LESS SPACE.....
    self.receive(data)
    File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/axolotl/layer_receive.py", line 88, in handleEncMessage
    self.handleWhisperMessage(node)
    File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/axolotl/layer_receive.py", line 144, in handleWhisperMessage
    ...NOT ENOUGH SPACE...
    packages/yowsup/layers/protocol_media/protocolentities/message_media.py", line 102, in __init__
    super(MediaMessageProtocolEntity, self).__init__("media", _id, _from, to, notify, timestamp, participant, offline, retry)
    File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/protocol_messages/protocolentities/message.py", line 13, in __init__
    assert (to or _from), "Must specify either to or _from jids to create the message"
    AssertionError: Must specify either to or _from jids to create the message

    ReplyDelete
    Replies
    1. It seems related to the yowsup version. This happened to other people and the answer in the yowsup is:
      "Its related to the yowsup version you are using. the param order is different. just look at the error and debug it."
      if your yowsup version is the latest, probably they changed something in the syntax. Otherwise you can try to update your yowsup

      Delete
  26. Really useful post. I am trying to send video. But getting exception ERROR REQUEST VIDEO.Please help me.

    ReplyDelete
    Replies
    1. Unfortunately I'm not using yowsup anymore, so cannot help. You can check the following things:
      - is your yowsup updated to latest version?
      - is the video in mp4 format?
      - can you open with python (as a file, no need to actually play it)?

      Also please note that since i wrote the article above something could have been changed, so it could be a good idea to check the developers page for news, guides and help.

      Delete