diff --git a/.homeychangelog.json b/.homeychangelog.json index 450dd10..713328a 100644 --- a/.homeychangelog.json +++ b/.homeychangelog.json @@ -642,6 +642,10 @@ "1.8.6": { "en": "Internal optimization.", "de": "Interne Optimierung." + }, + "1.9.0": { + "en": "Added widget for alarm control panel.", + "de": "Widget für Alarmzentrale hinzugefügt." } } diff --git a/.homeycompose/app.json b/.homeycompose/app.json index 124b1a8..7e48761 100644 --- a/.homeycompose/app.json +++ b/.homeycompose/app.json @@ -1,6 +1,6 @@ { "id": "io.home-assistant.community", - "version": "1.8.6", + "version": "1.9.0", "compatibility": ">=12.2.0", "sdk": 3, "brandColor": "#0DA6EA", diff --git a/.vscode/launch.json b/.vscode/launch.json index cb8b0c2..0f7d3de 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -29,8 +29,8 @@ "type": "node", "request": "attach", "restart": true, - "name": "Attach HA to Homey23 DEV Docker", - "address": "192.168.1.16", + "name": "Attach HA to Homey 23 DEV Docker", + "address": "192.168.1.42", "port": 9229, "localRoot": "${workspaceFolder}", "remoteRoot": "/app/" diff --git a/app.js b/app.js index 3002401..59a93d3 100644 --- a/app.js +++ b/app.js @@ -147,6 +147,8 @@ class App extends Homey.App { await this._registerFlowTriggers(); await this._registerFlowConditions(); await this._registerFlowArguments(); + // register widgets + await this._registerWidgets(); // App events this.homey.settings.on("set", async (key) => { @@ -1608,7 +1610,64 @@ class App extends Homey.App { } }); } - + // WIDGETS ============================================================================== + async _registerWidgets(){ + this.homey.dashboards.getWidget('alarm_control_panel').registerSettingAutocompleteListener('device', async (query, settings) => { + let devices = []; + let alarm_devices = this.homey.drivers.getDriver('alarm_control_panel').getDevices(); + alarm_devices.forEach(device => { + devices.push({ + name: device.getName(), + device_id: device.getData().id, + driver_id: 'alarm_control_panel' + }) + }); + return devices.filter((item) => item.name.toLowerCase().includes(query.toLowerCase())); + }); + } + + // WIDGET API ============================================================================ + async apiWidgetUpdate(driver_id, device_id){ + let device = this.homey.drivers.getDriver(driver_id).getDevices().filter(e=>{ return ( e.getData().id == device_id ) })[0]; + await device.widgetUpdate( ); + } + + async apiWidgetPost(driver_id, device_id, body){ + if (!device_id || !driver_id){ + throw new Error("Device not set in widget settings"); + } + try{ + let device = this.homey.drivers.getDriver(driver_id).getDevices().filter(e=>{ return ( e.getData().id == device_id ) })[0]; + await device.widgetPost( body ); + } + catch(error){ + try{ + let message = JSON.parse(error.message) + throw new Error(message.message); + } + catch(error){ + throw new Error(error.message); + } + } + } + async apiWidgetGet(driver_id, device_id, command){ + if (!device_id || !driver_id){ + throw new Error("Device not set in widget settings"); + } + try{ + let device = this.homey.drivers.getDriver(driver_id).getDevices().filter(e=>{ return ( e.getData().id == device_id ) })[0]; + await device.widgetGet( command ); + } + catch(error){ + try{ + let message = JSON.parse(error.message) + throw new Error(message.message); + } + catch(error){ + throw new Error(error.message); + } + } + } } module.exports = App; \ No newline at end of file diff --git a/app.json b/app.json index 8d1c453..8216a20 100644 --- a/app.json +++ b/app.json @@ -1,7 +1,7 @@ { "_comment": "This file is generated. Please edit .homeycompose/app.json instead.", "id": "io.home-assistant.community", - "version": "1.8.6", + "version": "1.9.0", "compatibility": ">=12.2.0", "sdk": 3, "brandColor": "#0DA6EA", @@ -9962,6 +9962,41 @@ ] } ], + "widgets": { + "alarm_control_panel": { + "name": { + "en": "Alarm control panel", + "de": "Alarmzentrale", + "nl": "Alarmbedieningspaneel" + }, + "settings": [ + { + "id": "device", + "type": "autocomplete", + "title": { + "en": "Gerät", + "de": "Device" + } + } + ], + "height": 226, + "api": { + "apiWidgetUpdate": { + "method": "GET", + "path": "/widget_update" + }, + "apiWidgetPost": { + "method": "POST", + "path": "/widget_post" + }, + "apiWidgetGet": { + "method": "GET", + "path": "/widget_get" + } + }, + "id": "alarm_control_panel" + } + }, "capabilities": { "alarm_control_panel_alarm": { "type": "boolean", diff --git a/drivers/alarm_control_panel/device.js b/drivers/alarm_control_panel/device.js index e2dcece..58daae1 100644 --- a/drivers/alarm_control_panel/device.js +++ b/drivers/alarm_control_panel/device.js @@ -87,6 +87,7 @@ class AlarmControlPanelDevice extends BaseDevice { if ((features & 32) == 32) { modes.push( this.homey.app.manifest.capabilities.alarm_control_panel_mode.values.find(u => u.id === 'armed_vacation') ); } + modes.push( this.homey.app.manifest.capabilities.alarm_control_panel_mode.values.find(u => u.id === 'disarmed') ); await this.setCapabilityEnumList("alarm_control_panel_mode", modes); if ((features & 8) != 8 && this.hasCapability('alarm_control_panel_alarm_trigger')){ @@ -113,6 +114,8 @@ class AlarmControlPanelDevice extends BaseDevice { try{ await this.setCapabilityValue("alarm_control_panel_mode", data.state); + // Realtime event - Widget update + await this.widgetUpdate(); } catch(error){ this.log("Error setting alarm_control_panel_mode" + error.message); @@ -139,6 +142,9 @@ class AlarmControlPanelDevice extends BaseDevice { // Capabilities =========================================================================================== async _onCapabilityAlarmControlPanelMode( value, opts ) { let code = this.getSetting('code'); + if (opts.code){ + code = opts.code; + } let service = ''; switch (value){ case 'disarmed': @@ -183,6 +189,29 @@ class AlarmControlPanelDevice extends BaseDevice { await this._onCapabilityAlarmControlPanelMode( args.mode, {}); } + // Widget Actions + async widgetUpdate(){ + // Redefinition: Trigger Widget update by sending a realtime event + let modes = []; + try{ + modes = await this.getCapabilityEnumList("alarm_control_panel_mode"); + } + catch(error){} + await this.homey.api.realtime("alarm_control_panel_state_changed", {driver_id:'alarm_control_panel', device_id: this.getData().id, + mode: this.getCapabilityValue("alarm_control_panel_mode"), + modes: modes + } ); + } + + async widgetPost(body){ + // Redefinition: Process HTTP POST from Widget + switch (body.command){ + case 'set_alarm_control_panel_mode': + await this._onCapabilityAlarmControlPanelMode( body.mode, {code: body.code}); + break; + } + } + } module.exports = AlarmControlPanelDevice; \ No newline at end of file diff --git a/drivers/basedevice.js b/drivers/basedevice.js index f5bdb55..107c59f 100644 --- a/drivers/basedevice.js +++ b/drivers/basedevice.js @@ -234,6 +234,27 @@ class BaseDevice extends Homey.Device { }catch(error){} } + async getCapabilityEnumList(capability){ + let values = []; + if (this.hasCapability(capability)){ + try{ + let capabilityOptions = this.getCapabilityOptions(capability); + if (capabilityOptions.values){ + values = capabilityOptions.values; + } + } + catch(error){ + this.log("getCapabilityEnumList(): Error getting capabilityOptions: "+error.message); + // Read capability definition if no options are set + let capabilityDef = Capability.getCapability(capability); + if (capabilityDef.values){ + values = capabilityDef.values; + } + } + } + return values; + } + async onInitDevice(){ // Init device on satrtup with latest data to have initial values before HA sends updates this.homey.clearTimeout(this.timeoutInitDevice); @@ -1348,6 +1369,17 @@ class BaseDevice extends Homey.Device { // } // }); // } + + // Widget functions =========================================================================================== + async widgetUpdate(){ + // Redefine in subclass. Trigger widget update by sending a realtime event + } + async widgetPost(body){ + // Redefine in subclass. Process HTTP POST message in the device + } + async widgetGet(command){ + // Redefine in subclass. Process HTTP GET message in the device + } } module.exports = BaseDevice; \ No newline at end of file diff --git a/widgets/alarm_control_panel/api.js b/widgets/alarm_control_panel/api.js new file mode 100644 index 0000000..b923e7e --- /dev/null +++ b/widgets/alarm_control_panel/api.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = { + + async apiWidgetUpdate({ homey, query }) { + return await homey.app.apiWidgetUpdate( query.driver_id, query.device_id ); + }, + async apiWidgetPost({ homey, query, body }) { + return await homey.app.apiWidgetPost( query.driver_id, query.device_id, body ); + }, + async apiWidgetGet({ homey, query }) { + return await homey.app.apiWidgetGet( query.driver_id, query.device_id, query.command ); + } +}; \ No newline at end of file diff --git a/widgets/alarm_control_panel/preview-dark.png b/widgets/alarm_control_panel/preview-dark.png new file mode 100644 index 0000000..e42e0e5 Binary files /dev/null and b/widgets/alarm_control_panel/preview-dark.png differ diff --git a/widgets/alarm_control_panel/preview-light.png b/widgets/alarm_control_panel/preview-light.png new file mode 100644 index 0000000..4b70a1b Binary files /dev/null and b/widgets/alarm_control_panel/preview-light.png differ diff --git a/widgets/alarm_control_panel/public/0.svg b/widgets/alarm_control_panel/public/0.svg new file mode 100644 index 0000000..5941a52 --- /dev/null +++ b/widgets/alarm_control_panel/public/0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/1.svg b/widgets/alarm_control_panel/public/1.svg new file mode 100644 index 0000000..2abbb46 --- /dev/null +++ b/widgets/alarm_control_panel/public/1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/2.svg b/widgets/alarm_control_panel/public/2.svg new file mode 100644 index 0000000..7b4c3d4 --- /dev/null +++ b/widgets/alarm_control_panel/public/2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/3.svg b/widgets/alarm_control_panel/public/3.svg new file mode 100644 index 0000000..155a905 --- /dev/null +++ b/widgets/alarm_control_panel/public/3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/4.svg b/widgets/alarm_control_panel/public/4.svg new file mode 100644 index 0000000..949380e --- /dev/null +++ b/widgets/alarm_control_panel/public/4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/5.svg b/widgets/alarm_control_panel/public/5.svg new file mode 100644 index 0000000..07eb202 --- /dev/null +++ b/widgets/alarm_control_panel/public/5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/6.svg b/widgets/alarm_control_panel/public/6.svg new file mode 100644 index 0000000..45be895 --- /dev/null +++ b/widgets/alarm_control_panel/public/6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/7.svg b/widgets/alarm_control_panel/public/7.svg new file mode 100644 index 0000000..4c6a861 --- /dev/null +++ b/widgets/alarm_control_panel/public/7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/8.svg b/widgets/alarm_control_panel/public/8.svg new file mode 100644 index 0000000..4939c01 --- /dev/null +++ b/widgets/alarm_control_panel/public/8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/9.svg b/widgets/alarm_control_panel/public/9.svg new file mode 100644 index 0000000..44aedbf --- /dev/null +++ b/widgets/alarm_control_panel/public/9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/armed_away.svg b/widgets/alarm_control_panel/public/armed_away.svg new file mode 100644 index 0000000..5c0eb3f --- /dev/null +++ b/widgets/alarm_control_panel/public/armed_away.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/armed_custom_bypass.svg b/widgets/alarm_control_panel/public/armed_custom_bypass.svg new file mode 100644 index 0000000..e1f0243 --- /dev/null +++ b/widgets/alarm_control_panel/public/armed_custom_bypass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/armed_home.svg b/widgets/alarm_control_panel/public/armed_home.svg new file mode 100644 index 0000000..ccee5c3 --- /dev/null +++ b/widgets/alarm_control_panel/public/armed_home.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/armed_night.svg b/widgets/alarm_control_panel/public/armed_night.svg new file mode 100644 index 0000000..b82ae0f --- /dev/null +++ b/widgets/alarm_control_panel/public/armed_night.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/armed_vacation.svg b/widgets/alarm_control_panel/public/armed_vacation.svg new file mode 100644 index 0000000..7f6fdd0 --- /dev/null +++ b/widgets/alarm_control_panel/public/armed_vacation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/clear.svg b/widgets/alarm_control_panel/public/clear.svg new file mode 100644 index 0000000..ba9d8f7 --- /dev/null +++ b/widgets/alarm_control_panel/public/clear.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/disarmed.svg b/widgets/alarm_control_panel/public/disarmed.svg new file mode 100644 index 0000000..d10e698 --- /dev/null +++ b/widgets/alarm_control_panel/public/disarmed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/alarm_control_panel/public/index.css b/widgets/alarm_control_panel/public/index.css new file mode 100644 index 0000000..ef67597 --- /dev/null +++ b/widgets/alarm_control_panel/public/index.css @@ -0,0 +1,140 @@ +#div_panel{ + display: flex; +} + +#div_commands{ + display: flow; +} +#div_buttons{ + display: flow; + justify-content: center; + align-items: flex-start; + width: 100%; +} +#div_message{ + display: flex; + flex-wrap: nowrap; + justify-content: center +} + +[class^="homey-custom-icon"]{ + margin: 10px; + /* --homey-icon-size: var(--homey-icon-size-medium); */ + width: 30px; + height: 30px; +} +.icon_command{ + width: 25px; + height: 25px; + background-color: var(--homey-icon-color-light); +} +.icon_button{ + width: 30px; + height: 30px; +} +.inactive{ + background-color: var(--homey-icon-color-light); +} +.selected{ + background-color: var(--homey-color-highlight); +} +.success{ + background-color: var(--homey-color-success); +} +.error{ + background-color: var(--homey-color-danger); +} + +.text_message{ + text-align: center; + color: var(--homey-icon-color-light); +} + +.text_message_error{ + text-align: center; + color: var(--homey-color-warning); +} + +.div_grid{ + display: grid; + justify-content: center; +} +.div_line{ + display: inline-flex; + justify-content: center; +} + +.homey-custom-icon-armed_home { + -webkit-mask-image: url('armed_home.svg'); + mask-image: url('armed_home.svg'); +} +.homey-custom-icon-armed_away { + -webkit-mask-image: url('armed_away.svg'); + mask-image: url('armed_away.svg'); +} +.homey-custom-icon-armed_night { + -webkit-mask-image: url('armed_night.svg'); + mask-image: url('armed_night.svg'); +} +.homey-custom-icon-armed_vacation { + -webkit-mask-image: url('armed_vacation.svg'); + mask-image: url('armed_vacation.svg'); +} +.homey-custom-icon-armed_custom_bypass { + -webkit-mask-image: url('armed_custom_bypass.svg'); + mask-image: url('armed_custom_bypass.svg'); +} +.homey-custom-icon-disarmed { + -webkit-mask-image: url('disarmed.svg'); + mask-image: url('disarmed.svg'); +} + +.homey-custom-icon-ok { + -webkit-mask-image: url('ok.svg'); + mask-image: url('ok.svg'); +} +.homey-custom-icon-clear { + -webkit-mask-image: url('clear.svg'); + mask-image: url('clear.svg'); +} + +.homey-custom-icon-1 { + -webkit-mask-image: url('1.svg'); + mask-image: url('1.svg'); +} +.homey-custom-icon-2 { + -webkit-mask-image: url('2.svg'); + mask-image: url('2.svg'); +} +.homey-custom-icon-3 { + -webkit-mask-image: url('3.svg'); + mask-image: url('3.svg'); +} +.homey-custom-icon-4 { + -webkit-mask-image: url('4.svg'); + mask-image: url('4.svg'); +} +.homey-custom-icon-5 { + -webkit-mask-image: url('5.svg'); + mask-image: url('5.svg'); +} +.homey-custom-icon-6 { + -webkit-mask-image: url('6.svg'); + mask-image: url('6.svg'); +} +.homey-custom-icon-7 { + -webkit-mask-image: url('7.svg'); + mask-image: url('7.svg'); +} +.homey-custom-icon-8 { + -webkit-mask-image: url('8.svg'); + mask-image: url('8.svg'); +} +.homey-custom-icon-9 { + -webkit-mask-image: url('9.svg'); + mask-image: url('9.svg'); +} +.homey-custom-icon-0 { + -webkit-mask-image: url('0.svg'); + mask-image: url('0.svg'); +} diff --git a/widgets/alarm_control_panel/public/index.html b/widgets/alarm_control_panel/public/index.html new file mode 100644 index 0000000..31e2183 --- /dev/null +++ b/widgets/alarm_control_panel/public/index.html @@ -0,0 +1,210 @@ + + +
+ + + + + + +