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
2 changes: 2 additions & 0 deletions src/pyipp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
)
from .ipp import IPP
from .models import (
Counters,
Info,
Marker,
Printer,
Expand All @@ -17,6 +18,7 @@
)

__all__ = [
"Counters",
"Info",
"Marker",
"Printer",
Expand Down
4 changes: 4 additions & 0 deletions src/pyipp/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
"marker-low-levels",
"marker-names",
"marker-types",
"printer-impressions-completed",
"printer-impressions-completed-col",
"printer-pages-completed",
"printer-media-sheets-completed",
]

DEFAULT_PORT = 631
Expand Down
32 changes: 32 additions & 0 deletions src/pyipp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,34 @@ class Uri:
security: str | None


@dataclass
class Counters:
"""Object holding page counter information from IPP."""

impressions_completed: int
impressions_completed_col: dict[str, int]
pages_completed: int
media_sheets_completed: int

@staticmethod
def from_dict(data: dict[str, Any]) -> Counters:
"""Return Counters object from IPP response."""
col = data.get("printer-impressions-completed-col", {})
if not isinstance(col, dict):
col = {}

impressions = data.get("printer-impressions-completed", -1)
if impressions == -1 and col:
impressions = sum(col.values())

return Counters(
impressions_completed=impressions,
impressions_completed_col=col,
pages_completed=data.get("printer-pages-completed", -1),
media_sheets_completed=data.get("printer-media-sheets-completed", -1),
)


@dataclass
class State:
"""Object holding the IPP printer state."""
Expand Down Expand Up @@ -143,6 +171,7 @@ class Printer:
"""Object holding the IPP printer information."""

info: Info
counters: Counters
markers: list[Marker]
state: State
uris: list[Uri]
Expand All @@ -152,6 +181,7 @@ def as_dict(self) -> dict[str, Any]:
"""Return dictionary version of this printer."""
return {
"info": asdict(self.info),
"counters": asdict(self.counters),
"state": asdict(self.state),
"markers": [asdict(marker) for marker in self.markers],
"uris": [asdict(uri) for uri in self.uris],
Expand All @@ -163,6 +193,7 @@ def update_from_dict(self, data: dict[str, Any]) -> Printer:
last_uptime = self.info.uptime

self.info = Info.from_dict(data)
self.counters = Counters.from_dict(data)
self.markers = Printer.merge_marker_data(data)
self.state = State.from_dict(data)
self.uris = Printer.merge_uri_data(data)
Expand All @@ -180,6 +211,7 @@ def from_dict(data: dict[str, Any]) -> Printer:

return Printer(
info=info,
counters=Counters.from_dict(data),
markers=Printer.merge_marker_data(data),
state=State.from_dict(data),
uris=Printer.merge_uri_data(data),
Expand Down
3 changes: 3 additions & 0 deletions src/pyipp/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,7 @@
"media": IppTag.NAME,
"center-of-pixel": IppTag.BOOLEAN,
"sides": IppTag.KEYWORD,
"printer-impressions-completed": IppTag.INTEGER,
"printer-pages-completed": IppTag.INTEGER,
"printer-media-sheets-completed": IppTag.INTEGER,
}
91 changes: 91 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,97 @@ async def test_printer_with_single_supported_uri_invalid_uri() -> None:
assert len(printer.uris) == 0


@pytest.mark.asyncio
async def test_counters() -> None:
"""Test Counters model."""
data: dict[str, Any] = {
"printer-impressions-completed": 1234,
"printer-pages-completed": 5678,
"printer-media-sheets-completed": 9012,
}

counters = models.Counters.from_dict(data)

assert counters
assert counters.impressions_completed == 1234
assert counters.impressions_completed_col == {}
assert counters.pages_completed == 5678
assert counters.media_sheets_completed == 9012


@pytest.mark.asyncio
async def test_counters_defaults() -> None:
"""Test Counters model with missing data."""
counters = models.Counters.from_dict({})

assert counters
assert counters.impressions_completed == -1
assert counters.impressions_completed_col == {}
assert counters.pages_completed == -1
assert counters.media_sheets_completed == -1


@pytest.mark.asyncio
async def test_counters_col() -> None:
"""Test Counters model with collection-based impressions."""
data: dict[str, Any] = {
"printer-impressions-completed-col": {
"monochrome": 0,
"full-color": 10,
},
}

counters = models.Counters.from_dict(data)

assert counters
assert counters.impressions_completed == 10
assert counters.impressions_completed_col == {"monochrome": 0, "full-color": 10}


@pytest.mark.asyncio
async def test_counters_col_does_not_override_scalar() -> None:
"""Test that scalar impressions-completed takes precedence over col."""
data: dict[str, Any] = {
"printer-impressions-completed": 500,
"printer-impressions-completed-col": {
"monochrome": 100,
"full-color": 200,
},
}

counters = models.Counters.from_dict(data)

assert counters.impressions_completed == 500
assert counters.impressions_completed_col == {"monochrome": 100, "full-color": 200}


@pytest.mark.asyncio
async def test_printer_counters() -> None:
"""Test Printer model includes counters."""
data = IPPE10_PRINTER_ATTRS.copy()
data["printer-impressions-completed"] = 500
data["printer-pages-completed"] = 400
data["printer-media-sheets-completed"] = 300

printer = models.Printer.from_dict(data)
assert printer
assert printer.counters.impressions_completed == 500
assert printer.counters.pages_completed == 400
assert printer.counters.media_sheets_completed == 300


@pytest.mark.asyncio
async def test_printer_counters_in_as_dict() -> None:
"""Test Printer as_dict includes counters."""
data = IPPE10_PRINTER_ATTRS.copy()
data["printer-impressions-completed"] = 100

printer = models.Printer.from_dict(data)
printer_dict = printer.as_dict()
assert "counters" in printer_dict
assert printer_dict["counters"]["impressions_completed"] == 100


@pytest.mark.asyncio
async def test_printer_with_single_supported_uri_with_security() -> None:
"""Test Printer model with multiple markers."""
Expand Down