- you will need
nodejs
andgit
git clone https://github.com/ppisljar/espeasy_new_ui
cd espeasy_new_ui
npm install
npm start
-
upload
build/index.dev.html
to espeasy -
naviate to
http://esp-easy-ip/index.dev.htm
upload build/index.html
to espeasy
To add new devices follow this steps:
-
create a new file inside
/src/devices
namedXX_PluginName.js
whereXX
is your plugin number. For example1_input_switch.js
note: its best if you just copy existing file (for example copy1_input_switch.js
into103_your_plugin_name.js
) -
open your file and add correct form information. If you copied an existing file make sure to rename the export (line 10 in
1_input_switch.js
) to your plugins for example changeexport const inputSwitch = {
toexport const yourPluginName = {
-
correctly update the form settings. lets look at simplified
1_input_switch.js
:
import { pins } from './_defs'; // import list of pins, so we can fill it to the GPIO dropdown
const switchType = [ // defines switchType array, with two entries. We will fill this to Switch Type dropdown
{ name: 'switch', value: 0 },
{ name: 'dimmer', value: 3 },
]
export const inputSwitch = { // definition of our form
defaults: () => ({ // each form can define default values
gpio1: 255,
interval: 60,
'configs_float[1]': 1000, // if name contains weird charracters wrap it in string
'configs_float[2]': 1000,
'settings.values[0].name': 'Switch',// default name of first output variable
}),
sensor: { // each form can define multiple groups of configuration
name: 'Sensor', // name of the group
configs: { // options in this group
pullup: { name: 'Internal PullUp', type: 'checkbox', var: 'pin1pullup' }, // first option of type checkbox
gpio: { name: 'GPIO', type: 'select', options: pins, var: 'gpio1' }, // second option of type select (dropdown)
switch_type: { name: 'Switch Type', type: 'select', options: switchType, var: 'configs[0]' },
send_boot_state: { name: 'Send Boot State', type: 'checkbox', var: 'configs[3]' },
}
},
advanced: { // second group of options
name: 'Advanced event management',
configs: {
longpress_interval: { name: 'Longpress min interval (ms)', min: 1000, max: 5000, type: 'number', var: 'configs_float[2]' },
safe_button: { name: 'Use safe button', type: 'checkbox', var: 'configs_float[3]' },
}
},
data: true, // settings `data` to true will show 'send to controller' settings
vals: 1, // number of output values this device has
}
lets look into above in more detail.
Each form can define multiple groups of ouptions. Each group has a name and a list of options. First option from above looks like this:
pullup: { name: 'Internal PullUp', type: 'checkbox', var: 'pin1pullup' },
^ ^
ID Option object
This is the list of Option object
properties:
- name: name of the option (as shown in form)
- type: type of the option
Possible types:
- string: renders text box input
- number: renders number input
- select: renders dropdown.
options
property must be provided and must be an array of objects with propertyname
andvalue
- checkbox: renders checkbox
- password: renders password input
- button: renders button
- if: [optional] name of a variable that will be evaluated, if false the option will not show
- var: [optional] name of the variable to read/write
ID of option defines which variable the option represents. For example if option with id pullup
is in the group with id sensor
, then by default the variable to read/write that option would be task[taskId].sensor.pullup
. But if option has a var property, that variable will be used instead. In above case instead of using task[taskId].sensor.pullup
we use `task[taskId].pin1pullup
List of commonly used variables and their arduino code counterpart:
pin1pullup
:Device[deviceCount].PullUpOption
pin1inversed
:Device[deviceCount].InverseLogicOption
gpio1
:CONFIG_PIN1
(same for gpio2, gpio3)port
:CONFIG_PORT
configs[i]
:PCONFIG(i)
configs_long[i]
:PCONFIG_LONG(i)
configs_float[i]
:PCONFIG_FLOAT(i)
extra.plugin_config[i]
:ExtraTaskSettings.TaskDevicePluginConfig[i]
extra.plugin_config_long[i]
:ExtraTaskSettings.TaskDevicePluginConfigLong[i]
-
now that your file is complete, you need to import it in
index.js
(in/src/devices/
folder) you will need to add a line like thisimport { yourPluginName } from './103_your_plugin_name';
near the top, using the name you exported inside {} and pointing to your file name (without .js extension) before the last line insert something like{ name: 'My Plugin Name', value: 103, fields: yourPluginName },
, where name is the name as shown in UI, value is your plugin id number and for fields pass the imported object. -
save all files and from project root run
npm run build
, which will build new files for you in/build
folder. Its time to test!
/src/index.html
index html file, should not require any changes
/src/app.js
main entry file, loads configs, plugins and handles navigation
/src/plugins
plugins directory, currently there is only dashboard plugin there. plugins are not part of the main bundle (they get build into separate .js file and loaded on runtime)
/src/pages
all core pages (plugins can add more)
/src/lib
different library functions
/src/lib/node_definitions.js
definitions of all 'nodes' for automation editor
/src/devices
definition of all devices (tasks)
/src/conf/config.dat.js
definition of espeasy config file (for parsing/writing)
/src/components/espeasy_p2p
component for handling espeasy p2p network
/src/components/floweditor
drag and drop editor component used for rule editor
/src/components/form
form component
/src/components/menu
menu component
/src/components/page
page component
create a new file inside /src/pages
, lets call it testpage.js
, paste this into it:
import { h, Component } from 'preact';
export class TestPage extends Component {
render(props) {
return (
<h1>hello world</h1>
);
}
}
We are using preact, which is lightweight version of react for our rendering framework. To create a new page we create a new class which extends from Component.
For minimal implementation we have to define at least the render method, which needs to return JSX. (that html wrapped inside js). Our test page just writes out hello world.
We should also add our page to src/pages/index.js
:
export * from './testpage';
Next we are going to add a menu entry to access our page. Open src/lib/menu.js
, you will see the pages being imported on the top, and menu definitions a bit down:
const menus = [
{ title: 'Devices', href: 'devices', component: DevicesPage, children: [] },
{ title: 'Controllers', href: 'controllers', component: ControllersPage, children: [] },
{ title: 'Automation', href: 'rules', component: RulesEditorPage, class: 'full', children: [] },
{ title: 'Config', href: 'config', component: ConfigPage, children: [
{ title: 'Hardware', href: 'config/hardware', component: ConfigHardwarePage },
{ title: 'Advanced', href: 'config/advanced', component: ConfigAdvancedPage },
{ title: 'Rules', href: 'config/rules', component: RulesPage },
{ title: 'Save', href: 'config/save', action: saveConfig },
{ title: 'Load', href: 'config/load', component: LoadPage },
{ title: 'Reboot', href: 'config/reboot', component: RebootPage },
{ title: 'Factory Reset', href: 'config/factory', component: FactoryResetPage },
] },
{ title: 'Tools', href: 'tools', component: ToolsPage, children: [
{ title: 'Discover', href: 'tools/discover', component: DiscoverPage },
{ title: 'Info', href: 'tools/sysinfo', component: SysVarsPage },
{ title: 'Update', href: 'tools/update', component: UpdatePage },
{ title: 'Filesystem', href: 'tools/fs', component: FSPage },
{ title: 'Back to old UI', href: 'tools/oldui', action: oldUI },
] },
];
we are going to add our menu as the first item there:
const menus = [
{ title: 'Test Page', href: 'test', component: TestPage, children: [] },
{ title: 'Devices', href: 'devices', component: DevicesPage, children: [] },
{ title: 'Controllers', href: 'controllers', component: ControllersPage, children: [] },
{ title: 'Automation', href: 'rules', component: RulesEditorPage, class: 'full', children: [] },
{ title: 'Config', href: 'config', component: ConfigPage, children: [
{ title: 'Hardware', href: 'config/hardware', component: ConfigHardwarePage },
{ title: 'Advanced', href: 'config/advanced', component: ConfigAdvancedPage },
{ title: 'Rules', href: 'config/rules', component: RulesPage },
{ title: 'Save', href: 'config/save', action: saveConfig },
{ title: 'Load', href: 'config/load', component: LoadPage },
{ title: 'Reboot', href: 'config/reboot', component: RebootPage },
{ title: 'Factory Reset', href: 'config/factory', component: FactoryResetPage },
] },
{ title: 'Tools', href: 'tools', component: ToolsPage, children: [
{ title: 'Discover', href: 'tools/discover', component: DiscoverPage },
{ title: 'Info', href: 'tools/sysinfo', component: SysVarsPage },
{ title: 'Update', href: 'tools/update', component: UpdatePage },
{ title: 'Filesystem', href: 'tools/fs', component: FSPage },
{ title: 'Back to old UI', href: 'tools/oldui', action: oldUI },
] },
];
and don't forget to add import for TestPage on the top.
each entry has the following properties:
title
: name of the menu as shown to the user
href
: url of the menu (as seen in navigation bar)
component
: your page component that we created in previous step
children
: array of submenus, allows nesting menus
this is it, you should have your test page show up in espeasy.
settings
(/src/lib/settings
) gives you access to espeasy configuration
settings.get(prop)
gets specified prop from settingssettings.set(prop, val)
sets specified prop to value
loader
(/src/lib/loader
) allows you to show/hide loader on async actions
loader.show()
loader.hide()
- setup your development environment as described under
developing
- to upgrade dependencies to latest versions run
yarn upgrade
- c