AppDaemon / appdaemon

:page_facing_up: Python Apps for Home Automation
Other
850 stars 418 forks source link

Add support for getting shopping list from HA #667

Open sriramsv opened 5 years ago

sriramsv commented 5 years ago

HA has an inbuilt shopping list which can be queried using the HA api. I would like to have a method get_shopping_list() which can be used in automations in AD.

ReneTode commented 5 years ago

do you have an url how you can query that list?

dlashua commented 5 years ago

@sriramsv Sadly, Home Assistant has not provided documentation for this API (neither the REST API nor the Websocket API) and pieces of it are not accessible via Home Assistant States/Services/Events.

That being said, you should still be able to call "shopping_list/add_item" and "shopping_list/complete_item" via AppDaemon's "call_service" method, though I've not tested this as I don't use the Shopping List feature.

Without available documentation for this feature, adding support for it will require reverse engineering the API. It's not overly complex, but, it will still require some time.

AppDaemon Developers: This is the only reference I can find for this service: https://www.home-assistant.io/components/shopping_list/

Though, adding it as a component in HASS, I am able to see websocket calls like the following:

type: "shopping_list/items"
id: 47

complete: true
id: 46
item_id: "ce190babec9546c8981a7e0e78bd1cc1"
type: "shopping_list/items/update"

id: 57
name: "soda"
type: "shopping_list/items/add"

and responses like this:

result: {name: "soda", id: "6f2642ad45054c88b13ddfa0f684a3b9", complete: false}
success: true
type: "result"
ReneTode commented 5 years ago

thats why i asked for an API url. because he said it can be queried, so i guess he has done that.

sriramsv commented 5 years ago

@ReneTode @dlashua yes, the shopping_list can be got by doing a GET on <hass_url>/api/shopping_list

sriramsv commented 5 years ago

I have a sample implementation here:


import appdaemon.plugins.hass.hassapi as hass
import requests
from globals import IntentRegistry

class ShoppingList(hass.Hass):
    def initialize(self):
        IntentRegistry.register("shopping-list-add", self.add_shopping_list)
        IntentRegistry.register("shopping-list-rm", self.rm_shopping_list)
        IntentRegistry.register("shopping-list-get", self.show_shopping_list)
        IntentRegistry.register("shopping-list-clear", self.clear_shopping_list)
        self.add_service = "shopping_list/add_item"
        self.complete_service = "shopping_list/complete_item"
        self.hass_url = self.args.get("hass_url")
        self.current_list = []
        self.hass_token = self.args.get("hass_token")
        self.timer = self.run_every(self.get_shopping_list, datetime.now(), 2 * 60)

    def get_shopping_list(self, kwargs):
        url = "{}/api/shopping_list".format(self.hass_url)
        headers = {"Authorization": "Bearer {}".format(self.hass_token)}
        result = requests.get(url, headers=headers)
        if result.status_code != 200:
            raise Exception("Error fetching current shopping list from HASS")

        self.current_list = []
        for item in result.json():
            if item.get("complete"):
                continue
            itemName = item.get("name")
            if self.item_exists(itemName):
                continue
            self.current_list.append(itemName.lower())

    def add_shopping_list(self, item):
        self.log(item)
        if self.item_exists(item):
            return "{} already exists".format(item)
        self.call_service(self.add_service, name=item)
        self.run_in(self.get_shopping_list, 2)
        return "added {} to shopping list".format(item)

    def rm_shopping_list(self, item):
        self.log(item)
        self.call_service(self.complete_service, name=item)
        self.run_in(self.get_shopping_list, 2)
        return "removed {} from shopping list".format(item)

    def item_exists(self, item):
        return item.lower() in self.current_list

    def show_shopping_list(self):
        if self.current_list == []:
            return "No items on list currently"
        s = ""
        for index, item in enumerate(self.current_list):
            s += "\n{}) {}".format(index + 1, item)
        return s

    def clear_shopping_list(self):
        count = len(self.current_list)
        for item in self.current_list:
            self.call_service(self.complete_service, name=item)
        self.run_in(self.get_shopping_list, 2)
        return "cleared {} items".format(count)

    def terminate(self):
        self.cancel_timer(self.timer)
ReneTode commented 5 years ago

i never understand why people want to add something to appdaemon if they already have a working function for it. ;) nor do i see what you can automate with a shoppinglist (or at least i dont want to automate anything when it come to shopping ;) )

i have no idea how it should be added to AD though. because its not an entity, or event or service. i guess it could be translated to an entity, allthough i dont suspect that there will be a lot of people using it for automations at all.

if someone came to me up front i would suggest using a sensor or an input_text or an input_select. which are available in AD and HA.

sriramsv commented 5 years ago

well, the answer is ease of maintenance and making AD more feature rich. There is a lot of stuff you can automate, make HA send you a telegram message whenever you are entering the grocery store is so convenient.

ReneTode commented 5 years ago

but the question is if the path that HA did chose is the right one ;)

like i said, there are other and in my eyes better ways to maintain something like that. AD is not always better of by following HA, sometimes its better to give an alternative.

and i think you can have an alternative without making use of the HA shoppinglist. which would probably better fit the AD way.

and i am not sure if its a good automation when you go to a store and you get a shoppinglist send to you when you enter there. to much things can go wrong there. in that case its better if the automation saves the list on your mobile regularly, on a certain place where you can reach it. i dont want to be in situations like: 1) hmm i cant buy anything in this store because HA wont send me my shoppinglist, i need to go to the store HA knows 2) hmm, thats to bad, i have no internet, now i dont know what to buy 3) hmm, HA should have send me my shoppinglist, is something wrong with HA, internet, my network, my automations, etc?

so convenience?? i think there are way more convenient ways, that are way more reliable. and more features wont make it automaticly more rich.

i can think of a thousand convenience functions that will make 1 person happy. but implementing those 1000 functions would make all other people sad, because AD would use way more memory and cpu.

so every convenience that is added, there is always the question if its worth it. you request something that you yourself already got working. so one of us needs to go do work to save you a few lines of code, so its available for others. so the biggest question in this is: will others benifit? because you already got it, so you dont really benifit.

or do we say, hmm please share your app with others. and if a lot of people want to use it, we are going to try to implement it.

and if you ask which way i tend too? i would say: i suspect its something thats rarely used. if you figure out how it should be implemented in AD and make a PR, i think @acockburn will consider it. but i dont recommend any use of time from our devs to implement this.

but thats my personal view. and Andrew always has the last word in everything (which makes it easy for me to hide :P)

i hope my opinion isnt to dissapointing. but its just an opinion ;)

dlashua commented 5 years ago

@sriramsv What is this?

from globals import IntentRegistry

Regardless, it looks like you've cooked up a nice implementation of this. We'll leave it to andrew if he wants this API to be exposed in AppDaemon. If so, your app is a great starting point.

And, I've always wanted a shopping list in Home Assistant/App Daemon. Ideally one that integrates with Todoist, since that's what I use normally. But, even for that, this is a great start.

ReneTode commented 5 years ago

@dlashua why dont you use an input_select if you want a shopping list. a list is a list isnt it? or an input_text containing the list? or just any python list.

sriramsv commented 5 years ago

@sriramsv What is this?

from globals import IntentRegistry

Regardless, it looks like you've cooked up a nice implementation of this. We'll leave it to andrew if he wants this API to be exposed in AppDaemon. If so, your app is a great starting point.

And, I've always wanted a shopping list in Home Assistant/App Daemon. Ideally one that integrates with Todoist, since that's what I use normally. But, even for that, this is a great start.

@dlashua That is my implementation of the dialogflow webhook to NLP. Whenever an intent is matched, AD will look for a callback attached to that intent and execute it and send response back to dialogflow. I have hooked up both my own bot in Google Assistant and also telegram so that I can directly execute AD code by voice :P.

dlashua commented 5 years ago

@sriramsv that sounds interesting! I'd love to see that code if you have the time to put it up on github somewhere. It could be very useful.

dlashua commented 5 years ago

@ReneTode speaking about Home Assistant alone, input_select doesn't work well because adding items to that list after HA has started get weird. An input_text could certainly work though, in Home Assistant alone, that gets tedious to manage without a custom_component (at which point I would no longer need to use an input_text). A Python list would work just fine if implemented in AppDaemon (it would be fairly trivial, really) but the UI elements to add and check off items would take some extra effort. I think the drive to use the in-built HA shopping list is that the UI elements are already present.

Going with an AD (and Dashboard) solution would be a bit easier, I think, but there's still more work to do in UI (custom widget I imagine) to accompany the "just a python list" in the app.

ReneTode commented 5 years ago

its just about putting it in an input_text in AD, and then add a widget that displays the input_text in a certain way. i remembered that the HA input_text might not be the best thing thoug, because its only 255 characters. so an input_select with a special widget would be a better way i think. because that is already a list with options to delete, sort, add and select.