Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
npm-debug.log*
.DS_Store
.idea
node_modules


# Added by Homey CLI
/.homeybuild/
12 changes: 5 additions & 7 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
// eslint-disable-next-line import/no-unresolved
const Homey = require('homey');
const Mill = require('./lib/mill');
const { debug } = require('./lib/util');

const {debug} = require('./../../lib/util');
class MillApp extends Homey.App {
onInit() {
this.millApi = new Mill();
this.user = null;
this.isAuthenticated = false;
this.isAuthenticating = false;

debug(`${Homey.manifest.id} is running..`);
debug(this.homey,`${Homey.manifest.id} is running..`);
}

async connectToMill() {
const username = Homey.ManagerSettings.get('username');
const password = Homey.ManagerSettings.get('password');
const username = this.homey.settings.get('username');
const password = this.homey.settings.get('password');

return this.authenticate(username, password);
}
Expand All @@ -26,7 +24,7 @@ class MillApp extends Homey.App {
this.isAuthenticating = true;
this.user = await this.millApi.login(username, password) || null;
this.isAuthenticated = true;
debug('Mill authenticated');
debug(this.homey,'Mill authenticated');
return true;
} finally {
this.isAuthenticating = false;
Expand Down
5 changes: 3 additions & 2 deletions app.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"id": "com.mill",
"version": "1.0.8",
"compatibility": ">=1.5.0",
"sdk": 2,
"compatibility": ">=5.0.0",
"sdk": 3,
"name": {
"en": "Mill WiFi"
},
"brandColor": "#20DFCB",
"tags": {
"en": [
"mill",
Expand Down
73 changes: 34 additions & 39 deletions drivers/mill/device.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// eslint-disable-next-line import/no-unresolved
const Homey = require('homey');
const { Log } = require('./../../lib/log');
const { debug, error } = require('./../../lib/util');
const Room = require('./../../lib/models');
const { Log } = require('./../../lib/log');
const {debug,error} = require('./../../lib/util');

class MillDevice extends Homey.Device {
onInit() {
this.deviceId = this.getData().id;

debug(`[${this.getName()}] ${this.getClass()} (${this.deviceId}) initialized`);
debug(this.homey,`[${this.getName()}] ${this.getClass()} (${this.deviceId}) initialized`);

// Add new capailities for devices that don't have them yet
if (!this.getCapabilities().includes('onoff')) {
Expand All @@ -24,31 +24,26 @@ class MillDevice extends Homey.Device {
this.registerCapabilityListener('onoff', this.onCapabilityOnOff.bind(this));

// triggers
this.modeChangedTrigger = new Homey.FlowCardTriggerDevice('mill_mode_changed');
this.modeChangedTrigger.register();

this.modeChangedToTrigger = new Homey.FlowCardTriggerDevice('mill_mode_changed_to');
this.modeChangedTrigger = this.homey.flow.getDeviceTriggerCard('mill_mode_changed');

this.modeChangedToTrigger = this.homey.flow.getDeviceTriggerCard('mill_mode_changed_to');
this.modeChangedToTrigger
.register()
.registerRunListener((args, state) => args.mill_mode === state.mill_mode);

// conditions
this.isHeatingCondition = new Homey.FlowCardCondition('mill_is_heating');
this.isHeatingCondition =this.homey.flow.getConditionCard('mill_is_heating');
this.isHeatingCondition
.register()
.registerRunListener(() => (this.room && this.room.heatStatus === 1));

this.isMatchingModeCondition = new Homey.FlowCardCondition('mill_mode_matching');
this.isMatchingModeCondition = this.homey.flow.getConditionCard('mill_mode_matching');
this.isMatchingModeCondition
.register()
.registerRunListener(args => (args.mill_mode === this.room.modeName));

// actions
this.setProgramAction = new Homey.FlowCardAction('mill_set_mode');
this.setProgramAction = this.homey.flow.getActionCard('mill_set_mode');
this.setProgramAction
.register()
.registerRunListener((args) => {
debug(`[${args.device.getName()}] Flow changed mode to ${args.mill_mode}`);
debug(this.homey,`[${args.device.getName()}] Flow changed mode to ${args.mill_mode}`);
return args.device.setThermostatMode(args.mill_mode);
});

Expand All @@ -58,28 +53,28 @@ class MillDevice extends Homey.Device {
}

async refreshState() {
debug(`[${this.getName()}] Refreshing state`);
debug(this.homey,`[${this.getName()}] Refreshing state`);

if (this.refreshTimeout) {
clearTimeout(this.refreshTimeout);
this.refreshTimeout = null;
}

try {
if (Homey.app.isConnected()) {
if (this.homey.app.isConnected()) {
await this.refreshMillService();
this.setAvailable();
} else {
debug(`[${this.getName()}] Mill not connected`);
debug(this.homey,`[${this.getName()}] Mill not connected`);
this.setUnavailable();
await Homey.app.connectToMill().then(() => {
await this.homey.app.connectToMill().then(() => {
this.scheduleRefresh(10);
}).catch((err) => {
error('Error caught while refreshing state', err);
error(this.homey,'Error caught while refreshing state', err);
});
}
} catch (e) {
error('Exception caught', e);
error(this.homey,'Exception caught', e);
Log.captureException(e);
} finally {
if (this.refreshTimeout === null) {
Expand All @@ -89,17 +84,17 @@ class MillDevice extends Homey.Device {
}

scheduleRefresh(interval) {
const refreshInterval = interval || Homey.ManagerSettings.get('interval');
const refreshInterval = interval || this.homey.settings.get('interval');
this.refreshTimeout = setTimeout(this.refreshState.bind(this), refreshInterval * 1000);
debug(`[${this.getName()}] Next refresh in ${refreshInterval} seconds`);
debug(this.homey,`[${this.getName()}] Next refresh in ${refreshInterval} seconds`);
}

async refreshMillService() {
const millApi = Homey.app.getMillApi();
const millApi = this.homey.app.getMillApi();

return millApi.listDevices(this.getData().id)
.then((room) => {
debug(`[${this.getName()}] Mill state refreshed`, {
debug(this.homey,`[${this.getName()}] Mill state refreshed`, {
comfortTemp: room.comfortTemp,
awayTemp: room.awayTemp,
holidayTemp: room.holidayTemp,
Expand All @@ -112,7 +107,7 @@ class MillDevice extends Homey.Device {

if (room.programMode !== undefined) {
if (this.room && !this.room.modesMatch(room) && this.room.modeName !== room.modeName) {
debug(`[${this.getName()}] Triggering mode change from ${this.room.modeName} to ${room.modeName}`);
debug(this.homey,`[${this.getName()}] Triggering mode change from ${this.room.modeName} to ${room.modeName}`);
// not needed, setCapabilityValue will trigger
// this.modeChangedTrigger.trigger(this, { mill_mode: room.modeName })
// .catch(this.error);
Expand Down Expand Up @@ -142,43 +137,43 @@ class MillDevice extends Homey.Device {
});
}
}).catch((err) => {
error(`[${this.getName()}] Error caught while refreshing state`, err);
error(this.homey,`[${this.getName()}] Error caught while refreshing state`, err);
});
}

onAdded() {
debug('device added', this.getState());
debug(this.homey,'device added', this.getState());
}

onDeleted() {
clearTimeout(this.refreshTimeout);
debug('device deleted', this.getState());
debug(this.homey,'device deleted', this.getState());
}

onCapabilityTargetTemperature(value, opts, callback) {
debug(`onCapabilityTargetTemperature(${value})`);
debug(this.homey,`onCapabilityTargetTemperature(${value})`);
const temp = Math.ceil(value);
if (temp !== value && this.room.modeName !== 'Off') { // half degrees isn't supported by Mill, need to round it up
this.setCapabilityValue('target_temperature', temp);
debug(`onCapabilityTargetTemperature(${value}=>${temp})`);
debug(this.homey,`onCapabilityTargetTemperature(${value}=>${temp})`);
}
const millApi = Homey.app.getMillApi();
const millApi = this.homey.app.getMillApi();
this.room.targetTemp = temp;
millApi.changeRoomTemperature(this.deviceId, this.room)
.then(() => {
debug(`onCapabilityTargetTemperature(${temp}) done`);
debug(`[${this.getName()}] Changed temp to ${temp}: currentMode: ${this.room.currentMode}/${this.room.programMode}, comfortTemp: ${this.room.comfortTemp}, awayTemp: ${this.room.awayTemp}, avgTemp: ${this.room.avgTemp}, sleepTemp: ${this.room.sleepTemp}`);
debug(this.homey,`onCapabilityTargetTemperature(${temp}) done`);
debug(this.homey,`[${this.getName()}] Changed temp to ${temp}: currentMode: ${this.room.currentMode}/${this.room.programMode}, comfortTemp: ${this.room.comfortTemp}, awayTemp: ${this.room.awayTemp}, avgTemp: ${this.room.avgTemp}, sleepTemp: ${this.room.sleepTemp}`);
callback(null, temp);
}).catch((err) => {
debug(`onCapabilityTargetTemperature(${temp}) error`);
debug(`[${this.getName()}] Change temp to ${temp} resultet in error`, err);
debug(this.homey,`onCapabilityTargetTemperature(${temp}) error`);
debug(this.homey,`[${this.getName()}] Change temp to ${temp} resultet in error`, err);
callback(err);
});
}

setThermostatMode(value) {
return new Promise((resolve, reject) => {
const millApi = Homey.app.getMillApi();
const millApi = this.homey.app.getMillApi();
this.room.modeName = value;
const jobs = [];
if (this.room.modeName !== 'Off') {
Expand All @@ -187,10 +182,10 @@ class MillDevice extends Homey.Device {
jobs.push(millApi.changeRoomMode(this.deviceId, this.room.currentMode));

Promise.all(jobs).then(() => {
debug(`[${this.getName()}] Changed mode to ${value}: currentMode: ${this.room.currentMode}/${this.room.programMode}, comfortTemp: ${this.room.comfortTemp}, awayTemp: ${this.room.awayTemp}, avgTemp: ${this.room.avgTemp}, sleepTemp: ${this.room.sleepTemp}`);
debug(this.homey,`[${this.getName()}] Changed mode to ${value}: currentMode: ${this.room.currentMode}/${this.room.programMode}, comfortTemp: ${this.room.comfortTemp}, awayTemp: ${this.room.awayTemp}, avgTemp: ${this.room.avgTemp}, sleepTemp: ${this.room.sleepTemp}`);
resolve(value);
}).catch((err) => {
error(`[${this.getName()}] Change mode to ${value} resulted in error`, err);
error(this.homey,`[${this.getName()}] Change mode to ${value} resulted in error`, err);
reject(err);
});
});
Expand Down
11 changes: 5 additions & 6 deletions drivers/mill/driver.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
// eslint-disable-next-line import/no-unresolved
const Homey = require('homey');
const { debug } = require('./../../lib/util');

const {debug,error} = require('./../../lib/util');
class MillDriver extends Homey.Driver {
async onInit() {
}

async onPairListDevices(data, callback) {
if (!Homey.app.isConnected()) {
// eslint-disable-next-line no-underscore-dangle
debug('Unable to pair, not authenticated');
debug(this.homey,'Unable to pair, not authenticated');
callback(new Error(Homey.__('pair.messages.notAuthorized')));
} else {
debug('Pairing');
debug(this.homey,'Pairing');
const millApi = Homey.app.getMillApi();
const homes = await millApi.listHomes();
debug(`Found following homes: ${homes.homeList.map(home => `${home.homeName} (${home.homeId})`).join(', ')}`);
debug(this.homey,`Found following homes: ${homes.homeList.map(home => `${home.homeName} (${home.homeId})`).join(', ')}`);

const rooms = await Promise.all(homes.homeList.map(async (home) => {
const rooms = await millApi.listRooms(home.homeId);
debug(`Found following rooms in ${home.homeName}: ${rooms.roomInfo.map(room => `${room.roomName} (${room.roomId})`).join(', ')}`);
debug(this.homey,`Found following rooms in ${home.homeName}: ${rooms.roomInfo.map(room => `${room.roomName} (${room.roomId})`).join(', ')}`);

return rooms.roomInfo.map(room => (
{
Expand Down
2 changes: 1 addition & 1 deletion lib/mill.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const crypto = require('crypto');
const Room = require('./models');
const { fetchJSON } = require('./util');
const {fetchJSON} = require('./util');

const TOKEN_SAFE_ZONE = 5 * 60 * 1000;

Expand Down
27 changes: 13 additions & 14 deletions lib/util.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
// eslint-disable-next-line import/no-unresolved
const fetch = require('node-fetch');

const log = (severity, message, data) => {
const log = (app,severity, message, data) => {
// Homey will not be available in tests
let Homey;
try { Homey = require('homey'); } catch (e) {}
if (!Homey) {
console.log(`${severity}: ${message}`, data || '');
return;
}

if (Homey.ManagerSettings.get('debug')) {
const debugLog = Homey.ManagerSettings.get('debugLog') || [];
const debug = app.settings.get('debug');
if ( debug ) {
const debugLog = app.settings.get('debugLog') || [];
const entry = { registered: new Date().toLocaleString(), severity, message };
if (data) {
if (typeof data === 'string') {
Expand All @@ -27,22 +26,22 @@ const log = (severity, message, data) => {
if (debugLog.length > 100) {
debugLog.splice(0, 1);
}
Homey.app.log(`${severity}: ${message}`, data || '');
Homey.ManagerSettings.set('debugLog', debugLog);
Homey.ManagerApi.realtime('debugLog', entry);
app.log(`${severity}: ${message}`, data || '');
app.settings.set('debugLog', debugLog);
app.api.realtime('debugLog', entry);
}
};

const debug = (message, data) => {
log('DEBUG', message, data);
const debug = (app, message, data) => {
log(app,'DEBUG', message, data);
};

const warn = (message, data) => {
log('WARN', message, data);
const warn = (app,message, data) => {
log(app,'WARN', message, data);
};

const error = (message, data) => {
log('ERROR', message, data);
const error = (app,message, data) => {
log(app,'ERROR', message, data);
};

const fetchJSON = async (endpoint, options) => {
Expand Down
22 changes: 0 additions & 22 deletions node_modules/@sentry/apm/README.md

This file was deleted.

5 changes: 0 additions & 5 deletions node_modules/@sentry/apm/dist/hubextensions.d.ts

This file was deleted.

1 change: 0 additions & 1 deletion node_modules/@sentry/apm/dist/hubextensions.d.ts.map

This file was deleted.

Loading