Skip to content

Commit

Permalink
Merge pull request #27 from iloveicedgreentea/new_cmds
Browse files Browse the repository at this point in the history
Add new commands
  • Loading branch information
iloveicedgreentea authored Feb 18, 2024
2 parents 728abd5 + 4d9e572 commit 709f90d
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 12 deletions.
66 changes: 64 additions & 2 deletions jvc_projector/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,66 @@ class ThreeD(Enum):
sbs = b"3"
tb = b"4"

class ResolutionModes(Enum):
r_480p = b"02"
r_576p = b"03"
r_720p50 = b"04"
r_720p60 = b"05"
r_1080i50 = b"06"
r_1080i60 = b"07"
r_1080p24 = b"08"
r_1080p50 = b"09"
r_1080p60 = b"0A"
NoSignal = b"0B"
r_720p_3D = b"0C"
r_1080i_3D = b"0D"
r_1080p_3D = b"0E"
OutofRange = b"0F"
r_4K_4096p60 = b"10"
r_4K_4096p50 = b"11"
r_4K_4096p30 = b"12"
r_4K_4096p25 = b"13"
r_4K_4096p24 = b"14"
r_4K_3840p60 = b"15"
r_4K_3840p50 = b"16"
r_4K_3840p30 = b"17"
r_4K_3840p25 = b"18"
r_4K_3840p24 = b"19"
r_1080p25 = b"1C"
r_1080p30 = b"1D"
r_2048x1080p24 = b"1E"
r_2048x1080p25 = b"1F"
r_2048x1080p30 = b"20"
r_2048x1080p50 = b"21"
r_2048x1080p60 = b"22"
r_3840x2160p120 = b"23"
r_4096x2160p120 = b"24"
VGA_640x480 = b"25"
SVGA_800x600 = b"26"
XGA_1024x768 = b"27"
SXGA_1280x1024 = b"28"
WXGA_1280x768 = b"29"
WXGAplus_1440x900 = b"2A"
WSXGAplus_1680x1050 = b"2B"
WUXGA_1920x1200 = b"2C"
WXGA_1280x800 = b"2D"
FWXGA_1366x768 = b"2E"
WXGAplus_1600x900 = b"2F"
UXGA_1600x1200 = b"30"
QXGA = b"31"
WOXGA = b"32"
r_4096x2160_100Hz = b"34"
r_3840x2160_100Hz = b"35"
r_1080p100 = b"36"
r_1080p120 = b"37"
r_8K_7680x4320p60 = b"38"
r_8K_7680x4320p50 = b"39"
r_8K_7680x4320p30 = b"3A"
r_8K_7680x4320p25 = b"3B"
r_8K_7680x4320p24 = b"3C"
WQHD60 = b"3D"
WOQHD120 = b"3E"
r_8K_7680x4320p48 = b"3F"

class Commands(Enum):

Expand Down Expand Up @@ -397,25 +457,27 @@ class Commands(Enum):

# NZ Series Laser Dimming commands
laser_mode = b"PMDC", LaserDimModes, ACKs.picture_ack
# fw 3.0 and up
laser_value = b"PMCV", int, ACKs.picture_ack

# Lamp power
lamp_power = b"PMLP", LampPowerModes, ACKs.picture_ack

# Lamp time
lamp_time = b"IFLT", int, ACKs.info_ack

# Lens Aperture commands
aperture = b"PMDI", ApertureModes, ACKs.picture_ack

# Anamorphic commands
# I don't use this, untested
anamorphic = b"INVS", AnamorphicModes, ACKs.lens_ack

# e-shift
eshift_mode = b"PMUS", EshiftModes, ACKs.picture_ack

# source status
source_status = b"SC", SourceStatuses, ACKs.source_ack
source_disaply = b"IFIS", ResolutionModes, ACKs.info_ack

# 3d
signal_3d = b"IS3D", ThreeD, ACKs.hdmi_ack
Expand Down
60 changes: 51 additions & 9 deletions jvc_projector/jvc_projector.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
PJ_ACK,
PJ_OK,
PJ_REQ,
ResolutionModes,
ACKs,
AspectRatioModes,
ColorSpaceModes,
Expand All @@ -34,6 +35,7 @@
LowLatencyModes,
MaskModes,
PictureModes,
AnamorphicModes,
PowerStates,
SourceStatuses,
TheaterOptimizer,
Expand All @@ -58,24 +60,27 @@ class JVCAttributes: # pylint: disable=too-many-instance-attributes
power_state: bool = False
signal_status: str = ""
picture_mode: str = ""
installation_mode: str = ""
resolution: str = ""
low_latency: bool = False
laser_power: str = ""
laser_value: int = 0
laser_mode: str = ""
lamp_power: str = ""
model: str = ""
installation_mode: str = ""
content_type: str = ""
content_type_trans: str = ""
hdr_data: str = ""
hdr_processing: str = ""
hdr_level: str = ""
theater_optimizer: str = ""
low_latency: bool = False
input_mode: str = ""
input_level: str = ""
color_mode: str = ""
aspect_ratio: str = ""
eshift: str = ""
mask_mode: str = ""
aspect_ratio: str = ""
anamophic_mode: str = ""
software_version: str = ""
laser_time: int = 0
lamp_time: int = 0
Expand Down Expand Up @@ -214,11 +219,17 @@ async def exec_command(

async def close_connection(self):
"""Close the projector connection asynchronously"""
self.writer.close()
await self.writer.wait_closed()
self.commander.reader = self.reader
self.commander.writer = self.writer
self.logger.info("Connection closed")
try:
if self.writer:
self.writer.close()
await self.writer.wait_closed()
self.commander.reader = self.reader
self.commander.writer = self.writer
self.logger.info("Connection closed")
except BrokenPipeError:
self.logger.warning("Connection already closed - Broken pipe encountered")
except Exception as e:
self.logger.error("Error closing JVC Projector connection - %s", e)

async def info(self) -> tuple[str, bool]:
"""
Expand Down Expand Up @@ -333,7 +344,22 @@ async def get_software_version(self) -> str:
"get_software_version", ACKs.info_ack
)
# returns something like 0210PJ as bytes
return state.replace(ACKs.info_ack.value, b"").replace(b"PJ", b"").decode()
ver: str = (
state.replace(ACKs.info_ack.value, b"")
.replace(b"PJ", b"")
.decode()
# remove leading 0
.lstrip("0")
)
# add a dot to the version
return f"{ver[:1]}.{ver[1:]}"

async def get_laser_value(self) -> int:
"""
Get the current software version FW 3.0+ only
"""
state, _ = await self.commander.do_reference_op("laser_value", ACKs.picture_ack)
return int(state.replace(ACKs.picture_ack.value, b""), 16)

async def get_content_type(self) -> str:
"""
Expand Down Expand Up @@ -406,6 +432,12 @@ async def get_aspect_ratio(self) -> str:
"aspect_ratio", ACKs.hdmi_ack, AspectRatioModes
)

async def get_anamorphic(self) -> str:
"""
Return anamorphic mode
"""
return await self._get_attribute("anamorphic", ACKs.lens_ack, AnamorphicModes)

async def get_source_status(self) -> str:
"""
Return source status
Expand All @@ -414,6 +446,16 @@ async def get_source_status(self) -> str:
"source_status", ACKs.source_ack, SourceStatuses
)

async def get_source_display(self) -> str:
"""
Return source display resolution like 4k_4096p60
"""
res = await self._get_attribute(
"source_disaply", ACKs.info_ack, ResolutionModes
)

return res.replace("r_", "")

async def _get_power_state(self) -> str:
"""
Return the current power state
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="pyjvc",
version="4.1.0",
version="4.1.1",
author="iloveicedgreentea2",
description="A package to control JVC projectors over IP",
long_description=long_description,
Expand Down
15 changes: 15 additions & 0 deletions tests/test_coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
ContentTypeTrans,
EshiftModes,
InputLevel,
AnamorphicModes,
InputModes,
InstallationModes,
LampPowerModes,
LaserDimModes,
LaserPowerModes,
ResolutionModes,
LowLatencyModes,
MaskModes,
PictureModes,
Expand All @@ -32,6 +34,7 @@

class TestCoordinator(unittest.IsolatedAsyncioTestCase):
"""Test running commands"""

async def asyncSetUp(self):
# set up connection
options = JVCInput(TEST_IP, TEST_PASSWORD, TEST_PORT, 5)
Expand Down Expand Up @@ -71,10 +74,22 @@ async def assert_modes(self): # pylint: disable=too-many-locals
LowLatencyModes.__members__,
f"Unexpected low latency state: {low_latency_state}",
)
# test getting resolution
res = await self.coordinator.get_source_display()
self.assertIn(res, ["4K_4096p24", "NoSignal"], "Source display not as expected")

res = await self.coordinator.get_anamorphic()
self.assertIn(res, AnamorphicModes.__members__, "Anamorphic not as expected")

res = await self.coordinator.get_software_version()
self.assertIsInstance(res, str) # sw can change, but should be str

# fw 3.0 and above
if float(res) > 210:
res = await self.coordinator.get_laser_value()
print(res)
self.assertIsInstance(res, int)

picture_mode = await self.coordinator.get_picture_mode()
self.assertIn(
picture_mode, PictureModes.__members__, "Picture mode not as expected."
Expand Down

0 comments on commit 709f90d

Please sign in to comment.