First stab at Appdeamon

In your automation you are assigning the room variable and then using it to assign the entity_id.

room = "media_player.office_tv__dish"
entity_id = room

To make them function the same, you could have done this for the script parameters for the automation (I believe - not sure if this is actually allowed but functionally this may help you see how they are different):

{"entity_id":"media_player.office_tv__dish","channel":"1"}

in appdaemon you call a service and you give the values for the datafields.
you dont fill the variables that you create in a template (those are not visible from outside HA)

the service has some vars to fill, in this case source and entity_Id

thats why the error says: invalid service data

I’m not sure I quite understand what you are attempting. If you want to run the script you need to use the call_service api. I’m not sure what turning on a script does.

Personally, I would move whatever the script does to appdaemon.

its also possible to use turn on for scripts. (i needed to look it up too :wink: )
but i also would call_service or do all parts in AD.

Hello @toddw65,

I think it should be

self.turn_on("script.dish_keys", data = {channel = "1", room = "media_player.office_tv__dish"})

If I remember correctly, I have come across this problem before, but not using appdaemon, but when I newly started with python scripts before moving to appdaemon. Try the above, or

self.turn_on("script.dish_keys", data = {'channel' : '1', 'room' : 'media_player.office_tv__dish'})

I think one of them should solve it. It is expecting only one key which is data in my opinion. So you pass your dictionary within it.

Hope it helps.

Thanks guys. Figured it out. My goal was to combine this project and this project and thanks to you guys I was finally successful.

Now I can say, “Hey Google. Change the office tv to channel 132.” or “Ok Google. Change the master bedroom tv to channel 205.”

Not much really, but I’m happy with it.

Here’s what I ended up with:

import appdaemon.plugins.hass.hassapi as hass

#
# Change Channel on Dish Receiver using BroadLink IR
# To call it, fire a change_channel event with a "channel" and a "room" param
#

class SetChannel(hass.Hass):
    def initialize(self):
        self.log("SetChannel loaded")
        self.listen_event(self.do_change, 'change_channel')

    def do_change(self, event_name, data, kwargs): 
        delay = 0        
        for c in str(data["channel"]):
            if delay == 0:
                self.do_channel({"channel":c, "room":data["room"]})
            else:
                self.run_in(self.do_channel, delay, channel = c, room = data["room"])
            delay += 1

    def do_channel(self, kwargs):
        self.call_service("media_player/select_source", source = "chan_0"+kwargs["channel"], entity_id = "media_player."+kwargs["room"]+"_tv__dish")
4 Likes

Hi, do you mind giving a proper instruction for appdeamon noobs like me?
What you’re doing is exactly what I intend to with changing TV channels at different rooms

Well, keeping in mind that I am an AD newbie myself, here’s what I did:

First I set up Vassilis’ cool custom component per this thread. This creates a media player component that you use to change channels by using the ‘media_player.select_source’ service. For example, in the case of my Dish Network Joey, I have channels 0-9 set up like this:

chan_01 = JgBIAA3EDF0LXQxdDDYLXQxdC10LXg1cDFwMXQtdC14LXgtdDVwLxQxeDFwMXQs2C10MXQxdC10NXAtdC14MXQxcDF0LXQteDAANBQ==
chan_02 = JgBIAAzFDF0NWwteCzYLXg00DF0LXQxdC10MXQxdDFwOWwtdDF0Mww5dC10MXQs2C14LNgxdDVsMXQtdDF0MXQtdDF0LXQxdDAANBQ==
chan_03 = JgBIAAvFDV0MXAtdDDYLNgxdC10MXQxdC10LXgtdDF0LXQxdC14LxgxdC10LXQw2CzYMXQxdDVsMXQtdDF0MXQtdDF0MXAteDQANBQ==
chan_04 = JgBIAA3GDlsLXQs3DFwMXA5bDF0NWwxdC10OWw1cDFwMXQtdDlsMxQxdC10OMg5bDlwLXQxdC10MXQxdDFwMXQtdDlsNXAtdDAANBQ==
chan_05 = JgBIAAzGC10MXQs2C14LXQw1DF0NXAtdDF0LXQxdDF0MXAxdC10NxQtdDVwLNgteC10NNA1cDVwLXQ1cC10MXQxdDFwMXQxdDAANBQ==
chan_06 = JgBIAA7FDF0MXAw2DFwLNwtdDF0MWg5dC14LXQxdC14LXQteC10NxAxdC10MNgtdDDYLXQxdDF0NWwteC10NXAxdC10NXAtdDAANBQ==
chan_07 = JgBIAAvFDF0MXQs2DDYMXAxdC10NXAxdC10LXgtdDF0MXQtdC14LxgteDFwLNgw2C10MXQxdC10MXQtdC14MXQxcC14LXQteCwANBQ==
chan_08 = JgBIAAvGDF0LXQw2DTQNXAs2C10MXQxdC10MXQtdDF0NXAtdDF0LxgxdC10MNgw1C14LNgtdDF0NXAxcDVwLXQxdDF0NWwxdCwANBQ==
chan_09 = JgBIAAzFDF0MXQ00DDYLNgteDFwLXQ1cDVwLXgtdC10MXQxdDFwMxQxdDF0LNgw2DDUMXQtdC14LXQxdC14NWwtdDF0MXQtdDAANBQ==
chan_00 = JgBIAA3EDF0MNQxdC10MXQw1DVwMXQtdC14LXQ5bC14MXAtdDF0NxAxdCzYMXQxcDF0MNQxdDF0LXQteC10MXQ1cDFwLXgtdDQANBQ==

And my media player is set up like:

- platform: broadlink
  name: Office TV & Dish
  host: 192.168.0.132
  mac: 'XX:XX:XX:XX:XX:XX'
  ircodes_ini: '/devices/broadlink/broadlink_media_codes/office.ini'

Next I set up an IFTTT applet that uses webhooks and the HASS API like this:

CaptureCapture2Capture3

The IFTTT applet calls a HASS script (change_channel.yaml) that consists of:

alias: "Change the Dish channel"
sequence:
  - service: python_script.set_channel
    data_template:
      channel: "{{channel}}"
      room: "{{room}}"

This, in turn, calls the HASS python script (set_channel.py) below:

channel = data.get('channel')
#for some reason IFTT passes the word "the" when sending "Change the $ tv to channel #" so strip out the word "the"
room = data.get('room').replace('the ', '')
#replace any spaces in the room name with underscores
room = room.replace(" ","_")
hass.bus.fire("change_channel", {"channel": channel, "room": room})

Once the change channel event fires, it’s picked up by the appdaemon script (set_channel.py) below:

import appdaemon.plugins.hass.hassapi as hass

#
# Change Channel on Dish Receiver
# To call it, fire a change_channel event with a "channel" and a "room" param
#

class SetChannel(hass.Hass):
    def initialize(self):
        self.log("SetChannel loaded")
        self.listen_event(self.do_change, 'change_channel')

    def do_change(self, event_name, data, kwargs): 
        delay = 1      
        for c in str(data["channel"]):
            if delay == 0:
                self.do_channel({"channel":c, "room":data["room"]})
            else:
                self.run_in(self.do_channel, delay, channel = c, room = data["room"])
            delay += 1
            
    def do_channel(self, kwargs):
        self.call_service("media_player/select_source", source = "chan_0"+kwargs["channel"], entity_id = "media_player."+kwargs["room"]+"_tv__dish")

which then calls the media_player.select_source service sending the concatenated source and entity_id.

It’s a real Rube Goldberg machine that I’m sure can be improved upon a lot but it works well.
Hopefully that covers it all. Good luck!

1 Like

Thanks for the detailed instructions!
So far, I’ve already setup custom component, media player & iftt
Just wanna make sure I get it right for the HASS scripts & appdaemon scripts,

HASS python script --> /.homeassistant/python_scripts/set_channel.py
appdaemon scripts --> /appdaemon/conf/apps/set_channel.py
For change_channel script in configuration.yaml :
–> change_channel: !include change_channel.yaml
–> /.homeassistant/change_channel.yaml)

Yep. This looks correct and don’t forget to add to your apps.yaml for AD.

set channel:
  module: set_channel
  class: SetChannel

Hi, just one more thing, which phrase should I use for ifttt?
Should I use the fourth option since there’s numbers & text.

Yes. You’ll want the fourth one. Like “Change the $ tv to channel #”

Thanks, I’m just getting error with invalid config for the script

 Configuration invalid
Testing configuration at /home/homeassistant/.homeassistant
ERROR:homeassistant.loader:Unable to find component change_channel
ERROR:homeassistant.setup:Setup failed for change_channel: Component not found.
Failed config
  General Errors: 
    - Component not found: change_channel
    - Setup failed for change_channel: Component not found.

I’m not quite sure how to fix this. anything else I might be missing?

Looking back at what you said earlier, I’m not sure this is correct. I’m assuming you have other scripts besides this one so if those are in a single scripts.yaml file, the config would look like:

script: !include scripts.yaml

and then the change_channel script would be in that file.

I’m curious if this could be used to search the Dish guide and automate my television to turn to a certain station on days my favorite sports teams are playing? That would be awesome

it is possible, but i wouldnt try it like the way it is done here.

you can do it with 1 app without the need for hass scripts and hass python scripts.

if you have a mediaplayer in your HA that can change channels for your TV, you can do that from appdaemon
and you can search any webpage for anything with python too.

but it would involve some programming.

I do have a harmony hub that I can change channels with, its figuring out how to search the guide on dish and tell harmony to change the channel at a certain date and time.

Its probably way over my head at this point but Ill research it a bit, it would be very cool to do!

Thanks for your reply ReneTode! :slight_smile:

i got several apps that get information from the internet.
ill give you one so you can look at it.

this app searches 2 websites for waterlevels from the river i live by.
then the app creates sensors in HA with that info.

in your case i would create a simular app.
it should search your guide and then create sensors for the next time a channel should be changed.
in another app you could then make use from the created sensors.

here it is:

###########################################################################################
#                                                                                         #
#  Rene Tode ( [email protected] )                                                            #
#                                                                                         #
#  2017/09/04 Germany                                                                     #
#                                                                                         #
#                                                                                         #
#  create waterlevel sensors                                                              #
#                                                                                         #
###########################################################################################

import appdaemon.plugins.hass.hassapi as hass
import datetime
from socket import timeout
import time
import requests
from bs4 import BeautifulSoup

class waterstanden(hass.Hass):

  def initialize(self):
    self.url = "http://www.hochwasser-rlp.de/vorhersage-wasserstaende/uebersicht/flussgebiet/mosel"
    self.wsv_url = "https://www.pegelonline.wsv.de/gast/pegelinformationen?scrollPosition=0&gewaesser=MOSEL&order_flusskm_desc.x=7&order_flusskm_desc.y=6"
    self.orten = {"trier": "TRIER UP","perl": "PERL","zeltingen":"ZELTINGEN UP","cochem":"COCHEM"}
    runtime = datetime.time(12, 37, 12)
    self.timestamp = datetime.datetime.now()
    self.run_hourly(self.checknow,runtime)
    self.checknow(self)


  def checknow(self,kwargs):
    actual_time = datetime.datetime.now()
    data = self.get_RLP_values()
    if data == "no data":
      self.get_wsv_values()
      for ort,wsv_ort in self.orten.items():
        self.set_state("sensor.voorspelling6_waterstand_" + ort, state = "geen", attributes = {"friendly_name": "6 uurs voorspelling"})
        self.set_state("sensor.voorspelling9_waterstand_" + ort, state = "geen", attributes = {"friendly_name": "9 uurs voorspelling"})
        self.set_state("sensor.voorspelling24_waterstand_" + ort, state = "geen", attributes = {"friendly_name": "24 uurs voorspelling"})

  def get_RLP_values(self):
    try:
      response = requests.get(self.url,timeout=10)
      page = response.content
      soup = BeautifulSoup(page, "html.parser")
      table = soup("table", class_="datatable wasserstaende")
      rows = table[0]("tr")
      #self.log("got data from RLP")
    except:
      return "no data"
    mosel = rows[1]("td")
    mosel_prev_time = mosel[1].text
    mosel_last_time = mosel[2].text
    counter = 2
    while counter < 6:
      ortsline = rows[counter]("td")
      if not "-" in ortsline[2].text:
        self.set_state("sensor.waterstand_" + ortsline[0].text.lower(), state = ortsline[2].text, attributes = {"friendly_name":mosel_last_time,"tijd": mosel_last_time,"website": "RLP"})
      if not "-" in ortsline[1].text:
        self.set_state("sensor.vorige_waterstand_" + ortsline[0].text.lower(), state = ortsline[1].text, attributes = {"friendly_name":mosel_prev_time,"tijd": mosel_prev_time,"website": "RLP"})
      #self.log(self.get_state("sensor.vorige_waterstand_" + ortsline[0].text.lower()))
      try:
        prev_level = int(self.get_state("sensor.vorige_waterstand_" + ortsline[0].text.lower()))
        actual_level = int(self.get_state("sensor.waterstand_" + ortsline[0].text.lower()))
        state = actual_level - prev_level
      except:
        state = "onbekend"
      self.set_state("sensor.verschil_waterstand_" + ortsline[0].text.lower(), state = state, attributes = {"friendly_name":"verschil","tijd": mosel_last_time,"website": "RLP"}) 
      counter = counter + 1
    #self.log(waterlevels)
    try:
      table = soup("table", class_="datatable vorhersagen")
      rows = table[0]("tr")
      times = rows[0]("th")
      time1=times[1].text.split(":")
      sixhourtime = time1[1][4:]+":"+time1[2][:-4]
      sixhourtime = sixhourtime.replace(".","-")
      time2=times[2].text.split(":")
      ninehourtime = time2[1][4:]+":"+time2[2][:-4]
      ninehourtime = ninehourtime.replace(".","-")
      time3=times[3].text.split(":")
      twentyfourhourtime = time3[1][4:]+":"+time3[2][:-4]
      twentyfourhourtime = twentyfourhourtime.replace(".","-")
      counter = 2
      while counter < 6:
        ortsline = rows[counter]("td") 
        self.set_state("sensor.voorspelling6_waterstand_" + ortsline[0].text.lower(), state = self.get_prediction_str(ortsline[0].text.lower(),ortsline[1].text), attributes = {"friendly_name": sixhourtime,"tijd": sixhourtime})
        self.set_state("sensor.voorspelling9_waterstand_" + ortsline[0].text.lower(), state = self.get_prediction_str(ortsline[0].text.lower(),ortsline[3].text), attributes = {"friendly_name": ninehourtime,"tijd": ninehourtime})
        self.set_state("sensor.voorspelling24_waterstand_" + ortsline[0].text.lower(), state = self.get_prediction_str(ortsline[0].text.lower(),ortsline[5].text), attributes = {"friendly_name": twentyfourhourtime,"tijd": twentyfourhourtime})
        counter = counter + 1
      return "sensors set"
    except:
      return "no data"

  def get_prediction_str(self,ort,raw_prediction):
    try:
      predictionparts = raw_prediction.split("-")
      predictionaverage = int((int(predictionparts[0]) + int(predictionparts[1]))/2)
      predictionrange = int((int(predictionparts[0]) - int(predictionparts[1]))/2)
      actual = int(self.get_state("sensor.waterstand_" + ort))
      if predictionrange < 0:
        predictionrange = -(predictionrange)
      predictiondifference = int(predictionaverage - actual)
      predictionstr = str(predictiondifference) + " +- " + str(predictionrange)       
    except:
      predictionstr = "onbekend"
    return predictionstr

  def get_wsv_values(self):
    #self.log("no rlp so get wsv")
    try:
      try:
          response = requests.get(self.wsv_url,timeout=10)
      except:
          self.log("wsv timeout")
          return
      page = response.content
      soup = BeautifulSoup(page, "html.parser")
      table = soup("table")
      body = table[1]("tbody")
      rows = body[0]("tr")
      for counter in range(1,12):
        cellen = rows[counter]("td")
        for ort,wsv_ort in self.orten.items():
          if cellen[0].text == wsv_ort:
            datestr = cellen[4].text
            timestr = cellen[5].text.strip()
            waterlevel = cellen[6].text.strip()
            waterlevel = waterlevel[:-2].strip()
            prevtime = ""
            prevlevel = ""
            prevtime2 = ""
            prevlevel2 = ""
            if self.entity_exists("sensor.waterstand_" + ort):
              prevlevel = self.get_state("sensor.waterstand_" + ort)
              prevtime = self.get_state("sensor.waterstand_" + ort,attribute = "tijd")
            if self.entity_exists("sensor.vorige_waterstand_" + ort):
              prevlevel2 = self.get_state("sensor.vorige_waterstand_" + ort)
              prevtime2 = self.get_state("sensor.vorige_waterstand_" + ort,attribute = "tijd")
            if prevtime != timestr :
              #self.log("waterstand niveau in HA gevonden: " + ort + " : " + prevtime + " : " + prevlevel + ", " + prevlevel2 + " > sensor gezet")
              self.set_state("sensor.waterstand_" + ort, state = waterlevel, attributes = {"friendly_name":timestr,"tijd": timestr,"website": "WSV"})
              if prevtime2 != "" and prevtime != "" and prevlevel != "":
                self.set_state("sensor.vorige_waterstand_" + ort, state = prevlevel, attributes = {"friendly_name":prevtime,"tijd": prevtime,prevtime2: prevlevel2,"website": "WSV"})
                difference = int(int(waterlevel) - int(prevlevel))
                self.set_state("sensor.verschil_waterstand_" + ort, state = difference, attributes = {"friendly_name":"verschil","tijd": timestr,"website": "WSV"})
              elif prevtime != "" and prevlevel != "":
                self.set_state("sensor.vorige_waterstand_" + ort, state = prevlevel, attributes = {"friendly_name":prevtime,"tijd": prevtime,"website": "WSV"})
      #self.log("got data from WSV")         
    except:
      self.log("error in getting WSV data")



  def make_utf(self, date):
    new_date = date.replace("januari","januar")
    new_date = new_date.replace("februari","februar")
    new_date = new_date.replace("maart","march")
    new_date = new_date.replace("mei","may")
    new_date = new_date.replace("juni","june")
    new_date = new_date.replace("juli","july")
    new_date = new_date.replace("augustus","august")
    new_date = new_date.replace("oktober","october")
    return datetime.datetime.strptime(new_date, "%d %B %Y")

there are probably a lot of things that can be done better. this app is over a year old and i have learned a lot of new stuff in python since then. but it works so i dont care if it is pretty or best written :wink:

edit: and sorry that there are no comments, it wasnt written to share :wink:

Thanks again for sharing this, I haven
t used this service yet so Im sure it will take some time to figure it out but now I have a direction to go! I really appreciate you sharing it with me! Thanks Again!

1 Like

yea, this is way over my head. maybe sooner or later Dish Network will have a hass component.