From 2a0c0b21a40912943b33062ede86e03a89881ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 17 Mar 2022 16:04:50 +0100 Subject: [PATCH 01/16] Add translated command labels --- hass-shutter-card.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hass-shutter-card.js b/hass-shutter-card.js index 72ecb4f..0d7a5a5 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -52,9 +52,9 @@ class ShutterCard extends HTMLElement {
-
-
- +
+
+
From b8168ba9eb7bb661d795594006f0d052f01f8a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 17 Mar 2022 16:05:53 +0100 Subject: [PATCH 02/16] Add support for tilt and partial closing. --- hass-shutter-card.js | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/hass-shutter-card.js b/hass-shutter-card.js index 0d7a5a5..dbc27c6 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -62,6 +62,14 @@ class ShutterCard extends HTMLElement {
+
+ `+(entity.partial?`
`:``)+` + ` + (entity.can_tilt?` + +
+ + `:``) + ` +
@@ -138,6 +146,7 @@ class ShutterCard extends HTMLElement { const command = this.dataset.command; let service = ''; + let args = '' switch (command) { case 'up': @@ -147,14 +156,29 @@ class ShutterCard extends HTMLElement { case 'down': service = 'close_cover'; break; - + case 'stop': service = 'stop_cover'; break; + case 'partial': + service = 'set_cover_position'; + args = { + position: this.dataset.position + } + break; + case 'tilt-open': + service = 'open_cover_tile'; + break; + case 'tilt-close': + service = 'close_cover_tile'; + break; + default: + return } hass.callService('cover', service, { - entity_id: entityId + entity_id: entityId, + ...args }); }; }); From c98c0f0bed1e5c85e6fd2ff881ac95116db50300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 17 Mar 2022 16:19:28 +0100 Subject: [PATCH 03/16] Add visual indicators for movement and partial position --- hass-shutter-card.js | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/hass-shutter-card.js b/hass-shutter-card.js index dbc27c6..f7d9f6f 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -59,7 +59,14 @@ class ShutterCard extends HTMLElement {
-
+
`+ + (entity.partial? + `
`:`` + ) + ` +
+ + +
@@ -195,12 +202,19 @@ class ShutterCard extends HTMLElement { .sc-shutter-middle { display: flex; width: 210px; margin: auto; } .sc-shutter-buttons { flex: 1; text-align: center; margin-top: 0.4rem; } .sc-shutter-selector { flex: 1; } + .sc-shutter-selector-partial { position: absolute; top:0; left: 9px; width: 88%; height: 1px; background-color: gray; } .sc-shutter-selector-picture { position: relative; margin: auto; background-size: cover; min-height: 150px; max-height: 100%; width: 153px; } .sc-shutter-selector-picture { background-image: url(data:@file/png;base64,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); } .sc-shutter-selector-slide { position: absolute; top: 19px; left: 9px; width: 88%; height: 0; } .sc-shutter-selector-slide { background-image: url(data:@file/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAGCAYAAAACEPQxAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAIGNIUk0AAHpPAACA1wAA+5gAAH6BAAB41AAA6bgAADhTAAAa8BY+DIIAAAAoSURBVHjaYjh48OBqpt+/f3sx8fHxcTFJSkoyMHFycjIwcXJyHgYMAKRuB6wLmIXlAAAAAElFTkSuQmCC); } .sc-shutter-selector-picker { position: absolute; top: 19px; left: 9px; width: 88%; cursor: pointer; height: 20px; background-repeat: no-repeat; } .sc-shutter-selector-picker { background-image: url(data:@file/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIkAAAAHCAYAAAA8nm5hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAIGNIUk0AAHpPAACA1wAA+5gAAH6BAAB41AAA6bgAADhTAAAa8BY+DIIAAAG4SURBVHja7JYxstowEIZ/rWTJNuPODZyAjipFCq6Qw+RyHILuVXTMQGMzYxwDwpawrVT2MEkeL34UITP+GzXaWUnfr91lm83m+2w2+6qUgnMOQ9W2LYioX0e9rpxzYIzBOQfn3ENeRITD4aCLongTxpgvVVV9q+t6kEkYYwAAzjk8z0NVVf1BRr2eOl5dMTDGPNxLRDDG/DDGKKG1llpr2blqCGTOOeI4BuccdV3jer2ONF64ioRhiCiKYK1FVVWw1vbm+RPbsiyDsiylUEq1vu9jqEkYY7jdbrhcLphMJmCMQSn1btJR/17WWmit++ovhHiXNxFBStlIKVsRBAEFQQDO+aCEnHPsdjusVissFgvM53P4vj+SeFEREdbrNbbbLZbLJabTKdq2fbjfGMOttSTyPLee5xnOeT/Y/G0l2e/3iKIIYRgiy7J+ILrvgaOeaxG/zhSfiWeMoa5rZFmGOI4BAGmaPuwaRISiKMrT6WRFkiRra63oTDLEmWmaAgDyPMfxePztUv/DEHv/+B9BeRbaMyYZkvc+rvv4TdMgz3PEcYwkSdA0zYd8z+ez1lq//QQAAP//AwAV5u5HIxEL5wAAAABJRU5ErkJggg==); } + .sc-shutter-movement-overlay { + position: absolute; top: 19px; left: 9px; width: 88%; height: 100%; + background-color: rgba(0,0,0,0.3); text-align: center; --mdc-icon-size: 80px + } + .sc-shutter-movement-open {display: none} + .sc-shutter-movement-close {display: none} .sc-shutter-top { text-align: center; margin-bottom: 1rem; } .sc-shutter-bottom { text-align: center; margin-top: 1rem; } .sc-shutter-label { display: inline-block; font-size: 20px; vertical-align: middle; } @@ -230,6 +244,7 @@ class ShutterCard extends HTMLElement { const state = hass.states[entityId]; const friendlyName = (entity && entity.name) ? entity.name : state ? state.attributes.friendly_name : 'unknown'; const currentPosition = state ? state.attributes.current_position : 'unknown'; + const movementState = state? state.state : 'unknown'; shutter.querySelectorAll('.sc-shutter-label').forEach(function(shutterLabel) { shutterLabel.innerHTML = friendlyName; @@ -245,9 +260,16 @@ class ShutterCard extends HTMLElement { } else { _this.setPickerPositionPercentage(100 - currentPosition, picker, slide); } + + _this.setMovement(movementState, shutter); } }); } + + calculatePositionFromPercent(percent, inverted) { + return (this.maxPosition - this.minPosition) * (inverted?percent:100-percent) / 100 + this.minPosition; + } + getPictureTop(picture) { let pictureBox = picture.getBoundingClientRect(); @@ -262,6 +284,26 @@ class ShutterCard extends HTMLElement { return pictureTop; } + + setMovement(movement, shutter) { + if (movement == "opening" || movement == "closing") { + let opening = movement == "opening" + shutter.querySelectorAll(".sc-shutter-movement-overlay").forEach( + (overlay) => overlay.style.display = "block" + ) + shutter.querySelectorAll(".sc-shutter-movement-open").forEach( + (overlay) => overlay.style.display = opening?"block":"none" + ) + shutter.querySelectorAll(".sc-shutter-movement-close").forEach( + (overlay) => overlay.style.display = opening?"none":"block" + ) + } + else { + shutter.querySelectorAll(".sc-shutter-movement-overlay").forEach( + (overlay) => overlay.style.display = "none" + ) + } + } setPickerPositionPercentage(position, picker, slide) { let realPosition = (this.maxPosition - this.minPosition) * position / 100 + this.minPosition; From 352819e820d87255d6727e04ed2d761cee53ded2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 17 Mar 2022 16:28:41 +0100 Subject: [PATCH 04/16] Update README for tilt and partial position. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 40ba1cc..894678f 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ This card allows to open, close or set a shutter to the opening rate you want. | buttons_position | string | False | `left` | Set buttons on `left` or on `right` of the shutter | title_position | string | False | `top` | Set title on `top` or on `bottom` of the shutter | invert_percentage | boolean | False | `false` | Set it to `true` if your shutter is 100% when it is closed, and 0% when it is opened +| tilt | boolean | False | `false` | Set it to `true` if your shutters support tilting. +| partial | int | False | `0` | Set it to a percentage (0-100) if you want to be able to quickly go to this "partially closed" state using a button. _Remark : you can also just give the entity ID (without to specify `entity:`) if you don't need to specify the other configurations._ From 354167f3fbdd03cb89c21f8723e88827983dc2cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 17 Mar 2022 16:31:28 +0100 Subject: [PATCH 05/16] Show entity details on title click --- hass-shutter-card.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hass-shutter-card.js b/hass-shutter-card.js index f7d9f6f..395c61f 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -91,6 +91,20 @@ class ShutterCard extends HTMLElement { let picture = shutter.querySelector('.sc-shutter-selector-picture'); let slide = shutter.querySelector('.sc-shutter-selector-slide'); let picker = shutter.querySelector('.sc-shutter-selector-picker'); + let labels = shutter.querySelectorAll('.sc-shutter-label'); + + let detailOpen = function(event) { + let e = new Event('hass-more-info', { composed: true }); + e.detail = { + entityId + }; + _this.dispatchEvent(e); + } + + labels.forEach((labelDOM) => { + labelDOM.addEventListener('click', detailOpen); + } + ) let mouseDown = function(event) { if (event.cancelable) { @@ -217,7 +231,7 @@ class ShutterCard extends HTMLElement { .sc-shutter-movement-close {display: none} .sc-shutter-top { text-align: center; margin-bottom: 1rem; } .sc-shutter-bottom { text-align: center; margin-top: 1rem; } - .sc-shutter-label { display: inline-block; font-size: 20px; vertical-align: middle; } + .sc-shutter-label { display: inline-block; font-size: 20px; vertical-align: middle; cursor: pointer;} .sc-shutter-position { display: inline-block; vertical-align: middle; padding: 0 6px; margin-left: 1rem; border-radius: 2px; background-color: var(--secondary-background-color); } `; From 0547e84d9def7978f99468dc21edbe5052562e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 17 Mar 2022 17:40:01 +0100 Subject: [PATCH 06/16] Add offset and improve naming --- README.md | 3 +- hass-shutter-card.js | 65 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 894678f..bba5ad9 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ This card allows to open, close or set a shutter to the opening rate you want. | title_position | string | False | `top` | Set title on `top` or on `bottom` of the shutter | invert_percentage | boolean | False | `false` | Set it to `true` if your shutter is 100% when it is closed, and 0% when it is opened | tilt | boolean | False | `false` | Set it to `true` if your shutters support tilting. -| partial | int | False | `0` | Set it to a percentage (0-100) if you want to be able to quickly go to this "partially closed" state using a button. +| partial_close_percentage | int | False | `0` | Set it to a percentage (0-100) if you want to be able to quickly go to this "partially closed" state using a button. +| offset_closed_percentage | int | False | `0` | Set it to a percentage (0-100) of travel that will still be considered a "closed" state in the visualization. _Remark : you can also just give the entity ID (without to specify `entity:`) if you don't need to specify the other configurations._ diff --git a/hass-shutter-card.js b/hass-shutter-card.js index 395c61f..ff0d498 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -36,6 +36,21 @@ class ShutterCard extends HTMLElement { if (entity && entity.invert_percentage) { invertPercentage = entity.invert_percentage; } + + let partial = 0; + if (entity && entity.partial_close_percentage) { + partial = Math.max(0,Math.min(100,entity.partial_close_percentage)); // make sure this is valid range + } + + let offset = 0; + if (entity && entity.offset_closed_percentage) { + offset = Math.max(0,Math.min(100,entity.offset_closed_percentage)); // make sure this is valid range + } + + let tilt = false; + if (entity && entity.can_tilt) { + tilt = entity.can_tilt; + } let shutter = document.createElement('div'); @@ -60,8 +75,8 @@ class ShutterCard extends HTMLElement {
`+ - (entity.partial? - `
`:`` + (partial&&!offset? + `
`:`` ) + `
@@ -70,8 +85,8 @@ class ShutterCard extends HTMLElement {
- `+(entity.partial?`
`:``)+` - ` + (entity.can_tilt?` + `+(partial?`
`:``)+` + ` + (tilt?`
@@ -139,7 +154,7 @@ class ShutterCard extends HTMLElement { if (newPosition > _this.maxPosition) newPosition = _this.maxPosition; - let percentagePosition = (newPosition - _this.minPosition) * 100 / (_this.maxPosition - _this.minPosition); + let percentagePosition = (newPosition - _this.minPosition) * (1-offset) / (_this.maxPosition - _this.minPosition); if (invertPercentage) { _this.updateShutterPosition(hass, entityId, percentagePosition); @@ -251,6 +266,11 @@ class ShutterCard extends HTMLElement { invertPercentage = entity.invert_percentage; } + let offset = false; + if (entity && entity.offset_closed_percentage) { + offset = entity.offset_closed_percentage; + } + const shutter = _this.card.querySelector('div[data-shutter="' + entityId +'"]'); const slide = shutter.querySelector('.sc-shutter-selector-slide'); const picker = shutter.querySelector('.sc-shutter-selector-picker'); @@ -266,22 +286,37 @@ class ShutterCard extends HTMLElement { if (!_this.isUpdating) { shutter.querySelectorAll('.sc-shutter-position').forEach(function (shutterPosition) { - shutterPosition.innerHTML = currentPosition + '%'; + let visiblePosition; + let positionText; + if (invertPercentage) { + visiblePosition = Math.round(Math.min(100, currentPosition + offset)); + positionText = visiblePosition + ' %'; + if (visiblePosition == 100) { + positionText += ' / '+ 100-Math.round(Math.abs(currentPosition-visiblePosition)/offset*100) +' %'; + } + } + else { + visiblePosition = Math.max(0, currentPosition - offset); + positionText = visiblePosition + ' %'; + if (visiblePosition == 0) { + positionText += ' / '+ Math.round(Math.abs(currentPosition-visiblePosition)/offset*100) +' %'; + } + } + + shutterPosition.innerHTML = positionText; + }) - if (invertPercentage) { - _this.setPickerPositionPercentage(currentPosition, picker, slide); - } else { - _this.setPickerPositionPercentage(100 - currentPosition, picker, slide); - } + _this.setPickerPositionPercentage(currentPosition, picker, slide, invertPercentage, offset); + _this.setMovement(movementState, shutter); } }); } - calculatePositionFromPercent(percent, inverted) { - return (this.maxPosition - this.minPosition) * (inverted?percent:100-percent) / 100 + this.minPosition; + calculatePositionFromPercent(percent, inverted, offset) { + return (this.maxPosition - this.minPosition) * (inverted?(percent-offset):(100-percent+offset)) / 100 + this.minPosition; } @@ -319,8 +354,8 @@ class ShutterCard extends HTMLElement { } } - setPickerPositionPercentage(position, picker, slide) { - let realPosition = (this.maxPosition - this.minPosition) * position / 100 + this.minPosition; + setPickerPositionPercentage(percentage, picker, slide, inverted, offset) { + let realPosition = this.calculatePositionFromPercent(percentage, inverted, offset); this.setPickerPosition(realPosition, picker, slide); } From 71b4149a495bdee6af71a8806d39876921e88d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 17 Mar 2022 20:51:11 +0100 Subject: [PATCH 07/16] Fix GUI and position bugs in offset --- hass-shutter-card.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hass-shutter-card.js b/hass-shutter-card.js index ff0d498..6d24a3f 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -154,7 +154,7 @@ class ShutterCard extends HTMLElement { if (newPosition > _this.maxPosition) newPosition = _this.maxPosition; - let percentagePosition = (newPosition - _this.minPosition) * (1-offset) / (_this.maxPosition - _this.minPosition); + let percentagePosition = (newPosition - _this.minPosition) * (100-offset) / (_this.maxPosition - _this.minPosition); if (invertPercentage) { _this.updateShutterPosition(hass, entityId, percentagePosition); @@ -291,14 +291,14 @@ class ShutterCard extends HTMLElement { if (invertPercentage) { visiblePosition = Math.round(Math.min(100, currentPosition + offset)); positionText = visiblePosition + ' %'; - if (visiblePosition == 100) { + if (visiblePosition == 100 && offset) { positionText += ' / '+ 100-Math.round(Math.abs(currentPosition-visiblePosition)/offset*100) +' %'; } } else { visiblePosition = Math.max(0, currentPosition - offset); positionText = visiblePosition + ' %'; - if (visiblePosition == 0) { + if (visiblePosition == 0 && offset) { positionText += ' / '+ Math.round(Math.abs(currentPosition-visiblePosition)/offset*100) +' %'; } } From c8a920d0ec117a3f9b219ba8de57c3f72cfb4cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 17 Mar 2022 21:05:13 +0100 Subject: [PATCH 08/16] Add translation to end positions --- README.md | 1 + hass-shutter-card.js | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bba5ad9..fb9fbba 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ This card allows to open, close or set a shutter to the opening rate you want. | tilt | boolean | False | `false` | Set it to `true` if your shutters support tilting. | partial_close_percentage | int | False | `0` | Set it to a percentage (0-100) if you want to be able to quickly go to this "partially closed" state using a button. | offset_closed_percentage | int | False | `0` | Set it to a percentage (0-100) of travel that will still be considered a "closed" state in the visualization. +| always_percentage | boolean | False | `false` | If set to `true`, the end states (open/closed) will be also as numbers (0 / 100 % ) instead of a text _Remark : you can also just give the entity ID (without to specify `entity:`) if you don't need to specify the other configurations._ diff --git a/hass-shutter-card.js b/hass-shutter-card.js index 6d24a3f..741ad50 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -290,16 +290,16 @@ class ShutterCard extends HTMLElement { let positionText; if (invertPercentage) { visiblePosition = Math.round(Math.min(100, currentPosition + offset)); - positionText = visiblePosition + ' %'; + positionText = _this.positionPercentToText(visiblePosition, invertPercentage, hass); if (visiblePosition == 100 && offset) { - positionText += ' / '+ 100-Math.round(Math.abs(currentPosition-visiblePosition)/offset*100) +' %'; + positionText += ' ('+ (100-Math.round(Math.abs(currentPosition-visiblePosition)/offset*100)) +' %)'; } } else { visiblePosition = Math.max(0, currentPosition - offset); - positionText = visiblePosition + ' %'; + positionText = _this.positionPercentToText(visiblePosition, invertPercentage, hass); if (visiblePosition == 0 && offset) { - positionText += ' / '+ Math.round(Math.abs(currentPosition-visiblePosition)/offset*100) +' %'; + positionText += ' ('+ (100-Math.round(Math.abs(currentPosition-visiblePosition)/offset*100)) +' %)'; } } @@ -315,6 +315,16 @@ class ShutterCard extends HTMLElement { }); } + positionPercentToText(percent, inverted, hass) { + if (percent == 100) { + return hass.localize(inverted?'ui.components.logbook.messages.was_closed':'ui.components.logbook.messages.was_opened'); + } + else if (percent == 0) { + return hass.localize(inverted?'ui.components.logbook.messages.was_opened':'ui.components.logbook.messages.was_closed'); + } + return percent + ' %'; + } + calculatePositionFromPercent(percent, inverted, offset) { return (this.maxPosition - this.minPosition) * (inverted?(percent-offset):(100-percent+offset)) / 100 + this.minPosition; } From 00447e3c7ab53842ec824475f88752552e262153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 17 Mar 2022 21:40:11 +0100 Subject: [PATCH 09/16] Add custom width definition --- hass-shutter-card.js | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/hass-shutter-card.js b/hass-shutter-card.js index 741ad50..14bc13e 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -51,6 +51,13 @@ class ShutterCard extends HTMLElement { if (entity && entity.can_tilt) { tilt = entity.can_tilt; } + + + let width = 153; + if (entity && entity.shutter_width_px) { + width = Math.max(10,entity.shutter_width_px); // make sure this is valid range + } + let shutter = document.createElement('div'); @@ -66,13 +73,21 @@ class ShutterCard extends HTMLElement {
+
+ `+(partial?`
`:``)+` + ` + (tilt?` + +
+ + `:``) + ` +


-
+
`+ (partial&&!offset? @@ -84,14 +99,6 @@ class ShutterCard extends HTMLElement {
-
- `+(partial?`
`:``)+` - ` + (tilt?` - -
- - `:``) + ` -
@@ -228,18 +235,18 @@ class ShutterCard extends HTMLElement { .sc-shutters { padding: 16px; } .sc-shutter { margin-top: 1rem; overflow: hidden; } .sc-shutter:first-child { margin-top: 0; } - .sc-shutter-middle { display: flex; width: 210px; margin: auto; } + .sc-shutter-middle { display: flex; width: min-content; margin: auto; } .sc-shutter-buttons { flex: 1; text-align: center; margin-top: 0.4rem; } .sc-shutter-selector { flex: 1; } .sc-shutter-selector-partial { position: absolute; top:0; left: 9px; width: 88%; height: 1px; background-color: gray; } - .sc-shutter-selector-picture { position: relative; margin: auto; background-size: cover; min-height: 150px; max-height: 100%; width: 153px; } + .sc-shutter-selector-picture { position: relative; margin: auto; background-size: 100% 100%; min-height: 150px; max-height: 100%; } .sc-shutter-selector-picture { background-image: url(data:@file/png;base64,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); } - .sc-shutter-selector-slide { position: absolute; top: 19px; left: 9px; width: 88%; height: 0; } + .sc-shutter-selector-slide { position: absolute; top: 19px; left: 6%; width: 88%; height: 0; } .sc-shutter-selector-slide { background-image: url(data:@file/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAGCAYAAAACEPQxAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAIGNIUk0AAHpPAACA1wAA+5gAAH6BAAB41AAA6bgAADhTAAAa8BY+DIIAAAAoSURBVHjaYjh48OBqpt+/f3sx8fHxcTFJSkoyMHFycjIwcXJyHgYMAKRuB6wLmIXlAAAAAElFTkSuQmCC); } - .sc-shutter-selector-picker { position: absolute; top: 19px; left: 9px; width: 88%; cursor: pointer; height: 20px; background-repeat: no-repeat; } + .sc-shutter-selector-picker { position: absolute; top: 19px; left: 6%; width: 88%; cursor: pointer; height: 20px; background-repeat: no-repeat; } .sc-shutter-selector-picker { background-image: url(data:@file/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIkAAAAHCAYAAAA8nm5hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAIGNIUk0AAHpPAACA1wAA+5gAAH6BAAB41AAA6bgAADhTAAAa8BY+DIIAAAG4SURBVHja7JYxstowEIZ/rWTJNuPODZyAjipFCq6Qw+RyHILuVXTMQGMzYxwDwpawrVT2MEkeL34UITP+GzXaWUnfr91lm83m+2w2+6qUgnMOQ9W2LYioX0e9rpxzYIzBOQfn3ENeRITD4aCLongTxpgvVVV9q+t6kEkYYwAAzjk8z0NVVf1BRr2eOl5dMTDGPNxLRDDG/DDGKKG1llpr2blqCGTOOeI4BuccdV3jer2ONF64ioRhiCiKYK1FVVWw1vbm+RPbsiyDsiylUEq1vu9jqEkYY7jdbrhcLphMJmCMQSn1btJR/17WWmit++ovhHiXNxFBStlIKVsRBAEFQQDO+aCEnHPsdjusVissFgvM53P4vj+SeFEREdbrNbbbLZbLJabTKdq2fbjfGMOttSTyPLee5xnOeT/Y/G0l2e/3iKIIYRgiy7J+ILrvgaOeaxG/zhSfiWeMoa5rZFmGOI4BAGmaPuwaRISiKMrT6WRFkiRra63oTDLEmWmaAgDyPMfxePztUv/DEHv/+B9BeRbaMyYZkvc+rvv4TdMgz3PEcYwkSdA0zYd8z+ez1lq//QQAAP//AwAV5u5HIxEL5wAAAABJRU5ErkJggg==); } .sc-shutter-movement-overlay { - position: absolute; top: 19px; left: 9px; width: 88%; height: 100%; + position: absolute; top: 19px; left: 6%; width: 88%; height: 100%; background-color: rgba(0,0,0,0.3); text-align: center; --mdc-icon-size: 80px } .sc-shutter-movement-open {display: none} From 87cb7d32b6fdd5efa8c737248f158171befc374e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 17 Mar 2022 22:31:48 +0100 Subject: [PATCH 10/16] Allow buttons top/bottom --- hass-shutter-card.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/hass-shutter-card.js b/hass-shutter-card.js index 14bc13e..e477362 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -25,7 +25,12 @@ class ShutterCard extends HTMLElement { let buttonsPosition = 'left'; if (entity && entity.buttons_position) { buttonsPosition = entity.buttons_position.toLowerCase(); + if (!['left', 'top', 'bottom', 'right'].includes(buttonsPosition)) { + buttonsPosition = 'left' + } } + const buttonsInRow = buttonsPosition == 'top' || buttonsPosition == 'bottom'; + const buttonsContainerReversed = buttonsPosition == 'bottom' || buttonsPosition == 'right'; let titlePosition = 'top'; if (entity && entity.title_position) { @@ -51,13 +56,11 @@ class ShutterCard extends HTMLElement { if (entity && entity.can_tilt) { tilt = entity.can_tilt; } - let width = 153; if (entity && entity.shutter_width_px) { width = Math.max(10,entity.shutter_width_px); // make sure this is valid range } - let shutter = document.createElement('div'); @@ -72,18 +75,17 @@ class ShutterCard extends HTMLElement {
-
-
- `+(partial?`
`:``)+` +
+
+ `+(partial?``:``)+` ` + (tilt?` -
`:``) + `
-
-
-
+
+ +
@@ -235,8 +237,9 @@ class ShutterCard extends HTMLElement { .sc-shutters { padding: 16px; } .sc-shutter { margin-top: 1rem; overflow: hidden; } .sc-shutter:first-child { margin-top: 0; } - .sc-shutter-middle { display: flex; width: min-content; margin: auto; } - .sc-shutter-buttons { flex: 1; text-align: center; margin-top: 0.4rem; } + .sc-shutter-middle { display: flex; width: fit-content; max-width: 100%; margin: auto; } + .sc-shutter-buttons { flex: 1; text-align: center; margin-top: 0.4rem; display: flex; max-width: 100% } + .sc-shutter-buttons ha-icon-button { display: block; width: min-content } .sc-shutter-selector { flex: 1; } .sc-shutter-selector-partial { position: absolute; top:0; left: 9px; width: 88%; height: 1px; background-color: gray; } .sc-shutter-selector-picture { position: relative; margin: auto; background-size: 100% 100%; min-height: 150px; max-height: 100%; } From 8ffe09a5ff57898b53d125ee51d2a5047987656d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 17 Mar 2022 22:34:43 +0100 Subject: [PATCH 11/16] Update README with width and buttons position changes --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fb9fbba..afea04d 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,14 @@ This card allows to open, close or set a shutter to the opening rate you want. | ---- | ---- | -------- | ------- | ----------- | entity | string | True | - | The shutter entity ID | name | string | False | _Friendly name of the entity_ | Name to display for the shutter -| buttons_position | string | False | `left` | Set buttons on `left` or on `right` of the shutter +| buttons_position | string | False | `left` | Set buttons on `left`, `right`, `top` or `bottom` of the shutter | title_position | string | False | `top` | Set title on `top` or on `bottom` of the shutter | invert_percentage | boolean | False | `false` | Set it to `true` if your shutter is 100% when it is closed, and 0% when it is opened | tilt | boolean | False | `false` | Set it to `true` if your shutters support tilting. | partial_close_percentage | int | False | `0` | Set it to a percentage (0-100) if you want to be able to quickly go to this "partially closed" state using a button. | offset_closed_percentage | int | False | `0` | Set it to a percentage (0-100) of travel that will still be considered a "closed" state in the visualization. | always_percentage | boolean | False | `false` | If set to `true`, the end states (open/closed) will be also as numbers (0 / 100 % ) instead of a text +| shutter_width_px | int | False | `153` | Set shutter visualization width in px. You can make it thicker or narrower to fit your layout. _Remark : you can also just give the entity ID (without to specify `entity:`) if you don't need to specify the other configurations._ From 4053ae2d66f65a7184ad01093325d2ec8cbfe9ee Mon Sep 17 00:00:00 2001 From: chylek <1331917+chylek@users.noreply.github.com> Date: Fri, 18 Mar 2022 07:37:16 +0100 Subject: [PATCH 12/16] Fix typo preventing tilt from working --- hass-shutter-card.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hass-shutter-card.js b/hass-shutter-card.js index e477362..0118d71 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -212,10 +212,10 @@ class ShutterCard extends HTMLElement { } break; case 'tilt-open': - service = 'open_cover_tile'; + service = 'open_cover_tilt'; break; case 'tilt-close': - service = 'close_cover_tile'; + service = 'close_cover_tilt'; break; default: return @@ -418,4 +418,4 @@ class ShutterCard extends HTMLElement { } } -customElements.define("shutter-card", ShutterCard); \ No newline at end of file +customElements.define("shutter-card", ShutterCard); From 8287c19f0ee4901ba901c328d2c08fa3c110dc25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Fri, 18 Mar 2022 09:18:15 +0100 Subject: [PATCH 13/16] Fix offset math --- hass-shutter-card.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/hass-shutter-card.js b/hass-shutter-card.js index 0118d71..4b942fa 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -249,8 +249,8 @@ class ShutterCard extends HTMLElement { .sc-shutter-selector-picker { position: absolute; top: 19px; left: 6%; width: 88%; cursor: pointer; height: 20px; background-repeat: no-repeat; } .sc-shutter-selector-picker { background-image: url(data:@file/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIkAAAAHCAYAAAA8nm5hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAIGNIUk0AAHpPAACA1wAA+5gAAH6BAAB41AAA6bgAADhTAAAa8BY+DIIAAAG4SURBVHja7JYxstowEIZ/rWTJNuPODZyAjipFCq6Qw+RyHILuVXTMQGMzYxwDwpawrVT2MEkeL34UITP+GzXaWUnfr91lm83m+2w2+6qUgnMOQ9W2LYioX0e9rpxzYIzBOQfn3ENeRITD4aCLongTxpgvVVV9q+t6kEkYYwAAzjk8z0NVVf1BRr2eOl5dMTDGPNxLRDDG/DDGKKG1llpr2blqCGTOOeI4BuccdV3jer2ONF64ioRhiCiKYK1FVVWw1vbm+RPbsiyDsiylUEq1vu9jqEkYY7jdbrhcLphMJmCMQSn1btJR/17WWmit++ovhHiXNxFBStlIKVsRBAEFQQDO+aCEnHPsdjusVissFgvM53P4vj+SeFEREdbrNbbbLZbLJabTKdq2fbjfGMOttSTyPLee5xnOeT/Y/G0l2e/3iKIIYRgiy7J+ILrvgaOeaxG/zhSfiWeMoa5rZFmGOI4BAGmaPuwaRISiKMrT6WRFkiRra63oTDLEmWmaAgDyPMfxePztUv/DEHv/+B9BeRbaMyYZkvc+rvv4TdMgz3PEcYwkSdA0zYd8z+ez1lq//QQAAP//AwAV5u5HIxEL5wAAAABJRU5ErkJggg==); } .sc-shutter-movement-overlay { - position: absolute; top: 19px; left: 6%; width: 88%; height: 100%; - background-color: rgba(0,0,0,0.3); text-align: center; --mdc-icon-size: 80px + position: absolute; top: 19px; left: 6%; width: 88%; height: 118px; + background-color: rgba(0,0,0,0.3); text-align: center; --mdc-icon-size: 60px } .sc-shutter-movement-open {display: none} .sc-shutter-movement-close {display: none} @@ -299,14 +299,14 @@ class ShutterCard extends HTMLElement { let visiblePosition; let positionText; if (invertPercentage) { - visiblePosition = Math.round(Math.min(100, currentPosition + offset)); + visiblePosition = offset?Math.min(100, Math.round(currentPosition / offset * 100 )):currentPosition; positionText = _this.positionPercentToText(visiblePosition, invertPercentage, hass); if (visiblePosition == 100 && offset) { positionText += ' ('+ (100-Math.round(Math.abs(currentPosition-visiblePosition)/offset*100)) +' %)'; } } else { - visiblePosition = Math.max(0, currentPosition - offset); + visiblePosition = offset?Math.max(0, Math.round((currentPosition - offset) / (100-offset) * 100 )):currentPosition; positionText = _this.positionPercentToText(visiblePosition, invertPercentage, hass); if (visiblePosition == 0 && offset) { positionText += ' ('+ (100-Math.round(Math.abs(currentPosition-visiblePosition)/offset*100)) +' %)'; @@ -336,7 +336,14 @@ class ShutterCard extends HTMLElement { } calculatePositionFromPercent(percent, inverted, offset) { - return (this.maxPosition - this.minPosition) * (inverted?(percent-offset):(100-percent+offset)) / 100 + this.minPosition; + let visiblePosition; + if (inverted) { + visiblePosition = offset?Math.min(100, Math.round(percent / offset * 100 )):percent; + } + else { + visiblePosition = offset?Math.max(0, Math.round((percent - offset) / (100-offset) * 100 )):percent; + } + return (this.maxPosition - this.minPosition) * (inverted?visiblePosition:100-visiblePosition) / 100 + this.minPosition; } From 751ec100a1664de2ffb5dbd052ccc37612f84e85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Tue, 29 Mar 2022 08:48:26 +0200 Subject: [PATCH 14/16] Fix "always_percentage" not working --- hass-shutter-card.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/hass-shutter-card.js b/hass-shutter-card.js index 4b942fa..272a047 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -281,6 +281,10 @@ class ShutterCard extends HTMLElement { offset = entity.offset_closed_percentage; } + let alwaysPercentage = false; + if (entity && entity.always_percentage) { + alwaysPercentage = entity.always_percentage; + } const shutter = _this.card.querySelector('div[data-shutter="' + entityId +'"]'); const slide = shutter.querySelector('.sc-shutter-selector-slide'); const picker = shutter.querySelector('.sc-shutter-selector-picker'); @@ -300,14 +304,14 @@ class ShutterCard extends HTMLElement { let positionText; if (invertPercentage) { visiblePosition = offset?Math.min(100, Math.round(currentPosition / offset * 100 )):currentPosition; - positionText = _this.positionPercentToText(visiblePosition, invertPercentage, hass); + positionText = _this.positionPercentToText(visiblePosition, invertPercentage, alwaysPercentage, hass); if (visiblePosition == 100 && offset) { positionText += ' ('+ (100-Math.round(Math.abs(currentPosition-visiblePosition)/offset*100)) +' %)'; } } else { visiblePosition = offset?Math.max(0, Math.round((currentPosition - offset) / (100-offset) * 100 )):currentPosition; - positionText = _this.positionPercentToText(visiblePosition, invertPercentage, hass); + positionText = _this.positionPercentToText(visiblePosition, invertPercentage, alwaysPercentage, hass); if (visiblePosition == 0 && offset) { positionText += ' ('+ (100-Math.round(Math.abs(currentPosition-visiblePosition)/offset*100)) +' %)'; } @@ -325,12 +329,14 @@ class ShutterCard extends HTMLElement { }); } - positionPercentToText(percent, inverted, hass) { - if (percent == 100) { - return hass.localize(inverted?'ui.components.logbook.messages.was_closed':'ui.components.logbook.messages.was_opened'); - } - else if (percent == 0) { - return hass.localize(inverted?'ui.components.logbook.messages.was_opened':'ui.components.logbook.messages.was_closed'); + positionPercentToText(percent, inverted, alwaysPercentage, hass) { + if (!alwaysPercentage) { + if (percent == 100) { + return hass.localize(inverted?'ui.components.logbook.messages.was_closed':'ui.components.logbook.messages.was_opened'); + } + else if (percent == 0) { + return hass.localize(inverted?'ui.components.logbook.messages.was_opened':'ui.components.logbook.messages.was_closed'); + } } return percent + ' %'; } From 75a8d641e6bbf578b28f5c55d5104dc29d3d2098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ch=C3=BDlek?= Date: Thu, 31 Mar 2022 15:06:32 +0200 Subject: [PATCH 15/16] Add option to disable up/down buttons in end positions --- README.md | 3 ++- hass-shutter-card.js | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index afea04d..ed87a5d 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,9 @@ This card allows to open, close or set a shutter to the opening rate you want. | tilt | boolean | False | `false` | Set it to `true` if your shutters support tilting. | partial_close_percentage | int | False | `0` | Set it to a percentage (0-100) if you want to be able to quickly go to this "partially closed" state using a button. | offset_closed_percentage | int | False | `0` | Set it to a percentage (0-100) of travel that will still be considered a "closed" state in the visualization. -| always_percentage | boolean | False | `false` | If set to `true`, the end states (open/closed) will be also as numbers (0 / 100 % ) instead of a text +| always_percentage | boolean | False | `false` | If set to `true`, the end states (opened/closed) will be also as numbers (0 / 100 % ) instead of a text | shutter_width_px | int | False | `153` | Set shutter visualization width in px. You can make it thicker or narrower to fit your layout. +| disable_end_buttons | boolean | False | `false` | If set to `true`, the end states (opened/closed) will also deactivate the buttons for that direction (i.e. the "up" button will be disabled when the shutters are fully open) _Remark : you can also just give the entity ID (without to specify `entity:`) if you don't need to specify the other configurations._ diff --git a/hass-shutter-card.js b/hass-shutter-card.js index 272a047..454403d 100644 --- a/hass-shutter-card.js +++ b/hass-shutter-card.js @@ -285,6 +285,12 @@ class ShutterCard extends HTMLElement { if (entity && entity.always_percentage) { alwaysPercentage = entity.always_percentage; } + + let disableEnd = false; + if (entity && entity.disable_end_buttons) { + disableEnd = entity.disable_end_buttons; + } + const shutter = _this.card.querySelector('div[data-shutter="' + entityId +'"]'); const slide = shutter.querySelector('.sc-shutter-selector-slide'); const picker = shutter.querySelector('.sc-shutter-selector-picker'); @@ -305,6 +311,9 @@ class ShutterCard extends HTMLElement { if (invertPercentage) { visiblePosition = offset?Math.min(100, Math.round(currentPosition / offset * 100 )):currentPosition; positionText = _this.positionPercentToText(visiblePosition, invertPercentage, alwaysPercentage, hass); + if (disableEnd) { + _this.changeButtonState(shutter, currentPosition, invertPercentage); + } if (visiblePosition == 100 && offset) { positionText += ' ('+ (100-Math.round(Math.abs(currentPosition-visiblePosition)/offset*100)) +' %)'; } @@ -312,6 +321,9 @@ class ShutterCard extends HTMLElement { else { visiblePosition = offset?Math.max(0, Math.round((currentPosition - offset) / (100-offset) * 100 )):currentPosition; positionText = _this.positionPercentToText(visiblePosition, invertPercentage, alwaysPercentage, hass); + if (disableEnd) { + _this.changeButtonState(shutter, currentPosition, invertPercentage); + } if (visiblePosition == 0 && offset) { positionText += ' ('+ (100-Math.round(Math.abs(currentPosition-visiblePosition)/offset*100)) +' %)'; } @@ -329,6 +341,33 @@ class ShutterCard extends HTMLElement { }); } + changeButtonState(shutter, percent, inverted) { + if (percent == 0) { + shutter.querySelectorAll('.sc-shutter-button-up').forEach(function(button) { + button.disabled = inverted; + }); + shutter.querySelectorAll('.sc-shutter-button-down').forEach(function(button) { + button.disabled = !inverted; + }); + } + else if (percent == 100) { + shutter.querySelectorAll('.sc-shutter-button-up').forEach(function(button) { + button.disabled = !inverted; + }); + shutter.querySelectorAll('.sc-shutter-button-down').forEach(function(button) { + button.disabled = inverted; + }) ; + } + else { + shutter.querySelectorAll('.sc-shutter-button-up').forEach(function(button) { + button.disabled = false; + }); + shutter.querySelectorAll('.sc-shutter-button-down').forEach(function(button) { + button.disabled = false; + }) ; + } + } + positionPercentToText(percent, inverted, alwaysPercentage, hass) { if (!alwaysPercentage) { if (percent == 100) { From 8ba42023833c3963f6bdfdcdff7fe5c7c2fb8df2 Mon Sep 17 00:00:00 2001 From: chylek <1331917+chylek@users.noreply.github.com> Date: Mon, 2 May 2022 10:22:33 +0200 Subject: [PATCH 16/16] Fix entity parameter for tilt --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ed87a5d..293ca89 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ This card allows to open, close or set a shutter to the opening rate you want. | buttons_position | string | False | `left` | Set buttons on `left`, `right`, `top` or `bottom` of the shutter | title_position | string | False | `top` | Set title on `top` or on `bottom` of the shutter | invert_percentage | boolean | False | `false` | Set it to `true` if your shutter is 100% when it is closed, and 0% when it is opened -| tilt | boolean | False | `false` | Set it to `true` if your shutters support tilting. +| can_tilt | boolean | False | `false` | Set it to `true` if your shutters support tilting. | partial_close_percentage | int | False | `0` | Set it to a percentage (0-100) if you want to be able to quickly go to this "partially closed" state using a button. | offset_closed_percentage | int | False | `0` | Set it to a percentage (0-100) of travel that will still be considered a "closed" state in the visualization. | always_percentage | boolean | False | `false` | If set to `true`, the end states (opened/closed) will be also as numbers (0 / 100 % ) instead of a text @@ -59,4 +59,4 @@ If you don't use HACS, you can download js file from [latest releases](https://g resources: - url: /local/hass-shutter-card.js type: module -``` \ No newline at end of file +```