Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added
- Entity module to encapsulate data storage

### Changed
- Fixed settings by moving code to companion
- API refactoring

## Version 1.0

### Added
Expand Down
3 changes: 3 additions & 0 deletions app/i18n/de-DE.po
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
msgid "unavailable"
msgstr "N/A"

msgid "loading"
msgstr "Lade..."

msgid "on"
msgstr "AN"

Expand Down
3 changes: 3 additions & 0 deletions app/i18n/en-US.po
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
msgid "unavailable"
msgstr "N/A"

msgid "loading"
msgstr "Loading..."

msgid "on"
msgstr "ON"

Expand Down
15 changes: 15 additions & 0 deletions app/i18n/it-IT.po
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
msgid "unavailable"
msgstr "N/D"

msgid "loading"
msgstr "Caricare..."

msgid "on"
msgstr "ACC"

Expand All @@ -10,3 +13,15 @@ msgstr "SPE"

msgid "exe"
msgstr "ESE"

msgid "open"
msgstr "APE"

msgid "opening"
msgstr "APE"

msgid "closing"
msgstr "CHI"

msgid "closed"
msgstr "CHI"
75 changes: 12 additions & 63 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,18 @@ import * as messaging from "messaging";

import { me } from "appbit";
import { gettext } from "i18n";
import { settingsType, settingsFile } from "../common/constants";
import { sendData } from "../common/utils";

import document from "document";

let Available = false;
const EntityList = document.getElementById("entityList");
const AddressText = document.getElementById("addressText");
AddressText.text = gettext("unavailable");

// Load settings
let settings = loadSettings();

// Register for the unload event
me.onunload = saveSettings;
AddressText.text = gettext("loading");

// List of {id: "", name: "", state: ""}
let Entities = [];

// List of state change by touch event
const NextStates = {
on: "turn_off",
off: "turn_on",
Expand All @@ -47,95 +41,50 @@ function setupList(list, data) {
tile.getElementById("itemText").text = `${info.name}`;
tile.getElementById("itemState").text = `${gettext(info.state)}`;
let touch = tile.getElementById("itemTouch");
touch.onclick = () => sendData({key: "change", entity: Entities[info.index].id, state: NextStates[info.state]});
touch.onclick = () => sendData({key: "set", entity: Entities[info.index].id, state: NextStates[info.state]});
}
}
};
list.length = data.length;
}

// Received message
// Received message from companion / HomeAssistantAPI
messaging.peerSocket.onmessage = (evt) => {
console.log(`Received: ${JSON.stringify(evt)}`);
if (evt.data.key === "clear") {
if (evt.data.key === "update" && evt.data.value === "begin") {
Entities = [];
settings.entities = [];
}
else if (evt.data.key === "add") {
Entities.push({id: evt.data.id, name: evt.data.name, state: evt.data.state});
settings.entities.push({name: evt.data.id});
}
else if (evt.data.key === "update" && evt.data.value === "end") {
setupList(EntityList, Entities);
}
else if (evt.data.key === "change") {
else if (evt.data.key === "set") {
Entities.forEach((entity, index) => {
if (entity.id === evt.data.id) {
//DEBUG console.log(`Updated: ${evt.data.id} to ${evt.data.state}`);
Entities[index].state = evt.data.state;
setupList(EntityList, Entities);
}
})
}
else if (evt.data.key === "api") {
if (evt.data.value === "ok") {
Available = true;
AddressText.text = evt.data.name;
}
else {
Available = false;
AddressText.text = evt.data.value;
}
}
else if (evt.data.key === "url") {
settings.url = evt.data.value;
sendData({key: "url", value: settings.url});
}
else if (evt.data.key === "port") {
settings.port = evt.data.value;
sendData({key: "port", value: settings.port});
}
else if (evt.data.key === "token") {
settings.token = evt.data.value;
sendData({key: "token", value: settings.token});
}
else if (evt.data.key === "force") {
settings.force = evt.data.value;
sendData({key: "force", value: settings.force});
}
}

// Message socket opens
messaging.peerSocket.onopen = () => {
console.log("Socket open");
sendData({key: "url", value: settings.url});
sendData({key: "port", value: settings.port});
sendData({key: "token", value: settings.token});
sendData({key: "entities", value: settings.entities});
sendData({key: "force", value: settings.force});
console.log("App socket open");
sendData({key: "refresh"});
};

// Message socket closes
messaging.peerSocket.onclose = () => {
console.log("Socket closed");
console.log("App socket closed");
};

// Load settings
function loadSettings() {
try {
return fs.readFileSync(settingsFile, settingsType);
}
catch (ex) {
console.error("Error loading settings");
// Default values
return {
url: "localhost",
port: "8123",
token: "",
force: true
};
}
}

// Save settings
function saveSettings() {
fs.writeFileSync(settingsFile, settings, settingsType);
}
130 changes: 130 additions & 0 deletions companion/Entity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* @module Entity
* @brief Provides container for HA entities
*/


/**
* Create Entity class object
* @param {string} id - Entity ID
* @param {string} name - Entity name
* @param {string] state - Entity state
*/
export function Entity(id, name, state) {
this.id = id;
this.name = name;
this.state = state;
}

/**
* Entity validity
* @return True if the entity has a valid id and a valid state
*/
Entity.prototype.isValid = function() {
let self = this;
return self.id !== undefined && self.id !== "" && self.state !== undefined && self.state !== "";
}

/**
* Create Entities class object
*/
export function Entities() {
this.list = [];
}

/**
* Clear entity list
*/
Entities.prototype.clear = function() {
let self = this;
self.list = [];
}

/**
* Add new entity
* @param {string} id - Entity id
* @param {string} name - Entity name
* @param {string} state - Entity state
*/
Entities.prototype.add = function(id, name, state) {
let self = this;
if (self.findById(id) === -1) {
let entity = new Entity(id, name, state);
if (entity.isValid()) {
self.list.push(entity);
}
}
}

/**
* Remove entity
* @param {string} id - Entity id
*/
Entities.prototype.remove = function(id) {
let self = this;
let index = self.findById(id);
if (index !== -1) {
self.list.splice(index, 1);
}
}

/**
* Find entity by its id
* @param {string} id - Entity id
* @return Entity index or -1 if entity wasn't found
*/
Entities.prototype.findById = function(id) {
let self = this;
self.list.forEach((entity, index) => {
if (entity.id === id) {
return index;
}
})
return -1;
}

/**
* Find entity by its name
* @param {string} name - Entity name
* @return Entity index or -1 if entity wasn't found
*/
Entities.prototype.findByName = function(name) {
let self = this;
self.list.forEach((entity, index) => {
if (entity.name === name) {
return index;
}
})
return -1;
}

/**
* Set state of an entity
* @param {string} id - Entity id
* @param {string} state - Entity state
*/
Entities.prototype.set = function(id, state) {
let self = this;
let index = self.findById(id);
if (index !== -1) {
self.list[index].state = state;
}
}

/**
* Sort entities by ids
*/
Entities.prototype.sort = function() {
let self = this;
self.list.sort(function(entityA, entityB) {
let idA = entityA.id.toUpperCase();
let idB = entityB.id.toUpperCase();
if (idA < idB) {
return -1;
}
else if (idA > idB) {
return 1;
}
return 0;
})
}
Loading