WallPanel w moim wykonaniu

Jestem krótko na tym forum, jak i w ekosystemie Home Assistanta i mam nadzieją że mogę się pochwalić WallPanel w moim wykonaniu. Nie wiem tylko czy to dobre miejsce.
Strona główna

Starowanie ogrzewaniem


Na razie trochę mało integracji, ale dopiero od miesiąca walczę z HA, a czasu też nie jest tyle ile by się chciało.
Wykonanie: aplikacja WallPanel, tablet Huawei MediaPad T3 10". Mocowanie do ściany – etui obrotowe za 19 zł – bardzo łatwo odłączyć plecki, dwie śrubki i gotowe. Muszę jeszcze tylko dokupić kątowe złącze zasilania.

3 polubienia

Ja zrobiłem sobie trochę maksymalistyczny dashboard, który na jednej stronie pokazuje wszystkie stany głównych encji. Wygląda trochę jak zestawy kontrolek na samochodowym kokpicie… Dodatkowo “kontrolki” zmieniają kolor lub sposób animacji w zależności od wartości encji…

Całość obsługuję z panelu LCD lodówki Samsung:

Wszystko, co widzicie na panelu (poza badge’ami i kartą zegara), zrobiłem wyłącznie przy użyciu custom: button-card

EDIT:
Ponieważ dostałem mnóstwo próśb na PW o udostępnienie kodu ‘samochodowych kontrolek’, ochoczo dzielę się efektem swojej pracy. Poniższy kod obejmuje tylko kartę [Gabinet], ale karty pozostałych pomieszczeń są zrobione - co do zasady - w identyczny sposób:

type: custom:button-card
name: Gabinet
icon: mdi:desktop-classic
style: |
  ha-card:hover {
  transform: scale(1.05);
  }
tap_action:
  action: navigate
  navigation_path: gabinet
extra_styles: |
  @keyframes obrot {100%{transform:rotatez(360deg)}}
styles:
  grid:
    - grid-template-areas: >-
        "i temp" "i wilg" "n n" "klim klim" "okna okna" "swiatlo swiatlo" "ruch
        ruch"
    - grid-template-columns: 1fr 1fr
    - grid-template-rows: 1fr min-content min-contentfr1
  card:
    - font-size: 13px
    - width: 120px
    - height: 120px
    - margin: 3px
    - border-radius: 10%
    - background-color: var(--primary-background-color)
    - box-shadow: 0px 0px 10px cyan, 0px 0px 10px blue
  name:
    - align-self: start
    - justify-self: start
    - justify-items: left
    - padding-bottom: 50%
    - padding-left: 5%
  img_cell:
    - justify-content: start
    - align-items: start
    - padding-left: 0px
    - padding-bottom: 10%
    - margin: 7px
  icon:
    - color: var(--paper-item-icon-active-color)
    - align-self: middle
    - height: 70%
    - top: 10%
    - left: 20%
    - justify-items: middle
    - padding-left: 5%
  custom_fields:
    temp:
      - color: orange
      - position: absolute
      - top: 10%
      - left: 48%
      - align-self: start
      - justify-self: left
      - font-size: 10px
      - padding-bottom: 2px
    wilg:
      - color: dodgerblue
      - position: absolute
      - top: 30%
      - left: 48%
      - align-self: start
      - justify-self: left
      - font-size: 10px
      - padding-bottom: 2px
    klim:
      - background-color: var(--primary-background-color)
      - position: absolute
      - top: 50%
      - left: 2%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 20px
      - width: 20px
      - color: |
          [[[
            if (states["climate.haier_ac1"].state == 'heat') return 'crimson'
            if (states["climate.haier_ac1"].state == 'cool') return 'dodgerblue'
            if (states["climate.haier_ac1"].state == 'dry') return 'orange'
            if (states["climate.haier_ac1"].state == 'fan') return 'aqua'
            if (states["climate.haier_ac1"].state == 'auto') return 'darkorchid'
            else return 'dimgray'
          ]]]
      - animation: |
          [[[
            if ((states["climate.haier_ac1"].state == 'heat') || (states["climate.haier_ac1"].state == 'cool') || (states["climate.haier_ac1"].state == 'dry') || (states["climate.haier_ac1"].state == 'fan') || (states["climate.haier_ac1"].state == 'auto')) return 'blink 2s infinite'
            else return 'null'
          ]]]
    grzej:
      - background-color: var(--primary-background-color) ]]]
      - position: absolute
      - top: 50%
      - left: 22%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 20px
      - width: 20px
      - color: |
          [[[
            if (states["climate.grzejnik_gabinet"].state == 'heat') return 'magenta'
            else return 'dimgray'
          ]]]
    okna:
      - background-color: var(--primary-background-color)
      - position: absolute
      - top: 50%
      - left: 42%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 20px
      - width: 20px
      - color: |
          [[[
            if (states["group.okna_gabinet"].state == 'on') return 'turquoise'
            else return 'dimgray'
          ]]]
      - animation: |
          [[[
            if (states["group.okna_gabinet"].state == 'on') return 'blink 0.5s infinite'
            else return 'null'
          ]]]
    swiatlo:
      - background-color: var(--primary-background-color)
      - position: absolute
      - top: 50%
      - left: 62%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 20px
      - width: 20px
      - color: |
          [[[
            if (states["group.oswietlenie_gabinet"].state == 'on') return 'gold'
            else return 'dimgray'
          ]]]
      - animation: |
          [[[
            if (states["group.oswietlenie_gabinet"].state == 'on') return 'blink 2s infinite'
            else return 'null'
          ]]]
    ruch:
      - background-color: var(--primary-background-color)
      - position: absolute
      - top: 50%
      - left: 82%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 20px
      - width: 20px
      - color: |
          [[[
            if (states["binary_sensor.czujnik_gabinet_occupancy"].state == 'on') return 'lime'
            else return 'dimgray'
          ]]]
      - animation: |
          [[[
            if (states["binary_sensor.czujnik_gabinet_occupancy"].state == 'on') return 'blink 2s infinite'
            else return 'null'
          ]]]
    philips:
      - background-color: var(--primary-background-color)
      - position: absolute
      - top: 65%
      - left: 2%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 17px
      - width: 20px
      - color: |
          [[[
            if ((states["media_player.philips_tam8905"] != 'undefined') && (states["media_player.philips_tam8905"] != 'unavailable'))
              {if (states["media_player.philips_tam8905"].state == 'playing') return 'lime'
              else return 'dimgray'}
            else return 'dimgray'
          ]]]
      - animation: |
          [[[
            if ((states["media_player.philips_tam8905"] != 'undefined') && (states["media_player.philips_tam8905"] != 'unavailable'))
              if (states["media_player.philips_tam8905"].state == 'playing') return 'rotating 2.5s linear infinite'
          ]]]
    dysk_nas:
      - background-color: var(--primary-background-color) ]]]
      - position: absolute
      - top: 65%
      - left: 22%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 20px
      - width: 20px
      - color: |
          [[[
            if (states["switch.dysk_sieciowy_nas"].state == 'on') return 'lime'
            else return 'dimgray'
          ]]]
      - animation: |
          [[[
            if (states["switch.dysk_sieciowy_nas"].state == 'on') return 'blink 2s infinite'
            else return 'null'
          ]]]
    lampa_bankiera:
      - background-color: var(--primary-background-color)
      - position: absolute
      - top: 65%
      - left: 42%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 20px
      - width: 20px
      - color: |
          [[[
            if (states["light.lampa_bankiera"].state == 'on') return 'gold'
            else return 'dimgray'
          ]]]
      - animation: |
          [[[
            if (states["light.lampa_bankiera"].state == 'on') return 'blink 2s infinite'
            else return 'null'
          ]]]
    biurko:
      - background-color: var(--primary-background-color)
      - position: absolute
      - top: 65%
      - left: 62%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 20px
      - width: 20px
      - color: |
          [[[
            if (states["switch.office_desk"].state == 'on') return 'gold'
            else return 'dimgray'
          ]]]
      - animation: |
          [[[
            if (states["switch.office_desk"].state == 'on') return 'blink 2s infinite'
            else return 'null'
          ]]]
    online:
      - background-color: var(--primary-background-color)
      - position: absolute
      - top: 65%
      - left: 82%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 20px
      - width: 20px
      - color: |
          [[[
            if (states["switch.lan_hub_ii_pietro"].state == 'on') return 'lime'
            else return 'crimson'
          ]]]
      - animation: |
          [[[
            if (states["switch.lan_hub_ii_pietro"].state == 'off') return 'blink 0.5s infinite'
              else return 'null'
          ]]]
    filtr_powietrza:
      - background-color: var(--primary-background-color)
      - position: absolute
      - top: 80%
      - left: 2%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 20px
      - width: 20px
      - color: |
          [[[
            if (states["fan.filtr_gabinet"].state == 'sleep') return 'lime'
            if (states["fan.filtr_gabinet"].state == 'auto') return 'magenta'
            if (states["fan.filtr_gabinet"].state == 'favorite') return 'dodgerblue'
            if (states["fan.filtr_gabinet"].state == 'off') return 'dimgray'
          ]]]
      - animation: |
          [[[
            if (states["fan.filtr_gabinet"].state != 'off') return 'blink 2s ease infinite'
            else return 'null'
          ]]]
    nawilzacz:
      - background-color: var(--primary-background-color)
      - position: absolute
      - top: 80%
      - left: 22%
      - padding-bottom: 0px
      - border-radius: 50%
      - height: 20px
      - width: 20px
      - color: |
          [[[
            if (states["humidifier.nawilzacz_gabinet"].state == 'on') return 'dodgerblue'
            else return 'dimgray'
          ]]]
      - animation: |
          [[[
            if (states["humidifier.nawilzacz_gabinet"].state != 'off') return 'blink 2s ease infinite'
            else return 'null'
          ]]]
custom_fields:
  temp: |
    [[[
      return `<ha-icon
        icon="mdi:thermometer"
        style="width: 22px; height: 18px;">
        </ha-icon>${states['sensor.termometr_gabinet_temperature'].state } °C`
    ]]]
  wilg: |
    [[[
      return `<ha-icon
        icon="mdi:water-percent"
        style="width: 22px; height: 18px;">
        </ha-icon>${states['sensor.termometr_gabinet_humidity'].state } %`
    ]]]
  klim: |
    [[[
      return `<ha-icon
        icon="mdi:air-conditioner"
        style="width: 20px; height: 14px;">
        </ha-icon>`
    ]]]
  grzej: |
    [[[
      return `<ha-icon
        icon="mdi:radiator"
        style="width: 20px; height: 14px;">
        </ha-icon>`
    ]]]
  okna: |
    [[[
      return `<ha-icon
        icon="mdi:window-closed-variant"
        style="width: 20px; height: 14px;">
        </ha-icon>`
    ]]]
  swiatlo: |
    [[[
      return `<ha-icon
        icon="mdi:ceiling-light-multiple"
        style="width: 20px; height: 14px;">
        </ha-icon>`
    ]]]  
  ruch: |
    [[[
      return `<ha-icon
        icon="mdi:run"
        style="width: 20px; height: 14px;">
        </ha-icon>`
    ]]]
  philips: |
    [[[
      if (states["media_player.philips_tam8905"].state != 'undefined')
      {return `<ha-icon
        icon="mdi:spotify"
        style="width: 20px; height: 15px;">
        </ha-icon>`}
    ]]]
  dysk_nas: |
    [[[
      return `<ha-icon
        icon="mdi:nas"
        style="width: 20px; height: 14px;">
        </ha-icon>`
    ]]]
  lampa_bankiera: |
    [[[
      return `<ha-icon
        icon="mdi:desk-lamp"
        style="width: 20px; height: 14px;">
        </ha-icon>`
    ]]]
  biurko: |
    [[[
      return `<ha-icon
        icon="mdi:desktop-classic"
        style="width: 20px; height: 14px;">
        </ha-icon>`
    ]]]  
  online: |
    [[[
      return `<ha-icon
        icon="mdi:router-wireless"
        style="width: 20px; height: 14px;">
        </ha-icon>`
    ]]]
  filtr_powietrza: |
    [[[
      return `<ha-icon
        icon="mdi:air-purifier"
        style="width: 20px; height: 14px;">
        </ha-icon>`
    ]]]
  nawilzacz: |
    [[[
      return `<ha-icon
        icon="mdi:air-humidifier"
        style="width: 20px; height: 14px;">
        </ha-icon>`
    ]]]
5 polubień

Z widoków encji wnioskuję, że wszystko masz podłączone do HA, musisz jeszcze tylko Seata zintegrować :grin:.
W lazience masz chyba (?) grzejnik elektryczny, jak nim sterujesz? Zwykle smart gniazdko?

W łazience, poza klasycznym grzejnikiem z głowicą zigbee, mam dodatkowo elektryczne ogrzewanie podłogowe, dotychczas sterowane dedykowanym sterownikiem wifi ale z powodu braku możliwości jego intergracji z HA właśnie przechodzę na zwykły switch zigbee, którego programowanie szybko ogarnę sobie w NR…

Z resza już ogarnąłem:

Górny flow to elektryczne ogrzewanie podłogowe, oba dolne sterują klasycznym ogrzewaniem gazowym.

Jaki masz model? Jak upchnąłeś tam cokolwiek innego niż soft badziew Samsunga?

To jest Samsung RF56N9740SG
Ponieważ soft pokładowy jest rzeczywiście badziewny (nicniemający i zniczymniegadający TIZEN), swoją instancję obsługuję przez… wbudowaną przeglądarke internetową. Oczywiście serwer HA musiałem sobie wystawić na zewnątrz (znacznie dalej, niż tylko poza lodówkę :rofl: :rofl: :rofl: )

Niestety, moja Altea nie ma takich sensorów, które dałoby się zaciągnąć do HA… :wink:

Też tak kiedyś myślałem o moim oplu :grin:

1 polubienie

@Sputnik jak te dane powyciągałeś z opelka?

Jest trochę kombinacji, zadanie dla wytrwałych, z cyklu przecież się da :smile:
Tak na szybko - mam w samochodzie stację z androidem ale można też na telefonie. Potrzebny jakiś interfejs obd, ja mam icar. Na androidzie zainstalowałem torque, taka apka co łączy się przez bluetooth z icarem i czyta błędy w aucie ale ma jeszcze dużo innych możliwości, można np. ustawić żeby różne parametry logowała do pliku csv. I teraz już mamy prosto :wink: instalujemy jeszcze taskera do tego dodatek MQTT Publisher Plugin, taskerem wyciągamy dane z csv i przez MQTT wysyłamy do HA. Potem już zabawa w node red żeby to obrobić. Chwile się cieszymy że działa a potem zapominamy i do niczego nie używamy, tak przynajmniej jest u mnie :grin:

Myślałem, że może masz moduł LWPM a w nim bluetooth i jak wjedziesz do garażu to zczytuje Ci dane z łopla :wink:
Da się jednak też inaczej co pokazałeś :wink:

@Sputnik - szacun, niezła rzeźba.

Gratulacje! Szczególnie za estetykę - to naprawdę ładnie wygląda i nie ma druciarstwa.

Jak rozwiązałeś kwestie zasilania? Podpięcie cały czas do ładowarki nie służy takim sprzętom.

Kiedyś rozważałem następujące rozwiązanie - apka na tablet monitorująca do HA stan baterii, zaś ładowarka podpięta do sterowanego gniazdka. Czyli: jak bateria się naładuje to odłączamy zasilanie i działa na baterii, jak stan baterii spadnie np do 5% to włączamy zasilanie i ładujemy do 100%. Ale skończyło się na etapie koncepcji - niemniej tak bym zrobił gdybym robił :slight_smile:

Zarówno aplikacja WallPanel jak i aplikacja HA zainstalowana na telefonie/ tablecie udostępnia stan baterii. Automatyzacja w Node-Red zapewnia ładowania. Jedna uwaga - nie od 5 do 100%. Baterie bardzo nie lubią głębokiego rozładowania i pełnego naładowania. Szkoły są różne, ja mam 20 / 80%.

1 polubienie

Jak zrobiłeś ładowanie baterii w Node-Red? Możesz pokazać kawałek takiego kodu? No chyba, że źle zrozumiałem to zdanie… na razie rozumiem je dosłownie.

Tego także nie bardzo rozumiem… czy to oznacza, że np. do czujnika zasilanego baterią ciągniesz kabel z ładowarki? Nie wyobrażam sobie pajęczyny kabli np. na oknie ładujących baterie w czujnikach otwarcia…
Co więcej, nie wyobrażam sobie takiego rozwiązania także dla tabletu z panelem HA. Bateria w tablecie byłaby permanentnie rozładowywana i ładowana z powrotem. I tak “dookoła wojtek”, To zarżnęłoby ją na amen prawdopodobnie już po kilku tygodniach, bo każdy cykl ładowania skraca przecież jej żywotność…

Kiedyś Nicola Tesla próbował dostarczać energię el. na odległość bezprzewodowo. Jesteś na tropie?

Te śmieszki naprawdę niepotrzebne… kwestia konieczności nieładowania akumulatorów litowych do pełna była poruszana chyba miliony razy (no może nie na tym forum), mimo to warto przypominać, że ładowanie tych aku w fazie CV jest tym co najbardziej skraca im żywotność, więc warto ograniczać ładowanie do 70% czy 80% deklarowanej pojemności (trzeba pamiętać, że odmian aku litowych różniących się chemią jest przynajmniej kilka jeśli nie kilkanaście, a wszystkie wrzucone do jednego wora, mimo, że różnią się nieco ich charakterystyki, dodatkowo w przypadku akumulatorów zintegrowanych z urządzeniem sporo zależy od tego, jak producent sprzętu zaprojektował i zaprogramował układ ładowania - wyższe napięcie końcowe daje finalnie wyższą pojemność i to nawet sporo, w zamian za znaczne skrócenie trwałości aku).
Układy ładowania niektórych laptopów istotnie domyślnie ustawiają utrzymanie naładowania aku w trybie stałego zasilania sieciowego na przedział 70-80%.
Niektórzy producenci umożliwiają modyfikowanie tego przedziału.

Natomiast kwestia głębokiego rozładowania jest taka, że typowe urządzenie nie powinno w ogóle dopuścić do wystąpienia takiej sytuacji, a w przypadku wystąpienia powinno zablokować możliwość ponownego naładowania.
Świetny artykulik na ten temat
https://lygte-info.dk/info/BatteryLowVoltage%20UK.html

Proste rozwiązanie: ładowarka tabletu jest podłączona do smart gniazdka, tablet zintegrowany jest z HA czyli widzisz stan naładowania baterii tabletu. Teraz prosta automatyzacja: na podstawie stopnia naładowania baterii robisz on/off smart gniazdka. Zamiast smart gniazdka możesz użyć USB Smart Adaptor, żadne dodatkowe kabelki nie są potrzebne.
P.S. Dokładnie to rozwiązanie zastosuje także u siebie :slight_smile:.

Rozwiązanie jest oczywiste ale dla mnie byłoby nie do zaakceptowania. U mnie w domu bowiem UI HA musi być aktywny 24/7, bo korzystają z niego wszyscy domownicy. Służy on także do podglądu obrazów z kamer z monitoringu. Stąd koniecznośc stałego jego zasilania. Dlatego nie mogłem zrozumieć sensu podpinania go do gniazda smart i tworzenia automatyzacji na ładowanie jego baterii…
U mnie panel HA stanowi fabryczny tablet lodówki, zasilanej 24/7, więc nie ma problemu z koniecznością ładowania baterii.