Skip to content

Commit

Permalink
setValue method will now fire a SET event again
Browse files Browse the repository at this point in the history
  • Loading branch information
Supereg committed Oct 6, 2020
1 parent e16a3e4 commit 50be234
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 27 deletions.
1 change: 1 addition & 0 deletions src/lib/Characteristic.spec.ts
Expand Up @@ -440,6 +440,7 @@ describe('Characteristic', () => {
adminOnlyAccess: [Access.NOTIFY, Access.READ],
},
value: "testValue",
eventOnlyCharacteristic: false,
};

const characteristic = Characteristic.deserialize(json);
Expand Down
35 changes: 25 additions & 10 deletions src/lib/Characteristic.ts
Expand Up @@ -89,7 +89,7 @@ export interface SerializedCharacteristic {
UUID: string,
props: CharacteristicProps,
value: Nullable<CharacteristicValue>,
eventOnlyCharacteristic?: boolean,
eventOnlyCharacteristic: boolean,
}

export const enum CharacteristicEventTypes {
Expand Down Expand Up @@ -120,13 +120,13 @@ export type AdditionalAuthorizationHandler = (additionalAuthorizationData: strin
export declare interface Characteristic {

on(event: "get", listener: (callback: CharacteristicGetCallback, context: any, connection?: HAPConnection) => void): this;
on(event: "set", listener: (value: CharacteristicValue, callback: CharacteristicSetCallback, context: any, connection: HAPConnection) => void): this
on(event: "set", listener: (value: CharacteristicValue, callback: CharacteristicSetCallback, context: any, connection?: HAPConnection) => void): this
on(event: "change", listener: (change: CharacteristicChange) => void): this;
on(event: "subscribe", listener: VoidCallback): this;
on(event: "unsubscribe", listener: VoidCallback): this;

emit(event: "get", callback: CharacteristicGetCallback, context: any, connection?: HAPConnection): boolean;
emit(event: "set", value: CharacteristicValue, callback: CharacteristicSetCallback, context: any, connection: HAPConnection): boolean;
emit(event: "set", value: CharacteristicValue, callback: CharacteristicSetCallback, context: any, connection?: HAPConnection): boolean;
emit(event: "change", change: CharacteristicChange): boolean;
emit(event: "subscribe"): boolean;
emit(event: "unsubscribe"): boolean;
Expand Down Expand Up @@ -518,8 +518,22 @@ export class Characteristic extends EventEmitter {
});
}

setValue(newValue: Nullable<CharacteristicValue>, callback?: () => void, context?: any): Characteristic {
return this.updateValue(newValue, callback, context)
setValue(value: CharacteristicValue, callback?: CharacteristicSetCallback, context?: any): Characteristic {
this.handleSetRequest(value, undefined, context).then(value => {
if (callback) {
if (value) { // possible write response
callback(null, value);
} else {
callback(null);
}
}
}, reason => {
if (callback) {
callback(reason);
}
});

return this;
}

updateValue(value: Nullable<CharacteristicValue>, callback?: () => void, context?: any): Characteristic {
Expand Down Expand Up @@ -604,14 +618,15 @@ export class Characteristic extends EventEmitter {
/**
* Called when a HAP requests update the current value of the characteristic.
*
* @param value - The update value
* @param value - The updated value
* @param connection - The connection from which the request originated from
* @param context - Deprecated parameter. There for backwards compatibility.
* @returns Promise resolve to void in normal operation. When characteristic supports write response, the
* HAP request requests write response and the set handler returns a write response value, the respective
* write response value is resolved.
* @internal
*/
handleSetRequest(value: CharacteristicValue, connection: HAPConnection): Promise<CharacteristicValue | void> {
handleSetRequest(value: CharacteristicValue, connection?: HAPConnection, context?: any): Promise<CharacteristicValue | void> {
if (!this.props.perms.includes(Perms.PAIRED_WRITE)) { // check if we are allowed to write to this characteristic
return Promise.reject(Status.READ_ONLY_CHARACTERISTIC);
}
Expand All @@ -624,7 +639,7 @@ export class Characteristic extends EventEmitter {

if (this.listeners(CharacteristicEventTypes.SET).length === 0) {
this.value = value;
this.emit(CharacteristicEventTypes.CHANGE, { originator: connection, oldValue: oldValue, newValue: value });
this.emit(CharacteristicEventTypes.CHANGE, { originator: connection, oldValue: oldValue, newValue: value, context: context });
return Promise.resolve();
} else {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -655,8 +670,8 @@ export class Characteristic extends EventEmitter {
resolve();
}

this.emit(CharacteristicEventTypes.CHANGE, { originator: connection, oldValue: oldValue, newValue: value });
}), undefined, connection);
this.emit(CharacteristicEventTypes.CHANGE, { originator: connection, oldValue: oldValue, newValue: value, context: context });
}), context, connection);
} catch (error) {
console.warn(`[${this.displayName}] Unhandled error thrown inside write handler for characteristic: ${error.stack}`);
this.status = Status.SERVICE_COMMUNICATION_FAILURE;
Expand Down
12 changes: 9 additions & 3 deletions src/lib/camera/RTPStreamManagement.ts
Expand Up @@ -17,6 +17,7 @@ import { Service } from '../Service';
import { HAPConnection, HAPConnectionEvent } from "../util/eventedhttp";
import * as tlv from '../util/tlv';
import RTPProxy from './RTPProxy';
import assert from "assert";

const debug = createDebug('HAP-NodeJS:Camera:RTPStreamManagement');
// ---------------------------------- TLV DEFINITIONS START ----------------------------------
Expand Down Expand Up @@ -566,16 +567,21 @@ export class RTPStreamManagement {
this.service.setCharacteristic(Characteristic.SetupEndpoints, this.setupEndpointsResponse); // reset SetupEndpoints to default

this.service.getCharacteristic(Characteristic.SelectedRTPStreamConfiguration)!
.on(CharacteristicEventTypes.GET, (callback: CharacteristicGetCallback) => {
.on(CharacteristicEventTypes.GET, callback => {
callback(null, this.selectedConfiguration);
})
.on(CharacteristicEventTypes.SET, this._handleSelectedStreamConfigurationWrite.bind(this));

this.service.getCharacteristic(Characteristic.SetupEndpoints)!
.on(CharacteristicEventTypes.GET, (callback: CharacteristicGetCallback) => {
.on(CharacteristicEventTypes.GET, callback => {
callback(null, this.setupEndpointsResponse);
})
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback, context: any, connection: HAPConnection) => {
.on(CharacteristicEventTypes.SET, (value, callback, context, connection) => {
if (!connection) {
debug("Set event handler for SetupEndpoints cannot be called from plugin. Connection undefined!");
callback(Status.INVALID_VALUE_IN_REQUEST);
return;
}
this.handleSetupEndpoints(value, callback, connection);
});
}
Expand Down
15 changes: 10 additions & 5 deletions src/lib/controller/RemoteController.ts
Expand Up @@ -1184,20 +1184,25 @@ export class RemoteController extends EventEmitter implements SerializableContro
}

this.targetControlManagementService.getCharacteristic(Characteristic.TargetControlList)!
.on(CharacteristicEventTypes.GET, (callback: CharacteristicGetCallback) => {
.on(CharacteristicEventTypes.GET, callback => {
callback(null, this.targetConfigurationsString);
})
.on(CharacteristicEventTypes.SET, this.handleTargetControlWrite.bind(this));

this.targetControlService.getCharacteristic(Characteristic.ActiveIdentifier)!
.on(CharacteristicEventTypes.GET, (callback: CharacteristicGetCallback) => {
.on(CharacteristicEventTypes.GET, callback => {
callback(undefined, this.activeIdentifier);
});
this.targetControlService.getCharacteristic(Characteristic.Active)!
.on(CharacteristicEventTypes.GET, (callback: CharacteristicGetCallback) => {
.on(CharacteristicEventTypes.GET, callback => {
callback(undefined, this.isActive());
})
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback, context: any, connection: HAPConnection) => {
.on(CharacteristicEventTypes.SET, (value, callback, context, connection) => {
if (!connection) {
debug("Set event handler for Remote.Active cannot be called from plugin. Connection undefined!");
callback(Status.INVALID_VALUE_IN_REQUEST);
return;
}
this.handleActiveWrite(value, callback, connection);
});
this.targetControlService.getCharacteristic(Characteristic.ButtonEvent)!
Expand All @@ -1207,7 +1212,7 @@ export class RemoteController extends EventEmitter implements SerializableContro

if (this.audioSupported) {
this.audioStreamManagementService!.getCharacteristic(Characteristic.SelectedAudioStreamConfiguration)!
.on(CharacteristicEventTypes.GET, (callback: CharacteristicGetCallback) => {
.on(CharacteristicEventTypes.GET, callback => {
callback(null, this.selectedAudioConfigurationString);
})
.on(CharacteristicEventTypes.SET, this.handleSelectedAudioConfigurationWrite.bind(this))
Expand Down
18 changes: 9 additions & 9 deletions src/lib/datastream/DataStreamManagement.ts
@@ -1,11 +1,6 @@
import assert from "assert";
import createDebug from "debug";
import { CharacteristicValue } from "../../types";
import {
Characteristic,
CharacteristicEventTypes,
CharacteristicGetCallback,
CharacteristicSetCallback
} from "../Characteristic";
import { Characteristic, CharacteristicEventTypes, CharacteristicSetCallback } from "../Characteristic";
import { DataStreamTransportManagement } from "../gen/HomeKit-DataStream";
import { Status } from "../HAPServer";
import { Service } from "../Service";
Expand Down Expand Up @@ -211,10 +206,15 @@ export class DataStreamManagement {

private setupServiceHandlers() {
this.dataStreamTransportManagementService.getCharacteristic(Characteristic.SetupDataStreamTransport)!
.on(CharacteristicEventTypes.GET, (callback: CharacteristicGetCallback) => {
.on(CharacteristicEventTypes.GET, callback => {
callback(null, this.lastSetupDataStreamTransportResponse);
})
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback, context: any, connection: HAPConnection) => {
.on(CharacteristicEventTypes.SET, (value, callback, context, connection) => {
if (!connection) {
debug("Set event handler for SetupDataStreamTransport cannot be called from plugin! Connection undefined!");
callback(Status.INVALID_VALUE_IN_REQUEST);
return;
}
this.handleSetupDataStreamTransportWrite(value, callback, connection);
})
.updateValue(this.lastSetupDataStreamTransportResponse);
Expand Down

0 comments on commit 50be234

Please sign in to comment.