Skip to content

Commit

Permalink
Add experimental support for blinds
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCalzone committed Jul 9, 2019
1 parent 0cdf277 commit 570f9b6
Show file tree
Hide file tree
Showing 17 changed files with 574 additions and 9 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## __WORK IN PROGRESS__
* (AlCalzone) Add experimental support for blinds. This is expected to be buggy since I had to guess a couple of times.

## 1.5.0 (2018-11-12)
* (AlCalzone) Bundle declaration files before publishing on npm
* (AlCalzone) Update `shared-utils` dependency to fix compile errors
Expand Down
6 changes: 5 additions & 1 deletion build/lib/accessory.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Blind } from "./blind";
import { DeviceInfo } from "./deviceInfo";
import { IPSODevice } from "./ipsoDevice";
import { Light } from "./light";
Expand All @@ -16,7 +17,9 @@ export declare enum AccessoryTypes {
/** A smart plug */
plug = 3,
/** A motion sensor (currently unsupported) */
motionSensor = 4
motionSensor = 4,
/** A smart blind */
blind = 5
}
export declare class Accessory extends IPSODevice {
type: AccessoryTypes;
Expand All @@ -27,6 +30,7 @@ export declare class Accessory extends IPSODevice {
plugList: Plug[];
sensorList: Sensor[];
switchList: IPSODevice[];
blindList: Blind[];
otaUpdateState: number;
/**
* Fixes property values that are known to be bugged
Expand Down
18 changes: 18 additions & 0 deletions build/lib/accessory.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
const blind_1 = require("./blind");
const deviceInfo_1 = require("./deviceInfo");
const ipsoDevice_1 = require("./ipsoDevice");
const ipsoObject_1 = require("./ipsoObject");
Expand All @@ -33,6 +34,8 @@ var AccessoryTypes;
AccessoryTypes[AccessoryTypes["plug"] = 3] = "plug";
/** A motion sensor (currently unsupported) */
AccessoryTypes[AccessoryTypes["motionSensor"] = 4] = "motionSensor";
/** A smart blind */
AccessoryTypes[AccessoryTypes["blind"] = 5] = "blind";
})(AccessoryTypes = exports.AccessoryTypes || (exports.AccessoryTypes = {}));
class Accessory extends ipsoDevice_1.IPSODevice {
constructor() {
Expand Down Expand Up @@ -74,6 +77,12 @@ class Accessory extends ipsoDevice_1.IPSODevice {
swtch.link(client);
}
}
/* istanbul ignore next */
if (this.blindList != null) {
for (const blind of this.blindList) {
blind.link(client);
}
}
return this;
}
/**
Expand All @@ -96,6 +105,10 @@ class Accessory extends ipsoDevice_1.IPSODevice {
if (this.switchList != null) {
this.switchList = this.switchList.map(swtch => swtch.fixBuggedProperties());
}
/* istanbul ignore next */
if (this.blindList != null) {
this.blindList = this.blindList.map(blind => blind.fixBuggedProperties());
}
return this;
}
}
Expand Down Expand Up @@ -136,6 +149,11 @@ __decorate([
ipsoObject_1.deserializeWith(/* istanbul ignore next */ (obj, me) => new ipsoDevice_1.IPSODevice(me.options).parse(obj)),
__metadata("design:type", Array)
], Accessory.prototype, "switchList", void 0);
__decorate([
ipsoObject_1.ipsoKey("15015"),
ipsoObject_1.deserializeWith((obj, me) => new blind_1.Blind(me.options, me).parse(obj)),
__metadata("design:type", Array)
], Accessory.prototype, "blindList", void 0);
__decorate([
ipsoObject_1.ipsoKey("9054"),
__metadata("design:type", Number)
Expand Down
40 changes: 40 additions & 0 deletions build/lib/blind.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Accessory } from "./accessory";
import { IPSODevice } from "./ipsoDevice";
import { IPSOOptions } from "./ipsoObject";
export declare type BlindOperation = Partial<Pick<Blind, "position">>;
export declare class Blind extends IPSODevice {
constructor(options?: IPSOOptions, accessory?: Accessory);
private _modelName;
private _accessory;
position: number;
/**
* Returns true if the current blind is dimmable
*/
readonly isDimmable: boolean;
/**
* Returns true if the current blind is switchable
*/
readonly isSwitchable: boolean;
clone(): this;
/**
* Creates a proxy which redirects the properties to the correct internal one, does nothing now
*/
createProxy(): this;
/**
* Ensures this instance is linked to a tradfri client and an accessory
* @throws Throws an error if it isn't
*/
private ensureLink;
/** Open these blinds */
open(): Promise<boolean>;
/** Close these blinds */
close(): Promise<boolean>;
private operateBlind;
/**
* Changes this plug's "brightness". Any value > 0 turns the plug on, 0 turns it off.
* @returns true if a request was sent, false otherwise
*/
setPosition(value: number): Promise<boolean>;
/** Turns this object into JSON while leaving out the potential circular reference */
toJSON(): {};
}
112 changes: 112 additions & 0 deletions build/lib/blind.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"use strict";
// wotan-disable no-useless-predicate
// Until I'm sure that the properties may be nullable, we have to allow these "useless" checks
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
const math_1 = require("alcalzone-shared/math");
const accessory_1 = require("./accessory");
const ipsoDevice_1 = require("./ipsoDevice");
const ipsoObject_1 = require("./ipsoObject");
class Blind extends ipsoDevice_1.IPSODevice {
constructor(options, accessory) {
super(options);
this.position = 0.0; // <float>
// In order for the simplified API to work, the
// accessory reference must be a proxy
if (accessory != null && !accessory.isProxy) {
accessory = accessory.createProxy();
}
this._accessory = accessory;
// get the model number to detect features
if (accessory != null &&
accessory.deviceInfo != null &&
accessory.deviceInfo.modelNumber != null &&
accessory.deviceInfo.modelNumber.length > 0) {
this._modelName = accessory.deviceInfo.modelNumber;
}
}
/**
* Returns true if the current blind is dimmable
*/
get isDimmable() {
return true; // we know no blinds that are dimmable
}
/**
* Returns true if the current blind is switchable
*/
get isSwitchable() {
return false; // we know no blinds that aren't switchable
}
clone() {
const ret = super.clone(this._accessory);
ret._modelName = this._modelName;
return ret;
}
/**
* Creates a proxy which redirects the properties to the correct internal one, does nothing now
*/
createProxy() {
return this;
}
// =================================
// Simplified API access
/**
* Ensures this instance is linked to a tradfri client and an accessory
* @throws Throws an error if it isn't
*/
ensureLink() {
if (this.client == null) {
throw new Error("Cannot use the simplified API on devices which aren't linked to a client instance.");
}
if (!(this._accessory instanceof accessory_1.Accessory)) {
throw new Error("Cannot use the simplified API on plugs which aren't linked to an Accessory instance.");
}
}
/** Open these blinds */
open() {
return this.operateBlind({ position: 100 }); // TODO: is this the right way around?
}
/** Close these blinds */
close() {
return this.operateBlind({ position: 0 }); // TODO: is this the right way around?
}
operateBlind(operation) {
this.ensureLink();
return this.client.operateBlind(this._accessory, operation);
}
/**
* Changes this plug's "brightness". Any value > 0 turns the plug on, 0 turns it off.
* @returns true if a request was sent, false otherwise
*/
setPosition(value) {
value = math_1.clamp(value, 0, 100);
return this.operateBlind({ position: value });
}
/** Turns this object into JSON while leaving out the potential circular reference */
toJSON() {
return {
position: this.position,
};
}
}
__decorate([
ipsoObject_1.doNotSerialize,
__metadata("design:type", Object)
], Blind.prototype, "_modelName", void 0);
__decorate([
ipsoObject_1.doNotSerialize,
__metadata("design:type", Object)
], Blind.prototype, "_accessory", void 0);
__decorate([
ipsoObject_1.ipsoKey("5536"),
__metadata("design:type", Number)
], Blind.prototype, "position", void 0);
exports.Blind = Blind;
2 changes: 2 additions & 0 deletions build/lib/operation-provider.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Accessory } from "./accessory";
import { BlindOperation } from "./blind";
import { Group, GroupOperation } from "./group";
import { LightOperation } from "./light";
import { PlugOperation } from "./plug";
export interface OperationProvider {
operateGroup(group: Group, operation: GroupOperation, force?: boolean): Promise<boolean>;
operateLight(accessory: Accessory, operation: LightOperation): Promise<boolean>;
operatePlug(accessory: Accessory, operation: PlugOperation): Promise<boolean>;
operateBlind(accessory: Accessory, operation: BlindOperation): Promise<boolean>;
}
8 changes: 8 additions & 0 deletions build/tradfri-client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { LoggerFunction } from "./lib/logger";
import { OperationProvider } from "./lib/operation-provider";
import { PlugOperation } from "./lib/plug";
import { ConnectionWatcherOptions } from "./lib/watcher";
import { BlindOperation } from "./lib/blind";
export declare type ObserveResourceCallback = (resp: CoapResponse) => void;
export declare type ObserveDevicesCallback = (addedDevices: Accessory[], removedDevices: Accessory[]) => void;
export interface TradfriClient {
Expand Down Expand Up @@ -192,6 +193,13 @@ export declare class TradfriClient extends EventEmitter implements OperationProv
* @returns true if a request was sent, false otherwise
*/
operatePlug(accessory: Accessory, operation: PlugOperation): Promise<boolean>;
/**
* Sets some properties on a blind
* @param accessory The parent accessory of the blind
* @param operation The properties to be set
* @returns true if a request was sent, false otherwise
*/
operateBlind(accessory: Accessory, operation: BlindOperation): Promise<boolean>;
/**
* Sends a custom request to a resource
* @param path The path of the resource
Expand Down
15 changes: 15 additions & 0 deletions build/tradfri-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,21 @@ class TradfriClient extends events_1.EventEmitter {
newAccessory.plugList[0].merge(operation);
return this.updateResource(`${endpoints_1.endpoints.devices}/${accessory.instanceId}`, newAccessory, reference);
}
/**
* Sets some properties on a blind
* @param accessory The parent accessory of the blind
* @param operation The properties to be set
* @returns true if a request was sent, false otherwise
*/
operateBlind(accessory, operation) {
if (accessory.type !== accessory_1.AccessoryTypes.blind) {
throw new Error("The parameter accessory must be a blind!");
}
const reference = accessory.clone();
const newAccessory = reference.clone();
newAccessory.blindList[0].merge(operation);
return this.updateResource(`${endpoints_1.endpoints.devices}/${accessory.instanceId}`, newAccessory, reference);
}
/**
* Sends a custom request to a resource
* @param path The path of the resource
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"source-map-support": "^0.5.5",
"ts-node": "^8.0.3",
"tslint": "^5.11.0",
"typescript": "^3.1.6",
"typescript": "^3.5.3",
"yargs": "^13.2.2"
},
"dependencies": {
Expand Down
17 changes: 17 additions & 0 deletions src/lib/accessory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// wotan-disable no-useless-predicate
// Until I'm sure that the properties may be nullable, we have to allow these "useless" checks

import { Blind } from "./blind";
import { DeviceInfo } from "./deviceInfo";
import { IPSODevice } from "./ipsoDevice";
import { deserializeWith, ipsoKey } from "./ipsoObject";
Expand All @@ -24,6 +25,8 @@ export enum AccessoryTypes {
plug = 3,
/** A motion sensor (currently unsupported) */
motionSensor = 4,
/** A smart blind */
blind = 5, // TODO: This is a guess. Double-check when the blinds are out
}

export class Accessory extends IPSODevice {
Expand Down Expand Up @@ -62,6 +65,10 @@ export class Accessory extends IPSODevice {
@deserializeWith(/* istanbul ignore next */ (obj, me: Accessory) => new IPSODevice(me.options).parse(obj))
public switchList!: IPSODevice[]; // <[Switch]> // seems unsupported atm.

@ipsoKey("15015")
@deserializeWith((obj, me: Accessory) => new Blind(me.options, me).parse(obj))
public blindList!: Blind[];

@ipsoKey("9054")
public otaUpdateState: number = 0; // boolean?

Expand Down Expand Up @@ -103,6 +110,12 @@ export class Accessory extends IPSODevice {
swtch.link(client);
}
}
/* istanbul ignore next */
if (this.blindList != null) {
for (const blind of this.blindList) {
blind.link(client);
}
}
return this;
}

Expand All @@ -126,6 +139,10 @@ export class Accessory extends IPSODevice {
if (this.switchList != null) {
this.switchList = this.switchList.map(swtch => swtch.fixBuggedProperties());
}
/* istanbul ignore next */
if (this.blindList != null) {
this.blindList = this.blindList.map(blind => blind.fixBuggedProperties());
}
return this;
}

Expand Down

0 comments on commit 570f9b6

Please sign in to comment.