Skip to content

Commit

Permalink
Merge pull request #420 from athombv/develop
Browse files Browse the repository at this point in the history
mdim
  • Loading branch information
jeroenwienk authored Aug 14, 2024
2 parents 1f4a9c8 + 7024a44 commit 485aae7
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 32 deletions.
5 changes: 5 additions & 0 deletions assets/homey/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

const Homey = require('homey');

module.exports = Homey;
5 changes: 5 additions & 0 deletions assets/homey/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "homey",
"main": "index.js",
"type": "commonjs"
}
14 changes: 14 additions & 0 deletions assets/templates/app/app.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Homey from 'homey';

class MyApp extends Homey.App {

/**
* onInit is called when the app is initialized.
*/
async onInit() {
this.log('MyApp has been initialized');
}

}

export default MyApp;
4 changes: 2 additions & 2 deletions assets/templates/app/drivers/device.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use strict';

const { Device } = require('homey');
const Homey = require('homey');

class MyDevice extends Device {
class MyDevice extends Homey.Device {

/**
* onInit is called when the device is initialized.
Expand Down
49 changes: 49 additions & 0 deletions assets/templates/app/drivers/device.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Homey from 'homey';

class MyDevice extends Homey.Device {

/**
* onInit is called when the device is initialized.
*/
async onInit() {
this.log('MyDevice has been initialized');
}

/**
* onAdded is called when the user adds the device, called just after pairing.
*/
async onAdded() {
this.log('MyDevice has been added');
}

/**
* onSettings is called when the user updates the device's settings.
* @param {object} event the onSettings event data
* @param {object} event.oldSettings The old settings object
* @param {object} event.newSettings The new settings object
* @param {string[]} event.changedKeys An array of keys changed since the previous version
* @returns {Promise<string|void>} return a custom message that will be displayed
*/
async onSettings({ oldSettings, newSettings, changedKeys }) {
this.log('MyDevice settings where changed');
}

/**
* onRenamed is called when the user updates the device's name.
* This method can be used this to synchronise the name to the device.
* @param {string} name The new name
*/
async onRenamed(name) {
this.log('MyDevice was renamed');
}

/**
* onDeleted is called when the user deleted the device.
*/
async onDeleted() {
this.log('MyDevice has been deleted');
}

}

export default MyDevice;
4 changes: 2 additions & 2 deletions assets/templates/app/drivers/driver.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use strict';

const { Driver } = require('homey');
const Homey = require('homey');

class MyDriver extends Driver {
class MyDriver extends Homey.Driver {

/**
* onInit is called when the driver is initialized.
Expand Down
34 changes: 34 additions & 0 deletions assets/templates/app/drivers/driver.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Homey from 'homey';

class MyDriver extends Homey.Driver {

/**
* onInit is called when the driver is initialized.
*/
async onInit() {
this.log('MyDriver has been initialized');
}

/**
* onPairListDevices is called when a user is adding a device
* and the 'list_devices' view is called.
* This should return an array with the data of devices that are available for pairing.
*/
async onPairListDevices() {
return [
// Example device data, note that `store` is optional
// {
// name: 'My Device',
// data: {
// id: 'my-device',
// },
// store: {
// address: '127.0.0.1',
// },
// },
];
}

}

export default MyDriver;
69 changes: 61 additions & 8 deletions lib/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,19 @@ class App {
return false;
}

static usesModules({ appPath }) {
const pkgPath = path.join(appPath, 'package.json');

try {
const pkg = fse.readJSONSync(pkgPath);
return Boolean(pkg && pkg.type === 'module');
} catch (error) {
// Ignore
}

return false;
}

static async transpileToTypescript({ appPath }) {
Log.success('Typescript detected. Compiling...');

Expand Down Expand Up @@ -814,7 +827,9 @@ $ sudo systemctl restart docker

// Build app.json from Homey Compose files
if (App.hasHomeyCompose({ appPath: this.path })) {
await HomeyCompose.build({ appPath: this.path });
const usesModules = App.usesModules({ appPath: this.path });

await HomeyCompose.build({ appPath: this.path, usesModules });
}

// Clear the .homeybuild/ folder
Expand Down Expand Up @@ -867,6 +882,8 @@ $ sudo systemctl restart docker
const hasNodeModules = fs.existsSync(path.join(this.path, 'node_modules'));
const hasPackageJSON = fs.existsSync(path.join(this.path, 'package.json'));

fse.ensureDirSync(path.join(this._homeyBuildPath, 'node_modules'));

if (hasNodeModules === true && hasPackageJSON === false) {
// `npm ls` (in getProductionDependencies) needs a package.json to list dependencies
// If an app has a node_modules folder but no pacakge.json we just copy it wholesale.
Expand Down Expand Up @@ -896,6 +913,13 @@ $ sudo systemctl restart docker
},
});
}

// Overwrite the `node_modules/homey` module with the one from the CLI. This is so that apps can
// import ... from 'homey';
fse.ensureDirSync(path.join(this._homeyBuildPath, 'node_modules', 'homey'));
fse.emptyDirSync(path.join(this._homeyBuildPath, 'node_modules', 'homey'));
const homeyModulePath = path.join(__dirname, '..', 'assets', 'homey');
await fse.copy(homeyModulePath, path.join(this._homeyBuildPath, 'node_modules', 'homey'));
}

async version(version) {
Expand Down Expand Up @@ -1750,12 +1774,30 @@ $ sudo systemctl restart docker
await writeFileAsync(path.join(driverPath, 'driver.settings.compose.json'), JSON.stringify(driverWireless.settings, false, 2));
}

const templatePath = path.join(__dirname, '..', 'assets', 'templates', 'app', 'drivers');

// Matter devices do not need a device/driver file. As apps cannot add functionality to Matter devices.
if (!driverWireless.matter) {
const fileExtension = App.usesTypeScript({ appPath: this.path }) ? '.ts' : '.js';
const templatePath = path.join(__dirname, '..', 'assets', 'templates', 'app', 'drivers');
await copyFileAsync(path.join(templatePath, `driver${fileExtension}`), path.join(driverPath, `driver${fileExtension}`));
await copyFileAsync(path.join(templatePath, `device${fileExtension}`), path.join(driverPath, `device${fileExtension}`));
if (App.usesModules({ appPath: this.path })) {
await copyFileAsync(
path.join(templatePath, 'driver.mjs'),
path.join(driverPath, 'driver.js'),
);
await copyFileAsync(
path.join(templatePath, 'device.mjs'),
path.join(driverPath, 'device.js'),
);
} else {
const fileExtension = App.usesTypeScript({ appPath: this.path }) ? '.ts' : '.js';
await copyFileAsync(
path.join(templatePath, `driver${fileExtension}`),
path.join(driverPath, `driver${fileExtension}`),
);
await copyFileAsync(
path.join(templatePath, `device${fileExtension}`),
path.join(driverPath, `device${fileExtension}`),
);
}
}

await fse.ensureDir(path.join(driverPath, 'assets'));
Expand Down Expand Up @@ -2864,13 +2906,18 @@ $ sudo systemctl restart docker
const packageJson = {
name: answers.id,
version: '1.0.0',
main: answers.typescript ? 'app.ts' : 'app.js',
main: 'app.js',
scripts: {
build: answers.typescript ? 'tsc' : undefined,
build: undefined,
lint: answers.eslint ? 'eslint --ext .js,.ts --ignore-path .gitignore .' : undefined,
},
};

if (answers.typescript) {
delete packageJson.main;
packageJson.scripts.build = 'tsc';
}

await writeFileAsync(path.join(appPath, 'package.json'), JSON.stringify(packageJson, false, 2));

const appJson = {
Expand Down Expand Up @@ -2969,10 +3016,16 @@ $ sudo systemctl restart docker
// copy files
const templatePath = path.join(__dirname, '..', 'assets', 'templates', 'app');
const files = [
packageJson.main,
path.join('assets', 'icon.svg'),
];

// eslint-disable-next-line no-constant-condition
if (false) { // Add question.
await copyFileAsync(path.join(templatePath, 'app.mjs'), path.join(appPath, 'app.js'));
} else {
files.push(answers.typescript ? 'app.ts' : packageJson.main);
}

if (answers.license) {
files.push('LICENSE');
files.push('CODE_OF_CONDUCT.md');
Expand Down
11 changes: 8 additions & 3 deletions lib/HomeyCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@ const FLOW_TYPES = ['triggers', 'conditions', 'actions'];
class HomeyCompose {

// Temporary simpler api
static async build({ appPath }) {
const compose = new HomeyCompose(appPath);
static async build({ appPath, usesModules }) {
const compose = new HomeyCompose(appPath, usesModules);
await compose.run();
}

constructor(appPath) {
constructor(appPath, usesModules) {
this._appPath = appPath;
this._usesModules = usesModules;
}

async run() {
Expand All @@ -74,6 +75,10 @@ class HomeyCompose {
if (err.code !== 'ENOENT') throw new Error(err);
}

if (this._usesModules) {
this._appJson.esm = true;
}

await this._composeFlow();
await this._composeDrivers();
await this._composeCapabilities();
Expand Down
30 changes: 15 additions & 15 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "homey",
"version": "3.6.2",
"version": "3.6.3",
"description": "Command-line interface and type declarations for Homey Apps",
"main": "bin/homey.js",
"files": [
Expand Down Expand Up @@ -35,7 +35,7 @@
"fs-extra": "^10.1.0",
"get-port": "^5.1.1",
"homey-api": "^3.5.3",
"homey-lib": "^2.30.1",
"homey-lib": "^2.31.1",
"ignore-walk": "^3.0.3",
"inquirer": "8.1.2",
"is-wsl": "^2.2.0",
Expand Down

0 comments on commit 485aae7

Please sign in to comment.