Example #1
0
def setup(opp: OpenPeerPower, base_config):  # noqa: C901
    """Set up the CEC capability."""

    # Parse configuration into a dict of device name to physical address
    # represented as a list of four elements.
    device_aliases = {}
    devices = base_config[DOMAIN].get(CONF_DEVICES, {})
    _LOGGER.debug("Parsing config %s", devices)
    device_aliases.update(parse_mapping(devices))
    _LOGGER.debug("Parsed devices: %s", device_aliases)

    platform = base_config[DOMAIN].get(CONF_PLATFORM, SWITCH)

    loop = (
        # Create own thread if more than 1 CPU
        opp.loop if multiprocessing.cpu_count() < 2 else None)
    host = base_config[DOMAIN].get(CONF_HOST)
    display_name = base_config[DOMAIN].get(CONF_DISPLAY_NAME,
                                           DEFAULT_DISPLAY_NAME)
    if host:
        adapter = TcpAdapter(host, name=display_name, activate_source=False)
    else:
        adapter = CecAdapter(name=display_name[:12], activate_source=False)
    hdmi_network = HDMINetwork(adapter, loop=loop)

    def _adapter_watchdog(now=None):
        _LOGGER.debug("Reached _adapter_watchdog")
        event.async_call_later(opp, WATCHDOG_INTERVAL, _adapter_watchdog)
        if not adapter.initialized:
            _LOGGER.info("Adapter not initialized; Trying to restart")
            opp.bus.fire(EVENT_HDMI_CEC_UNAVAILABLE)
            adapter.init()

    hdmi_network.set_initialized_callback(
        partial(event.async_call_later, opp, WATCHDOG_INTERVAL,
                _adapter_watchdog))

    def _volume(call):
        """Increase/decrease volume and mute/unmute system."""
        mute_key_mapping = {
            ATTR_TOGGLE: KEY_MUTE_TOGGLE,
            ATTR_ON: KEY_MUTE_ON,
            ATTR_OFF: KEY_MUTE_OFF,
        }
        for cmd, att in call.data.items():
            if cmd == CMD_UP:
                _process_volume(KEY_VOLUME_UP, att)
            elif cmd == CMD_DOWN:
                _process_volume(KEY_VOLUME_DOWN, att)
            elif cmd == CMD_MUTE:
                hdmi_network.send_command(
                    KeyPressCommand(mute_key_mapping[att],
                                    dst=ADDR_AUDIOSYSTEM))
                hdmi_network.send_command(
                    KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))
                _LOGGER.info("Audio muted")
            else:
                _LOGGER.warning("Unknown command %s", cmd)

    def _process_volume(cmd, att):
        if isinstance(att, (str, )):
            att = att.strip()
        if att == CMD_PRESS:
            hdmi_network.send_command(
                KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM))
        elif att == CMD_RELEASE:
            hdmi_network.send_command(KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))
        else:
            att = 1 if att == "" else int(att)
            for _ in range(0, att):
                hdmi_network.send_command(
                    KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM))
                hdmi_network.send_command(
                    KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))

    def _tx(call):
        """Send CEC command."""
        data = call.data
        if ATTR_RAW in data:
            command = CecCommand(data[ATTR_RAW])
        else:
            if ATTR_SRC in data:
                src = data[ATTR_SRC]
            else:
                src = ADDR_UNREGISTERED
            if ATTR_DST in data:
                dst = data[ATTR_DST]
            else:
                dst = ADDR_BROADCAST
            if ATTR_CMD in data:
                cmd = data[ATTR_CMD]
            else:
                _LOGGER.error("Attribute 'cmd' is missing")
                return False
            if ATTR_ATT in data:
                if isinstance(data[ATTR_ATT], (list, )):
                    att = data[ATTR_ATT]
                else:
                    att = reduce(lambda x, y: f"{x}:{y:x}", data[ATTR_ATT])
            else:
                att = ""
            command = CecCommand(cmd, dst, src, att)
        hdmi_network.send_command(command)

    def _standby(call):
        hdmi_network.standby()

    def _power_on(call):
        hdmi_network.power_on()

    def _select_device(call):
        """Select the active device."""
        addr = call.data[ATTR_DEVICE]
        if not addr:
            _LOGGER.error("Device not found: %s", call.data[ATTR_DEVICE])
            return
        if addr in device_aliases:
            addr = device_aliases[addr]
        else:
            entity = opp.states.get(addr)
            _LOGGER.debug("Selecting entity %s", entity)
            if entity is not None:
                addr = entity.attributes["physical_address"]
                _LOGGER.debug("Address acquired: %s", addr)
                if addr is None:
                    _LOGGER.error("Device %s has not physical address",
                                  call.data[ATTR_DEVICE])
                    return
        if not isinstance(addr, (PhysicalAddress, )):
            addr = PhysicalAddress(addr)
        hdmi_network.active_source(addr)
        _LOGGER.info("Selected %s (%s)", call.data[ATTR_DEVICE], addr)

    def _update(call):
        """
        Update if device update is needed.

        Called by service, requests CEC network to update data.
        """
        hdmi_network.scan()

    def _new_device(device):
        """Handle new devices which are detected by HDMI network."""
        key = f"{DOMAIN}.{device.name}"
        opp.data[key] = device
        ent_platform = base_config[DOMAIN][CONF_TYPES].get(key, platform)
        discovery.load_platform(
            opp,
            ent_platform,
            DOMAIN,
            discovered={ATTR_NEW: [key]},
            opp_config=base_config,
        )

    def _shutdown(call):
        hdmi_network.stop()

    def _start_cec(callback_event):
        """Register services and start HDMI network to watch for devices."""
        opp.services.register(DOMAIN, SERVICE_SEND_COMMAND, _tx,
                              SERVICE_SEND_COMMAND_SCHEMA)
        opp.services.register(DOMAIN,
                              SERVICE_VOLUME,
                              _volume,
                              schema=SERVICE_VOLUME_SCHEMA)
        opp.services.register(
            DOMAIN,
            SERVICE_UPDATE_DEVICES,
            _update,
            schema=SERVICE_UPDATE_DEVICES_SCHEMA,
        )
        opp.services.register(DOMAIN, SERVICE_POWER_ON, _power_on)
        opp.services.register(DOMAIN, SERVICE_STANDBY, _standby)
        opp.services.register(DOMAIN, SERVICE_SELECT_DEVICE, _select_device)

        hdmi_network.set_new_device_callback(_new_device)
        hdmi_network.start()

    opp.bus.listen_once(EVENT_OPENPEERPOWER_START, _start_cec)
    opp.bus.listen_once(EVENT_OPENPEERPOWER_STOP, _shutdown)
    return True
Example #2
0
def setup(hass: HomeAssistant, base_config):
    """Setup CEC capability."""
    from pycec.network import HDMINetwork
    from pycec.commands import CecCommand, KeyReleaseCommand, KeyPressCommand
    from pycec.const import KEY_VOLUME_UP, KEY_VOLUME_DOWN, KEY_MUTE_ON, \
        KEY_MUTE_OFF, KEY_MUTE_TOGGLE, ADDR_AUDIOSYSTEM, ADDR_BROADCAST, \
        ADDR_UNREGISTERED
    from pycec.cec import CecAdapter
    from pycec.tcp import TcpAdapter

    # Parse configuration into a dict of device name to physical address
    # represented as a list of four elements.
    device_aliases = {}
    devices = base_config[DOMAIN].get(CONF_DEVICES, {})
    _LOGGER.debug("Parsing config %s", devices)
    device_aliases.update(parse_mapping(devices))
    _LOGGER.debug("Parsed devices: %s", device_aliases)

    platform = base_config[DOMAIN].get(CONF_PLATFORM, SWITCH)

    loop = (
        # Create own thread if more than 1 CPU
        hass.loop if multiprocessing.cpu_count() < 2 else None)
    host = base_config[DOMAIN].get(CONF_HOST, None)
    display_name = base_config[DOMAIN].get(CONF_DISPLAY_NAME,
                                           DEFAULT_DISPLAY_NAME)
    if host:
        adapter = TcpAdapter(host, name=display_name, activate_source=False)
    else:
        adapter = CecAdapter(name=display_name, activate_source=False)
    hdmi_network = HDMINetwork(adapter, loop=loop)

    def _volume(call):
        """Increase/decrease volume and mute/unmute system."""
        mute_key_mapping = {
            ATTR_TOGGLE: KEY_MUTE_TOGGLE,
            ATTR_ON: KEY_MUTE_ON,
            ATTR_OFF: KEY_MUTE_OFF
        }
        for cmd, att in call.data.items():
            if cmd == CMD_UP:
                _process_volume(KEY_VOLUME_UP, att)
            elif cmd == CMD_DOWN:
                _process_volume(KEY_VOLUME_DOWN, att)
            elif cmd == CMD_MUTE:
                hdmi_network.send_command(
                    KeyPressCommand(mute_key_mapping[att],
                                    dst=ADDR_AUDIOSYSTEM))
                hdmi_network.send_command(
                    KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))
                _LOGGER.info("Audio muted")
            else:
                _LOGGER.warning("Unknown command %s", cmd)

    def _process_volume(cmd, att):
        if isinstance(att, (str, )):
            att = att.strip()
        if att == CMD_PRESS:
            hdmi_network.send_command(
                KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM))
        elif att == CMD_RELEASE:
            hdmi_network.send_command(KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))
        else:
            att = 1 if att == "" else int(att)
            for _ in range(0, att):
                hdmi_network.send_command(
                    KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM))
                hdmi_network.send_command(
                    KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))

    def _tx(call):
        """Send CEC command."""
        data = call.data
        if ATTR_RAW in data:
            command = CecCommand(data[ATTR_RAW])
        else:
            if ATTR_SRC in data:
                src = data[ATTR_SRC]
            else:
                src = ADDR_UNREGISTERED
            if ATTR_DST in data:
                dst = data[ATTR_DST]
            else:
                dst = ADDR_BROADCAST
            if ATTR_CMD in data:
                cmd = data[ATTR_CMD]
            else:
                _LOGGER.error("Attribute 'cmd' is missing")
                return False
            if ATTR_ATT in data:
                if isinstance(data[ATTR_ATT], (list, )):
                    att = data[ATTR_ATT]
                else:
                    att = reduce(lambda x, y: "%s:%x" % (x, y), data[ATTR_ATT])
            else:
                att = ""
            command = CecCommand(cmd, dst, src, att)
        hdmi_network.send_command(command)

    def _standby(call):
        hdmi_network.standby()

    def _power_on(call):
        hdmi_network.power_on()

    def _select_device(call):
        """Select the active device."""
        from pycec.network import PhysicalAddress

        addr = call.data[ATTR_DEVICE]
        if not addr:
            _LOGGER.error("Device not found: %s", call.data[ATTR_DEVICE])
            return
        if addr in device_aliases:
            addr = device_aliases[addr]
        else:
            entity = hass.states.get(addr)
            _LOGGER.debug("Selecting entity %s", entity)
            if entity is not None:
                addr = entity.attributes['physical_address']
                _LOGGER.debug("Address acquired: %s", addr)
                if addr is None:
                    _LOGGER.error("Device %s has not physical address.",
                                  call.data[ATTR_DEVICE])
                    return
        if not isinstance(addr, (PhysicalAddress, )):
            addr = PhysicalAddress(addr)
        hdmi_network.active_source(addr)
        _LOGGER.info("Selected %s (%s)", call.data[ATTR_DEVICE], addr)

    def _update(call):
        """
        Callback called when device update is needed.

        - called by service, requests CEC network to update data.
        """
        hdmi_network.scan()

    def _new_device(device):
        """Called when new device is detected by HDMI network."""
        key = DOMAIN + '.' + device.name
        hass.data[key] = device
        ent_platform = base_config[DOMAIN][CONF_TYPES].get(key, platform)
        discovery.load_platform(hass,
                                ent_platform,
                                DOMAIN,
                                discovered={ATTR_NEW: [key]},
                                hass_config=base_config)

    def _shutdown(call):
        hdmi_network.stop()

    def _start_cec(event):
        """Register services and start HDMI network to watch for devices."""
        descriptions = load_yaml_config_file(
            os.path.join(os.path.dirname(__file__), 'services.yaml'))[DOMAIN]
        hass.services.register(DOMAIN, SERVICE_SEND_COMMAND, _tx,
                               descriptions[SERVICE_SEND_COMMAND],
                               SERVICE_SEND_COMMAND_SCHEMA)
        hass.services.register(DOMAIN, SERVICE_VOLUME, _volume,
                               descriptions[SERVICE_VOLUME],
                               SERVICE_VOLUME_SCHEMA)
        hass.services.register(DOMAIN, SERVICE_UPDATE_DEVICES, _update,
                               descriptions[SERVICE_UPDATE_DEVICES],
                               SERVICE_UPDATE_DEVICES_SCHEMA)
        hass.services.register(DOMAIN, SERVICE_POWER_ON, _power_on)
        hass.services.register(DOMAIN, SERVICE_STANDBY, _standby)
        hass.services.register(DOMAIN, SERVICE_SELECT_DEVICE, _select_device)

        hdmi_network.set_new_device_callback(_new_device)
        hdmi_network.start()

    hass.bus.listen_once(EVENT_HOMEASSISTANT_START, _start_cec)
    hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown)
    return True
Example #3
0
def setup(hass: HomeAssistant, base_config: ConfigType) -> bool:  # noqa: C901
    """Set up the CEC capability."""

    # Parse configuration into a dict of device name to physical address
    # represented as a list of four elements.
    device_aliases = {}
    devices = base_config[DOMAIN].get(CONF_DEVICES, {})
    _LOGGER.debug("Parsing config %s", devices)
    device_aliases.update(parse_mapping(devices))
    _LOGGER.debug("Parsed devices: %s", device_aliases)

    platform = base_config[DOMAIN].get(CONF_PLATFORM, SWITCH)

    loop = (
        # Create own thread if more than 1 CPU
        hass.loop if multiprocessing.cpu_count() < 2 else None)
    host = base_config[DOMAIN].get(CONF_HOST)
    display_name = base_config[DOMAIN].get(CONF_DISPLAY_NAME,
                                           DEFAULT_DISPLAY_NAME)
    if host:
        adapter = TcpAdapter(host, name=display_name, activate_source=False)
    else:
        adapter = CecAdapter(name=display_name[:12], activate_source=False)
    hdmi_network = HDMINetwork(adapter, loop=loop)

    def _adapter_watchdog(now=None):
        _LOGGER.debug("Reached _adapter_watchdog")
        event.async_call_later(hass, WATCHDOG_INTERVAL, _adapter_watchdog)
        if not adapter.initialized:
            _LOGGER.info("Adapter not initialized; Trying to restart")
            hass.bus.fire(EVENT_HDMI_CEC_UNAVAILABLE)
            adapter.init()

    @callback
    def _async_initialized_callback(*_: Any):
        """Add watchdog on initialization."""
        return event.async_call_later(hass, WATCHDOG_INTERVAL,
                                      _adapter_watchdog)

    hdmi_network.set_initialized_callback(_async_initialized_callback)

    def _volume(call: ServiceCall) -> None:
        """Increase/decrease volume and mute/unmute system."""
        mute_key_mapping = {
            ATTR_TOGGLE: KEY_MUTE_TOGGLE,
            ATTR_ON: KEY_MUTE_ON,
            ATTR_OFF: KEY_MUTE_OFF,
        }
        for cmd, att in call.data.items():
            if cmd == CMD_UP:
                _process_volume(KEY_VOLUME_UP, att)
            elif cmd == CMD_DOWN:
                _process_volume(KEY_VOLUME_DOWN, att)
            elif cmd == CMD_MUTE:
                hdmi_network.send_command(
                    KeyPressCommand(mute_key_mapping[att],
                                    dst=ADDR_AUDIOSYSTEM))
                hdmi_network.send_command(
                    KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))
                _LOGGER.info("Audio muted")
            else:
                _LOGGER.warning("Unknown command %s", cmd)

    def _process_volume(cmd, att):
        if isinstance(att, (str, )):
            att = att.strip()
        if att == CMD_PRESS:
            hdmi_network.send_command(
                KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM))
        elif att == CMD_RELEASE:
            hdmi_network.send_command(KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))
        else:
            att = 1 if att == "" else int(att)
            for _ in range(0, att):
                hdmi_network.send_command(
                    KeyPressCommand(cmd, dst=ADDR_AUDIOSYSTEM))
                hdmi_network.send_command(
                    KeyReleaseCommand(dst=ADDR_AUDIOSYSTEM))

    def _tx(call: ServiceCall) -> None:
        """Send CEC command."""
        data = call.data
        if ATTR_RAW in data:
            command = CecCommand(data[ATTR_RAW])
        else:
            if ATTR_SRC in data:
                src = data[ATTR_SRC]
            else:
                src = ADDR_UNREGISTERED
            if ATTR_DST in data:
                dst = data[ATTR_DST]
            else:
                dst = ADDR_BROADCAST
            if ATTR_CMD in data:
                cmd = data[ATTR_CMD]
            else:
                _LOGGER.error("Attribute 'cmd' is missing")
                return
            if ATTR_ATT in data:
                if isinstance(data[ATTR_ATT], (list, )):
                    att = data[ATTR_ATT]
                else:
                    att = reduce(lambda x, y: f"{x}:{y:x}", data[ATTR_ATT])
            else:
                att = ""
            command = CecCommand(cmd, dst, src, att)
        hdmi_network.send_command(command)

    def _standby(call: ServiceCall) -> None:
        hdmi_network.standby()

    def _power_on(call: ServiceCall) -> None:
        hdmi_network.power_on()

    def _select_device(call: ServiceCall) -> None:
        """Select the active device."""
        if not (addr := call.data[ATTR_DEVICE]):
            _LOGGER.error("Device not found: %s", call.data[ATTR_DEVICE])
            return
        if addr in device_aliases:
            addr = device_aliases[addr]
        else:
            entity = hass.states.get(addr)
            _LOGGER.debug("Selecting entity %s", entity)
            if entity is not None:
                addr = entity.attributes["physical_address"]
                _LOGGER.debug("Address acquired: %s", addr)
                if addr is None:
                    _LOGGER.error("Device %s has not physical address",
                                  call.data[ATTR_DEVICE])
                    return
        if not isinstance(addr, (PhysicalAddress, )):
            addr = PhysicalAddress(addr)
        hdmi_network.active_source(addr)
        _LOGGER.info("Selected %s (%s)", call.data[ATTR_DEVICE], addr)