mirror of
https://github.com/koush/scrypted.git
synced 2026-09-16 16:50:40 +01:00
homekit: refresh on user interaction. add fan service to thermostat when possible.
This commit is contained in:
4
plugins/homekit/package-lock.json
generated
4
plugins/homekit/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/homekit",
|
||||
"version": "0.0.101",
|
||||
"version": "0.0.102",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/homekit",
|
||||
"version": "0.0.101",
|
||||
"version": "0.0.102",
|
||||
"dependencies": {
|
||||
"hap-nodejs": "file:../../external/HAP-NodeJS",
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
@@ -40,5 +40,5 @@
|
||||
"@types/qrcode": "^1.4.1",
|
||||
"@types/url-parse": "^1.4.3"
|
||||
},
|
||||
"version": "0.0.101"
|
||||
"version": "0.0.102"
|
||||
}
|
||||
|
||||
@@ -8,10 +8,13 @@ export function maybeAddBatteryService(device: ScryptedDevice & Battery, accesso
|
||||
return;
|
||||
|
||||
const battery = new HAPBattery();
|
||||
bindCharacteristic(device, ScryptedInterface.Battery, battery, BatteryLevel, () => {
|
||||
battery.updateCharacteristic(Characteristic.StatusLowBattery, device.batteryLevel >= 20 ? StatusLowBattery.BATTERY_LEVEL_NORMAL : StatusLowBattery.BATTERY_LEVEL_LOW);
|
||||
bindCharacteristic(device, ScryptedInterface.Battery, battery, Characteristic.BatteryLevel, () => {
|
||||
return device.batteryLevel || 0;
|
||||
});
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.Battery, battery, Characteristic.StatusLowBattery, () => {
|
||||
return device.batteryLevel >= 20 ? StatusLowBattery.BATTERY_LEVEL_NORMAL : StatusLowBattery.BATTERY_LEVEL_LOW;
|
||||
});
|
||||
|
||||
accessory.addService(battery);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
import { EventListenerRegister, ScryptedDevice, ScryptedDeviceType, ScryptedInterface } from '@scrypted/sdk';
|
||||
import { EventDetails, EventListener, EventListenerRegister, Refresh, ScryptedDevice, ScryptedDeviceType, ScryptedInterface } from '@scrypted/sdk';
|
||||
import { CharacteristicEventTypes } from 'hap-nodejs';
|
||||
import { Accessory, Service, SnapshotRequest } from './hap';
|
||||
|
||||
export interface DummyDevice {
|
||||
@@ -29,11 +30,24 @@ export function addSupportedType(type: SupportedType) {
|
||||
supportedTypes[type.type] = type;
|
||||
}
|
||||
|
||||
export function bindCharacteristic(device: ScryptedDevice, event: ScryptedInterface, service: Service, characteristic: any, map: () => any, refresh?: boolean): EventListenerRegister {
|
||||
export function bindCharacteristic(device: ScryptedDevice, event: ScryptedInterface, service: Service, characteristic: any, map: (eventSource?: any, eventDetails?: EventDetails, eventData?: any) => any, refresh?: boolean): EventListenerRegister {
|
||||
service.updateCharacteristic(characteristic, map());
|
||||
|
||||
service.getCharacteristic(characteristic).on(CharacteristicEventTypes.GET, callback => {
|
||||
try {
|
||||
if (device.interfaces.includes(ScryptedInterface.Refresh)) {
|
||||
console.log('refreshing', device.name);
|
||||
(device as ScryptedDevice & Refresh).refresh(event, true);
|
||||
}
|
||||
callback(null, map());
|
||||
}
|
||||
catch (e) {
|
||||
callback(e);
|
||||
}
|
||||
});
|
||||
|
||||
return device.listen({
|
||||
event,
|
||||
watch: !refresh,
|
||||
}, () => service.updateCharacteristic(characteristic, map()));
|
||||
}, (source, details, data) => service.updateCharacteristic(characteristic, map(source, details, data)));
|
||||
}
|
||||
|
||||
@@ -184,15 +184,15 @@ class HomeKit extends ScryptedDeviceBase implements MixinProvider, Settings, Hom
|
||||
if (deviceInfo) {
|
||||
const info = accessory.getService(Service.AccessoryInformation)!;
|
||||
if (deviceInfo.manufacturer)
|
||||
info.setCharacteristic(Characteristic.Manufacturer, deviceInfo.manufacturer);
|
||||
info.updateCharacteristic(Characteristic.Manufacturer, deviceInfo.manufacturer);
|
||||
if (deviceInfo.model)
|
||||
info.setCharacteristic(Characteristic.Model, deviceInfo.model);
|
||||
info.updateCharacteristic(Characteristic.Model, deviceInfo.model);
|
||||
if (deviceInfo.serialNumber)
|
||||
info.setCharacteristic(Characteristic.SerialNumber, deviceInfo.serialNumber);
|
||||
info.updateCharacteristic(Characteristic.SerialNumber, deviceInfo.serialNumber);
|
||||
if (deviceInfo.firmware)
|
||||
info.setCharacteristic(Characteristic.FirmwareRevision, deviceInfo.firmware);
|
||||
info.updateCharacteristic(Characteristic.FirmwareRevision, deviceInfo.firmware);
|
||||
if (deviceInfo.version)
|
||||
info.setCharacteristic(Characteristic.HardwareRevision, deviceInfo.version);
|
||||
info.updateCharacteristic(Characteristic.HardwareRevision, deviceInfo.version);
|
||||
}
|
||||
|
||||
if (supportedType.noBridge) {
|
||||
@@ -214,10 +214,10 @@ class HomeKit extends ScryptedDeviceBase implements MixinProvider, Settings, Hom
|
||||
const username = this.getUsername();
|
||||
|
||||
const info = this.bridge.getService(Service.AccessoryInformation)!;
|
||||
info.setCharacteristic(Characteristic.Manufacturer, "scrypted.app");
|
||||
info.setCharacteristic(Characteristic.Model, "scrypted");
|
||||
info.setCharacteristic(Characteristic.SerialNumber, username);
|
||||
info.setCharacteristic(Characteristic.FirmwareRevision, packageJson.version);
|
||||
info.updateCharacteristic(Characteristic.Manufacturer, "scrypted.app");
|
||||
info.updateCharacteristic(Characteristic.Model, "scrypted");
|
||||
info.updateCharacteristic(Characteristic.SerialNumber, username);
|
||||
info.updateCharacteristic(Characteristic.FirmwareRevision, packageJson.version);
|
||||
|
||||
const publishInfo: PublishInfo = {
|
||||
username: username,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Camera, FFMpegInput, MotionSensor, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, VideoCamera, AudioSensor, Intercom, MediaStreamOptions, ObjectDetection } from '@scrypted/sdk'
|
||||
import { addSupportedType, DummyDevice, HomeKitSession } from '../common'
|
||||
import { AudioStreamingCodec, AudioStreamingCodecType, AudioStreamingSamplerate, CameraController, CameraStreamingDelegate, CameraStreamingOptions, Characteristic, H264Level, H264Profile, PrepareStreamCallback, PrepareStreamRequest, PrepareStreamResponse, SRTPCryptoSuites, StartStreamRequest, StreamingRequest, StreamRequestCallback, StreamRequestTypes, Resolution } from '../hap';
|
||||
import { addSupportedType, bindCharacteristic, DummyDevice, HomeKitSession } from '../common'
|
||||
import { AudioStreamingCodec, AudioStreamingCodecType, AudioStreamingSamplerate, CameraController, CameraStreamingDelegate, CameraStreamingOptions, Characteristic, H264Level, H264Profile, PrepareStreamCallback, PrepareStreamRequest, PrepareStreamResponse, SRTPCryptoSuites, StartStreamRequest, StreamingRequest, StreamRequestCallback, StreamRequestTypes } from '../hap';
|
||||
import { makeAccessory } from './common';
|
||||
import { fitHeightToWidth } from "../../../../common/src/resolution-utils";
|
||||
|
||||
import sdk from '@scrypted/sdk';
|
||||
import child_process from 'child_process';
|
||||
@@ -11,7 +10,7 @@ import dgram, { SocketType } from 'dgram';
|
||||
import { once } from 'events';
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
import { CameraRecordingDelegate, CharacteristicEventTypes, CharacteristicValue, NodeCallback } from 'hap-nodejs';
|
||||
import { CameraRecordingDelegate } from 'hap-nodejs';
|
||||
import { AudioRecordingCodec, AudioRecordingCodecType, AudioRecordingSamplerate, CameraRecordingOptions } from 'hap-nodejs/dist/lib/camera/RecordingManagement';
|
||||
import { ffmpegLogInitialOutput } from '@scrypted/common/src/media-helpers';
|
||||
import { RtpDemuxer } from '../rtp/rtp-demuxer';
|
||||
@@ -517,25 +516,18 @@ addSupportedType({
|
||||
() => !!motionDevice.motionDetected;
|
||||
|
||||
const service = controller.motionService;
|
||||
service.getCharacteristic(Characteristic.MotionDetected)
|
||||
.on(CharacteristicEventTypes.GET, (callback: NodeCallback<CharacteristicValue>) => {
|
||||
callback(null, motionDetected());
|
||||
});
|
||||
|
||||
motionDevice.listen({
|
||||
event: ScryptedInterface.MotionSensor,
|
||||
watch: false,
|
||||
}, (eventSource, eventDetails, data) => {
|
||||
service.updateCharacteristic(Characteristic.MotionDetected, motionDetected());
|
||||
});
|
||||
bindCharacteristic(motionDevice,
|
||||
ScryptedInterface.MotionSensor,
|
||||
service,
|
||||
Characteristic.MotionDetected,
|
||||
() => motionDetected(), true)
|
||||
|
||||
if (needAudioMotionService) {
|
||||
motionDevice.listen({
|
||||
event: ScryptedInterface.AudioSensor,
|
||||
watch: false,
|
||||
}, (eventSource, eventDetails, data) => {
|
||||
service.updateCharacteristic(Characteristic.MotionDetected, motionDetected());
|
||||
});
|
||||
bindCharacteristic(motionDevice,
|
||||
ScryptedInterface.AudioSensor,
|
||||
service,
|
||||
Characteristic.MotionDetected,
|
||||
() => motionDetected(), true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,55 +539,33 @@ addSupportedType({
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
const objectSensorMap = new Map<string, ContactSensor>();
|
||||
const peopleSensorMap = new Map<string, ContactSensor>();
|
||||
for (const ojs of new Set(objectDetectionContactSensors)) {
|
||||
const sensor = new ContactSensor(`${device.name}: ` + ojs, ojs);
|
||||
sensor?.updateCharacteristic(Characteristic.ContactSensorState, Characteristic.ContactSensorState.CONTACT_DETECTED);
|
||||
accessory.addService(sensor);
|
||||
if (ojs.startsWith('Person: ')) {
|
||||
peopleSensorMap.set(ojs, sensor);
|
||||
}
|
||||
else {
|
||||
objectSensorMap.set(ojs, sensor);
|
||||
}
|
||||
}
|
||||
const isPerson = ojs.startsWith('Person: ');
|
||||
|
||||
if (objectSensorMap.size || peopleSensorMap.size) {
|
||||
device.listen(ScryptedInterface.ObjectDetector, (eventSource, eventDetails, eventData: ObjectDetection) => {
|
||||
if (eventData.detections) {
|
||||
const unset = new Set(objectSensorMap.keys());
|
||||
const objects: string[] = [];
|
||||
objects.push(...eventData.detections.map(d => d.className));
|
||||
for (const type of objects) {
|
||||
const sensor = objectSensorMap.get(type);
|
||||
sensor?.updateCharacteristic(Characteristic.ContactSensorState, Characteristic.ContactSensorState.CONTACT_NOT_DETECTED);
|
||||
unset.delete(type);
|
||||
}
|
||||
let contactState = Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
|
||||
bindCharacteristic(device, ScryptedInterface.ObjectDetector, sensor, Characteristic.ContactSensorState, (source, details, data) => {
|
||||
if (!source)
|
||||
return contactState;
|
||||
|
||||
for (const type of unset) {
|
||||
const sensor = objectSensorMap.get(type);
|
||||
sensor?.updateCharacteristic(Characteristic.ContactSensorState, Characteristic.ContactSensorState.CONTACT_DETECTED);
|
||||
}
|
||||
const ed: ObjectDetection = data;
|
||||
if (!isPerson) {
|
||||
if (!ed.detections)
|
||||
return contactState;
|
||||
const objects = ed.detections.map(d => d.className);
|
||||
contactState = objects.includes(ojs) ? Characteristic.ContactSensorState.CONTACT_DETECTED : Characteristic.ContactSensorState.CONTACT_NOT_DETECTED
|
||||
return contactState;
|
||||
}
|
||||
|
||||
if (eventData.people) {
|
||||
const unset = new Set(peopleSensorMap.keys());
|
||||
const people: string[] = [];
|
||||
people.push(...eventData.people.map(p => p.label));
|
||||
for (const type of people) {
|
||||
const personType = 'Person: ' + type;
|
||||
const sensor = peopleSensorMap.get(personType);
|
||||
sensor?.updateCharacteristic(Characteristic.ContactSensorState, Characteristic.ContactSensorState.CONTACT_NOT_DETECTED);
|
||||
unset.delete(personType);
|
||||
}
|
||||
if (!ed.people)
|
||||
return contactState;
|
||||
|
||||
for (const type of unset) {
|
||||
const sensor = peopleSensorMap.get(type);
|
||||
sensor?.updateCharacteristic(Characteristic.ContactSensorState, Characteristic.ContactSensorState.CONTACT_DETECTED);
|
||||
}
|
||||
}
|
||||
});
|
||||
const people = ed.people.map(d => 'Person: ' + d.label);
|
||||
contactState = people.includes(ojs) ? Characteristic.ContactSensorState.CONTACT_DETECTED : Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
|
||||
|
||||
return contactState;
|
||||
}, true);
|
||||
}
|
||||
|
||||
return accessory;
|
||||
|
||||
@@ -28,7 +28,7 @@ addSupportedType({
|
||||
() => !!device.running ? Characteristic.InUse.IN_USE : Characteristic.InUse.NOT_IN_USE);
|
||||
|
||||
// todo: fix this.
|
||||
service.setCharacteristic(Characteristic.RemainingDuration, 1800)
|
||||
service.updateCharacteristic(Characteristic.RemainingDuration, 1800)
|
||||
|
||||
return accessory;
|
||||
}
|
||||
|
||||
@@ -35,9 +35,6 @@ addSupportedType({
|
||||
callback();
|
||||
h = value as number;
|
||||
delaySet();
|
||||
})
|
||||
.on(CharacteristicEventTypes.GET, (callback: NodeCallback<CharacteristicValue>) => {
|
||||
callback(null, device.hsv?.h || 0);
|
||||
});
|
||||
|
||||
service.addCharacteristic(Characteristic.Saturation)
|
||||
@@ -45,11 +42,13 @@ addSupportedType({
|
||||
callback();
|
||||
s = value as number;
|
||||
delaySet();
|
||||
})
|
||||
.on(CharacteristicEventTypes.GET, (callback: NodeCallback<CharacteristicValue>) => {
|
||||
callback(null, (device.hsv?.s || 0) * 100);
|
||||
});
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.ColorSettingHsv, service, Characteristic.Hue,
|
||||
() => device.hsv?.h || 0);
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.ColorSettingHsv, service, Characteristic.Saturation,
|
||||
() => (device.hsv?.s || 0) * 100);
|
||||
}
|
||||
|
||||
return accessory;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import { Lock, LockState, ScryptedDevice, ScryptedDeviceType, ScryptedInterface } from '@scrypted/sdk'
|
||||
import { addSupportedType, DummyDevice } from '../common'
|
||||
import { addSupportedType, bindCharacteristic, DummyDevice } from '../common'
|
||||
import { Characteristic, CharacteristicEventTypes, CharacteristicSetCallback, CharacteristicValue, NodeCallback, Service } from '../hap';
|
||||
import { makeAccessory } from './common';
|
||||
|
||||
@@ -33,18 +33,9 @@ addSupportedType({
|
||||
}
|
||||
}
|
||||
|
||||
service.getCharacteristic(Characteristic.LockCurrentState)
|
||||
.on(CharacteristicEventTypes.GET, (callback: NodeCallback<CharacteristicValue>) => {
|
||||
callback(null, toCurrentState(device.lockState));
|
||||
});
|
||||
|
||||
|
||||
let targetState = toTargetState(device.lockState);
|
||||
|
||||
service.getCharacteristic(Characteristic.LockTargetState)
|
||||
.on(CharacteristicEventTypes.GET, (callback: NodeCallback<CharacteristicValue>) => {
|
||||
callback(null, targetState);
|
||||
})
|
||||
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
|
||||
targetState = value as number;
|
||||
callback();
|
||||
@@ -56,17 +47,15 @@ addSupportedType({
|
||||
device.lock();
|
||||
break;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.Lock, service, Characteristic.LockTargetState, () => {
|
||||
targetState = toTargetState(device.lockState);
|
||||
return targetState;
|
||||
})
|
||||
|
||||
device.listen({
|
||||
event: ScryptedInterface.Lock,
|
||||
watch: true,
|
||||
}, (source, details, data) => {
|
||||
targetState = toTargetState(data);
|
||||
service.updateCharacteristic(Characteristic.LockTargetState, targetState);
|
||||
service.updateCharacteristic(Characteristic.LockCurrentState, toCurrentState(data));
|
||||
});
|
||||
bindCharacteristic(device, ScryptedInterface.Lock, service, Characteristic.LockCurrentState,
|
||||
() => toCurrentState(device.lockState));
|
||||
|
||||
return accessory;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import { VideoCamera, MediaPlayer, MediaPlayerState, ScryptedDevice, ScryptedDeviceType, ScryptedInterface } from '@scrypted/sdk'
|
||||
import { addSupportedType, DummyDevice } from '../common'
|
||||
import { addSupportedType, bindCharacteristic, DummyDevice } from '../common'
|
||||
import { Categories, Characteristic, CharacteristicEventTypes, CharacteristicSetCallback, CharacteristicValue, NodeCallback, Service } from '../hap';
|
||||
import { makeAccessory } from './common';
|
||||
import sdk from '@scrypted/sdk';
|
||||
@@ -18,35 +18,27 @@ addSupportedType({
|
||||
const service = accessory.addService(Service.Television, "Television", "Television");
|
||||
// service.setPrimaryService(true);
|
||||
|
||||
let active = false;
|
||||
let activeIdentifier = 0;
|
||||
const allowedIdentifiers = new Set<string>();
|
||||
service.getCharacteristic(Characteristic.Active)
|
||||
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
|
||||
active = value === Characteristic.Active.ACTIVE;
|
||||
if (!active)
|
||||
if (value !== Characteristic.Active.ACTIVE)
|
||||
device.stop();
|
||||
callback();
|
||||
})
|
||||
.on(CharacteristicEventTypes.GET, async (callback: NodeCallback<CharacteristicValue>) => {
|
||||
try {
|
||||
if (active) {
|
||||
callback(null, Characteristic.Active.ACTIVE);
|
||||
return;
|
||||
}
|
||||
const mediaStatus = await device.getMediaStatus();
|
||||
if (!mediaStatus || mediaStatus.mediaPlayerState === MediaPlayerState.Idle) {
|
||||
callback(null, Characteristic.Active.INACTIVE);
|
||||
return;
|
||||
}
|
||||
active = true;
|
||||
callback(null, Characteristic.Active.ACTIVE);
|
||||
}
|
||||
catch (e) {
|
||||
callback(e);
|
||||
}
|
||||
});
|
||||
|
||||
let active = false;
|
||||
bindCharacteristic(device, ScryptedInterface.MediaPlayer, service, Characteristic.Active,
|
||||
() => {
|
||||
// trigger an actual fetch here but return something cached immediately.
|
||||
(async() => {
|
||||
const mediaStatus = await device.getMediaStatus();
|
||||
active = mediaStatus && mediaStatus.mediaPlayerState !== MediaPlayerState.Idle;
|
||||
service.updateCharacteristic(Characteristic.Active, active ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE);
|
||||
})();
|
||||
return active ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE;
|
||||
})
|
||||
|
||||
service.getCharacteristic(Characteristic.ActiveIdentifier)
|
||||
.on(CharacteristicEventTypes.SET, async (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
|
||||
activeIdentifier = value as number;
|
||||
@@ -76,18 +68,17 @@ addSupportedType({
|
||||
callback(null, activeIdentifier);
|
||||
});
|
||||
|
||||
|
||||
service.setCharacteristic(Characteristic.ConfiguredName, device.name);
|
||||
service.updateCharacteristic(Characteristic.ConfiguredName, device.name);
|
||||
|
||||
service.getCharacteristic(Characteristic.RemoteKey)
|
||||
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
|
||||
callback();
|
||||
});
|
||||
|
||||
service.setCharacteristic(Characteristic.SleepDiscoveryMode, Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE);
|
||||
service.updateCharacteristic(Characteristic.SleepDiscoveryMode, Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE);
|
||||
|
||||
const speaker = accessory.addService(Service.TelevisionSpeaker);
|
||||
speaker.setCharacteristic(Characteristic.Active, Characteristic.Active.ACTIVE)
|
||||
speaker.updateCharacteristic(Characteristic.Active, Characteristic.Active.ACTIVE)
|
||||
|
||||
speaker.getCharacteristic(Characteristic.Mute)
|
||||
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
|
||||
@@ -97,16 +88,13 @@ addSupportedType({
|
||||
callback(null, false);
|
||||
});
|
||||
|
||||
|
||||
|
||||
const idle = accessory.addService(Service.InputSource, 'idle', 'Idle');
|
||||
idle.setCharacteristic(Characteristic.Identifier, 0)
|
||||
.setCharacteristic(Characteristic.ConfiguredName, 'Idle')
|
||||
.setCharacteristic(Characteristic.IsConfigured, Characteristic.IsConfigured.CONFIGURED)
|
||||
.setCharacteristic(Characteristic.InputSourceType, Characteristic.InputSourceType.APPLICATION);
|
||||
idle.updateCharacteristic(Characteristic.Identifier, 0)
|
||||
.updateCharacteristic(Characteristic.ConfiguredName, 'Idle')
|
||||
.updateCharacteristic(Characteristic.IsConfigured, Characteristic.IsConfigured.CONFIGURED)
|
||||
.updateCharacteristic(Characteristic.InputSourceType, Characteristic.InputSourceType.APPLICATION);
|
||||
service.addLinkedService(idle);
|
||||
|
||||
|
||||
for (const id of Object.keys(systemManager.getSystemState())) {
|
||||
const check = systemManager.getDeviceById(id);
|
||||
if (check.type !== ScryptedDeviceType.Camera)
|
||||
@@ -115,10 +103,10 @@ addSupportedType({
|
||||
allowedIdentifiers.add(check.id);
|
||||
|
||||
const input = accessory.addService(Service.InputSource, check.name, `input-${check.id}`);
|
||||
input.setCharacteristic(Characteristic.Identifier, check.id)
|
||||
.setCharacteristic(Characteristic.ConfiguredName, check.name)
|
||||
.setCharacteristic(Characteristic.IsConfigured, Characteristic.IsConfigured.CONFIGURED)
|
||||
.setCharacteristic(Characteristic.InputSourceType, Characteristic.InputSourceType.APPLICATION);
|
||||
input.updateCharacteristic(Characteristic.Identifier, check.id)
|
||||
.updateCharacteristic(Characteristic.ConfiguredName, check.name)
|
||||
.updateCharacteristic(Characteristic.IsConfigured, Characteristic.IsConfigured.CONFIGURED)
|
||||
.updateCharacteristic(Characteristic.InputSourceType, Characteristic.InputSourceType.APPLICATION);
|
||||
|
||||
service.addLinkedService(input);
|
||||
}
|
||||
|
||||
@@ -15,24 +15,16 @@ addSupportedType({
|
||||
if (device.interfaces.includes(ScryptedInterface.BinarySensor)) {
|
||||
const contactSensorService = accessory.addService(Service.ContactSensor, device.name);
|
||||
contactSensorService.getCharacteristic(Characteristic.ContactSensorState)
|
||||
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.BinarySensor, contactSensorService, Characteristic.ContactSensorState,
|
||||
() => !!device.binaryState);
|
||||
}
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.MotionSensor)) {
|
||||
const motionSensorService = accessory.addService(Service.MotionSensor, device.name);
|
||||
motionSensorService.getCharacteristic(Characteristic.MotionDetected)
|
||||
.on(CharacteristicEventTypes.GET, (callback: NodeCallback<CharacteristicValue>) => {
|
||||
callback(null, !!device.motionDetected);
|
||||
});
|
||||
|
||||
device.listen({
|
||||
event: ScryptedInterface.MotionSensor,
|
||||
watch: false,
|
||||
}, (eventSource, eventDetails, data) => {
|
||||
motionSensorService.updateCharacteristic(Characteristic.MotionDetected, !!device.motionDetected);
|
||||
});
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.MotionSensor, motionSensorService, Characteristic.MotionDetected,
|
||||
() => !!device.motionDetected, true);
|
||||
}
|
||||
|
||||
// todo: more sensors.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
|
||||
import { HumiditySensor, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceProperty, TemperatureSetting, TemperatureUnit, Thermometer, ThermostatMode } from '@scrypted/sdk'
|
||||
import { HumiditySensor, OnOff, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, ScryptedInterfaceProperty, TemperatureSetting, TemperatureUnit, Thermometer, ThermostatMode } from '@scrypted/sdk'
|
||||
import { access } from 'fs';
|
||||
import { Fanv2 } from 'hap-nodejs/dist/lib/definitions';
|
||||
import { addSupportedType, bindCharacteristic, DummyDevice } from '../common'
|
||||
import { Characteristic, CharacteristicEventTypes, CharacteristicSetCallback, CharacteristicValue, NodeCallback, Service } from '../hap';
|
||||
import { makeAccessory } from './common';
|
||||
@@ -11,7 +13,7 @@ addSupportedType({
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
getAccessory: async (device: ScryptedDevice & TemperatureSetting & Thermometer & HumiditySensor) => {
|
||||
getAccessory: async (device: ScryptedDevice & TemperatureSetting & Thermometer & HumiditySensor & OnOff) => {
|
||||
const accessory = makeAccessory(device);
|
||||
const service = accessory.addService(Service.Thermostat, device.name);
|
||||
|
||||
@@ -54,21 +56,17 @@ addSupportedType({
|
||||
}
|
||||
}
|
||||
|
||||
service.getCharacteristic(Characteristic.CurrentHeatingCoolingState)
|
||||
.on(CharacteristicEventTypes.GET, (callback: NodeCallback<CharacteristicValue>) => {
|
||||
callback(null, toCurrentMode(device.thermostatActiveMode));
|
||||
});
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.TemperatureSetting, service, Characteristic.CurrentHeatingCoolingState,
|
||||
() => toCurrentMode(device.thermostatActiveMode));
|
||||
|
||||
service.getCharacteristic(Characteristic.TargetHeatingCoolingState)
|
||||
.on(CharacteristicEventTypes.GET, (callback: NodeCallback<CharacteristicValue>) => {
|
||||
callback(null, toTargetMode(device.thermostatMode));
|
||||
})
|
||||
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
|
||||
callback();
|
||||
device.setThermostatMode(fromTargetMode(value as number));
|
||||
})
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.TemperatureSetting, service, Characteristic.TargetHeatingCoolingState,
|
||||
() => toTargetMode(device.thermostatActiveMode));
|
||||
|
||||
function getTargetTemperature() {
|
||||
return device.thermostatSetpoint ||
|
||||
@@ -77,26 +75,17 @@ addSupportedType({
|
||||
}
|
||||
|
||||
service.getCharacteristic(Characteristic.TargetTemperature)
|
||||
.on(CharacteristicEventTypes.GET, (callback: NodeCallback<CharacteristicValue>) => {
|
||||
callback(null, getTargetTemperature());
|
||||
})
|
||||
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
|
||||
callback();
|
||||
device.setThermostatSetpoint(value as number);
|
||||
})
|
||||
});
|
||||
|
||||
service.setCharacteristic(Characteristic.TemperatureDisplayUnits,
|
||||
device.temperatureUnit === TemperatureUnit.C ? Characteristic.TemperatureDisplayUnits.CELSIUS : Characteristic.TemperatureDisplayUnits.FAHRENHEIT);
|
||||
bindCharacteristic(device, ScryptedInterface.TemperatureSetting, service, Characteristic.TargetTemperature,
|
||||
() => getTargetTemperature());
|
||||
|
||||
device.listen({
|
||||
event: ScryptedInterface.TemperatureSetting,
|
||||
watch: true,
|
||||
}, (source, details, data) => {
|
||||
service.updateCharacteristic(Characteristic.TargetHeatingCoolingState, toTargetMode(device.thermostatMode));
|
||||
service.updateCharacteristic(Characteristic.CurrentHeatingCoolingState, toCurrentMode(device.thermostatActiveMode));
|
||||
service.updateCharacteristic(Characteristic.TargetTemperature, getTargetTemperature());
|
||||
service.updateCharacteristic(Characteristic.TemperatureDisplayUnits, device.temperatureUnit === TemperatureUnit.C ? Characteristic.TemperatureDisplayUnits.CELSIUS : Characteristic.TemperatureDisplayUnits.FAHRENHEIT);
|
||||
});
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.TemperatureSetting, service, Characteristic.TemperatureDisplayUnits,
|
||||
() => device.temperatureUnit === TemperatureUnit.C ? Characteristic.TemperatureDisplayUnits.CELSIUS : Characteristic.TemperatureDisplayUnits.FAHRENHEIT);
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.Thermometer, service, Characteristic.CurrentTemperature,
|
||||
() => device.temperature || 0);
|
||||
@@ -106,6 +95,12 @@ addSupportedType({
|
||||
() => device.humidity || 0);
|
||||
}
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.OnOff)) {
|
||||
const fanService = accessory.addService(Fanv2);
|
||||
bindCharacteristic(device, ScryptedInterface.OnOff, fanService, Characteristic.Active,
|
||||
() => device.on ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE);
|
||||
}
|
||||
|
||||
return accessory;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user