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
6 changes: 5 additions & 1 deletion htd_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ async def async_get_client(
serial_address: str = None,
network_address: Tuple[str, int] = None,
loop: asyncio.AbstractEventLoop = None,
retry_attempts: int = HtdConstants.DEFAULT_RETRY_ATTEMPTS,
) -> BaseClient:
"""
Create a new client object.
Expand All @@ -36,6 +37,7 @@ async def async_get_client(
network_address (str): The address to communicate with over TCP.
serial_address (str): The location of the serial port.
loop (asyncio.AbstractEventLoop): The event loop to use.
retry_attempts (int): Number of times to retry a command before failing.

Returns:
HtdClient: The new client object.
Expand All @@ -53,6 +55,7 @@ async def async_get_client(
model_info,
network_address=network_address,
serial_address=serial_address,
retry_attempts=retry_attempts,
)

elif model_info["kind"] == HtdDeviceKind.lync:
Expand All @@ -61,10 +64,11 @@ async def async_get_client(
model_info,
network_address=network_address,
serial_address=serial_address,
retry_attempts=retry_attempts,
)

else:
raise ValueError(f"Unknown Device Kind: {model_info["kind"]}")
raise ValueError(f"Unknown Device Kind: {model_info['kind']}")

await client.async_connect()

Expand Down
83 changes: 71 additions & 12 deletions htd_client/base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ async def async_connect(self):
self._buffer = bytearray()
self._zone_data = {}
self._zones_loaded = 0
self._zone_data = {}
self._source_names = {}
self._zone_names = {}
self._connection = None
self._disconnected = False

Expand Down Expand Up @@ -186,7 +187,11 @@ async def _async_reconnect(self):


async def async_wait_until_ready(self):
pass
start_time = time.time()
while not self._ready:
if time.time() - start_time > self._socket_timeout_sec:
raise Exception("Timed out waiting for device to be ready")
await asyncio.sleep(0.1)

def has_zone_data(self, zone: int):
return zone in self._zone_data
Expand Down Expand Up @@ -284,7 +289,6 @@ def _process_next_command(self, data: bytes):

def _parse_command(self, zone, cmd, data):
if cmd == HtdCommonCommands.KEYPAD_EXISTS_RECEIVE_COMMAND:
print(f"DEBUG: inside _parse_command _zone_data id: {id(self._zone_data)}")
# if len(self._zone_data) == 0:
# this is zone 0 with all zone data
# second byte is zone 1 - 8
Expand Down Expand Up @@ -330,14 +334,17 @@ def _parse_command(self, zone, cmd, data):
# remove the extra null bytes

elif cmd == HtdCommonCommands.ZONE_NAME_RECEIVE_COMMAND:
name = str(data[0:11].decode().rstrip('\0')).lower()
self._zone_data[zone].name = name

elif cmd == HtdCommonCommands.SOURCE_NAME_RECEIVE_COMMAND:
source = data[11]
name = str(data[0:10].decode().rstrip('\0')).lower()
# self.zone_info[zone]['source_list'][source] = name
# self.source_info[zone][name] = source
name = str(data[0:11].decode(errors="ignore").rstrip('\0')).lower()
self._zone_names[zone] = name
if self.has_zone_data(zone):
self._zone_data[zone].name = name

elif cmd == HtdCommonCommands.SOURCE_NAME_RECEIVE_COMMAND or cmd == HtdCommonCommands.ZONE_SOURCE_NAME_RECEIVE_COMMAND_LYNC:
source = data[11] + 1
name = str(data[0:10].decode(errors="ignore").rstrip('\0')).lower()
self._source_names[source] = name
if self.has_zone_data(zone):
self._zone_data[zone].source_name = name
#
# elif cmd == HtdCommonCommands.MP3_ON_RECEIVE_COMMAND:
# self.mp3_status['state'] = 'on'
Expand Down Expand Up @@ -389,7 +396,7 @@ def _parse_zone(self, zone_number: int, zone_data: bytearray) -> ZoneDetail | No
HtdConstants.POWER_STATE_TOGGLE_INDEX
)
zone.mute = htd_client.utils.is_bit_on(state_toggles, HtdConstants.MUTE_STATE_TOGGLE_INDEX)
zone.mode = htd_client.utils.is_bit_on(state_toggles, HtdConstants.MODE_STATE_TOGGLE_INDEX)
zone.dnd = htd_client.utils.is_bit_on(state_toggles, HtdConstants.DND_STATE_TOGGLE_INDEX)

zone.source = zone_data[HtdConstants.SOURCE_ZONE_DATA_INDEX] + HtdConstants.SOURCE_QUERY_OFFSET
zone.volume = volume
Expand Down Expand Up @@ -497,6 +504,14 @@ def get_source_count(self) -> int:
"""
return self._model_info['sources']

def get_source_name(self, source: int) -> str:
"""Get the name of a source if it has been fetched."""
return self._source_names.get(source, f"Source {source}")

def get_zone_name(self, zone: int) -> str | None:
"""Get the cached zone name, or None if not yet queried."""
return self._zone_names.get(zone)

def get_zone(self, zone: int):
"""
Query a zone and return `ZoneDetail`
Expand Down Expand Up @@ -569,6 +584,10 @@ async def async_power_on(self, zone: int):
@abstractmethod
async def async_power_off(self, zone: int):
pass

@abstractmethod
async def async_set_bass(self, zone: int, bass: int):
pass

@abstractmethod
async def async_bass_up(self, zone: int):
Expand All @@ -578,6 +597,10 @@ async def async_bass_up(self, zone: int):
async def async_bass_down(self, zone: int):
pass

@abstractmethod
async def async_set_treble(self, zone: int, treble: int):
pass

@abstractmethod
async def async_treble_up(self, zone: int):
pass
Expand All @@ -593,3 +616,39 @@ async def async_balance_left(self, zone: int):
@abstractmethod
async def async_balance_right(self, zone: int):
pass

@abstractmethod
async def async_set_balance(self, zone: int, balance: int):
pass

@abstractmethod
async def async_set_dnd(self, zone: int, dnd: bool):
pass

@abstractmethod
async def async_set_echo(self, echo: bool):
pass

@abstractmethod
async def async_query_id(self):
pass

@abstractmethod
async def async_query_all_zone_status(self):
pass

@abstractmethod
async def async_query_zone_name(self, zone: int):
pass

@abstractmethod
async def async_query_source_name(self, source: int):
pass

@abstractmethod
async def async_set_zone_name(self, zone: int, name: str):
pass

@abstractmethod
async def async_set_source_name(self, source: int, name: str):
pass
37 changes: 28 additions & 9 deletions htd_client/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,26 @@ class HtdConstants:

VOLUME_OFFSET = MAX_RAW_VOLUME - MAX_VOLUME

MIN_BASS = -10
MAX_BASS = 10

MIN_TREBLE = -10
MAX_TREBLE = 10

MIN_BALANCE = -18
MAX_BALANCE = 18
# Lync Constants
LYNC_MIN_BASS = -10
LYNC_MAX_BASS = 10
LYNC_MIN_TREBLE = -10
LYNC_MAX_TREBLE = 10
LYNC_MIN_BALANCE = -18
LYNC_MAX_BALANCE = 18

# MCA Constants
# Raw values exposed to the user (-12 to 12)
MCA_MIN_BASS = -12
MCA_MAX_BASS = 12
MCA_MIN_TREBLE = -12
MCA_MAX_TREBLE = 12
MCA_MIN_BALANCE = -12
MCA_MAX_BALANCE = 12

# Step size exposed to user
MCA_BASS_TREBLE_STEP = 4
MCA_BALANCE_STEP = 6

# each message we get is chunked at 14 bytes
MESSAGE_CHUNK_SIZE = 14
Expand All @@ -105,7 +117,7 @@ class HtdConstants:
# indexes of each state toggle
POWER_STATE_TOGGLE_INDEX = 0
MUTE_STATE_TOGGLE_INDEX = 1
MODE_STATE_TOGGLE_INDEX = 2
DND_STATE_TOGGLE_INDEX = 2

# the byte index for where to locate the corresponding setting
SOURCE_ZONE_DATA_INDEX = 4
Expand Down Expand Up @@ -134,6 +146,7 @@ class HtdCommonCommands:
MP3_ARTIST_NAME_RECEIVE_COMMAND = 0x12
MP3_ON_RECEIVE_COMMAND = 0x13
MP3_OFF_RECEIVE_COMMAND = 0x14
QUERY_ID_CODE_RECEIVE_COMMAND = 0x08
ERROR_RECEIVE_COMMAND = 0x1b

EXPECTED_MESSAGE_LENGTH_MAP = {
Expand All @@ -150,6 +163,7 @@ class HtdCommonCommands:
MP3_ON_RECEIVE_COMMAND: 1,
MP3_OFF_RECEIVE_COMMAND: 17,
ERROR_RECEIVE_COMMAND: 9,
QUERY_ID_CODE_RECEIVE_COMMAND: 1,
}

class HtdMcaConstants:
Expand All @@ -171,8 +185,10 @@ class HtdLyncCommands:
BALANCE_SETTING_CONTROL_COMMAND_CODE = 0x16
TREBLE_SETTING_CONTROL_COMMAND_CODE = 0x17
BASS_SETTING_CONTROL_COMMAND_CODE = 0x18
SET_ECHO_COMMAND_CODE = 0x19
SET_AUDIO_TO_DEFAULT_COMMAND_CODE = 0x1c
SET_NAME_TO_DEFAULT_COMMAND_CODE = 0x1e
QUERY_ID_CODE = 0x08

MP3_FAST_FORWARD_COMMAND_CODE = 0x0a
MP3_PLAY_PAUSE_COMMAND_CODE = 0x0b
Expand Down Expand Up @@ -214,6 +230,9 @@ class HtdLyncConstants:

BASS_COMMAND_OFFSET = 0x80
TREBLE_COMMAND_OFFSET = 0x80

STATUS_REFRESH_CODE = 0x1F



class HtdMcaCommands:
Expand Down
Loading