Stairs/web/static/assets/ts/stairs.ts
2017-05-06 15:27:20 +02:00

389 lines
8.4 KiB
TypeScript

import jquery = require('jquery');
import ko = require('knockout');
import { Log } from 'log';
var instance: Stairs = null;
export class StairsStaticMode
{
public Brightness = ko.observable(0);
public Ease = ko.observable(false);
public read(data: any): void
{
this.Brightness(data.brightness);
}
}
export interface IStairsCustomBrightness
{
value: KnockoutObservable<number>;
}
export class StairsCustomMode
{
public Brightness = ko.observableArray<IStairsCustomBrightness>([]);
public init(stepCount: number): void
{
// Each item is an object containing an observable; we can't
// add the observable directly otherwise the foreach template
// will unwrap it before we can attach it to the range input.
var values: Array<IStairsCustomBrightness> = [];
for (var index = 0; index < stepCount; index++)
values.push({ value: ko.observable(0) });
this.Brightness(values);
}
public read(data: any): void
{
var brightness = data.brightness.map((item: number): IStairsCustomBrightness =>
{
return { value: ko.observable(item) };
});
brightness.reverse();
this.Brightness(brightness);
}
}
export class StairsAlternateMode
{
public Interval = ko.observable(500);
public Brightness = ko.observable(0);
public read(data: any): void
{
this.Interval(data.interval);
this.Brightness(data.brightness);
}
}
export enum StairsMode
{
Unknown = 0,
Static = 1,
Custom = 2,
Alternate = 3
}
export class StairsModeParameters
{
public Static = new StairsStaticMode();
public Custom = new StairsCustomMode();
public Alternate = new StairsAlternateMode();
public Current = ko.observable(StairsMode.Unknown);
public read(data: any): void
{
switch (this.Current())
{
case StairsMode.Static: this.Static.read(data); break;
case StairsMode.Custom: this.Custom.read(data); break;
case StairsMode.Alternate: this.Alternate.read(data); break;
}
}
}
export class StairsRangeValue
{
public Start = ko.observable(0);
public End = ko.observable(4095);
}
export class StairsRange
{
public UseScaling = ko.observable(false);
public Values = ko.observableArray<StairsRangeValue>([]);
public read(data: any): void
{
this.UseScaling(data.useScaling);
var values = data.values.map((item: any): StairsRangeValue =>
{
var value = new StairsRangeValue();
value.Start(item.start);
value.End(item.end);
return value;
});
values.reverse();
this.Values(values);
}
}
export class Stairs
{
public Loading = ko.observable(false);
public SavingMode = ko.observable(false);
public SavingSettings = ko.observable(false);
public Saving = ko.pureComputed<boolean>(() =>
{
return this.SavingMode() || this.SavingSettings();
});
public Mode = new StairsModeParameters();
public Range = new StairsRange();
private pingTimer: number = null;
private pingRequest: JQueryXHR = null;
private updatingFromServer = true;
private updateModeTimeout: number = null;
private updateRangeTimeout: number = null;
public static instance(): Stairs
{
if (instance == null)
instance = new Stairs();
return instance;
}
constructor()
{
this.ping();
}
public ping(): void
{
if (this.pingRequest !== null)
{
Log.verbose('Stairs.ping', 'Ping request already running, skipping');
return;
}
if (this.pingTimer !== null)
{
clearTimeout(this.pingTimer);
this.pingTimer = null;
}
if (this.Loading() || this.Saving())
{
this.pingTimer = setTimeout(() => this.ping(), 5000);
return;
}
Log.verbose('Stairs.ping', 'Starting Ping request');
this.updatingFromServer = true;
this.Loading(true);
this.pingRequest = $.ajax({
url: '/ping',
dataType: 'json',
cache: false
});
this.pingRequest.done((data: any) =>
{
Log.verbose('Stairs.ping', data);
this.Mode.Custom.init(data.stepCount);
$.when(this.getMode(), this.getRange())
.done(() => this.pingComplete(true))
.fail(() => this.pingComplete(false));
});
this.pingRequest.fail(() =>
{
Log.warning('Stairs.ping', 'Ping failed');
this.pingComplete(true);
});
}
private pingComplete(success: boolean)
{
this.pingRequest = null;
if (success)
//this.pingTimer = setTimeout(() => this.ping(), 5000);
{}
else
this.pingTimer = setTimeout(() => this.ping(), 5000);
if (success)
{
this.Loading(false);
this.updatingFromServer = false;
}
}
private getMode(): JQueryPromise<any>
{
Log.verbose('Stairs.getMode', 'Requesting Mode');
var request = $.ajax({
url: '/getMode',
dataType: 'json',
cache: false
});
request.done((response) =>
{
Log.verbose('Stairs.getMode', response);
this.Mode.Current(response.mode);
this.Mode.read(response.data);
});
return request;
}
private getRange(): JQueryPromise<any>
{
Log.verbose('Stairs.getRange', 'Requesting Range configuration');
var request = $.ajax({
url: '/getRange',
dataType: 'json',
cache: false
});
request.done((response) =>
{
Log.verbose('Stairs.getRange', response);
this.Range.read(response);
});
return request;
}
private updateMode = ko.computed(() =>
{
if (this.Loading() || this.SavingMode.peek()) return;
var url = '/setMode/';
switch (this.Mode.Current())
{
case StairsMode.Static:
url += 'Static?brightness=' + encodeURIComponent(this.Mode.Static.Brightness().toString());
break;
case StairsMode.Custom:
var values = this.Mode.Custom.Brightness().map((item: IStairsCustomBrightness) => { return item.value(); });
values.reverse();
url += 'Custom?brightness=' + encodeURIComponent(values.join());
break;
case StairsMode.Alternate:
url += 'Alternate?interval=' + encodeURIComponent(this.Mode.Alternate.Interval().toString()) +
'&brightness=' + encodeURIComponent(this.Mode.Alternate.Brightness().toString());
break;
/*
case 'Slide':
url += '?interval=' + encodeURIComponent(this.slide.interval()) +
'&brightness=' + encodeURIComponent(this.slide.brightness()) +
'&direction=' + encodeURIComponent(this.slide.direction()) +
'&fadeOutTime=' + encodeURIComponent(this.slide.fadeOutTime());
break;
*/
}
// Exit after checking all the parameters, so the observers
// are properly subscribed
if (this.updatingFromServer) return;
if (this.updateModeTimeout !== null)
{
clearTimeout(this.updateModeTimeout);
this.updateModeTimeout = null;
}
this.updateModeTimeout = setTimeout(() =>
{
Log.info("Stairs.updateMode", url);
// TODO retry on failure
this.SavingMode(true);
$.ajax(
{
url: url,
dataType: 'json',
cache: false
})
.always(() =>
{
this.SavingMode(false);
});
clearTimeout(this.updateModeTimeout);
this.updateModeTimeout = null;
}, 200);
return true;
});
private updateSettings = ko.computed(() =>
{
if (this.Loading() || this.SavingSettings.peek()) return;
var url = '/setRange?useScaling=' + this.Range.UseScaling().toString();
var start = this.Range.Values().map((item: StairsRangeValue): number => { return item.Start(); });
var end = this.Range.Values().map((item: StairsRangeValue): number => { return item.End(); });
start.reverse();
end.reverse();
url += '&start=' + encodeURIComponent(start.join());
url += '&end=' + encodeURIComponent(end.join());
// Exit after checking all the parameters, so the observers
// are properly subscribed
if (this.updatingFromServer) return;
if (this.updateRangeTimeout !== null)
{
clearTimeout(this.updateRangeTimeout);
this.updateRangeTimeout = null;
}
this.updateRangeTimeout = setTimeout(() =>
{
Log.info("Stairs.updateSettings", url);
// TODO retry on failure
this.SavingSettings(true);
$.ajax(
{
url: url,
dataType: 'json',
cache: false
})
.always(() =>
{
this.SavingSettings(false);
});
clearTimeout(this.updateRangeTimeout);
this.updateRangeTimeout = null;
}, 200);
return true;
});
}