Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
## Status
[![Build Status](https://travis-ci.org/koolsb/pyblackbird.svg?branch=master)](https://travis-ci.org/koolsb/pyblackbird)[![Coverage Status](https://coveralls.io/repos/github/koolsb/pyblackbird/badge.svg)](https://coveralls.io/github/koolsb/pyblackbird)
# pyblackbird
Python3 interface implementation for Monoprice Blackbird 4k 8x8 HDBaseT Matrix
Python3 interface implementation for Monoprice Blackbird HDMI Matrix Switches

## Notes
This is for use with [Home-Assistant](http://home-assistant.io)
Has been tested with models [21819](https://www.monoprice.com/product?p_id=21819) and [24180](https://www.monoprice.com/product?p_id=24180)

## Usage
```python
Expand All @@ -13,9 +14,15 @@ from pyblackbird import get_blackbird
# Connect via serial port
blackbird = get_blackbird('/dev/ttyUSB0')

# Connect via serial port with a device that doesn't support IR control
blackbird = get_blackbird('/dev/ttyUSB0', use_serial=True, ir_control=false)

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

# Connect via IP with a device that doesn't support IR control
blackbird = get_blackbird('192.168.1.50', use_serial=False, ir_control=false)

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

Expand Down
41 changes: 33 additions & 8 deletions pyblackbird/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
_LOGGER = logging.getLogger(__name__)
ZONE_PATTERN_ON = re.compile('\D\D\D\s(\d\d)\D\D\d\d\s\s\D\D\D\s(\d\d)\D\D\d\d\s')
ZONE_PATTERN_OFF = re.compile('\D\D\DOFF\D\D\d\d\s\s\D\D\D\D\D\D\D\D\d\d\s')
ZONE_PATTERN_ON_NO_IR = re.compile('\D\D\D\s(\d\d)\D\D\d\d\s\s')
ZONE_PATTERN_OFF_NO_IR = re.compile('\D\D\DOFF\D\D\d\d\\s\s')
EOL = b'\r'
LEN_EOL = len(EOL)
TIMEOUT = 2 # Number of seconds before serial operation timeout
Expand All @@ -34,10 +36,16 @@ def from_string(cls, zone: int, string: str):
return None
match_on = re.search (ZONE_PATTERN_ON, string)
if not match_on:
match_off = re.search (ZONE_PATTERN_OFF, string)
if not match_off:
return None
return ZoneStatus(zone,0,None,None)
match_on = re.search (ZONE_PATTERN_ON_NO_IR, string)
if not match_on:
match_off = re.search (ZONE_PATTERN_OFF, string)
if not match_off:
match_off = re.search (ZONE_PATTERN_OFF_NO_IR, string)
if not match_off:
return None
return ZoneStatus(zone,0,None,None)
return ZoneStatus(zone,0,None,None)
return ZoneStatus(zone,1,*[int(m) for m in match_on.groups()], None)
return ZoneStatus(zone,1,*[int(m) for m in match_on.groups()])

class LockStatus(object):
Expand Down Expand Up @@ -123,6 +131,10 @@ def _format_set_zone_source(zone: int, source: int) -> bytes:
source = int(max(1, min(source,8)))
return '{}B{}.\r'.format(source, zone).encode()

def _format_set_zone_source_no_ir(zone: int, source: int) -> bytes:
source = int(max(1, min(source,8)))
return '{}V{}.\r'.format(source, zone).encode()

def _format_set_all_zone_source(source: int) -> bytes:
source = int(max(1, min(source,8)))
return '{}All.\r'.format(source).encode()
Expand All @@ -137,7 +149,7 @@ def _format_lock_status() -> bytes:
return '%9961.\r'.encode()


def get_blackbird(url, use_serial=True):
def get_blackbird(url, use_serial=True, ir_control=True):
"""
Return synchronous version of Blackbird interface
:param port_url: serial port, i.e. '/dev/ttyUSB0'
Expand Down Expand Up @@ -228,7 +240,9 @@ def _process_request(self, request: bytes, skip=0):
@synchronized
def zone_status(self, zone: int):
# Returns status of a zone
return ZoneStatus.from_string(zone, self._process_request(_format_zone_status_request(zone), skip=20))
if ir_control:
return ZoneStatus.from_string(zone, self._process_request(_format_zone_status_request(zone), skip=20))
return ZoneStatus.from_string(zone, self._process_request(_format_zone_status_request(zone), skip=10))

@synchronized
def set_zone_power(self, zone: int, power: bool):
Expand All @@ -240,6 +254,11 @@ def set_zone_source(self, zone: int, source: int):
# Set zone source
self._process_request(_format_set_zone_source(zone, source))

@synchronized
def set_zone_source_no_ir(self, zone: int, source: int):
# Set zone source without IR
self._process_request(_format_set_zone_source_no_ir(zone, source))

@synchronized
def set_all_zone_source(self, source: int):
# Set all zones to one source
Expand Down Expand Up @@ -297,8 +316,14 @@ async def set_zone_source(self, zone: int, source: int):
await self._protocol.send(_format_set_zone_source(zone, source))

@locked_coro
async def set_all_zone_source(self, source: int):
await self._protocol.send(_format_set_all_zone_source(source))
@asyncio.coroutine
def set_zone_source_no_ir(self, zone: int, source: int):
await self._protocol.send(_format_set_zone_source_no_ir(zone, source))

@locked_coro
@asyncio.coroutine
def set_all_zone_source(self, source: int):
await self._protocol.send(_format_set_all_zone_source(source))

@locked_coro
async def lock_front_buttons(self):
Expand Down