Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Broken Home Assistant Configuration in HAss Core 2024.8.0 #2433

Closed
F5ancesco opened this issue Aug 8, 2024 · 108 comments · Fixed by #2434
Closed

Broken Home Assistant Configuration in HAss Core 2024.8.0 #2433

F5ancesco opened this issue Aug 8, 2024 · 108 comments · Fixed by #2434

Comments

@F5ancesco
Copy link

F5ancesco commented Aug 8, 2024

Describe the bug

Devices unresponsive under any circumstance, HA asking to reconfigure the extension but nothing happens.

To Reproduce

  1. Update Home Assistant Core to version 2024.8.0.
  2. Check the settings under devices and settings.
  3. Click on Alexa Media Player, check that the integration is working (it's not).
  4. See the error, Home Assistant asks to reconfigure the integration.

Expected behavior

The integration to properly work as it did before updating HAss Core to 2024.8.0.
To show all my echo devices as well as Alexa linked ones.

Screenshots

The UI Error is shown below:
Screenshot 2024-08-08 alle 15 35 14

System details

  • Home-assistant (version): 2024.8.0
  • alexa_media (version from const.py or HA startup): 4.12.6
  • alexapy (version from pip show alexapy or HA startup):
  • Amazon [2FA is enabled]: Yes

Logs

Don't know where to find them! Tried to take a look to the log files but nothing seems to be relevant...

Additional context

@jaypabla
Copy link

jaypabla commented Aug 8, 2024

I have the same issue, tried reauthenticating with amazon.co.uk, but authentication keeps failing. After latest update.

@rosicenko
Copy link

Same here

@Luke001IT
Copy link

Same here, I tried 3 times but no success!

@lluisd
Copy link

lluisd commented Aug 8, 2024

same here

@sca075
Copy link

sca075 commented Aug 8, 2024

"requirements": ["alexapy>=1.28.2", "packaging>=20.3", "wrapt>=1.14.0"], 

This worked, change the manifest.json.

@svalsemey
Copy link

Thank you SO much for this temporary workaround.

@simonjbrake
Copy link

"requirements": ["alexapy>=1.28.2", "packaging>=20.3", "wrapt>=1.14.0"], 

This worked, change the manifest.json.

thank you, work fine !

@sync85
Copy link

sync85 commented Aug 8, 2024

works for me too great info thanks <3

@Shaunpottie
Copy link

Sorry, please can you tell me where to add this: "requirements": ["alexapy>=1.28.2", "packaging>=20.3", "wrapt>=1.14.0"],

@simonjbrake
Copy link

oops,
notify not work

@LorenzoRogai
Copy link

@Shaunpottie you need to SSH inside the machine that has HA installed and locate the data directory like /etc/homeassistant/custom_components/alexa_media/ and then modify that file using a Text Editor like nano

@simonjbrake
Copy link

Sorry, please can you tell me where to add this: "requirements": ["alexapy>=1.28.2", "packaging>=20.3", "wrapt>=1.14.0"],

modify this :
/homeassistant/custom_components/alexa_media/manifest.json

@Shaunpottie
Copy link

Eish, let me see if I come right, thank you.

@Pyxo
Copy link

Pyxo commented Aug 8, 2024

Manifest fix worked for me as well.
Screenshot 2024-08-08 093312

@Shaunpottie
Copy link

Shaunpottie commented Aug 8, 2024

Thank you everyone for the help, I came right.

Working 100% now.

@MiBaSH
Copy link

MiBaSH commented Aug 8, 2024

It works. Thanks s lot

@Aberdon
Copy link

Aberdon commented Aug 8, 2024

@Shaunpottie you need to SSH inside the machine that has HA installed and locate the data directory like /etc/homeassistant/custom_components/alexa_media/ and then modify that file using a Text Editor like nano

Or simply use Studio Code Server from within HA 😉

Thank you @sca075, that worked - I use Alexa Media Player for so many announcements, playing music, etc, I was (temporarily) lost before you gave us a fix 👍

@herden-system
Copy link

With amazon.de it is not working. 😞

@sync85
Copy link

sync85 commented Aug 8, 2024

With amazon.de it is not working. 😞

I´m using amazon.de and its working for me whats your failure?

@herden-system
Copy link

With amazon.de it is not working. 😞

I´m using amazon.de and its working for me whats your failure?

The 500 error

@DSteiNeuro
Copy link

Edited in Studio Code Sever

"requirements": ["alexapy>=1.28.2", "packaging>=20.3", "wrapt>=1.14.0"],

Worked!

Ta!

@sca075
Copy link

sca075 commented Aug 8, 2024

@herden-system I use amazon.de too and work fine, 500 = "Internal Server Error" means that there is some issue on the configuration of your instance, . Probably just reconfigure it and if you did change the mainifest.json as above all should work as usual. Note that it looks it is now required an external (https) url of your home assistant in the config.
Hope this can help :)
Edit: "alexapy==1.28.2" HA will not load properly.. don't know if it was because I mistyped something.

@herden-system
Copy link

@sca075

It was the pulic URL! Thanks a lot!

@handcoding
Copy link

"requirements": ["alexapy>=1.28.2", "packaging>=20.3", "wrapt>=1.14.0"],

@sca075 Hugest of props—that worked perfectly! 🙌🏻

@lbuijk
Copy link

lbuijk commented Aug 8, 2024

"requirements": ["alexapy>=1.28.2", "packaging>=20.3", "wrapt>=1.14.0"], 

This worked, change the manifest.json.

Yep - thanks a lot!

@raymondjstone
Copy link

Can someone explain what public url it is that needed changed? I don't have any settings specific to me in the alexa manifest file so is this somewhere else?

@F5ancesco
Copy link
Author

Guys, that's the first time I've opened an Issue in Github, I didn't think I would get so much effort!

At this time, do you think I should close the Issue? I mean, is resolved no?

@handcoding
Copy link

Guys, that's the first time I've opened an Issue in Github, I didn't think I would get so much effort!
At this time, do you think I should close the Issue? I mean, is resolved no?

@F5ancesco I’d hold off from closing this issue until this fix is baked in to an official update from Alexa Media Player.

@sca075
Copy link

sca075 commented Aug 12, 2024

You need to have Nabu Casa!
Or use Assist

@danielbrunt57
Copy link
Collaborator

danielbrunt57 commented Aug 13, 2024

You need to have Nabu Casa! Or use Assist

No, you don't. I do not use Nabu Casa as my external URL is either url_1 via cloudflare tunnel or url_2 via DDNS to my firewall where I can enable/disable the inbound NAT.

The true flag in that file is set by Nabu Casa indicating that it's successfully logged into and connected to Nabu Casa.

image

Instead of NC, I chose the manual setup method to integrate the Alexa Home Assistant Skill and my alexa file is:

{
  "version": 1,
  "minor_version": 1,
  "key": "alexa",
  "data": {
    "authorized": false
  }

That alexa file has no bearing on or is used/referenced by AMP. I believe all of this public_url stuff is a red herring. As far as I've been able to determine (and I've had my head inside the code a lot deciphering and troubleshooting), the public_url is only used external notification services to know where to contact Home Assistant at when they've been invoked though HA's notification service.

I just re & re'd my integration yet again about 13 hours ago after successfully setting up AMP v4.10.3 in HA 2024.8.1 to double check detailed log files. After reinstalling AMP v4.12.7, everything is working fine.

@petrotti77
Copy link

@danielbrunt57 @sca075

After some testing to solve problems related to AMP integration I found that the “alexa” and “alexa_auth” files contained in homeassistant/.storage/ (which perhaps not everyone has in their configuration) are related to the configuration of Amazon aws to expose HA devices on Alexa. In fact, by removing the “alexa_auth” file (which contains a token) the “alexa” file cannot be changed to true. So I came to the conclusion that they actually have nothing to do with the AMP integration that still doesn't work for me if I upgrade to HA 2024.8.1.

@danielbrunt57
Copy link
Collaborator

danielbrunt57 commented Aug 13, 2024

@danielbrunt57 @sca075

After some testing to solve problems related to AMP integration I found that the “alexa” and “alexa_auth” files contained in homeassistant/.storage/ (which perhaps not everyone has in their configuration) are related to the configuration of Amazon aws to expose HA devices on Alexa. In fact, by removing the “alexa_auth” file (which contains a token) the “alexa” file cannot be changed to true.

That's not completely accurate as those two files are only created when Nabu Casa Cloud is configured to expose HA entities in Alexa. They do not exist with the AWS/Alexa manual skill method of exposing HA entities in Alexa. The only place authentication is configured when manually created is here:

smart_home:
  locale: en-CA
  endpoint: https://api.amazonalexa.com/v3/events
  client_id: amzn1.application-oa2-client.9cef<redacted>f4cd
  client_secret: amzn1.oa2-cs.v1.56b0<redacted>ee0548
  filter:
    include_domains:
      - light
      - cover
      - lock

    include_entities:

So I came to the conclusion that they actually have nothing to do with the AMP integration

that's correct

still doesn't work for me if I upgrade to HA 2024.8.1.

I'm wondering if my method of eliminating the partitioned cookie error might somehow be contributing to my 100% success rate since when not using it, AMP fails to load at restart due to partitioned cookie error but a reload loads it. I'm thinking that the pickled cookie file is in some way different because of that initial cookie error. Could you perhaps try replacing the /usr/local/lib/python3.12/http/cookies.py with the 3.13 version, remove the integration config entry, delete your pickle file, restart HA and try adding the integration again?

@petrotti77
Copy link

@danielbrunt57

How do you perform the procedure you describe? Could you explain it to me?

| Could you perhaps try replacing the /usr/local/lib/python3.12/http/cookies.py with the 3.13 version.

@danielbrunt57
Copy link
Collaborator

@danielbrunt57

How do you perform the procedure you describe? Could you explain it to me?

I usually use Portainer to sh to my homeassistant container but you can also get to it from your terminal ssh via docker exec -it homeassistant bash.

  • cd /usr/local/lib/python3.12/http
  • ls -al and verify you see cookies.py
  • mv cookies.py cookies_3.12.py to save existing.
  • vi cookies.py
  • Press i to enter insert mode
  • Paste contents of new cookies.py
  • Press ESC to exit insert mode
  • Type :wq to exit and write file

cookies.py v3.13:

####
# Copyright 2000 by Timothy O'Malley <[email protected]>
#
#                All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software
# and its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of
# Timothy O'Malley  not be used in advertising or publicity
# pertaining to distribution of the software without specific, written
# prior permission.
#
# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
#
####
#
# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
#   by Timothy O'Malley <[email protected]>
#
#  Cookie.py is a Python module for the handling of HTTP
#  cookies as a Python dictionary.  See RFC 2109 for more
#  information on cookies.
#
#  The original idea to treat Cookies as a dictionary came from
#  Dave Mitchell ([email protected]) in 1995, when he released the
#  first version of nscookie.py.
#
####

r"""
Here's a sample session to show how to use this module.
At the moment, this is the only documentation.

The Basics
----------

Importing is easy...

   >>> from http import cookies

Most of the time you start by creating a cookie.

   >>> C = cookies.SimpleCookie()

Once you've created your Cookie, you can add values just as if it were
a dictionary.

   >>> C = cookies.SimpleCookie()
   >>> C["fig"] = "newton"
   >>> C["sugar"] = "wafer"
   >>> C.output()
   'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'

Notice that the printable representation of a Cookie is the
appropriate format for a Set-Cookie: header.  This is the
default behavior.  You can change the header and printed
attributes by using the .output() function

   >>> C = cookies.SimpleCookie()
   >>> C["rocky"] = "road"
   >>> C["rocky"]["path"] = "/cookie"
   >>> print(C.output(header="Cookie:"))
   Cookie: rocky=road; Path=/cookie
   >>> print(C.output(attrs=[], header="Cookie:"))
   Cookie: rocky=road

The load() method of a Cookie extracts cookies from a string.  In a
CGI script, you would use this method to extract the cookies from the
HTTP_COOKIE environment variable.

   >>> C = cookies.SimpleCookie()
   >>> C.load("chips=ahoy; vienna=finger")
   >>> C.output()
   'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'

The load() method is darn-tootin smart about identifying cookies
within a string.  Escaped quotation marks, nested semicolons, and other
such trickeries do not confuse it.

   >>> C = cookies.SimpleCookie()
   >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
   >>> print(C)
   Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"

Each element of the Cookie also supports all of the RFC 2109
Cookie attributes.  Here's an example which sets the Path
attribute.

   >>> C = cookies.SimpleCookie()
   >>> C["oreo"] = "doublestuff"
   >>> C["oreo"]["path"] = "/"
   >>> print(C)
   Set-Cookie: oreo=doublestuff; Path=/

Each dictionary element has a 'value' attribute, which gives you
back the value associated with the key.

   >>> C = cookies.SimpleCookie()
   >>> C["twix"] = "none for you"
   >>> C["twix"].value
   'none for you'

The SimpleCookie expects that all values should be standard strings.
Just to be sure, SimpleCookie invokes the str() builtin to convert
the value to a string, when the values are set dictionary-style.

   >>> C = cookies.SimpleCookie()
   >>> C["number"] = 7
   >>> C["string"] = "seven"
   >>> C["number"].value
   '7'
   >>> C["string"].value
   'seven'
   >>> C.output()
   'Set-Cookie: number=7\r\nSet-Cookie: string=seven'

Finis.
"""

#
# Import our required modules
#
import re
import string
import types

__all__ = ["CookieError", "BaseCookie", "SimpleCookie"]

_nulljoin = ''.join
_semispacejoin = '; '.join
_spacejoin = ' '.join

#
# Define an exception visible to External modules
#
class CookieError(Exception):
    pass


# These quoting routines conform to the RFC2109 specification, which in
# turn references the character definitions from RFC2068.  They provide
# a two-way quoting algorithm.  Any non-text character is translated
# into a 4 character sequence: a forward-slash followed by the
# three-digit octal equivalent of the character.  Any '\' or '"' is
# quoted with a preceding '\' slash.
# Because of the way browsers really handle cookies (as opposed to what
# the RFC says) we also encode "," and ";".
#
# These are taken from RFC2068 and RFC2109.
#       _LegalChars       is the list of chars which don't require "'s
#       _Translator       hash-table for fast quoting
#
_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~:"
_UnescapedChars = _LegalChars + ' ()/<=>?@[]{}'

_Translator = {n: '\\%03o' % n
               for n in set(range(256)) - set(map(ord, _UnescapedChars))}
_Translator.update({
    ord('"'): '\\"',
    ord('\\'): '\\\\',
})

_is_legal_key = re.compile('[%s]+' % re.escape(_LegalChars)).fullmatch

def _quote(str):
    r"""Quote a string for use in a cookie header.

    If the string does not need to be double-quoted, then just return the
    string.  Otherwise, surround the string in doublequotes and quote
    (with a \) special characters.
    """
    if str is None or _is_legal_key(str):
        return str
    else:
        return '"' + str.translate(_Translator) + '"'


_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
_QuotePatt = re.compile(r"[\\].")

def _unquote(str):
    # If there aren't any doublequotes,
    # then there can't be any special characters.  See RFC 2109.
    if str is None or len(str) < 2:
        return str
    if str[0] != '"' or str[-1] != '"':
        return str

    # We have to assume that we must decode this string.
    # Down to work.

    # Remove the "s
    str = str[1:-1]

    # Check for special sequences.  Examples:
    #    \012 --> \n
    #    \"   --> "
    #
    i = 0
    n = len(str)
    res = []
    while 0 <= i < n:
        o_match = _OctalPatt.search(str, i)
        q_match = _QuotePatt.search(str, i)
        if not o_match and not q_match:              # Neither matched
            res.append(str[i:])
            break
        # else:
        j = k = -1
        if o_match:
            j = o_match.start(0)
        if q_match:
            k = q_match.start(0)
        if q_match and (not o_match or k < j):     # QuotePatt matched
            res.append(str[i:k])
            res.append(str[k+1])
            i = k + 2
        else:                                      # OctalPatt matched
            res.append(str[i:j])
            res.append(chr(int(str[j+1:j+4], 8)))
            i = j + 4
    return _nulljoin(res)

# The _getdate() routine is used to set the expiration time in the cookie's HTTP
# header.  By default, _getdate() returns the current time in the appropriate
# "expires" format for a Set-Cookie header.  The one optional argument is an
# offset from now, in seconds.  For example, an offset of -3600 means "one hour
# ago".  The offset may be a floating point number.
#

_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']

_monthname = [None,
              'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
              'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
    from time import gmtime, time
    now = time()
    year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
    return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \
           (weekdayname[wd], day, monthname[month], year, hh, mm, ss)


class Morsel(dict):
    """A class to hold ONE (key, value) pair.

    In a cookie, each such pair may have several attributes, so this class is
    used to keep the attributes associated with the appropriate key,value pair.
    This class also includes a coded_value attribute, which is used to hold
    the network representation of the value.
    """
    # RFC 2109 lists these attributes as reserved:
    #   path       comment         domain
    #   max-age    secure      version
    #
    # For historical reasons, these attributes are also reserved:
    #   expires
    #
    # This is an extension from Microsoft:
    #   httponly
    #
    # This dictionary provides a mapping from the lowercase
    # variant on the left to the appropriate traditional
    # formatting on the right.
    _reserved = {
        "expires": "expires",
        "path": "Path",
        "comment": "Comment",
        "domain": "Domain",
        "max-age": "Max-Age",
        "secure": "Secure",
        "httponly": "HttpOnly",
        "version": "Version",
        "samesite": "SameSite",
        "partitioned": "Partitioned",
    }

    _flags = {'secure', 'httponly', 'partitioned'}

    def __init__(self):
        # Set defaults
        self._key = self._value = self._coded_value = None

        # Set default attributes
        for key in self._reserved:
            dict.__setitem__(self, key, "")

    @property
    def key(self):
        return self._key

    @property
    def value(self):
        return self._value

    @property
    def coded_value(self):
        return self._coded_value

    def __setitem__(self, K, V):
        K = K.lower()
        if not K in self._reserved:
            raise CookieError("Invalid attribute %r" % (K,))
        dict.__setitem__(self, K, V)

    def setdefault(self, key, val=None):
        key = key.lower()
        if key not in self._reserved:
            raise CookieError("Invalid attribute %r" % (key,))
        return dict.setdefault(self, key, val)

    def __eq__(self, morsel):
        if not isinstance(morsel, Morsel):
            return NotImplemented
        return (dict.__eq__(self, morsel) and
                self._value == morsel._value and
                self._key == morsel._key and
                self._coded_value == morsel._coded_value)

    __ne__ = object.__ne__

    def copy(self):
        morsel = Morsel()
        dict.update(morsel, self)
        morsel.__dict__.update(self.__dict__)
        return morsel

    def update(self, values):
        data = {}
        for key, val in dict(values).items():
            key = key.lower()
            if key not in self._reserved:
                raise CookieError("Invalid attribute %r" % (key,))
            data[key] = val
        dict.update(self, data)

    def isReservedKey(self, K):
        return K.lower() in self._reserved

    def set(self, key, val, coded_val):
        if key.lower() in self._reserved:
            raise CookieError('Attempt to set a reserved key %r' % (key,))
        if not _is_legal_key(key):
            raise CookieError('Illegal key %r' % (key,))

        # It's a good key, so save it.
        self._key = key
        self._value = val
        self._coded_value = coded_val

    def __getstate__(self):
        return {
            'key': self._key,
            'value': self._value,
            'coded_value': self._coded_value,
        }

    def __setstate__(self, state):
        self._key = state['key']
        self._value = state['value']
        self._coded_value = state['coded_value']

    def output(self, attrs=None, header="Set-Cookie:"):
        return "%s %s" % (header, self.OutputString(attrs))

    __str__ = output

    def __repr__(self):
        return '<%s: %s>' % (self.__class__.__name__, self.OutputString())

    def js_output(self, attrs=None):
        # Print javascript
        return """
        <script type="text/javascript">
        <!-- begin hiding
        document.cookie = \"%s\";
        // end hiding -->
        </script>
        """ % (self.OutputString(attrs).replace('"', r'\"'))

    def OutputString(self, attrs=None):
        # Build up our result
        #
        result = []
        append = result.append

        # First, the key=value pair
        append("%s=%s" % (self.key, self.coded_value))

        # Now add any defined attributes
        if attrs is None:
            attrs = self._reserved
        items = sorted(self.items())
        for key, value in items:
            if value == "":
                continue
            if key not in attrs:
                continue
            if key == "expires" and isinstance(value, int):
                append("%s=%s" % (self._reserved[key], _getdate(value)))
            elif key == "max-age" and isinstance(value, int):
                append("%s=%d" % (self._reserved[key], value))
            elif key == "comment" and isinstance(value, str):
                append("%s=%s" % (self._reserved[key], _quote(value)))
            elif key in self._flags:
                if value:
                    append(str(self._reserved[key]))
            else:
                append("%s=%s" % (self._reserved[key], value))

        # Return the result
        return _semispacejoin(result)

    __class_getitem__ = classmethod(types.GenericAlias)


#
# Pattern for finding cookie
#
# This used to be strict parsing based on the RFC2109 and RFC2068
# specifications.  I have since discovered that MSIE 3.0x doesn't
# follow the character rules outlined in those specs.  As a
# result, the parsing rules here are less strict.
#

_LegalKeyChars  = r"\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\="
_LegalValueChars = _LegalKeyChars + r'\[\]'
_CookiePattern = re.compile(r"""
    \s*                            # Optional whitespace at start of cookie
    (?P<key>                       # Start of group 'key'
    [""" + _LegalKeyChars + r"""]+?   # Any word of at least one letter
    )                              # End of group 'key'
    (                              # Optional group: there may not be a value.
    \s*=\s*                          # Equal Sign
    (?P<val>                         # Start of group 'val'
    "(?:[^\\"]|\\.)*"                  # Any doublequoted string
    |                                  # or
    \w{3},\s[\w\d\s-]{9,11}\s[\d:]{8}\sGMT  # Special case for "expires" attr
    |                                  # or
    [""" + _LegalValueChars + r"""]*      # Any word or empty string
    )                                # End of group 'val'
    )?                             # End of optional value group
    \s*                            # Any number of spaces.
    (\s+|;|$)                      # Ending either at space, semicolon, or EOS.
    """, re.ASCII | re.VERBOSE)    # re.ASCII may be removed if safe.


# At long last, here is the cookie class.  Using this class is almost just like
# using a dictionary.  See this module's docstring for example usage.
#
class BaseCookie(dict):
    """A container class for a set of Morsels."""

    def value_decode(self, val):
        """real_value, coded_value = value_decode(STRING)
        Called prior to setting a cookie's value from the network
        representation.  The VALUE is the value read from HTTP
        header.
        Override this function to modify the behavior of cookies.
        """
        return val, val

    def value_encode(self, val):
        """real_value, coded_value = value_encode(VALUE)
        Called prior to setting a cookie's value from the dictionary
        representation.  The VALUE is the value being assigned.
        Override this function to modify the behavior of cookies.
        """
        strval = str(val)
        return strval, strval

    def __init__(self, input=None):
        if input:
            self.load(input)

    def __set(self, key, real_value, coded_value):
        """Private method for setting a cookie's value"""
        M = self.get(key, Morsel())
        M.set(key, real_value, coded_value)
        dict.__setitem__(self, key, M)

    def __setitem__(self, key, value):
        """Dictionary style assignment."""
        if isinstance(value, Morsel):
            # allow assignment of constructed Morsels (e.g. for pickling)
            dict.__setitem__(self, key, value)
        else:
            rval, cval = self.value_encode(value)
            self.__set(key, rval, cval)

    def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
        """Return a string suitable for HTTP."""
        result = []
        items = sorted(self.items())
        for key, value in items:
            result.append(value.output(attrs, header))
        return sep.join(result)

    __str__ = output

    def __repr__(self):
        l = []
        items = sorted(self.items())
        for key, value in items:
            l.append('%s=%s' % (key, repr(value.value)))
        return '<%s: %s>' % (self.__class__.__name__, _spacejoin(l))

    def js_output(self, attrs=None):
        """Return a string suitable for JavaScript."""
        result = []
        items = sorted(self.items())
        for key, value in items:
            result.append(value.js_output(attrs))
        return _nulljoin(result)

    def load(self, rawdata):
        """Load cookies from a string (presumably HTTP_COOKIE) or
        from a dictionary.  Loading cookies from a dictionary 'd'
        is equivalent to calling:
            map(Cookie.__setitem__, d.keys(), d.values())
        """
        if isinstance(rawdata, str):
            self.__parse_string(rawdata)
        else:
            # self.update() wouldn't call our custom __setitem__
            for key, value in rawdata.items():
                self[key] = value
        return

    def __parse_string(self, str, patt=_CookiePattern):
        i = 0                 # Our starting point
        n = len(str)          # Length of string
        parsed_items = []     # Parsed (type, key, value) triples
        morsel_seen = False   # A key=value pair was previously encountered

        TYPE_ATTRIBUTE = 1
        TYPE_KEYVALUE = 2

        # We first parse the whole cookie string and reject it if it's
        # syntactically invalid (this helps avoid some classes of injection
        # attacks).
        while 0 <= i < n:
            # Start looking for a cookie
            match = patt.match(str, i)
            if not match:
                # No more cookies
                break

            key, value = match.group("key"), match.group("val")
            i = match.end(0)

            if key[0] == "$":
                if not morsel_seen:
                    # We ignore attributes which pertain to the cookie
                    # mechanism as a whole, such as "$Version".
                    # See RFC 2965. (Does anyone care?)
                    continue
                parsed_items.append((TYPE_ATTRIBUTE, key[1:], value))
            elif key.lower() in Morsel._reserved:
                if not morsel_seen:
                    # Invalid cookie string
                    return
                if value is None:
                    if key.lower() in Morsel._flags:
                        parsed_items.append((TYPE_ATTRIBUTE, key, True))
                    else:
                        # Invalid cookie string
                        return
                else:
                    parsed_items.append((TYPE_ATTRIBUTE, key, _unquote(value)))
            elif value is not None:
                parsed_items.append((TYPE_KEYVALUE, key, self.value_decode(value)))
                morsel_seen = True
            else:
                # Invalid cookie string
                return

        # The cookie string is valid, apply it.
        M = None         # current morsel
        for tp, key, value in parsed_items:
            if tp == TYPE_ATTRIBUTE:
                assert M is not None
                M[key] = value
            else:
                assert tp == TYPE_KEYVALUE
                rval, cval = value
                self.__set(key, rval, cval)
                M = self[key]


class SimpleCookie(BaseCookie):
    """
    SimpleCookie supports strings as cookie values.  When setting
    the value using the dictionary assignment notation, SimpleCookie
    calls the builtin str() to convert the value to a string.  Values
    received from HTTP are kept as strings.
    """
    def value_decode(self, val):
        return _unquote(val), val

    def value_encode(self, val):
        strval = str(val)
        return strval, _quote(strval)

@danielbrunt57
Copy link
Collaborator

The primary change in the file is the addition of the "partitioned" key:

class Morsel(dict):
    """A class to hold ONE (key, value) pair.

    In a cookie, each such pair may have several attributes, so this class is
    used to keep the attributes associated with the appropriate key,value pair.
    This class also includes a coded_value attribute, which is used to hold
    the network representation of the value.
    """
    # RFC 2109 lists these attributes as reserved:
    #   path       comment         domain
    #   max-age    secure      version
    #
    # For historical reasons, these attributes are also reserved:
    #   expires
    #
    # This is an extension from Microsoft:
    #   httponly
    #
    # This dictionary provides a mapping from the lowercase
    # variant on the left to the appropriate traditional
    # formatting on the right.
    _reserved = {
        "expires": "expires",
        "path": "Path",
        "comment": "Comment",
        "domain": "Domain",
        "max-age": "Max-Age",
        "secure": "Secure",
        "httponly": "HttpOnly",
        "version": "Version",
        "samesite": "SameSite",
        "partitioned": "Partitioned",
    }

@sca075
Copy link

sca075 commented Aug 14, 2024

@danielbrunt57

Thanks for the really detailed explanation.
The alexa media player then is not using to check if Nabu exposed the entities.. this is the important thing here.

The auth process works fine so for the most the @petrotti77 issue looks to be a specific scenario.

Do you need some specific data to analyze it?

@danielbrunt57
Copy link
Collaborator

Thanks for the really detailed explanation. The alexa media player then is not using to check if Nabu exposed the entities.. this is the important thing here.

The auth process works fine so for the most the @petrotti77 issue looks to be a specific scenario.

@sca075 Correct. AMP is only scanning amazon for alexa media capable devices (like Echo) in Alexa.
The Alexa Smart Home skill is unrelated to AMP and enables seeing HA entities in the cloud. Home Assistant Cloud (Nabu Casa) makes that skill automatically available versus having to do all of the steps of creating a skill manually.

Do you need some specific data to analyze it?

I'm confused as to what your issue is now...

@danielbrunt57
Copy link
Collaborator

danielbrunt57 commented Aug 14, 2024

I've set my system options like this which seems to eliminate the API error: Too many requests which shuts down AMP requiring a reload to restart:

image

@petrotti77
Copy link

Good morning guys. I have reread the posts you have published these days. I sleep back once again to HA 2024.7.4 after a lot of frustrations to repair automations and zigbee devices that don't work after the downgrade and upgrade procedure.

@danielbrunt57 I still haven't figured out if the pickled cookie file procedure you describe you used it in your configuration or it's just a suggestion to try it out, because I'm really terrified to update even though I'm aware that I can't stay forever at HA 2024.7.4.

@danielbrunt57 You were telling me that you need specific data to analyze it. What would you need to understand what happened in my setup?

@danielbrunt57
Copy link
Collaborator

@petrotti77

You were telling me that you need specific data to analyze it. What would you need to understand what happened in my setup?

I'm just totally confused as to what your current issue is with 2024.8.1...

I still haven't figured out if the pickled cookie file procedure you describe you used it in your configuration or it's just a suggestion to try it out, because I'm really terrified to update even though I'm aware that I can't stay forever at HA 2024.7.4.

It's not a pickled cookie file procedure. It addresses the partitioned cookie error which requires always reloading AMP after a restart to get it going by replacing the cookies.py file with a newer version from a pending PR for Python 3.13 beta and is safe if you replace the file with the complete and correct 3.13 cookies.py file. I once did not but was able to successfully recover from my error! I have documented how to use amitfin's Patch custom component to automate the procedure, which others have successfully implemented. If you are interested, I can repost it here...

@LindsayReid
Copy link

LindsayReid commented Aug 15, 2024 via email

@danielbrunt57
Copy link
Collaborator

danielbrunt57 commented Aug 15, 2024

@LindsayReid I understand your frustration but am doing my best to figure this whole mess out. My installation is working fine and am doing my utmost to assist others in achieving the same results that I have here.

@petrotti77
Copy link

An info, who is left at HA 2024.7.4 which version of AMP are you using?

@danielbrunt57 So to understand you are using HA 2024.8.1 with AMP 4.12.7?

@danielbrunt57
Copy link
Collaborator

@danielbrunt57 So to understand you are using HA 2024.8.1 with AMP 4.12.7?

Yes.

@messere63
Copy link

messere63 commented Aug 16, 2024

I have prepared a script that centralizes the access to alexa_media.
At the beginning, the script restart alexa_media_player integration and, subsequently, a wait of 3 seconds.

sequence:

  • data:
    entry_id: your_alexa_integration_entry_id
    action: homeassistant.reload_config_entry
  • delay:
    hours: 0
    minutes: 0
    seconds: 3
    milliseconds: 0

I found my entry_id inside the file: /config/.storage/core.config_entries
.storage is an idden directory inside homeassistant main dir.
You have to go using terminal/ssh utility.

bye

@petrotti77
Copy link

@danielbrunt57

But you performed the procedure to replace python3.12/http/cookies.py with version 3.13?

@messere63
Copy link

@danielbrunt57

But you performed the procedure to replace python3.12/http/cookies.py with version 3.13?

No Daniel, this is the only procedure that run without errors.

@petrotti77
Copy link

@messere63 But with what automation do you activate the script. Could you explain it?

@messere63
Copy link

I do this with any automation. For example when the power absorbed in my house exceeds the maximum or when an alarm is triggered.

@petrotti77
Copy link

@messere63 I didn't understand?

@CGU1969
Copy link

CGU1969 commented Aug 23, 2024

Now it works for me for two days and without restarting AMP. I used it to announce sensor states via TTS, and after the update I had to restart AMP every few minutes.

  • I don't use 2FA, (I don't know if this may be a problem or not)
  • I update cookies.py,
  • And I have the AMP system options like this:

image

@messere63
Copy link

It works fine !! Thanks a lot !

@sca075
Copy link

sca075 commented Aug 24, 2024

Guys did update AMP to the last version.. it works perfectly as it was with the v4.12.7.. I think it is clear that for those that still have problems authenticating the issue is clearly something different. The Team here did an amazing job. Thanks a lot guys. 🥇

@petrotti77
Copy link

@CGU1969

Could you please answer the following questions?

  1. Could you explain to me how to access AMP without using 2FA?

  2. To update cookies.py do you need to use the danielbrunt57 guide posted here?

  3. For the update of cookies.py do you need to repeat the procedure with each update of HA?

Thank you.

@CGU1969
Copy link

CGU1969 commented Aug 25, 2024

@petrotti77

  1. Could you explain to me how to access AMP without using 2FA?
    I delete on my Amazon profile the 2FA validations, and don't put anything in the 2FA fields configurations.
    So you can try with 2FA enabled and say to us if work or not.
    Sorry, but I preffer don't change nothing, and test with 2FA in my instalation, because it's working, and I say, if it's working, don't tocuch anything.

  2. To update cookies.py do you need to use the @danielbrunt57 guide posted here?
    Yes, I do. And @danielbrunt57 thanks a lot for you work

  3. For the update of cookies.py do you need to repeat the procedure with each update of HA?
    I don't known at this moment, I don't update HA after I did this changes

I hope this can help you.
Regards.

@petrotti77
Copy link

@CGU1969

Good evening, for now I stayed on HA 2024.7.4 and I have only removed the 2FA validation in my Amazon account and updated AMP from 4.12.5 to 4.12.11 without making any changes to the cookies.py file. Everything seems to work but I'm a little afraid to update to HA 2024.8.3.

@LetThatSinkIn
Copy link

"requirements": ["alexapy>=1.28.2", "packaging>=20.3", "wrapt>=1.14.0"], 

This worked, change the manifest.json.

What was this before you made the change? Trying to determine if my from/to would be the same now in October as it was back in August.

@danielbrunt57
Copy link
Collaborator

What was this before you made the change? Trying to determine if my from/to would be the same now in October as it was back in August.

There is no need to edit alexapy in manifest.json. The current version of alexapy is 1.29.2 and that's what the last several releases of AMP have in manifest.json.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.