async def _async_check_and_create(self, step_id, user_input):
        """Validate input, proceed to create."""
        user_input[CONF_URL] = url_normalize(user_input[CONF_URL],
                                             default_scheme="http")
        if "://" not in user_input[CONF_URL]:
            return await self._async_show_form(
                step_id=step_id,
                user_input=user_input,
                errors={CONF_URL: "invalid_url"})

        # If we don't have a unique id, copy one from existing entry with same URL
        if not self.unique_id:
            for existing_entry in (x for x in self._async_current_entries()
                                   if x.data[CONF_URL] == user_input[CONF_URL]
                                   and x.unique_id):
                await self.async_set_unique_id(existing_entry.unique_id)
                break

        session = aiohttp_client.async_get_clientsession(self.hass)
        printer = SyncThru(user_input[CONF_URL], session)
        errors = {}
        try:
            await printer.update()
            if not user_input.get(CONF_NAME):
                user_input[CONF_NAME] = DEFAULT_NAME_TEMPLATE.format(
                    printer.model() or DEFAULT_MODEL)
        except ValueError:
            errors[CONF_URL] = "syncthru_not_supported"
        else:
            if printer.is_unknown_state():
                errors[CONF_URL] = "unknown_state"

        if errors:
            return await self._async_show_form(step_id=step_id,
                                               user_input=user_input,
                                               errors=errors)

        return self.async_create_entry(
            title=user_input.get(CONF_NAME),
            data=user_input,
        )
Beispiel #2
0
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
    """Set up the SyncThru component."""

    if discovery_info is not None:
        _LOGGER.info(
            "Discovered a new Samsung Printer at %s", discovery_info.get(CONF_HOST)
        )
        host = discovery_info.get(CONF_HOST)
        name = discovery_info.get(CONF_NAME, DEFAULT_NAME)
        # Main device, always added
    else:
        host = config.get(CONF_RESOURCE)
        name = config.get(CONF_NAME)
    # always pass through all of the obtained information
    monitored = DEFAULT_MONITORED_CONDITIONS

    session = aiohttp_client.async_get_clientsession(hass)

    printer = SyncThru(host, session)
    # Test if the discovered device actually is a syncthru printer
    # and fetch the available toner/drum/etc
    try:
        # No error is thrown when the device is off
        # (only after user added it manually)
        # therefore additional catches are inside the Sensor below
        await printer.update()
        supp_toner = printer.toner_status(filter_supported=True)
        supp_drum = printer.drum_status(filter_supported=True)
        supp_tray = printer.input_tray_status(filter_supported=True)
        supp_output_tray = printer.output_tray_status()
    except ValueError:
        # if an exception is thrown, printer does not support syncthru
        # and should not be set up
        # If the printer was discovered automatically, no warning or error
        # should be issued and printer should not be set up
        if discovery_info is not None:
            _LOGGER.info("Samsung printer at %s does not support SyncThru", host)
            return
        # Otherwise, emulate printer that supports everything
        supp_toner = TONER_COLORS
        supp_drum = DRUM_COLORS
        supp_tray = TRAYS
        supp_output_tray = OUTPUT_TRAYS

    devices = [SyncThruMainSensor(printer, name)]

    for key in supp_toner:
        if f"toner_{key}" in monitored:
            devices.append(SyncThruTonerSensor(printer, name, key))
    for key in supp_drum:
        if f"drum_{key}" in monitored:
            devices.append(SyncThruDrumSensor(printer, name, key))
    for key in supp_tray:
        if f"tray_{key}" in monitored:
            devices.append(SyncThruInputTraySensor(printer, name, key))
    for key in supp_output_tray:
        if f"output_tray_{key}" in monitored:
            devices.append(SyncThruOutputTraySensor(printer, name, key))

    async_add_entities(devices, True)
Beispiel #3
0
async def main() -> None:
    async with aiohttp.ClientSession() as session:
        printer = SyncThru(IP_PRINTER, session)
        await printer.update()

        # Is printer online?
        print("Printer online?:", printer.is_online())
        # Show the printer status
        print("Printer status:", printer.device_status())
        if printer.is_online():
            # Show details about the printer
            print("Printer model:", printer.model())
            # Get the details of a cartridge
            print("Toner Cyan details:", printer.toner_status()["cyan"])
            # Get the details about a tray
            print("Tray 1 Capacity:",
                  printer.input_tray_status()["tray_1"]["capa"])
        # Print all available details from the printer
        print("All data:\n", printer.raw())
Beispiel #4
0
def setup_platform(hass, config, add_entities, discovery_info=None):
    """Set up the SyncThru component."""
    from pysyncthru import SyncThru, test_syncthru

    if discovery_info is not None:
        host = discovery_info.get(CONF_HOST)
        name = discovery_info.get(CONF_NAME, DEFAULT_NAME)
        _LOGGER.debug("Discovered a new Samsung Printer: %s", discovery_info)
        # Test if the discovered device actually is a syncthru printer
        if not test_syncthru(host):
            _LOGGER.error("No SyncThru Printer found at %s", host)
            return
        monitored = DEFAULT_MONITORED_CONDITIONS
    else:
        host = config.get(CONF_RESOURCE)
        name = config.get(CONF_NAME)
        monitored = config.get(CONF_MONITORED_CONDITIONS)

    # Main device, always added
    try:
        printer = SyncThru(host)
    except TypeError:
        # if an exception is thrown, printer cannot be set up
        return

    printer.update()
    devices = [SyncThruMainSensor(printer, name)]

    for key in printer.toner_status(filter_supported=True):
        if 'toner_{}'.format(key) in monitored:
            devices.append(SyncThruTonerSensor(printer, name, key))
    for key in printer.drum_status(filter_supported=True):
        if 'drum_{}'.format(key) in monitored:
            devices.append(SyncThruDrumSensor(printer, name, key))
    for key in printer.input_tray_status(filter_supported=True):
        if 'tray_{}'.format(key) in monitored:
            devices.append(SyncThruInputTraySensor(printer, name, key))
    for key in printer.output_tray_status():
        if 'output_tray_{}'.format(key) in monitored:
            devices.append(SyncThruOutputTraySensor(printer, name, key))

    add_entities(devices, True)
def setup_platform(hass, config, add_devices, discovery_info=None):
    """Set up the SyncThru component."""
    from pysyncthru import SyncThru, test_syncthru

    if discovery_info is not None:
        host = discovery_info.get(CONF_HOST)
        name = discovery_info.get(CONF_NAME, DEFAULT_NAME)
        _LOGGER.debug("Discovered a new Samsung Printer: %s", discovery_info)
        # Test if the discovered device actually is a syncthru printer
        if not test_syncthru(host):
            _LOGGER.error("No SyncThru Printer found at %s", host)
            return
        monitored = DEFAULT_MONITORED_CONDITIONS
    else:
        host = config.get(CONF_RESOURCE)
        name = config.get(CONF_NAME)
        monitored = config.get(CONF_MONITORED_CONDITIONS)

    # Main device, always added
    try:
        printer = SyncThru(host)
    except TypeError:
        # if an exception is thrown, printer cannot be set up
        return

    printer.update()
    devices = [SyncThruMainSensor(printer, name)]

    for key in printer.toner_status(filter_supported=True):
        if 'toner_{}'.format(key) in monitored:
            devices.append(SyncThruTonerSensor(printer, name, key))
    for key in printer.drum_status(filter_supported=True):
        if 'drum_{}'.format(key) in monitored:
            devices.append(SyncThruDrumSensor(printer, name, key))
    for key in printer.input_tray_status(filter_supported=True):
        if 'tray_{}'.format(key) in monitored:
            devices.append(SyncThruInputTraySensor(printer, name, key))
    for key in printer.output_tray_status():
        if 'output_tray_{}'.format(key) in monitored:
            devices.append(SyncThruOutputTraySensor(printer, name, key))

    add_devices(devices, True)
Beispiel #6
0
async def async_setup_entry(hass, config_entry, async_add_entities):
    """Set up from config entry."""

    session = aiohttp_client.async_get_clientsession(hass)

    printer = SyncThru(config_entry.data[CONF_URL], session)
    # Test if the discovered device actually is a syncthru printer
    # and fetch the available toner/drum/etc
    try:
        # No error is thrown when the device is off
        # (only after user added it manually)
        # therefore additional catches are inside the Sensor below
        await printer.update()
        supp_toner = printer.toner_status(filter_supported=True)
        supp_drum = printer.drum_status(filter_supported=True)
        supp_tray = printer.input_tray_status(filter_supported=True)
        supp_output_tray = printer.output_tray_status()
    except ValueError as ex:
        raise SyncThruNotSupported from ex
    else:
        if printer.is_unknown_state():
            raise PlatformNotReady

    name = config_entry.data[CONF_NAME]
    devices = [SyncThruMainSensor(printer, name)]

    for key in supp_toner:
        devices.append(SyncThruTonerSensor(printer, name, key))
    for key in supp_drum:
        devices.append(SyncThruDrumSensor(printer, name, key))
    for key in supp_tray:
        devices.append(SyncThruInputTraySensor(printer, name, key))
    for key in supp_output_tray:
        devices.append(SyncThruOutputTraySensor(printer, name, key))

    async_add_entities(devices, True)
def device_connections(printer: SyncThru) -> set[tuple[str, str]]:
    """Get device connections for device registry."""
    if mac := printer.raw().get("identity", {}).get("mac_addr"):
        return {(dr.CONNECTION_NETWORK_MAC, mac)}
def device_identifiers(printer: SyncThru) -> set[tuple[str, str]] | None:
    """Get device identifiers for device registry."""
    serial = printer.serial_number()
    if serial is None:
        return None
    return {(DOMAIN, serial)}
Beispiel #9
0
 async def fetch():
     async with aiohttp.ClientSession() as session:
         self.syncthru = SyncThru(
             self.url, session, connection_mode=ConnectionMode.API
         )
         await self.syncthru.update()
Beispiel #10
0
class NonSyncthruWebTest(unittest.TestCase):

    server = None
    server_control = None  # type: Server
    port = 0
    url = "http://localhost:80"
    syncthru = None  # type: SyncThru

    def test_no_syncthru(self) -> None:
        """Test that an error is thrown when no syncthru is supported"""
        # Create an arbitrary subclass of TCP Server as the server to be started
        # Here, it is an Simple HTTP file serving server
        handler = SyncThruRequestHandler

        max_retries = 10
        r = 0
        while not self.server:
            try:
                # Connect to any open port
                self.server = SyncThruServer((ADDRESS, 0), handler)
            except OSError:
                if r < max_retries:
                    r += 1
                else:
                    raise
                time.sleep(1)

        self.server_control = Server(self.server)
        self.port = self.server_control.get_port()
        self.url = "{}:{}".format(ADDRESS, self.port)
        # Start test server before running any tests
        self.server_control.start_server()

        # Block server to make sure we get "no support"
        self.server.set_blocked()

        try:

            async def fetch() -> None:
                async with aiohttp.ClientSession() as session:
                    self.syncthru = SyncThru(
                        self.url, session, connection_mode=ConnectionMode.API
                    )
                    await self.syncthru.update()

            loop = asyncio.get_event_loop()
            loop.run_until_complete(fetch())
            self.fail(
                "No error thrown when noticing that the host does not support Syncthru"
            )
        except SyncThruAPINotSupported:
            pass

    def test_offline_unknown(self) -> None:
        """Test that nothing is returned when syncthru is offline"""

        async def fetch() -> None:
            async with aiohttp.ClientSession() as session:
                self.syncthru = SyncThru(
                    self.url, session, connection_mode=ConnectionMode.API
                )
                await self.syncthru.update()

        loop = asyncio.get_event_loop()
        loop.run_until_complete(fetch())
        self.assertFalse(self.syncthru.is_online())
        self.assertTrue(self.syncthru.is_unknown_state())
Beispiel #11
0
class SyncthruHTMLTest(unittest.TestCase):

    server = None
    server_control = None  # type: Server
    port = 0
    url = "http://localhost:80"
    syncthru = None  # type: SyncThru

    def setUp(self) -> None:
        # Create an arbitrary subclass of TCP Server as the server to be started
        # Here, it is an Simple HTTP file serving server
        handler = SyncThruRequestHandler

        max_retries = 10
        r = 0
        while not self.server:
            try:
                # Connect to any open port
                self.server = SyncThruServer((ADDRESS, 0), handler)
            except OSError:
                if r < max_retries:
                    r += 1
                else:
                    raise
                time.sleep(1)

        self.server_control = Server(self.server)
        self.port = self.server_control.get_port()
        self.url = "{}:{}".format(ADDRESS, self.port)
        # Start test server before running any tests
        self.server_control.start_server()

        async def fetch():
            async with aiohttp.ClientSession() as session:
                self.syncthru = SyncThru(
                    self.url, session, connection_mode=ConnectionMode.HTML
                )
                await self.syncthru.update()

        loop = asyncio.get_event_loop()
        loop.run_until_complete(fetch())

    def test_location(self) -> None:
        self.assertEqual(self.syncthru.location(), RAW_HTML["identity"]["location"])

    def test_model_name(self) -> None:
        self.assertEqual(self.syncthru.model(), RAW_HTML["identity"]["model_name"])

    def test_hostname(self) -> None:
        self.assertEqual(self.syncthru.hostname(), RAW_HTML["identity"]["host_name"])

    def test_mac_address(self) -> None:
        self.assertEqual(self.syncthru.mac_address(), RAW_HTML["identity"]["mac_addr"])

    def test_toner_filter(self) -> None:
        self.assertDictEqual(
            self.syncthru.toner_status(True),
            {"black": {"opt": 1, "remaining": 66, "newError": ""}},
        )

    def test_input_tray_filter(self) -> None:
        self.assertDictEqual(
            self.syncthru.input_tray_status(True),
            {
                "tray_1": {
                    "opt": 1,
                    "newError": "",
                }
            },
        )
Beispiel #12
0
class SyncthruAPITest(unittest.TestCase):

    server = None
    server_control = None  # type: Server
    port = 0
    url = "http://localhost:80"
    syncthru = None  # type: SyncThru

    def setUp(self) -> None:
        # Create an arbitrary subclass of TCP Server as the server to be started
        # Here, it is an Simple HTTP file serving server
        handler = SyncThruRequestHandler

        max_retries = 10
        r = 0
        while not self.server:
            try:
                # Connect to any open port
                self.server = SyncThruServer((ADDRESS, 0), handler)
            except OSError:
                if r < max_retries:
                    r += 1
                else:
                    raise
                time.sleep(1)

        self.server_control = Server(self.server)
        self.port = self.server_control.get_port()
        self.url = "{}:{}".format(ADDRESS, self.port)
        # Start test server before running any tests
        self.server_control.start_server()

        async def fetch():
            async with aiohttp.ClientSession() as session:
                self.syncthru = SyncThru(
                    self.url, session, connection_mode=ConnectionMode.API
                )
                await self.syncthru.update()

        loop = asyncio.get_event_loop()
        loop.run_until_complete(fetch())

    def test_online(self) -> None:
        self.assertTrue(self.syncthru.is_online())

    def test_status_normal(self) -> None:
        self.assertEqual(self.syncthru.device_status(), SyncthruState.NORMAL)

    def test_status_details(self) -> None:
        self.assertEqual(self.syncthru.device_status_details(), "Sleeping...")

    def test_model(self) -> None:
        self.assertEqual(self.syncthru.model(), RAW["identity"]["model_name"])

    def test_toner_filter(self) -> None:
        self.assertDictEqual(
            self.syncthru.toner_status(True),
            {"black": {"opt": 1, "remaining": 58, "cnt": 229, "newError": ""}},
        )

    def test_toner_no_filter(self) -> None:
        empty = {"opt": 0, "remaining": 0, "cnt": 0, "newError": ""}
        self.assertDictEqual(
            self.syncthru.toner_status(False),
            {
                "yellow": empty,
                "magenta": empty,
                "cyan": empty,
                "black": {"opt": 1, "remaining": 58, "cnt": 229, "newError": ""},
            },
        )

    def test_input_tray_filter(self) -> None:
        self.assertDictEqual(
            self.syncthru.input_tray_status(True),
            {
                "tray_1": {
                    "capa": 150,
                    "newError": "",
                    "opt": 1,
                    "paper_size1": 4,
                    "paper_size2": 0,
                    "paper_type1": 2,
                    "paper_type2": 0,
                }
            },
        )

    def test_input_tray_no_filter(self) -> None:
        self.assertDictEqual(
            self.syncthru.input_tray_status(False),
            {
                "tray_1": {
                    "capa": 150,
                    "newError": "",
                    "opt": 1,
                    "paper_size1": 4,
                    "paper_size2": 0,
                    "paper_type1": 2,
                    "paper_type2": 0,
                },
                "tray_2": {
                    "capa": 0,
                    "newError": "",
                    "opt": 0,
                    "paper_size1": 0,
                    "paper_size2": 0,
                    "paper_type1": 2,
                    "paper_type2": 0,
                },
                "tray_3": {
                    "capa": 0,
                    "newError": "",
                    "opt": 0,
                    "paper_size1": 0,
                    "paper_size2": 0,
                    "paper_type1": 2,
                    "paper_type2": 0,
                },
                "tray_4": {
                    "capa": 0,
                    "newError": "",
                    "opt": 2,
                    "paper_size1": 0,
                    "paper_size2": 0,
                    "paper_type1": 2,
                    "paper_type2": 0,
                },
                "tray_5": {
                    "opt": 0,
                    "paper_size1": 0,
                    "paper_size2": 0,
                    "paper_type1": 0,
                    "paper_type2": 0,
                    "capa": 0,
                    "newError": "0",
                },
                "mp": {
                    "opt": 0,
                    "paper_size1": 0,
                    "paper_size2": 0,
                    "paper_type1": 2,
                    "paper_type2": 0,
                    "capa": 0,
                    "newError": "",
                },
                "manual": {
                    "opt": 0,
                    "paper_size1": 0,
                    "paper_size2": 0,
                    "paper_type1": 2,
                    "paper_type2": 0,
                    "capa": 0,
                    "newError": "",
                },
            },
        )

    def test_output_tray(self) -> None:
        self.assertEqual(
            self.syncthru.output_tray_status(),
            {0: {"capacity": 100, "name": 1, "status": ""}},
        )

    def test_drum_status_filter(self) -> None:
        self.assertEqual(self.syncthru.drum_status(True), {})

    def test_drum_status_no_filter(self) -> None:
        self.assertEqual(
            self.syncthru.drum_status(False),
            {
                "black": {"newError": "", "opt": 0, "remaining": 0},
                "cyan": {"newError": "", "opt": 0, "remaining": 100},
                "magenta": {"newError": "", "opt": 0, "remaining": 100},
                "yellow": {"newError": "", "opt": 0, "remaining": 100},
            },
        )

    def test_location(self) -> None:
        self.assertEqual(self.syncthru.location(), RAW["identity"]["location"])

    def test_serial_number(self) -> None:
        self.assertEqual(self.syncthru.serial_number(), RAW["identity"]["serial_num"])

    def test_hostname(self) -> None:
        self.assertEqual(self.syncthru.hostname(), RAW["identity"]["host_name"])

    def test_cap(self) -> None:
        self.assertEqual(self.syncthru.capability(), RAW["capability"])

    def tearDown(self) -> None:
        self.server_control.stop_server()
Beispiel #13
0
def device_identifiers(printer: SyncThru) -> set[tuple[str, str]]:
    """Get device identifiers for device registry."""
    return {(DOMAIN, printer.serial_number())}