Skip to content

Commit

Permalink
Merge pull request #1 from koolsb/addip
Browse files Browse the repository at this point in the history
Add IP support
  • Loading branch information
koolsb authored Mar 29, 2018
2 parents 546a511 + c763655 commit 19dd805
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 55 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ This is for use with [Home-Assistant](http://home-assistant.io)
```python
from pyblackbird import get_blackbird

# Connect via serial port
blackbird = get_blackbird('/dev/ttyUSB0')

# Connect via IP
blackbird = get_blackbird('192.168.1.50', use_serial=False)

# Print system lock status
print('System Lock is {}'.format('On' if blackbird.lock_status() else 'Off'))

Expand Down
102 changes: 69 additions & 33 deletions pyblackbird/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
import re
import serial
import socket
from functools import wraps
from serial_asyncio import create_serial_connection
from threading import RLock
Expand All @@ -13,7 +14,8 @@
EOL = b'\r'
LEN_EOL = len(EOL)
TIMEOUT = 2 # Number of seconds before serial operation timeout
SYSTEM_POWER = None
PORT = 4001
SOCKET_RECV = 2048

class ZoneStatus(object):
def __init__(self,
Expand Down Expand Up @@ -53,6 +55,7 @@ def from_string(cls, string: str):
else:
return False


class Blackbird(object):
"""
Monoprice blackbird amplifier interface
Expand Down Expand Up @@ -134,13 +137,14 @@ def _format_lock_status() -> bytes:
return '%9961.\r'.encode()


def get_blackbird(port_url):
def get_blackbird(url, use_serial=True):
"""
Return synchronous version of Blackbird interface
:param port_url: serial port, i.e. '/dev/ttyUSB0'
:return: synchronous implementation of Blackbird interface
"""
lock = RLock()
print(serial)

def synchronized(func):
@wraps(func)
Expand All @@ -150,44 +154,76 @@ def wrapper(*args, **kwargs):
return wrapper

class BlackbirdSync(Blackbird):
def __init__(self, port_url):
self._port = serial.serial_for_url(port_url, do_not_open=True)
self._port.baudrate = 9600
self._port.stopbits = serial.STOPBITS_ONE
self._port.bytesize = serial.EIGHTBITS
self._port.parity = serial.PARITY_NONE
self._port.timeout = TIMEOUT
self._port.write_timeout = TIMEOUT
self._port.open()
def __init__(self, url):
"""
Initialize the client.
"""
if use_serial:
self._port = serial.serial_for_url(url, do_not_open=True)
self._port.baudrate = 9600
self._port.stopbits = serial.STOPBITS_ONE
self._port.bytesize = serial.EIGHTBITS
self._port.parity = serial.PARITY_NONE
self._port.timeout = TIMEOUT
self._port.write_timeout = TIMEOUT
self._port.open()

else:
self.host = url
self.port = PORT
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(TIMEOUT)
self.socket.connect((self.host, self.port))

# Clear login message
self.socket.recv(SOCKET_RECV)

def _process_request(self, request: bytes, skip=0):
"""
Send data to socket
:param request: request that is sent to the blackbird
:param skip: number of bytes to skip for end of transmission decoding
:return: ascii string returned by blackbird
"""
_LOGGER.debug('Sending "%s"', request)
# clear
self._port.reset_output_buffer()
self._port.reset_input_buffer()
# send
self._port.write(request)
self._port.flush()
# receive
result = bytearray()
while True:
c = self._port.read(1)
if c is None:
break
if not c:
raise serial.SerialTimeoutException(
'Connection timed out! Last received bytes {}'.format([hex(a) for a in result]))
result += c
if len(result) > skip and result [-LEN_EOL:] == EOL:
break
ret = bytes(result)
_LOGGER.debug('Received "%s"', ret)
return ret.decode('ascii')

if use_serial:
# clear
self._port.reset_output_buffer()
self._port.reset_input_buffer()
# send
self._port.write(request)
self._port.flush()
# receive
result = bytearray()
while True:
c = self._port.read(1)
if c is None:
break
if not c:
raise serial.SerialTimeoutException(
'Connection timed out! Last received bytes {}'.format([hex(a) for a in result]))
result += c
if len(result) > skip and result [-LEN_EOL:] == EOL:
break
ret = bytes(result)
_LOGGER.debug('Received "%s"', ret)
return ret.decode('ascii')

else:
self.socket.send(request)

response = ''

while True:

data = self.socket.recv(SOCKET_RECV)
response += data.decode('ascii')

if EOL in data and len(response) > skip:
break

return response

@synchronized
def zone_status(self, zone: int):
Expand Down Expand Up @@ -224,7 +260,7 @@ def lock_status(self):
# Report system locking status
return LockStatus.from_string(self._process_request(_format_lock_status()))

return BlackbirdSync(port_url)
return BlackbirdSync(url)


@asyncio.coroutine
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os
import sys

VERSION = '0.4'
VERSION = '0.5'

try:
from setuptools import setup
Expand Down
79 changes: 60 additions & 19 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,66 @@
import threading
import os
import pty
import socket


def create_dummy_port(responses):
def listener(port):
# continuously listen to commands on the master device
while 1:
res = b''
while not res.endswith(b"\r"):
# keep reading one byte at a time until we have a full line
res += os.read(port, 1)
print("command: %s" % res)

# write back the response
if res in responses:
resp = responses[res]
del responses[res]
os.write(port, resp)

master, slave = pty.openpty()
thread = threading.Thread(target=listener, args=[master], daemon=True)
thread.start()
return os.ttyname(slave)
def listener(port):
# continuously listen to commands on the master device
while 1:
res = b''
while not res.endswith(b"\r"):
# keep reading one byte at a time until we have a full line
res += os.read(port, 1)
print("command: %s" % res)

# write back the response
if res in responses:
resp = responses[res]
del responses[res]
os.write(port, resp)

master, slave = pty.openpty()
thread = threading.Thread(target=listener, args=[master], daemon=True)
thread.start()
return os.ttyname(slave)

def create_dummy_socket(responses):
HOST = '127.0.0.1'
PORT = 4001



def listener():

conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn.bind((HOST, PORT))
conn.listen(10)

while 1:
conn, addr = s.accept()

conn.send('Please Input Your Command :\r')

while True:
# Receive data
res = conn.recv(1024)
print("command: %s" % res)

# write back the response
if res in responses:
resp = responses[res]
del responses[res]
conn.sendall(resp)

if not res:
break

#while 1:

# start_new_thread(listener ,(conn,))

thread = threading.Thread(target=listener, daemon=True)
thread.start()

return HOST
4 changes: 2 additions & 2 deletions tests/test_blackbird.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import serial

from pyblackbird import (get_blackbird, get_async_blackbird, ZoneStatus)
from tests import create_dummy_port
from tests import (create_dummy_port, create_dummy_socket)
import asyncio


Expand Down Expand Up @@ -108,4 +108,4 @@ def test_timeout(self):
self.blackbird.set_zone_source(6, 6)

if __name__ == '__main__':
unittest.main()
unittest.main()

0 comments on commit 19dd805

Please sign in to comment.