Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Volume and DPad control #2

Open
alessaba opened this issue Feb 14, 2021 · 2 comments
Open

Volume and DPad control #2

alessaba opened this issue Feb 14, 2021 · 2 comments
Labels
enhancement New feature or request

Comments

@alessaba
Copy link

HI,
I stumbled upon your plugin and it works pretty nicely and reliably.
I noticed there's a feature missing though: Volume and Dpad control.
If you look, for example, at this plugin, you can see there is the option to access a dpad interface, and as far as i know, volume control is also possible.
I am very interested in this feature but honestly i don't know how to implement accessories in homebridge, so i am gonna file this issue.

A Nice and simple implementation would be having URLs in the config like

  • volUpURL
  • volDownURL
  • keyUpURL
  • keyDownURL
  • keyLeftURL
  • keyRightURL
    and so on.

This is part of how the additional interface should look like in the Home App (screenshot taken from the plugin linked above)
preview

Thanks in advance, and hope to hear back from you soon

@alessaba alessaba added the enhancement New feature or request label Feb 14, 2021
@Capevace
Copy link
Owner

Hey,

yeah I've been having trouble getting all the available TV characteristics to work but as long as it works in that other plugin I suppose it should work here too right? :)

I'll have a look when I'm back home as I don't have my Homebridge available atm.

Note to self: I think the following source part is relevant:
Link to source

    // handle remote control input
    tvService
      .getCharacteristic(this.Characteristic.RemoteKey)
      .on(`set`, async (newValue, callback) => {
        try {
          switch (newValue) {
            case this.Characteristic.RemoteKey.REWIND: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: REWIND`)
              await remote.rewind(this.getDevice(usn))
              break
            }
            case this.Characteristic.RemoteKey.FAST_FORWARD: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: FAST_FORWARD`)
              await remote.fastForward(this.getDevice(usn))
              break
            }
            case this.Characteristic.RemoteKey.NEXT_TRACK: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: NEXT_TRACK`)
              break
            }
            case this.Characteristic.RemoteKey.PREVIOUS_TRACK: {
              this.log.debug(
                `${tvName} - SET Remote Key Pressed: PREVIOUS_TRACK`,
              )
              break
            }
            case this.Characteristic.RemoteKey.ARROW_UP: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: ARROW_UP`)
              await remote.arrowUp(this.getDevice(usn))
              break
            }
            case this.Characteristic.RemoteKey.ARROW_DOWN: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: ARROW_DOWN`)
              await remote.arrowDown(this.getDevice(usn))
              break
            }
            case this.Characteristic.RemoteKey.ARROW_LEFT: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: ARROW_LEFT`)
              await remote.arrowLeft(this.getDevice(usn))
              break
            }
            case this.Characteristic.RemoteKey.ARROW_RIGHT: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: ARROW_RIGHT`)
              await remote.arrowRight(this.getDevice(usn))
              break
            }
            case this.Characteristic.RemoteKey.SELECT: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: SELECT`)
              await remote.select(this.getDevice(usn))
              break
            }
            case this.Characteristic.RemoteKey.BACK: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: BACK`)
              await remote.back(this.getDevice(usn))
              break
            }
            case this.Characteristic.RemoteKey.EXIT: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: EXIT`)
              await remote.exit(this.getDevice(usn))
              break
            }
            case this.Characteristic.RemoteKey.PLAY_PAUSE: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: PLAY_PAUSE`)
              break
            }
            case this.Characteristic.RemoteKey.INFORMATION: {
              this.log.debug(`${tvName} - SET Remote Key Pressed: INFORMATION`)
              await remote.info(this.getDevice(usn))
              break
            }
          }
        } catch (err) {
          callback(err)
          return
        }
        callback(null)
      })

    /**
     * Create a speaker service to allow volume control
     */
    const speakerService = tvAccessory.addService(
      this.Service.TelevisionSpeaker,
    )

    /**
     * We have these scenarios
     * 1. GetVolume + SetVolume:
     *    => VolumeControlType.Absolute
     *    => Add Volume Characteristic with get/set
     *    => Also add VolumeSelector
     * (2.) GetVolume but (no SetVolume)
     *    ...same as 1. because SetVolume can be simulated
     *       by inc./decr. volume step by step
     *    => ~~VolumeControlType.RELATIVE_WITH_CURRENT~~
     *    => ~~Add Volume Characteristic with getter only~~
     * 3. No GetVolume upnp capabilities:
     *    => VolumeControlType.RELATIVE
     *    => Add VolumeSelector Characteristic
     */

    let volumeControlType = this.Characteristic.VolumeControlType.ABSOLUTE
    const canGetVolume = hasCapability(device, `GetVolume`)
    if (!canGetVolume) {
      volumeControlType = this.Characteristic.VolumeControlType.RELATIVE
      this.log.debug(`${tvName} - VolumeControlType RELATIVE`)
    } else {
      this.log.debug(`${tvName} - VolumeControlType ABSOLUTE`)
    }

    speakerService
      .setCharacteristic(
        this.Characteristic.Active,
        this.Characteristic.Active.ACTIVE,
      )
      .setCharacteristic(
        this.Characteristic.VolumeControlType,
        volumeControlType,
      )

    if (canGetVolume) {
      speakerService
        .getCharacteristic(this.Characteristic.Volume)
        .on(`get`, async (callback) => {
          this.log.debug(`${tvName} - GET Volume`)
          try {
            const volume = await remote.getVolume(this.getDevice(usn))
            callback(null, volume)
          } catch (err) {
            callback(err)
          }
        })
    }

    // When we can get the volume, we can always set the volume
    // directly or simulate it by  multiple volup/downs
    if (canGetVolume) {
      speakerService
        .getCharacteristic(this.Characteristic.Volume)
        .on(`set`, async (newValue, callback) => {
          this.log.debug(`${tvName} - SET Volume => setNewValue: ${newValue}`)
          try {
            await remote.setVolume(this.getDevice(usn), newValue)
            speakerService
              .getCharacteristic(this.Characteristic.Mute)
              .updateValue(false)
            callback(null)
          } catch (err) {
            callback(err)
          }
        })
    }

    // VolumeSelector can be used in all scenarios
    speakerService
      .getCharacteristic(this.Characteristic.VolumeSelector)
      .on(`set`, async (newValue, callback) => {
        this.log.debug(
          `${tvName} - SET VolumeSelector => setNewValue: ${newValue}`,
        )
        try {
          if (newValue === this.Characteristic.VolumeSelector.INCREMENT) {
            await remote.volumeUp(this.getDevice(usn))
          } else {
            await remote.volumeDown(this.getDevice(usn))
          }
          const volume = await remote.getVolume(this.getDevice(usn))
          speakerService
            .getCharacteristic(this.Characteristic.Mute)
            .updateValue(false)
          speakerService
            .getCharacteristic(this.Characteristic.Volume)
            .updateValue(volume)
          callback(null)
        } catch (err) {
          callback(err)
        }
      })

    const canGetMute = hasCapability(device, `GetMute`)
    speakerService
      .getCharacteristic(this.Characteristic.Mute)
      .on(`get`, async (callback) => {
        this.log.debug(`${tvName} - GET Mute`)
        // When mute cannot be fetched always pretend not to be muted
        // for now...
        if (!canGetMute) {
          callback(null, false)
          return
        }
        try {
          const muted = await remote.getMute(this.getDevice(usn))
          callback(null, muted)
        } catch (err) {
          callback(err)
        }
      })
      .on(`set`, async (value, callback) => {
        this.log.debug(`${tvName} - SET Mute: ${value}`)
        try {
          await remote.setMute(this.getDevice(usn), value)
          callback(null)
        } catch (err) {
          callback(err)
        }
      })

    tvService.addLinkedService(speakerService)

@mdhiggins
Copy link

Not sure if others have seen this plugin or not but similar project that has the dpad working
https://github.com/emptygalaxy/homebridge-http-television

Looks like no speaker support yet though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants