-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #446 from athombv/develop
mdim
- Loading branch information
Showing
11 changed files
with
1,019 additions
and
129 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
'use strict'; | ||
|
||
module.exports = { | ||
async getSomething({ homey, query }) { | ||
// you can access query parameters like "/?foo=bar" through `query.foo` | ||
|
||
// you can access the App instance through homey.app | ||
// const result = await homey.app.getSomething(); | ||
// return result; | ||
|
||
// perform other logic like mapping result data | ||
|
||
return 'Hello from App'; | ||
}, | ||
|
||
async addSomething({ homey, body }) { | ||
// access the post body and perform some action on it. | ||
return homey.app.addSomething(body); | ||
}, | ||
|
||
async updateSomething({ homey, params, body }) { | ||
return homey.app.setSomething(body); | ||
}, | ||
|
||
async deleteSomething({ homey, params }) { | ||
return homey.app.deleteSomething(params.id); | ||
}, | ||
}; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<html> | ||
<head> | ||
<style> | ||
/* Example of a custom CSS class. */ | ||
.custom-image-class { | ||
margin: var(--homey-su-3) auto var(--homey-su-5); | ||
} | ||
</style> | ||
</head> | ||
|
||
<body class="homey-widget"> | ||
<img src="homey-logo.png" alt="Homey logo" class="custom-image-class" /> | ||
<p class="homey-text-regular homey-text-align-center">Edit public/index.html and hit refresh.</p> | ||
|
||
<script type="text/javascript"> | ||
function onHomeyReady(Homey) { | ||
Homey.ready({ height: 188 }); | ||
|
||
// View the settings the user provided if your widget has settings. | ||
console.log('Widget settings:', Homey.getSettings()); | ||
|
||
// Fetch something from your app. | ||
Homey.api('GET', '/', {}) | ||
.then((result) => { | ||
console.log(result); | ||
}) | ||
.catch(console.error); | ||
} | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
'use strict'; | ||
|
||
// exports.desc = 'Widget related commands'; | ||
exports.builder = yargs => { | ||
return yargs | ||
.commandDir('widget') | ||
.demandCommand() | ||
.help(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
'use strict'; | ||
|
||
const Log = require('../../../../lib/Log'); | ||
const App = require('../../../../lib/App'); | ||
|
||
exports.desc = 'Create a new Widget'; | ||
exports.handler = async yargs => { | ||
try { | ||
const app = new App(yargs.path); | ||
await app.createWidget(); | ||
process.exit(0); | ||
} catch (err) { | ||
Log.error(err); | ||
process.exit(1); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ const zlib = require('zlib'); | |
const http = require('http'); | ||
const stream = require('stream'); | ||
const { promisify } = require('util'); | ||
const sharp = require('sharp'); | ||
|
||
const { AthomAppsAPI, HomeyAPIV2 } = require('homey-api'); | ||
const { getAppLocales } = require('homey-lib'); | ||
|
@@ -444,6 +445,21 @@ class App { | |
}); | ||
|
||
// Proxy local assets | ||
|
||
const middlewares = {}; | ||
|
||
// During development with docker we get the widget public files from the source folder so that | ||
// the app does not have to be restarted when the widget files change. Making a change and | ||
// reloading the widget should fetch the new file. | ||
serverApp.use('/widgets/:widgetId/public', (req, res, next) => { | ||
const widgetId = req.params.widgetId; | ||
if (!middlewares[widgetId]) { | ||
const widgetPath = path.join(this.path, 'widgets', widgetId, 'public'); | ||
middlewares[widgetId] = express.static(widgetPath); | ||
} | ||
|
||
return middlewares[widgetId](req, res, next); | ||
}); | ||
serverApp.use('/', express.static(this._homeyBuildPath)); | ||
|
||
// Start the HTTP Server | ||
|
@@ -499,15 +515,14 @@ class App { | |
.on('getFile', ({ path }, callback) => { | ||
Promise.resolve().then(async () => { | ||
const res = await fetch(`http://localhost:${serverPort}${path}`); | ||
const { status } = res; | ||
const headers = { | ||
'Content-Type': res.headers.get('Content-Type') || undefined, | ||
'X-Homey-Hash': res.headers.get('X-Homey-Hash') || undefined, | ||
}; | ||
const body = await res.buffer(); | ||
|
||
return { | ||
status, | ||
status: res.status, | ||
headers, | ||
body, | ||
}; | ||
|
@@ -876,6 +891,44 @@ $ sudo systemctl restart docker | |
|
||
await fse.copy(fullSrc, fullDest); | ||
} | ||
|
||
const appJson = await fs.promises.readFile(path.join(this.path, 'app.json')).then(data => { | ||
return JSON.parse(data); | ||
}); | ||
|
||
if (appJson.widgets) { | ||
for (const [widgetId] of Object.entries(appJson.widgets)) { | ||
const previewLightPath = path.join(this.path, 'widgets', widgetId, 'preview-light.png'); | ||
const previewDarkPath = path.join(this.path, 'widgets', widgetId, 'preview-dark.png'); | ||
|
||
// eslint-disable-next-line no-useless-catch | ||
try { | ||
await fs.promises.access(previewLightPath); | ||
await fs.promises.access(previewDarkPath); | ||
|
||
const imageLight = sharp(previewLightPath); | ||
const imageDark = sharp(previewDarkPath); | ||
|
||
await fs.promises.mkdir(path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__'), { recursive: true }); | ||
await Promise.all([ | ||
fs.promises.copyFile(previewLightPath, path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', 'preview-light.png')), | ||
imageLight.resize(128, 128).toFile(path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', '[email protected]')), | ||
imageLight.resize(192, 192).toFile(path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', '[email protected]')), | ||
imageLight.resize(256, 256).toFile(path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', '[email protected]')), | ||
imageLight.resize(384, 384).toFile(path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', '[email protected]')), | ||
imageLight.resize(512, 512).toFile(path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', '[email protected]')), | ||
fs.promises.copyFile(previewDarkPath, path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', 'preview-dark.png')), | ||
imageDark.resize(128, 128).toFile(path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', '[email protected]')), | ||
imageDark.resize(192, 192).toFile(path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', '[email protected]')), | ||
imageDark.resize(256, 256).toFile(path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', '[email protected]')), | ||
imageDark.resize(384, 384).toFile(path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', '[email protected]')), | ||
imageDark.resize(512, 512).toFile(path.join(this._homeyBuildPath, 'widgets', widgetId, '__assets__', '[email protected]')), | ||
]); | ||
} catch (error) { | ||
throw error; | ||
} | ||
} | ||
} | ||
} | ||
|
||
async _copyAppProductionDependencies() { | ||
|
@@ -1305,6 +1358,8 @@ $ sudo systemctl restart docker | |
Log(colors.white(`\nVisit https://tools.developer.homey.app/apps/app/${appId}/build/${buildId} to publish your app.`)); | ||
} catch (error) { | ||
for (const undo of Object.values(undos)) { | ||
if (undo === null) continue; | ||
|
||
await undo().catch(err => { | ||
Log.error(err); | ||
}); | ||
|
@@ -1409,7 +1464,7 @@ $ sudo systemctl restart docker | |
'tsconfig.json', | ||
'env.json', | ||
'*.compose.json', | ||
'node_modules/*', | ||
'node_modules', | ||
]; | ||
// Add a "file" containing our default ignore rules for dotfiles, env.json and node_modules | ||
walker.onReadIgnoreFile(DEFAULT_IGNORE_RULES_FILE, ignoreRules.join('\r\n'), () => { }); | ||
|
@@ -2624,6 +2679,104 @@ $ sudo systemctl restart docker | |
Log.success(`Flow created in \`${flowPath}\``); | ||
} | ||
|
||
async createWidget() { | ||
if (App.hasHomeyCompose({ appPath: this.path }) === false) { | ||
// Note: this checks that we are in a valid homey app folder | ||
App.getManifest({ appPath: this.path }); | ||
|
||
if (await this._askComposeMigration()) { | ||
await this.migrateToCompose(); | ||
} else { | ||
throw new Error('This command requires Homey compose, run `homey app compose` to migrate!'); | ||
} | ||
} | ||
|
||
const { widgetName } = await inquirer.prompt([ | ||
{ | ||
type: 'input', | ||
name: 'widgetName', | ||
message: 'What is your Widgets\'s Name?', | ||
validate: input => input.length > 0, | ||
}, | ||
]); | ||
|
||
const { | ||
widgetId, | ||
} = await inquirer.prompt([ | ||
{ | ||
type: 'input', | ||
name: 'widgetId', | ||
message: 'What is your Widgets\'s ID?', | ||
default: () => { | ||
let name = widgetName; | ||
name = name.toLowerCase(); | ||
name = name.replace(/ /g, '-'); | ||
name = name.replace(INVALID_CHARACTERS, ''); | ||
return name; | ||
}, | ||
validate: input => { | ||
if (input.match(INVALID_CHARACTERS)) { | ||
throw new Error('Invalid characters: only use letters, numbers, minus (-) and underscore (_)'); | ||
} | ||
|
||
if (fs.existsSync(path.join(this.path, 'widgets', input))) { | ||
throw new Error('Widget directory already exists!'); | ||
} | ||
|
||
return true; | ||
}, | ||
}, | ||
]); | ||
|
||
const { confirm } = await inquirer.prompt([ | ||
{ | ||
type: 'confirm', | ||
name: 'confirm', | ||
message: 'Seems good?', | ||
}, | ||
]); | ||
|
||
if (!confirm) return; | ||
|
||
const widgetPath = path.join(this.path, 'widgets', widgetId); | ||
await fse.ensureDir(widgetPath); | ||
|
||
const widgetJson = { | ||
name: { en: widgetName }, | ||
settings: [], | ||
api: { | ||
getSomething: { | ||
method: 'GET', | ||
path: '/', | ||
}, | ||
addSomething: { | ||
method: 'POST', | ||
path: '/', | ||
}, | ||
updateSomething: { | ||
method: 'PUT', | ||
path: '/:id', | ||
}, | ||
deleteSomething: { | ||
method: 'DELETE', | ||
path: '/:id', | ||
}, | ||
}, | ||
}; | ||
|
||
await writeFileAsync(path.join(widgetPath, 'widget.compose.json'), JSON.stringify(widgetJson, false, 2)); | ||
|
||
const templatePath = path.join(__dirname, '..', 'assets', 'templates', 'app', 'widgets'); | ||
await fse.ensureDir(path.join(widgetPath, 'public')); | ||
await copyFileAsync(path.join(templatePath, 'public/index.html'), path.join(widgetPath, 'public/index.html')); | ||
await copyFileAsync(path.join(templatePath, 'public/homey-logo.png'), path.join(widgetPath, 'public/homey-logo.png')); | ||
await copyFileAsync(path.join(templatePath, 'api.js'), path.join(widgetPath, 'api.js')); | ||
await copyFileAsync(path.join(templatePath, 'preview-dark.png'), path.join(widgetPath, 'preview-dark.png')); | ||
await copyFileAsync(path.join(templatePath, 'preview-light.png'), path.join(widgetPath, 'preview-light.png')); | ||
|
||
Log.success(`Widget created in \`${widgetPath}\``); | ||
} | ||
|
||
async createDiscoveryStrategy() { | ||
if (App.hasHomeyCompose({ appPath: this.path }) === false) { | ||
// Note: this checks that we are in a valid homey app folder | ||
|
Oops, something went wrong.