Closed mvdwetering closed 3 years ago
I would have the same request, but this doesn't look promising. Even though the home-assistant docs say "ESC/VP21", looking at the code there's no ESC/VP21 anywhere here. This library seems to implement some new-fangled JSON over HTTP stuff that newer projectors apparently speak natively, triggered from a local web-UI. The JSON stuff needs to be polled, it can't even push projector status updates (as the serial connection will do).
I have ESC/VP21 code that I once wrote for FHEM in perl (https://github.com/henryk/fhem-escvp21), and am migrating from FHEM to home-assistant, so I might find time to extend here. Until then: no luck.
I will look into that next week. I don't think it is much problem to make a "switch" to choose between http with json, tcp or serial connection. I have USB only in projector, but as I know USB is on COM driver, so shouldn't be much differenct.
@henryk do you have some documentation for ESC/VP21.net ? I've tried to send commands over 3629 port several times before, but it wasn't working for me at all.
So, apparently Epson has taken down the epson322270eu.pdf file which I originally used. https://files.support.epson.com/pdf/pl600p/pl600pcm.pdf is still online though and has everything and more.
Connectivity: My projector only has RS-232. The way I understand it, the USB connection also is just a serial port (over USB) and the raw TCP/IP connection is the same protocol. (In my setup I have https://linux.die.net/man/8/ser2net running in order to expose the RS-232 as a TCP/IP port.)
@henryk as a minor clarification, the protocol implemented in this library is ESC/VP21. The protocol just consist of the control / query commands and is independent of the network communication protocol (which can be JSON over HTTP, serial and so on).
FYI the .PDF files seem to have been replaced by .XLSX files. At least for my projector the files are listed in the "Manuals" section of the product page on the Epson website. Note that this file contains commands for a lot of devices.
Filename: ESC/VP21 Command User’s Guide (XLSX) (D) https://www.epson.nl/products/projectors/home-cinema/epson-eh-tw3200#manuals
So, little update for you. For about two weeks I don't direct access to my Epson :-1: , so hard to test anything... Anyway, checkout testing branch https://github.com/pszafer/epson_projector/tree/testing TCP connection to Epson should work. About serial, this is very initial version. I'll try to make some virtual device so I can imitate Epson. I'll try to keep you all up to date.
Can somebody who have serial access to projector checkout testing branch? Maybe @mvdwetering ?, just run python3 test_serial.py it should power up your projector.
My projector won't respond over usb connection, it has only MiniUSB port, so my guess that it is not intended to be use for serial communication or I need some driver...
Sorry for the delay, I just had time today to put the cables back in place and test.
I first got an error NameError: name 'TIMEOUT' is not defined
which I got around by replacing it with 5 which I think is 5 seconds.
After this I got an error AttributeError: 'StreamWriter' object has no attribute 'is_closing'
which I avoided by removing that part of the check in the send_request() function, not sure how bad that is.
Then I started to run into:
Timeout error
Traceback (most recent call last):
File "test_serial.py", line 25, in <module>
loop.run_until_complete(main_serial(loop))
File "/usr/lib/python3.5/asyncio/base_events.py", line 466, in run_until_complete
return future.result()
File "/usr/lib/python3.5/asyncio/futures.py", line 293, in result
raise self._exception
File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
result = coro.send(None)
File "test_serial.py", line 11, in main_serial
await run(loop)
File "test_serial.py", line 18, in run
data = await projector.get_property(POWER)
File "/home/pi/epson_projector/epson_projector/main.py", line 56, in get_property
self.__get_timeout(command))
File "/home/pi/epson_projector/epson_projector/projector_serial.py", line 70, in get_property
command=command+'?\r'
File "/home/pi/epson_projector/epson_projector/projector_serial.py", line 97, in send_request
if response[0].decode() != ":":
AttributeError: 'int' object has no attribute 'decode'
I think this one is because of decoding only the first byte/character in the response instead of the whole reponse, but I am not sure. The encoding/decoding part of Python always confuses me and with some local changes I could not get it working.
However I added some prints to command and response and noticed that the command seems fine, but the response does not seem to be complete. It only prints "PW" instead of "PWR=00\r:"
When trying the "PWR?" command manually with the miniterm that comes with pyserial I do get the complete response.
python3 -m serial.tools.miniterm /dev/ttyUSB1 9600 --eol CR --echo
--- Miniterm on /dev/ttyUSB1 9600,8,N,1 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
PWR?
PWR=00
:
@mvdwetering thank you for testing. I repaired this TIMEOUT problem. About is_closing, can you test this module on Python 3.7, as is_closing is new in version 3.7, check it out here: https://docs.python.org/3/library/asyncio-stream.html (StreamWriter part).
About last error AttributeError: 'int' object has no attribute 'decode'
, let's check it out first with Python 3.7 and then we will see.
I installed Python 3.7 on the Pi (which surprisingly requires manual compiling of Python)
Now I get:
pi@automation-pi3:~/epson_projector $ python3.7 test_serial.py
Timeout error
Traceback (most recent call last):
File "test_serial.py", line 25, in <module>
loop.run_until_complete(main_serial(loop))
File "/usr/local/lib/python3.7/asyncio/base_events.py", line 568, in run_until_complete
return future.result()
File "test_serial.py", line 11, in main_serial
await run(loop)
File "test_serial.py", line 18, in run
data = await projector.get_property(POWER)
File "/home/pi/epson_projector/epson_projector/main.py", line 56, in get_property
self.__get_timeout(command))
File "/home/pi/epson_projector/epson_projector/projector_serial.py", line 79, in get_property
return response.split('=')[1]
IndexError: list index out of range
We've got timeout error. I extended timeout to 10 seconds for tests if it get's connected.
Try to run it like it, to get more verbose logging:
python3.7 test_serial.py --log=DEBUG
BTW any idea if I can connect to EH-TW5350 by serial USB? Do you connect to port named 'Service'?
My projector does not have USB, only a serial port (marked RS-232-C on the device). Judging from the connections on EH-TW5350 the Service USB port is the only one that would make sense (the other USB is the wrong type for connecting to a computer).
Output from the latest version:
pi@automation-pi3:~/epson_projector $ python3.7 test_serial.py --log=DEBUG
Timeout error during connection
Bad response P
False
W
Ok, I made test code from example pySerial-asyncio. Test it out and let me know: https://gist.github.com/pszafer/af7ce3c48890fdbd786f291330651f97
This is the output
pi@automation-pi3:~ $ python3.7 epson_test.py --log=DEBUG
port opened SerialTransport(<_UnixSelectorEventLoop running=True closed=False debug=False>, <__main__.Output object at 0x76586330>, Serial<id=0x76586350, open=True>(port='/dev/ttyUSB1', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=0, xonxoff=False, rtscts=False, dsrdtr=False))
data received b'P'
port closed
I have no idea what's wrong... I'm testing it with printer without problems... Try this update https://gist.github.com/pszafer/af7ce3c48890fdbd786f291330651f97
Can you give me ssh access to you rpi somehow? Contact me by mail if it is possible pszafer@gmail.com . Next week I could look into that.
@mvdwetering Run this command on rpi before executing any scripts:
stty -F /dev/ttyUSB1 raw
or if with first still no luck, try:
stty -F /dev/ttyUSB1 9600 line 0 min 1 time 0
After some more experimentation it turns out that the serial stuff seems to work OK, but that the commands that are sent need a '\r' instead of a '\n' to close them (so transport.write(b'PWR?\r')
).
When I make this change in the v2 test I get this output:
pi@automation-pi3:~ $ python3.7 epson_test_v2.py
port opened SerialTransport(<_UnixSelectorEventLoop running=True closed=False debug=False>, <__main__.Output object at 0x76583310>, Serial<id=0x7693bcd0, open=True>(port='/dev/ttyUSB1', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=0, xonxoff=False, rtscts=False, dsrdtr=False))
data received b'P'
data received b'WR'
data received b'='
data received b'0'
data received b'0\r'
data received b':'
which indicates the projector is at standby.
So in other words file epson_test_v1.py is working for you right now?
Ah, I now see. I did not test that one before since I thought that was the same as you had in the first test request, but it was changed and renamed. I only tested the _v2 the second time.
Anyway epson_test_v1.py is working.
pi@automation-pi3:~ $ python3.7 epson_test_v1.py
port opened SerialTransport(<_UnixSelectorEventLoop running=True closed=False debug=False>, <__main__.Output object at 0x7657a610>, Serial<id=0x7657a310, open=True>(port='/dev/ttyUSB1', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=0, xonxoff=False, rtscts=False, dsrdtr=False))
data received b'PW'
data received b'R'
data received b'='
data received b'00'
data received b'\r'
port closed
Can you try test_serial.py with setting before:
stty -F /dev/ttyUSB1 raw
For now I don't know reason why my module is not working...
I did some more debugging and got it mostly working. I attached a patchfile with the exact changes, but these are the main points
.read(16)
does not work, but I tried with .readuntil(b'\r:')
and that works. Hmm, looking at it again I could have left your ,decode()
line in which should also work just fine.test_serial.py
file. I have replaced the turning on of the projector with the actual string "PWR ON" the original code would only send "PWR"So in the end with the attached patch the projector status is printed and the projector powers on.
pi@automation-pi3:~/epson_projector $ python3.7 test_serial.py
2018-09-24 20:48:48,153 - asyncio - DEBUG - Using selector: EpollSelector
2018-09-24 20:48:48,185 - epson_projector.main - DEBUG - Getting property PWR
2018-09-24 20:48:48,189 - epson_projector.projector_serial - INFO - Just say open, the projector does not return anything on connecting.
2018-09-24 20:48:48,220 - epson_projector.projector_serial - INFO - Response from Epson PWR=00
00
2018-09-24 20:48:48,221 - epson_projector.main - DEBUG - Sending command to projector PWR ON
2018-09-24 20:48:58,230 - epson_projector.projector_serial - ERROR - Timeout error during sending request
False
BTW the PWR ON results in a timeout (but the projector was turned on) when it sends the request, but according to the documentation the "PWR ON" command can take up to 40, 70 or 100 seconds. So it seems that poweron command needs a custom timeout.
@mvdwetering , great work! thanks. Update testing branch and check it out.
TIMEOUT_TIMES = {
'PWR ON': 40,
'PWR OFF': 10,
'SOURCE': 5,
'ALL': 3
}
The test_serial.py now mostly works. For some reason I still get a timeout, however the projector did power on. I did a separate timing of manually sending the PWR ON
command and that took about 35 seconds, so not sure why the timeout kicked in when I ran it from test_serial (is it possible to get timing on the actual serial commands sent for debugging?). I will do a few more timings when I have to turn on the projector.
pi@automation-pi3:~/epson_projector $ python3.7 test_serial.py --log=DEBUG
00
Timeout error during sending request
False
Note that just sending :\r
(or \r
) to test the connection will return ERR\r:
, not just \r:
, not sure if that can cause issues.
I did try PWR OFF
(twice until now) and that powered off the projector just fine. It did not display the confirmation dialog or require any additional input. However the command seemed to take a bit more than 10 seconds until it showed the :
(I counted 15 in my head, but that is not very precise, need to test more on this)
Is somebody willing to check and repair testing branch? If not I'm closing this issue. I cannot connect to my projector via serial so cannot implement it properly.
Yes, sorry. I'm in the process of moving, so the projector and all the hi-fi equipment is still in boxes. But since I'm setting up home automation from scratch in the new place (previously was FHEM with my own Epson code), I'll get ample opportunity to test and fix the code.
Sorry for not coming back in 2018, I probably got distracted by something else and forgot to come back :( But I had a fresh look at it and found that the expected response in send_request() in projector_serial.py is incorrect for empty/success responses.
I added some additional logging and found that on a successful command the response is b':'
, in an error situation it is b'ERR\r:'
. This is printed with the following logging added @ line 105 _LOGGER.info("Response from Epson before %s", response)
(so just before the line with response = response[:-2].decode()
)
Output for PWR ON
command 2019-04-15 22:03:48,982 - epson_projector.projector_serial - INFO - Response from Epson before b':'
Output for BLAH
command (manually coded just to get an error for comparison) 2019-04-15 22:06:21,219 - epson_projector.projector_serial - INFO - Response from Epson before b'ERR\r:'
Output for PWR?
(for a "GET") 2019-04-15 22:22:49,323 - epson_projector.projector_serial - INFO - Response from Epson before b'PWR=01\r:'
So for commands that give back data in the response waiting for CR_COLON works fine, but for commands that do not (like PWR ON) that does not work, so we end up in the timeout (beause no CR).
I changed the read_until() into: response = await self._reader.readuntil(COLON.encode())
and then the Timeout exception is gone. FYI time for projector to turn on is ~31 seconds so timeout of 40 seems fine.
However 'data2' (the result of send_command(PWR_ON)) still prints False. I am not sure what the expected output of send_command()
should be in the success case. There is no actual message to return since the projector only responds with ':'.
I changed the check if not response:
in send_command() to if response is None:
and that gets rid of the False, but I am not sure if that is the intended behaviour.
Sorry for the long story, but I hope it still is clear enough to understand what I did.
For completeness here are the modifications I made to projector_serial.py
and the output of the test program.
# At the top of the file to get the debug output from the logger, not sure how to enable it otherwise
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
_LOGGER.addHandler(console_handler)
_LOGGER.setLevel(logging.DEBUG)
# Updated these functions
async def send_command(self, command, timeout):
"""Send command to Epson."""
response = await self.send_request(
timeout=timeout,
# command="BLAH"+CR)
command=command+CR)
if response is None:
return False
return response
async def send_request(self, timeout, command):
"""Send request to Epson over serial."""
if self._writer is None:
await self.async_init()
if self._writer and not self._writer.is_closing() and command:
try:
with async_timeout.timeout(timeout):
self._writer.write(command.encode())
response = await self._reader.readuntil(
COLON.encode())
_LOGGER.info("Response from Epson before %s", response)
response = response[:-2].decode()
_LOGGER.info("Response from Epson %s", response)
if response == ERROR:
_LOGGER.error("Error request")
else:
_LOGGER.info("Return response")
return response
except asyncio.TimeoutError:
_LOGGER.error("Timeout error during sending request")
return False
pi@automation-pi3:~/epson_projector $ python3.7 test_serial.py --log=DEBUG
2019-04-15 22:47:34,408 - epson_projector.projector_serial - INFO - Connection established, but wrong response.
2019-04-15 22:47:34,409 - epson_projector.projector_serial - INFO - Cannot open serial to Epson
2019-04-15 22:47:34,463 - epson_projector.projector_serial - INFO - Response from Epson before b'PWR=01\r:'
2019-04-15 22:47:34,463 - epson_projector.projector_serial - INFO - Response from Epson PWR=01
2019-04-15 22:47:34,464 - epson_projector.projector_serial - INFO - Return response
01
2019-04-15 22:47:34,505 - epson_projector.projector_serial - INFO - Response from Epson before b':'
2019-04-15 22:47:34,506 - epson_projector.projector_serial - INFO - Response from Epson
2019-04-15 22:47:34,506 - epson_projector.projector_serial - INFO - Return response
Note that the last line in the test_serial output is an empty line.
I've implemented the COLON change (and slightly improved the logging around that).
Side note for future users: In the case where the projector is not directly connected to the computer running this library (for example with home-assistant, note: the home-assistant epson plugin still doesn't support serial, needs a release of this change first) you can use ser2net
on a linux box connected to the projector like this:
In ser2net conf:
2000:raw:0:/dev/ttyUSB0:9600 8DATABITS NONE 1STOPBIT
Then epson_projector connect string (instead of '/dev/ttyUSB0'
): 'socket://ip.add.re.ss:2000'
.
(Note: https://pyserial.readthedocs.io/en/latest/url_handlers.html supports rfc2217
connection type, which equals telnet
instead of raw
in the ser2net configuration, but doesn't support timeouts and therefore doesn't work.)
Would it be possible to add support for the older Epson projectors that have a serial connection instead of network (e.g. EH-TW3200) ?
The protocol is the same otherwise (also ESC/VP21).