homebridge-soundtouch-plugins
Version:
Homebridge SoundTouch plugins
120 lines (108 loc) • 4.17 kB
text/typescript
import { KeyValue, SourceStatus } from '@bossoq/soundtouch-api'
import { deviceIsOn } from './sound-touch-device'
import { callbackify, HomebridgeAccessoryWrapper } from './utils'
import { Characteristic, Service } from 'homebridge'
import { DeviceOnOffListener, SoundTouchDevice } from './types'
export abstract class SoundTouchVolume {
protected readonly device: SoundTouchDevice
protected readonly accessoryWrapper: HomebridgeAccessoryWrapper<SoundTouchDevice> &
DeviceOnOffListener
protected readonly service: Service
public constructor(
device: SoundTouchDevice,
accessoryWrapper: HomebridgeAccessoryWrapper<SoundTouchDevice> &
DeviceOnOffListener
) {
this.device = device
this.accessoryWrapper = accessoryWrapper
this.service = this.initService()
if (this.device.volumeSettings.maxValue < 100)
this.getVolumeCharacteristic().props.maxValue = Math.min(
this.device.volumeSettings.maxValue,
100
)
this.getMuteCharacteristic()
.on('get', callbackify(this.isNotMuted.bind(this)))
.on('set', callbackify(this.unMuteDevice.bind(this)))
this.getVolumeCharacteristic()
.on('get', callbackify(this.getVolume.bind(this)))
.on('set', callbackify(this.setVolume.bind(this)))
.on('change', this.volumeChange.bind(this))
}
protected abstract initService(): Service
public abstract getVolumeCharacteristic(): Characteristic
public abstract getMuteCharacteristic(): Characteristic
public async isNotMuted(): Promise<boolean> {
const nowPlaying = await this.device.api.getNowPlaying()
const isOn = nowPlaying.source !== SourceStatus.standBy
if (isOn) {
const volume = await this.device.api.getVolume()
return !volume.isMuted
}
return false
}
public async unMuteDevice(unmute: boolean): Promise<boolean> {
let isOn = await deviceIsOn(this.device)
if (isOn) {
const volume = await this.device.api.getVolume()
if ((unmute && volume.isMuted) || (!unmute && !volume.isMuted)) {
if (this.device.verbose)
this.accessoryWrapper.log(
`[${this.accessoryWrapper.getDisplayName()}] ${
unmute ? 'Unmuted' : 'Muted'
}`
)
return this.device.api.pressKey(KeyValue.mute)
}
} else if (unmute) {
isOn = await this.device.api.pressKey(KeyValue.power)
if (isOn) return this.accessoryWrapper.deviceDidTurnOn(true)
}
return false
}
public async getVolume(): Promise<number> {
const volume = await this.device.api.getVolume()
if (this.device.verbose)
this.accessoryWrapper.log(
`[${this.accessoryWrapper.getDisplayName()}] Current volume is ${
volume.actual
}`
)
return volume.actual
}
public async setVolume(
volume: number,
updateCharacteristic?: boolean
): Promise<boolean> {
const volumeCharacteristic = this.getVolumeCharacteristic()
const secureVolume = this.secureVolume(volumeCharacteristic, {
newValue: volume,
oldValue: volumeCharacteristic.value as number,
})
if (secureVolume !== undefined) volume = secureVolume
if (this.device.verbose)
this.accessoryWrapper.log(
`[${this.accessoryWrapper.getDisplayName()}] Volume change to ${volume}`
)
if (updateCharacteristic === true) volumeCharacteristic.updateValue(volume)
return this.device.api.setVolume(volume)
}
private secureVolume(
characteristic: Characteristic,
change: { newValue: number; oldValue: number }
): number | undefined {
const maxValue = characteristic.props.maxValue
if (change.newValue === maxValue && change.oldValue <= maxValue / 2)
return Math.max(change.oldValue, this.device.volumeSettings.unmuteValue)
return undefined
}
protected volumeChange(change: { newValue: number; oldValue: number }) {
const volumeCharacteristic = this.getVolumeCharacteristic()
const newValue = this.secureVolume(volumeCharacteristic, change)
if (newValue !== undefined) {
setTimeout(() => {
volumeCharacteristic.updateValue(newValue)
}, 1000)
}
}
}