Skip to content

Instantly share code, notes, and snippets.

@burkeholland
Last active March 14, 2017 03:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save burkeholland/5a0b42a29adb8e47422cd14539ae380e to your computer and use it in GitHub Desktop.
Save burkeholland/5a0b42a29adb8e47422cd14539ae380e to your computer and use it in GitHub Desktop.
Proposed Changes To Settings.model For Syncronization
import * as appSettings from "application-settings";
import { UnitTypes } from "./app-enums";
import { DashboardWidgetInfo } from "./DashboardWidgetInfo.model";
const serializableMetadataKey = Symbol("serializable");
function serializable() {
return Reflect.metadata(serializableMetadataKey, true);
}
function isSerializable(target: any, propertyKey: string) {
return Reflect.getMetadata(serializableMetadataKey, target, propertyKey);
}
export class Settings {
/* Proposed changes to Settings object
Problem: Each setting may need to be pushed to Firebase at any time.
Solution: Create wrappers for the setType functions on the appsettings object
which can determine if a property should be pushed (per the serializable decorator)
and then push it, or store it for syncronization later if the system is offline
*/
// Begin Wrapper functions
private static pushToFirebase(key: string, value: any) {
// Firebase logic goes here
console.log(`Pushing ${key} to Firebase`);
}
private static setBoolean(key: string, value: boolean) {
appSettings.setBoolean(key, value);
// if the property is serializable, push it to Firebase automagically
if (isSerializable(this, key)) {
this.pushToFirebase(key, value);
}
}
private static setString(key: string, value: string) {
appSettings.setString(key, value);
// if the property is serializable, push it to Firebase automagically
if (isSerializable(this, key)) {
this.pushToFirebase(key, value);
}
}
private static setNumber(key: string, value: number) {
appSettings.setNumber(key, value);
// if the property is serializable, push it to Firebase automagically
if (isSerializable(this, key)) {
this.pushToFirebase(key, value);
}
}
// End Wrapper functions
// Most important item of note is that all setters now call the wrapper functions instead
// of directly calling appsettings
// isDebugMode
public static get isDebugMode(): boolean {
return appSettings.getBoolean("isDebugMode", false);
}
public static set isDebugMode(value: boolean) {
this.setBoolean("isDebugMode", value);
}
// isDemoMode
public static get isDemoMode(): boolean {
return appSettings.getBoolean("isDemoMode", false);
}
public static set isDemoMode(value: boolean) {
this.setBoolean("isDemoMode", value);
}
// isAppConfigured
public static get isAppConfigured(): boolean {
return appSettings.getBoolean("isAppConfigured", false);
}
public static set isAppConfigured(value: boolean) {
this.setBoolean("isAppConfigured", value);
}
// isAwayActive
@serializable()
public static get isAwayActive(): boolean {
return appSettings.getBoolean("isAwayActive", false);
}
public static set isAwayActive(value: boolean) {
this.setBoolean("isAwayActive", value);
}
// isSmartAwayEnabled
@serializable()
public static get isSmartAwayEnabled(): boolean {
return appSettings.getBoolean("isSmartAwayEnabled", true);
}
public static set isSmartAwayEnabled(value: boolean) {
this.setBoolean("isSmartAwayEnabled", value);
}
// isTutorialDone
@serializable()
public static get isTutorialDone(): boolean {
return appSettings.getBoolean("isTutorialDone", false);
}
public static set isTutorialDone(value: boolean) {
this.setBoolean("isTutorialDone", value);
}
// isLocationEnabled
@serializable()
public static get isLocationEnabled(): boolean {
return appSettings.getBoolean("isLocationEnabled", false);
}
public static set isLocationEnabled(value: boolean) {
this.setBoolean("isLocationEnabled", value);
// appSettings.setBoolean("isLocationEnabled", value);
}
// isPinRequired
@serializable()
public static get isPinRequired(): boolean {
return appSettings.getBoolean("isPinRequired", false);
}
public static set isPinRequired(value: boolean) {
this.setBoolean("isPinRequired", value);
}
// ignoreAwayWarning
@serializable()
public static get ignoreAwayWarning(): boolean {
return appSettings.getBoolean("ignoreAwayWarning", false);
}
public static set ignoreAwayWarning(value: boolean) {
this.setBoolean("ignoreAwayWarning", value);
}
// filterAutoScenes
@serializable()
public static get filterAutoScenes(): boolean {
return appSettings.getBoolean("filterAutoScenes", true);
}
public static set filterAutoScenes(value: boolean) {
this.setBoolean("filterAutoScenes", value);
}
// showAddWidgetWidget
@serializable()
public static get showAddWidgetWidget(): boolean {
return appSettings.getBoolean("showAddWidgetWidget", true);
}
public static set showAddWidgetWidget(value: boolean) {
this.setBoolean("showAddWidgetWidget", value);
}
// ignoredDeviceIds
@serializable()
public static get ignoredDeviceIds(): Array<number> {
let r = appSettings.getString("ignoredDeviceIds");
return (r === undefined) ? new Array<number>() : JSON.parse(r);
}
public static set ignoredDeviceIds(value: Array<number>) {
this.setString("ignoredDeviceIds", JSON.stringify(value));
}
// ignoredAutoAwayDeviceIds
@serializable()
public static get ignoredAutoAwayDeviceIds(): Array<number> {
let r = appSettings.getString("ignoredAutoAwayDeviceIds");
return (r === undefined) ? new Array<number>() : JSON.parse(r);
}
public static set ignoredAutoAwayDeviceIds(value: Array<number>) {
this.setString("ignoredAutoAwayDeviceIds", JSON.stringify(value));
}
// favoriteDeviceIds
@serializable()
public static get favoriteDeviceIds(): Array<number> {
let r = appSettings.getString("favoriteDeviceIds");
return (r === undefined) ? new Array<number>() : JSON.parse(r);
}
public static set favoriteDeviceIds(value: Array<number>) {
this.setString("favoriteDeviceIds", JSON.stringify(value));
}
// dashboardWidgets
@serializable()
public static get dashboardWidgets(): Array<DashboardWidgetInfo> {
let r = appSettings.getString("dashboardWidgets");
return (r === undefined) ? new Array<DashboardWidgetInfo>() : JSON.parse(r);
}
public static set dashboardWidgets(value: Array<DashboardWidgetInfo>) {
this.setString("dashboardWidgets", JSON.stringify(value));
}
// deviceCategories
@serializable()
public static get deviceCategories(): Object {
let r = appSettings.getString("deviceCategories");
return (r === undefined) ? {} : JSON.parse(r);
}
public static set deviceCategories(value: Object) {
this.setString("deviceCategories", JSON.stringify(value));
}
// securityDeviceCategoryIds
@serializable()
public static get securityDeviceCategoryIds(): Array<number> {
let r = appSettings.getString("securityDeviceCategoryIds");
return (r === undefined) ? new Array<number>() : JSON.parse(r);
}
public static set securityDeviceCategoryIds(value: Array<number>) {
this.setString("securityDeviceCategoryIds", JSON.stringify(value));
}
// resumePin
@serializable()
public static get resumePin(): number {
return appSettings.getNumber("resumePin");
}
public static set resumePin(value: number) {
this.setNumber("resumePin", value);
}
// controllerType
@serializable()
public static get controllerType(): string {
return appSettings.getString("controllerType", "vera");
}
public static set controllerType(value: string) {
this.setString("controllerType", value);
}
// baseUrl
@serializable()
public static get baseUrl(): string {
return appSettings.getString("baseUrl");
}
public static set baseUrl(value: string) {
this.setString("baseUrl", value);
}
// controllerStatus
@serializable()
public static get controllerStatus(): Object {
let r = appSettings.getString("controllerStatus");
return (r === undefined) ? undefined : JSON.parse(r);
}
public static set controllerStatus(value: Object) {
this.setString("controllerStatus", JSON.stringify(value));
}
// smartAwayResumeSceneId
@serializable()
public static get smartAwayResumeSceneId(): number {
return appSettings.getNumber("smartAwayResumeSceneId", 9999);
}
public static set smartAwayResumeSceneId(value: number) {
this.setNumber("smartAwayResumeSceneId", value);
}
// smartAwaySceneId
@serializable()
public static get smartAwaySceneId(): number {
return appSettings.getNumber("smartAwaySceneId", 9998);
}
public static set smartAwaySceneId(value: number) {
this.setNumber("smartAwaySceneId", value);
}
// awayDelaySeconds
@serializable()
public static get awayDelaySeconds(): number {
return appSettings.getNumber("awayDelaySeconds", 120);
}
public static set awayDelaySeconds(value: number) {
this.setNumber("awayDelaySeconds", value);
}
// zipCode
@serializable()
public static get zipCode(): string {
return appSettings.getString("zipCode");
}
public static set zipCode(value: string) {
this.setString("zipCode", value);
}
// showTutorial
@serializable()
public static get showTutorial(): boolean {
return appSettings.getBoolean("showTutorial", true);
}
public static set showTutorial(value: boolean) {
this.setBoolean("showTutorial", value);
}
// port
@serializable()
public static get port(): string {
return appSettings.getString("port");
}
public static set port(value: string) {
this.setString("port", value);
}
// ipaddr
@serializable()
public static get ipaddr(): string {
return appSettings.getString("ipaddr");
}
public static set ipaddr(value: string) {
this.setString("ipaddr", value);
}
// sonosPort
@serializable()
public static get sonosPort(): string {
return appSettings.getString("sonosPort");
}
public static set sonosPort(value: string) {
this.setString("sonosPort", value);
}
// lat
@serializable()
public static get lat(): string {
return appSettings.getString("lat", "");
}
public static set lat(value: string) {
this.setString("lat", value);
}
// long
@serializable()
public static get long(): string {
return appSettings.getString("long", "");
}
public static set long(value: string) {
this.setString("long", value);
}
// units
@serializable()
public static get units(): UnitTypes {
return appSettings.getNumber("units", UnitTypes.Auto);
}
public static set units(value: UnitTypes) {
this.setNumber("units", value);
}
// homeSceneId
@serializable()
public static get homeSceneId(): number {
return appSettings.getNumber("homeSceneId");
}
public static set homeSceneId(value: number) {
this.setNumber("homeSceneId", value);
}
// awaySceneId
@serializable()
public static get awaySceneId(): number {
return appSettings.getNumber("awaySceneId");
}
public static set awaySceneId(value: number) {
this.setNumber("awaySceneId", value);
}
// firebase user information
public static get userEmail(): string {
return appSettings.getString("userEmail");
}
public static set userEmail(value: string) {
this.setString("userEmail", value);
}
public static get userUIID(): string {
return appSettings.getString("userUIID");
}
public static set userUIID(value: string) {
this.setString("userUIID", value);
}
// is the user logged in
public static get isLoggedIn(): boolean {
return appSettings.getBoolean("isLoggedIn");
}
public static set isLoggedIn(value: boolean) {
this.setBoolean("isLoggedIn", value);
}
// syncData
public static get syncData(): boolean {
return appSettings.getBoolean("syncData");
}
public static set syncData(value: boolean) {
this.setBoolean("syncData", value);
}
// classes need a custom toJSON to be stringified ¯\_(ツ)_/¯
public static toJSON() {
var result = {};
for (var key in this) {
if (isSerializable(this, key)) {
result[key] = this[key];
}
}
return result;
}
// overwrites the settings object with any matched props
public static update(obj: any) {
for (var key in obj) {
this[key] = obj[key];
}
}
}
@toddanglin
Copy link

Overall, makes sense.

Small improvement in this version to check that the new property value has actually changed before processing continues:
https://gist.github.com/toddanglin/2749a80eeaf632cb9cc9fec742c68935

Sanity check: How do you prevent "loops" when Firebase updates the setting on a new device?

Scenario:

  • Device A changes isAway property from false to true
  • Device A triggers push to Firebase with new value for isAway
  • Device B gets notified by Firebase that there is a new value for isAway
  • Device B changes isAway from false to true (value from Firebase)
  • Device B triggers a new push to Firebase?

I guess at this point either A) Firebase filters out the extra update because the value on Device B and Firebase match, or B) Device A stops the cycle when it checks that the Setting value has changed before re-setting the property.

Okay. Seems to work. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment