diff --git a/README.md b/README.md index ee8ffa53..da316da4 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,288 @@ Edit it by adding the following lines: 2. Reboot HomeAssistant +## Docs +In this example, "My [insert thing]" will just be the placeholder +#### Entities +| Entity ID | Entity Name | Description | +| :-- | :-: | :-- | +| sensor.my_washer | My Washer | Washer, turns On when on, turns Off when off | +| binary_sensor.my_washer_wash_completed | My Washer Wash Completed | Turns On when washer completed wash. You can use it in automations by triggering them when it goes from Off to On. | +| binary_sensor.my_washer_error_state | My Washer Error State | Off/OK means that it's fine. On/Error means there's an error. | +| sensor.my_dryer | My Dryer | Dryer, turns On when on, turns Off when off | +| binary_sensor.my_dryer_dry_completed | My Dryer Dry Completed | Turns On when dryer completed wash. You can use it in automations by triggering them when it goes from Off to On. | +| binary_sensor.my_dryer_error_state | My Dryer Error State | Off/OK means that it's fine. On/Error means there's an error. | + +#### Attributes `sensor.my_washer` +Note: When something doesn't apply and/or is off, it may have a `-` as its value. Also, these are for my washer, values may differ for yours. Feel free to open an issue/PR. +
Hidden, click to expand + +| Attribute ID | Description | +| :-- | :-- | +| model | Model ID of washer | +| mac_address | Mac address of washer | +| run_completed | Turns On when washer completed wash, just like binary_sensor.my_washer_wash_completed. | +| error_state | Off/OK means that it's fine. On/Error means there's an error, just like binary_sensor.my_washer_error_state. | +| error_message | When there is an error, this is what it is. (Format unknown) | +| run_state | Current state of washer in words | +| pre_state | Previous state of washer in words | +| current_course | Current washing cycle in words | +| spin_option_state | Current spin mode in words | +| watertemp_option_state | Current option for water temperature in words | +| drylevel_option_state | Unknown attribute | +| tubclean_count | Unknown attribute, number | +| remain_time | How much more time is remaining, H:MM | +| initial_time | The orgiinal amount of time, H:MM | +| reserve_time | Unknown attribute, H:MM | +| doorlock_mode | Unknown attribute, on/off | +| doorclose_mode | Unknown attribute, on/off | +| childlock_mode | Child lock, on/off | +| remotestart_mode | Whether remote start is enabled or not | +| creasecare_mode | Unknown attribute, on/off | +| steam_mode | Unknown attribute, on/off | +| steam_softener_mode | Unknown attribute, on/off | +| prewash_mode | Unknown attribute, on/off | +| turbowash_mode | Whether or not Turbowash is enabled | + +
+ +#### Attributes `sensor.my_dryer` +Note: When something doesn't apply and/or is off, it may have a `-` as its value. Also, these are for my dryer, values may differ for yours. Feel free to open an issue/PR. +
Hidden, click to expand + +| Attribute ID | Description | +| :-- | :-- | +| model | Model ID of dryer | +| mac_address | Mac address of dryer | +| run_completed | Turns On when dryer completed dry, just like binary_sensor.my_dryer_dry_completed. | +| error_state | Off/OK means that it's fine. On/Error means there's an error, just like binary_sensor.my_dryer_error_state. | +| error_message | When there is an error, this is what it is. (Format unknown) | +| run_state | Current state of dryer in words | +| pre_state | Previous state of dryer in words | +| current_course | Current drying cycle in words | +| tempcontrol_option_state | Current option for dryer temperature in words | +| drylevel_option_state | Current level for how much to dry | +| remain_time | How much more time is remaining, H:MM | +| initial_time | The orgiinal amount of time, H:MM | +| reserve_time | Unknown attribute, H:MM | +| doorlock_mode | Unknown attribute, on/off | +| childlock_mode | Child lock, on/off | + +
+ +#### Examples (washer/dryer) +- Get a notification when the clothes are done drying (or when the clothes are done washing, automation) +```yaml +- id: 'dry_clothes_notification' + alias: Dry clothes notification + description: Alert when dryer finishes + trigger: + - entity_id: binary_sensor.my_dryer_dry_completed + platform: state + from: 'off' + to: 'on' + condition: [] + action: + - data: + title: 'The clothes are dry!' + message: 'Get them while they're hot!' + service: notify.notify +``` +Substitute "dry" and "dryer" for "wet" and "washer" if you want to use with a washer. +- Really, really neat custom card for dryer and washer (![Screenshot of laundry card](/washerpics/cardpic.png)) +
Hidden, click to expand + +custom JS module for card: +```js +// card-tools for more-info. MIT license (This isn't a substantial portion) +function lovelace_view() { + var root = document.querySelector("hc-main"); + if(root) { + root = root && root.shadowRoot; + root = root && root.querySelector("hc-lovelace"); + root = root && root.shadowRoot; + root = root && root.querySelector("hui-view") || root.querySelector("hui-panel-view"); + return root; + } + root = document.querySelector("home-assistant"); + root = root && root.shadowRoot; + root = root && root.querySelector("home-assistant-main"); + root = root && root.shadowRoot; + root = root && root.querySelector("app-drawer-layout partial-panel-resolver"); + root = root && root.shadowRoot || root; + root = root && root.querySelector("ha-panel-lovelace"); + root = root && root.shadowRoot; + root = root && root.querySelector("hui-root"); + root = root && root.shadowRoot; + root = root && root.querySelector("ha-app-layout #view"); + root = root && root.firstElementChild; + return root; +} +function fireEvent(ev, detail, entity=null) { + ev = new Event(ev, { + bubbles: true, + cancelable: false, + composed: true, + }); + ev.detail = detail || {}; + if(entity) { + entity.dispatchEvent(ev); + } else { + var root = lovelace_view(); + if (root) root.dispatchEvent(ev); + } +} +function moreInfo(entity, large=false) { + const root = document.querySelector("hc-main") || document.querySelector("home-assistant"); + fireEvent("hass-more-info", {entityId: entity}, root); + const el = root._moreInfoEl; + el.large = large; + return el; +} +class LgLaundryCard extends HTMLElement { + set hass(hass) { + const entityId = this.config.entity; + const state = hass.states[entityId]; + if (!state) { + throw new Error('Entity not found. Maybe check to make sure it exists.'); + } + const stateStr = state ? state.state : 'unavailable'; + const friendlyName = state.attributes.friendly_name; + const friendlyNameStr = friendlyName ? " " + friendlyName : ""; + const courseName = state.attributes.current_course; + const courseNameStr = courseName ? " " + courseName : "an unknown cycle"; + const stageName = state.attributes.run_state; + const stageNameStr = stageName ? " " + stageName : "unknown"; + const iconName = state.attributes.icon; + const iconNameStr = iconName ? iconName : ""; + const remainTime = state.attributes.remain_time; + const remainTimeStr = remainTime ? remainTime : "unknown"; + const totalTime = state.attributes.initial_time; + const totalTimeStr = totalTime ? totalTime : "unknown"; + var worked; + var percentDone; + try { + const minRemain = (parseInt(remainTimeStr.split(":")[0]) * 60) + parseInt(remainTimeStr.split(":")[1]); + const minTotal = (parseInt(totalTimeStr.split(":")[0]) * 60) + parseInt(totalTimeStr.split(":")[1]); + percentDone = String(Math.round((minTotal - minRemain) / minTotal * 100)) + "%"; + worked = !isNaN(Math.round((minTotal - minRemain) / minTotal * 100)); + } catch(err) { + console.log(err); + worked = false; + } + if (!this.content) { + this.contenta = document.createElement('a'); + this.contenta.href = "#"; + this.contenta.style.textDecoration = "unset"; + this.contenta.style.color = "unset"; + function laundryinfo() { + window.history.pushState({}, "", window.location.href.split("#")[0]); + moreInfo(this.entityId); + } + this.contenta.onclick = laundryinfo.bind({entityId: entityId}); + this.content = document.createElement('div'); + this.content.style.padding = '0 16px 16px'; + this.content.style.display = 'flex'; + const card = document.createElement('ha-card'); + card.header = friendlyNameStr; + this.contenta.appendChild(card); + card.appendChild(this.content); + this.appendChild(this.contenta); + } + var conthtml = ''; + conthtml = ` + +
${friendlyNameStr} is currently ${stateStr}. + `; + if (stateStr == 'on') { + conthtml += ` +
The ${courseNameStr} progress is ${stageNameStr}. +
There's ${remainTimeStr} remaining out of ${totalTimeStr} total. + `; + if (worked) { + conthtml += ` +
+
+
+ ${percentDone} +
+
+
+ `; + conthtml = conthtml.replace("8px 9px 12px 5px", "16px 9px 12px 5px"); + } else { + conthtml += ""; + } + } else { + conthtml += ""; + } + this.content.innerHTML = conthtml; + } + setConfig(config) { + if (!config.entity) { + throw new Error('You need to define a laundry entity'); + } + this.config = config; + } + getCardSize() { + return 3; + } + static getStubConfig() { + return { entity: "sensor.my_washing_machine" }; + } +} + +customElements.define('lg-laundry-card', LgLaundryCard); +window.customCards.push({type: "lg-laundry-card", name: "LG laundry card", description: "Card for LG laundry machines."}); +``` + +Lovelace: +```yaml +type: 'custom:lg-laundry-card' +entity: 'sensor.the_dryer_dryer' # Washers work too! +``` + +
+ +- Alternative: Washer picture status card (LG전자 / CC BY (https://creativecommons.org/licenses/by/2.0) for image. Find the images [here](/washerpics/)) +
Hidden, click to expand + +configuration.yaml: +```yaml +sensor: + - platform: template + sensors: + washer_cycle_state: + value_template: '{{state_attr(''sensor.my_washer'', ''remain_time'')}}' + friendly_name: Washer Cycle State + icon_template: 'mdi:washing-machine' +``` +lovelace: +```yaml +cards: + - type: conditional + conditions: + - entity: sensor.my_washer + state: "on" + card: + aspect_ratio: '1' + entity: sensor.washer_cycle_state + image: /local/washerrunning.gif + type: picture-entity + - type: conditional + conditions: + - entity: sensor.my_washer + not_state: "on" + card: + aspect_ratio: '1' + entity: sensor.my_washer + image: /local/washer.jpg + type: picture-entity +type: vertical-stack +``` + +
+ ## Be nice! If you like the component, why don't you support me by buying me a coffee? It would certainly motivate me to further improve this work. diff --git a/washerpics/cardpic.png b/washerpics/cardpic.png new file mode 100644 index 00000000..60b29bf8 Binary files /dev/null and b/washerpics/cardpic.png differ diff --git a/washerpics/washer.jpg b/washerpics/washer.jpg new file mode 100644 index 00000000..cfa219c5 Binary files /dev/null and b/washerpics/washer.jpg differ diff --git a/washerpics/washerrunning.gif b/washerpics/washerrunning.gif new file mode 100644 index 00000000..e97aa2f7 Binary files /dev/null and b/washerpics/washerrunning.gif differ